@ -1,3 +1,5 @@ | |||
build | |||
dist | |||
.vscode | |||
.vscode | |||
*.elf | |||
*.nacp | |||
*.nro |
@ -0,0 +1,140 @@ | |||
// Kosmos Updater | |||
// Copyright (C) 2019 Steven Mattera | |||
// | |||
// This program is free software; you can redistribute it and/or | |||
// modify it under the terms of the GNU General Public License | |||
// as published by the Free Software Foundation; either version 2 | |||
// of the License, or (at your option) any later version. | |||
// | |||
// This program 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 General Public License for more details. | |||
// | |||
// You should have received a copy of the GNU General Public License | |||
// along with this program; if not, write to the Free Software | |||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
#include "AssetManager.hpp" | |||
#include "SceneDirector.hpp" | |||
using namespace std; | |||
namespace ku { | |||
void AssetManager::dealloc() { | |||
if (AssetManager::large_button_font != NULL) | |||
TTF_CloseFont(AssetManager::large_button_font); | |||
if (AssetManager::button_font != NULL) | |||
TTF_CloseFont(AssetManager::button_font); | |||
if (AssetManager::subbody_font != NULL) | |||
TTF_CloseFont(AssetManager::subbody_font); | |||
if (AssetManager::body_font != NULL) | |||
TTF_CloseFont(AssetManager::body_font); | |||
if (AssetManager::header_font != NULL) | |||
TTF_CloseFont(AssetManager::header_font); | |||
if (AssetManager::icon != NULL) | |||
SDL_DestroyTexture(AssetManager::icon); | |||
if (AssetManager::downloading != NULL) | |||
SDL_DestroyTexture(AssetManager::downloading); | |||
if (AssetManager::checkmark != NULL) | |||
SDL_DestroyTexture(AssetManager::checkmark); | |||
if (AssetManager::handheld != NULL) | |||
SDL_DestroyTexture(AssetManager::handheld); | |||
if (AssetManager::y_button != NULL) | |||
SDL_DestroyTexture(AssetManager::y_button); | |||
if (AssetManager::x_button != NULL) | |||
SDL_DestroyTexture(AssetManager::x_button); | |||
if (AssetManager::b_button != NULL) | |||
SDL_DestroyTexture(AssetManager::b_button); | |||
if (AssetManager::a_button != NULL) | |||
SDL_DestroyTexture(AssetManager::a_button); | |||
} | |||
bool AssetManager::initialize() { | |||
Result rc; | |||
setsysGetColorSetId(&AssetManager::theme); | |||
if (AssetManager::theme == ColorSetId_Light) { | |||
AssetManager::background = { 235, 235, 235, 255 }; | |||
AssetManager::sidebard_background = { 240, 240, 240, 255 }; | |||
AssetManager::header_footer_divider = { 45, 45, 45, 255 }; | |||
AssetManager::header_bullet = { 121, 121, 121, 255 }; | |||
AssetManager::list_divider = { 205, 205, 205, 255 }; | |||
AssetManager::active_player_indicator = { 158, 228, 0, 255 }; | |||
AssetManager::player_indicator = { 125, 125, 125, 255 }; | |||
AssetManager::selected_background = { 253, 253, 253, 255 }; | |||
AssetManager::selected_border_1 = { 0, 255, 196, 255 }; | |||
AssetManager::selected_border_2 = { 22, 146, 197, 255 }; | |||
AssetManager::modal_faded_background = { 18, 27, 36, 229 }; | |||
AssetManager::modal_background = { 240, 240, 240, 255 }; | |||
AssetManager::text = { 45, 45, 45, 255 }; | |||
AssetManager::active_text = { 50, 80, 240, 255 }; | |||
AssetManager::disabled_text = { 165, 165, 165, 255 }; | |||
} else { | |||
AssetManager::background = { 45, 45, 45, 255 }; | |||
AssetManager::sidebard_background = { 51, 51, 51, 255 }; | |||
AssetManager::header_footer_divider = { 255, 255, 255, 255 }; | |||
AssetManager::header_bullet = { 160, 160, 160, 255 }; | |||
AssetManager::list_divider = { 77, 77, 77, 255 }; | |||
AssetManager::active_player_indicator = { 158, 228, 0, 255 }; | |||
AssetManager::player_indicator = { 125, 125, 125, 255 }; | |||
AssetManager::selected_background = { 31, 34, 39, 255 }; | |||
AssetManager::selected_border_1 = { 0, 255, 196, 255 }; | |||
AssetManager::selected_border_2 = { 22, 146, 197, 255 }; | |||
AssetManager::modal_faded_background = { 18, 27, 36, 229 }; | |||
AssetManager::modal_background = { 70, 70, 70, 255 }; | |||
AssetManager::text = { 255, 255, 255, 255 }; | |||
AssetManager::active_text = { 0, 255, 196, 255 }; | |||
AssetManager::disabled_text = { 125, 125, 125, 255 }; | |||
} | |||
rc = plGetSharedFontByType(&AssetManager::standardFontData, PlSharedFontType_Standard); | |||
if (R_FAILED(rc)) | |||
return false; | |||
AssetManager::header_font = TTF_OpenFontRW(SDL_RWFromMem(AssetManager::standardFontData.address, AssetManager::standardFontData.size), 1, 28); | |||
AssetManager::body_font = TTF_OpenFontRW(SDL_RWFromMem(AssetManager::standardFontData.address, AssetManager::standardFontData.size), 1, 23); | |||
AssetManager::subbody_font = TTF_OpenFontRW(SDL_RWFromMem(AssetManager::standardFontData.address, AssetManager::standardFontData.size), 1, 18); | |||
if (!AssetManager::header_font || !AssetManager::body_font) | |||
return false; | |||
rc = plGetSharedFontByType(&AssetManager::extendedFontData, PlSharedFontType_NintendoExt); | |||
if (R_FAILED(rc)) | |||
return false; | |||
AssetManager::button_font = TTF_OpenFontRW(SDL_RWFromMem(AssetManager::extendedFontData.address, AssetManager::extendedFontData.size), 1, 25); | |||
if (!AssetManager::button_font) | |||
return false; | |||
AssetManager::large_button_font = TTF_OpenFontRW(SDL_RWFromMem(AssetManager::extendedFontData.address, AssetManager::extendedFontData.size), 1, 70); | |||
if (!AssetManager::large_button_font) | |||
return false; | |||
return true; | |||
} | |||
void AssetManager::setRenderColor(SDL_Color color) { | |||
SDL_SetRenderDrawColor(SceneDirector::renderer, color.r, color.g, color.b, color.a); | |||
} | |||
SDL_Texture * AssetManager::loadAsset(string file) { | |||
string themeDirectory = (AssetManager::theme == ColorSetId_Light) ? "light" : "dark"; | |||
string path = "romfs:/" + themeDirectory + "/" + file; | |||
SDL_Surface * image = IMG_Load(path.c_str()); | |||
SDL_Texture * texture = SDL_CreateTextureFromSurface(SceneDirector::renderer, image); | |||
SDL_FreeSurface(image); | |||
return texture; | |||
} | |||
} |
@ -0,0 +1,71 @@ | |||
// Kosmos Updater | |||
// Copyright (C) 2019 Steven Mattera | |||
// | |||
// This program is free software; you can redistribute it and/or | |||
// modify it under the terms of the GNU General Public License | |||
// as published by the Free Software Foundation; either version 2 | |||
// of the License, or (at your option) any later version. | |||
// | |||
// This program 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 General Public License for more details. | |||
// | |||
// You should have received a copy of the GNU General Public License | |||
// along with this program; if not, write to the Free Software | |||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
#pragma once | |||
#include <SDL2/SDL.h> | |||
#include <SDL2/SDL_image.h> | |||
#include <SDL2/SDL_ttf.h> | |||
#include <string> | |||
#include <switch.h> | |||
namespace ku { | |||
class AssetManager { | |||
public: | |||
/* Textures */ | |||
static inline SDL_Texture * a_button = NULL; | |||
static inline SDL_Texture * b_button = NULL; | |||
static inline SDL_Texture * x_button = NULL; | |||
static inline SDL_Texture * y_button = NULL; | |||
static inline SDL_Texture * handheld = NULL; | |||
static inline SDL_Texture * checkmark = NULL; | |||
static inline SDL_Texture * downloading = NULL; | |||
static inline SDL_Texture * icon = NULL; | |||
/* Colors */ | |||
static inline ColorSetId theme; | |||
static inline SDL_Color background; | |||
static inline SDL_Color sidebard_background; | |||
static inline SDL_Color header_footer_divider; | |||
static inline SDL_Color header_bullet; | |||
static inline SDL_Color list_divider; | |||
static inline SDL_Color active_player_indicator; | |||
static inline SDL_Color player_indicator; | |||
static inline SDL_Color selected_background; | |||
static inline SDL_Color selected_border_1; | |||
static inline SDL_Color selected_border_2; | |||
static inline SDL_Color modal_faded_background; | |||
static inline SDL_Color modal_background; | |||
static inline SDL_Color text; | |||
static inline SDL_Color active_text; | |||
static inline SDL_Color disabled_text; | |||
/* Fonts */ | |||
static inline PlFontData standardFontData; | |||
static inline TTF_Font * header_font = NULL; | |||
static inline TTF_Font * body_font = NULL; | |||
static inline TTF_Font * subbody_font = NULL; | |||
static inline PlFontData extendedFontData; | |||
static inline TTF_Font * button_font = NULL; | |||
static inline TTF_Font * large_button_font = NULL; | |||
static bool initialize(); | |||
static void dealloc(); | |||
static void setRenderColor(SDL_Color color); | |||
static SDL_Texture * loadAsset(std::string file); | |||
}; | |||
} |
@ -0,0 +1,214 @@ | |||
// Kosmos Updater | |||
// Copyright (C) 2019 Steven Mattera | |||
// | |||
// This program is free software; you can redistribute it and/or | |||
// modify it under the terms of the GNU General Public License | |||
// as published by the Free Software Foundation; either version 2 | |||
// of the License, or (at your option) any later version. | |||
// | |||
// This program 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 General Public License for more details. | |||
// | |||
// You should have received a copy of the GNU General Public License | |||
// along with this program; if not, write to the Free Software | |||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
#include "ConfigManager.hpp" | |||
using namespace std; | |||
namespace ku { | |||
void ConfigManager::initialize() { | |||
config_init(&_cfg); | |||
config_init(&_internalDb); | |||
if(!config_read_file(&_cfg, CONFIG_FILENAME.c_str())) { | |||
config_setting_t * root, * setting; | |||
root = config_root_setting(&_cfg); | |||
setting = config_setting_add(root, HOST_KEY.c_str(), CONFIG_TYPE_STRING); | |||
config_setting_set_string(setting, HOST_DEF.c_str()); | |||
setting = config_setting_add(root, IGNORE_KEY.c_str(), CONFIG_TYPE_ARRAY); | |||
setting = config_setting_add(root, AUTOUPDATE_KEY.c_str(), CONFIG_TYPE_BOOL); | |||
config_setting_set_bool(setting, AUTOUPDATE_DEF); | |||
setting = config_setting_add(root, PROXY_ENABLED_KEY.c_str(), CONFIG_TYPE_BOOL); | |||
config_setting_set_bool(setting, PROXY_ENABLED_DEF); | |||
setting = config_setting_add(root, PROXY_URL_KEY.c_str(), CONFIG_TYPE_STRING); | |||
config_setting_set_string(setting, PROXY_URL_DEF.c_str()); | |||
setting = config_setting_add(root, PROXY_USERNAME_KEY.c_str(), CONFIG_TYPE_STRING); | |||
config_setting_set_string(setting, PROXY_USERNAME_DEF.c_str()); | |||
setting = config_setting_add(root, PROXY_PASSWORD_KEY.c_str(), CONFIG_TYPE_STRING); | |||
config_setting_set_string(setting, PROXY_PASSWORD_DEF.c_str()); | |||
config_write_file(&_cfg, CONFIG_FILENAME.c_str()); | |||
} | |||
if(!config_read_file(&_internalDb, INTERNAL_FILENAME.c_str())) { | |||
config_setting_t * root, * setting; | |||
root = config_root_setting(&_internalDb); | |||
setting = config_setting_add(root, VERSION_KEY.c_str(), CONFIG_TYPE_STRING); | |||
config_setting_set_string(setting, VERSION_DEF.c_str()); | |||
setting = config_setting_add(root, INSTALLED_FILES_KEY.c_str(), CONFIG_TYPE_ARRAY); | |||
setting = config_setting_add(root, RECEIVED_EXFAT_WARNING_KEY.c_str(), CONFIG_TYPE_BOOL); | |||
config_setting_set_bool(setting, RECEIVED_EXFAT_WARNING_DEF); | |||
config_write_file(&_internalDb, INTERNAL_FILENAME.c_str()); | |||
} | |||
} | |||
void ConfigManager::dealloc() { | |||
config_destroy(&_cfg); | |||
config_destroy(&_internalDb); | |||
} | |||
string ConfigManager::getHost() { | |||
return _readString(HOST_KEY, HOST_DEF, _cfg); | |||
} | |||
vector<string> ConfigManager::getFilesToIgnore() { | |||
vector<string> defaultValue; | |||
return _readArrayOfStrings(IGNORE_KEY, defaultValue, _cfg); | |||
} | |||
bool ConfigManager::shouldAutoUpdate() { | |||
#ifdef DEBUG | |||
return false; | |||
#endif | |||
return _readBoolean(AUTOUPDATE_KEY, AUTOUPDATE_DEF, _cfg); | |||
} | |||
bool ConfigManager::shouldUseProxy() { | |||
return _readBoolean(PROXY_ENABLED_KEY, PROXY_ENABLED_DEF, _cfg); | |||
} | |||
string ConfigManager::getProxy() { | |||
return _readString(PROXY_URL_KEY, PROXY_URL_DEF, _cfg); | |||
} | |||
string ConfigManager::getProxyUsername() { | |||
return _readString(PROXY_USERNAME_KEY, PROXY_USERNAME_DEF, _cfg); | |||
} | |||
string ConfigManager::getProxyPassword() { | |||
return _readString(PROXY_PASSWORD_KEY, PROXY_PASSWORD_DEF, _cfg); | |||
} | |||
string ConfigManager::getCurrentVersion() { | |||
return _readString(VERSION_KEY, VERSION_DEF, _internalDb); | |||
} | |||
vector<string> ConfigManager::getInstalledFiles() { | |||
vector<string> defaultValue; | |||
return _readArrayOfStrings(INSTALLED_FILES_KEY, defaultValue, _internalDb); | |||
} | |||
bool ConfigManager::getReceivedExFATWarning() { | |||
return _readBoolean(RECEIVED_EXFAT_WARNING_KEY, RECEIVED_EXFAT_WARNING_DEF, _internalDb); | |||
} | |||
bool ConfigManager::setCurrentVersion(string version) { | |||
return _writeString(VERSION_KEY, version, _internalDb, INTERNAL_FILENAME); | |||
} | |||
bool ConfigManager::setInstalledFiles(vector<string> files) { | |||
return _writeArrayOfStrings(INSTALLED_FILES_KEY, files, _internalDb, INTERNAL_FILENAME); | |||
} | |||
bool ConfigManager::setReceivedExFATWarning(bool received) { | |||
return _writeBoolean(RECEIVED_EXFAT_WARNING_KEY, received, _internalDb, INTERNAL_FILENAME); | |||
} | |||
// Private Methods | |||
bool ConfigManager::_readBoolean(string key, bool def, config_t config) { | |||
int result; | |||
if (!config_lookup_bool(&config, key.c_str(), &result)) | |||
return def; | |||
return result; | |||
} | |||
string ConfigManager::_readString(string key, string def, config_t config) { | |||
const char * result; | |||
if (!config_lookup_string(&config, key.c_str(), &result)) | |||
return def; | |||
return string(result); | |||
} | |||
vector<string> ConfigManager::_readArrayOfStrings(string key, vector<string> def, config_t config) { | |||
vector<string> result; | |||
config_setting_t * array = config_lookup(&config, key.c_str()); | |||
if (array == NULL) | |||
return def; | |||
int count = config_setting_length(array); | |||
for (int i = 0; i < count; i++) { | |||
result.push_back(string(config_setting_get_string_elem(array, i))); | |||
} | |||
return result; | |||
} | |||
bool ConfigManager::_writeBoolean(string key, bool value, config_t config, string filename) { | |||
config_setting_t * root, * setting; | |||
root = config_root_setting(&config); | |||
setting = config_setting_get_member(root, key.c_str()); | |||
if (setting == NULL) { | |||
setting = config_setting_add(root, key.c_str(), CONFIG_TYPE_BOOL); | |||
} | |||
config_setting_set_bool(setting, value); | |||
return config_write_file(&config, filename.c_str()); | |||
} | |||
bool ConfigManager::_writeString(string key, string value, config_t config, string filename) { | |||
config_setting_t * root, * setting; | |||
root = config_root_setting(&config); | |||
setting = config_setting_get_member(root, key.c_str()); | |||
if (setting == NULL) { | |||
setting = config_setting_add(root, key.c_str(), CONFIG_TYPE_STRING); | |||
} | |||
config_setting_set_string(setting, value.c_str()); | |||
return config_write_file(&config, filename.c_str()); | |||
} | |||
bool ConfigManager::_writeArrayOfStrings(string key, vector<string> values, config_t config, string filename) { | |||
config_setting_t * root = config_root_setting(&config); | |||
config_setting_remove(root, key.c_str()); | |||
config_setting_t * array = config_setting_add(root, key.c_str(), CONFIG_TYPE_ARRAY); | |||
for (auto const& value : values) { | |||
config_setting_set_string_elem(array, -1, value.c_str()); | |||
} | |||
return config_write_file(&config, filename.c_str()); | |||
} | |||
bool ConfigManager::_removeSetting(string key, config_t config, string filename) { | |||
config_setting_t * root = config_root_setting(&config); | |||
config_setting_remove(root, key.c_str()); | |||
return config_write_file(&config, filename.c_str()); | |||
} | |||
} |
@ -0,0 +1,92 @@ | |||
// Kosmos Updater | |||
// Copyright (C) 2019 Steven Mattera | |||
// | |||
// This program is free software; you can redistribute it and/or | |||
// modify it under the terms of the GNU General Public License | |||
// as published by the Free Software Foundation; either version 2 | |||
// of the License, or (at your option) any later version. | |||
// | |||
// This program 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 General Public License for more details. | |||
// | |||
// You should have received a copy of the GNU General Public License | |||
// along with this program; if not, write to the Free Software | |||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
#pragma once | |||
#include <libconfig.h> | |||
#include <string> | |||
#include <vector> | |||
namespace ku { | |||
class ConfigManager { | |||
public: | |||
static void initialize(); | |||
static void dealloc(); | |||
static std::string getHost(); | |||
static std::vector<std::string> getFilesToIgnore(); | |||
static bool shouldAutoUpdate(); | |||
static bool shouldUseProxy(); | |||
static std::string getProxy(); | |||
static std::string getProxyUsername(); | |||
static std::string getProxyPassword(); | |||
static std::string getCurrentVersion(); | |||
static std::vector<std::string> getInstalledFiles(); | |||
static bool getReceivedExFATWarning(); | |||
static bool setCurrentVersion(std::string version); | |||
static bool setInstalledFiles(std::vector<std::string> files); | |||
static bool setReceivedExFATWarning(bool received); | |||
private: | |||
static inline config_t _cfg; | |||
static inline config_t _internalDb; | |||
static bool _readBoolean(std::string key, bool def, config_t config); | |||
static std::string _readString(std::string key, std::string def, config_t config); | |||
static std::vector<std::string> _readArrayOfStrings(std::string key, std::vector<std::string> def, config_t config); | |||
static bool _writeBoolean(std::string key, bool value, config_t config, std::string filename); | |||
static bool _writeString(std::string key, std::string value, config_t config, std::string filename); | |||
static bool _writeArrayOfStrings(std::string key, std::vector<std::string> values, config_t config, std::string filename); | |||
static bool _removeSetting(std::string key, config_t config, std::string filename); | |||
static inline const std::string CONFIG_FILENAME = "settings.cfg"; | |||
static inline const std::string INTERNAL_FILENAME = "internal.db"; | |||
static inline const std::string HOST_KEY = "host"; | |||
static inline const std::string HOST_DEF = "http://kosmos-updater.teamatlasnx.com"; | |||
static inline const std::string IGNORE_KEY = "ignore"; | |||
static inline const std::string AUTOUPDATE_KEY = "autoupdate"; | |||
static inline const bool AUTOUPDATE_DEF = true; | |||
static inline const std::string PROXY_ENABLED_KEY = "proxy_enabled"; | |||
static inline const bool PROXY_ENABLED_DEF = false; | |||
static inline const std::string PROXY_URL_KEY = "proxy_url"; | |||
static inline const std::string PROXY_URL_DEF = ""; | |||
static inline const std::string PROXY_USERNAME_KEY = "proxy_username"; | |||
static inline const std::string PROXY_USERNAME_DEF = ""; | |||
static inline const std::string PROXY_PASSWORD_KEY = "proxy_password"; | |||
static inline const std::string PROXY_PASSWORD_DEF = ""; | |||
static inline const std::string VERSION_KEY = "version"; | |||
static inline const std::string VERSION_DEF = ""; | |||
static inline const std::string INSTALLED_FILES_KEY = "installed_files"; | |||
static inline const std::string RECEIVED_EXFAT_WARNING_KEY = "received_exfat_warning"; | |||
static inline const bool RECEIVED_EXFAT_WARNING_DEF = false; | |||
}; | |||
} |
@ -0,0 +1,368 @@ | |||
// Kosmos Updater | |||
// Copyright (C) 2019 Steven Mattera | |||
// | |||
// This program is free software; you can redistribute it and/or | |||
// modify it under the terms of the GNU General Public License | |||
// as published by the Free Software Foundation; either version 2 | |||
// of the License, or (at your option) any later version. | |||
// | |||
// This program 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 General Public License for more details. | |||
// | |||
// You should have received a copy of the GNU General Public License | |||
// along with this program; if not, write to the Free Software | |||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
#include <algorithm> | |||
#include <dirent.h> | |||
#include <string.h> | |||
#include <sys/stat.h> | |||
#include <unistd.h> | |||
#include "FileManager.hpp" | |||
#include "ConfigManager.hpp" | |||
#include "models/Ini.hpp" | |||
using namespace std; | |||
using namespace ku::models; | |||
namespace ku { | |||
bool FileManager::writeFile(string filename, NetRequest * request) { | |||
deleteFile(filename); | |||
FILE * file = fopen(filename.c_str(), "wb"); | |||
if (!file) { | |||
return false; | |||
} | |||
size_t result = fwrite(request->getData(), sizeof(char), request->getSize(), file); | |||
fflush(file); | |||
fclose(file); | |||
return (result == request->getSize()); | |||
} | |||
bool FileManager::deleteFile(string filename) { | |||
if (fileExists(filename)) { | |||
return remove(filename.c_str()) == 0; | |||
} | |||
return false; | |||
} | |||
bool FileManager::fileExists(string filename) { | |||
FILE * file = fopen(filename.c_str(), "r"); | |||
if (file) { | |||
fflush(file); | |||
fclose(file); | |||
return true; | |||
} | |||
return false; | |||
} | |||
// http://stackoverflow.com/a/11366985 | |||
bool FileManager::createSubfolder(string path) { | |||
bool bSuccess = false; | |||
int nRC = ::mkdir(path.c_str(), 0775); | |||
if(nRC == -1) | |||
{ | |||
switch(errno) | |||
{ | |||
case ENOENT: | |||
//parent didn't exist, try to create it | |||
if(createSubfolder(path.substr(0, path.find_last_of('/')))) | |||
//Now, try to create again. | |||
bSuccess = 0 == ::mkdir(path.c_str(), 0775); | |||
else | |||
bSuccess = false; | |||
break; | |||
case EEXIST: | |||
//Done! | |||
bSuccess = true; | |||
break;std::string getHost(); | |||
default: | |||
bSuccess = false; | |||
break; | |||
} | |||
} | |||
else | |||
bSuccess = true; | |||
return bSuccess; | |||
} | |||
void FileManager::extract(Zip * zip) { | |||
_createThread(_extract, zip); | |||
} | |||
void FileManager::cleanUpFiles(ThreadObj * status) { | |||
_createThread(_cleanUpFiles, status); | |||
} | |||
void FileManager::applyNoGC(ThreadObj * status) { | |||
_createThread(_applyNoGC, status); | |||
} | |||
Result FileManager::_createThread(ThreadFunc func, ThreadObj * arg) { | |||
Thread thread; | |||
Result res; | |||
if (R_FAILED( res = threadCreate(&thread, func, (void *) arg, 0x2000, 0x2B, -2))) | |||
return res; | |||
if (R_FAILED( res = threadStart(&thread))) | |||
return res; | |||
arg->thread = thread; | |||
_threads.push_back(thread); | |||
return 0; | |||
} | |||
void FileManager::_extract(void * ptr) { | |||
Zip * zipObj = (Zip *) ptr; | |||
unzFile unz = unzOpen(zipObj->getFilename().c_str()); | |||
vector<string> filesToIgnore = ConfigManager::getFilesToIgnore(); | |||
vector<string> filesInstalled = ConfigManager::getInstalledFiles(); | |||
int i = 0; | |||
for (;;) { | |||
int code; | |||
if (i == 0) { | |||
code = unzGoToFirstFile(unz); | |||
} else { | |||
code = unzGoToNextFile(unz); | |||
} | |||
i++; | |||
if (code == UNZ_END_OF_LIST_OF_FILE) { | |||
break; | |||
} else { | |||
unz_file_pos pos; | |||
unzGetFilePos(unz, &pos); | |||
mutexLock(&zipObj->mutexRequest); | |||
zipObj->progress = (double) i / (double) zipObj->getNumberOfFiles(); | |||
mutexUnlock(&zipObj->mutexRequest); | |||
} | |||
unz_file_info_s * fileInfo = _getFileInfo(unz); | |||
std::string fileName(zipObj->getDestination()); | |||
fileName += '/'; | |||
fileName += _getFullFileName(unz, fileInfo); | |||
if (find(begin(filesToIgnore), end(filesToIgnore), fileName) != end(filesToIgnore)) { | |||
free(fileInfo); | |||
continue; | |||
} | |||
if (fileName.back() != '/') { | |||
filesInstalled.push_back(fileName); | |||
int result = _extractFile(fileName.c_str(), unz, fileInfo); | |||
if (result < 0) { | |||
mutexLock(&zipObj->mutexRequest); | |||
zipObj->progress = 1; | |||
zipObj->isComplete = false; | |||
zipObj->hasError = true; | |||
zipObj->errorMessage = "There was an error while trying to extract files."; | |||
mutexUnlock(&zipObj->mutexRequest); | |||
free(fileInfo); | |||
return; | |||
} | |||
} | |||
free(fileInfo); | |||
} | |||
mutexLock(&zipObj->mutexRequest); | |||
zipObj->progress = 1; | |||
zipObj->isComplete = (i >= 1); | |||
zipObj->hasError = (i <= 0); | |||
if (i <= 0) { | |||
zipObj->errorMessage = "There was no files to extract."; | |||
} else { | |||
ConfigManager::setInstalledFiles(filesInstalled); | |||
} | |||
mutexUnlock(&zipObj->mutexRequest); | |||
unzClose(unz); | |||
} | |||
void FileManager::_cleanUpFiles(void * ptr) { | |||
ThreadObj * threadObj = (ThreadObj *) ptr; | |||
vector<string> installedFiles = ConfigManager::getInstalledFiles(); | |||
vector<string> filesToIgnore = ConfigManager::getFilesToIgnore(); | |||
int i = 0; | |||
for (auto const& fileName : installedFiles) { | |||
i++; | |||
mutexLock(&threadObj->mutexRequest); | |||
threadObj->progress = (double) i / (double) installedFiles.size(); | |||
threadObj->isComplete = false; | |||
mutexUnlock(&threadObj->mutexRequest); | |||
if (find(begin(filesToIgnore), end(filesToIgnore), fileName) != end(filesToIgnore)) { | |||
continue; | |||
} | |||
deleteFile(fileName); | |||
} | |||
vector<string> blankVector; | |||
ConfigManager::setInstalledFiles(blankVector); | |||
mutexLock(&threadObj->mutexRequest); | |||
threadObj->progress = 1; | |||
threadObj->isComplete = true; | |||
mutexUnlock(&threadObj->mutexRequest); | |||
} | |||
void FileManager::_applyNoGC(void * ptr) { | |||
ThreadObj * threadObj = (ThreadObj *) ptr; | |||
Ini * ini = Ini::parseFile(HEKATE_FILE); | |||
for (auto const& section : ini->sections) { | |||
if (section->isCaption() || section->isComment()) | |||
continue; | |||
if (section->value == "config") { | |||
bool patchApplied = false; | |||
for (auto const& option : section->options) { | |||
if (option->key == "autonogc") { | |||
option->value = "1"; | |||
patchApplied = true; | |||
break; | |||
} | |||
} | |||
if (!patchApplied) { | |||
section->options.push_back(new IniOption("autonogc", "1")); | |||
} | |||
break; | |||
} | |||
} | |||
ini->writeToFile(HEKATE_FILE); | |||
delete ini; | |||
mutexLock(&threadObj->mutexRequest); | |||
threadObj->progress = 1; | |||
threadObj->isComplete = true; | |||
mutexUnlock(&threadObj->mutexRequest); | |||
} | |||
unz_file_info_s * FileManager::_getFileInfo(unzFile unz) { | |||
unz_file_info_s * fileInfo = (unz_file_info_s*) malloc(sizeof(unz_file_info_s)); | |||
unzGetCurrentFileInfo(unz, fileInfo, NULL, 0, NULL, 0, NULL, 0); | |||
return fileInfo; | |||
} | |||
string FileManager::_getFullFileName(unzFile unz, unz_file_info_s * fileInfo) { | |||
char filePath[fileInfo->size_filename + 1]; | |||
unzGetCurrentFileInfo(unz, fileInfo, filePath, fileInfo->size_filename, NULL, 0, NULL, 0); | |||
filePath[fileInfo->size_filename] = '\0'; | |||
string path(filePath); | |||
path.resize(fileInfo->size_filename); | |||
return path; | |||
} | |||
bool FileManager::_makeDirectoryParents(string path) | |||
{ | |||
bool bSuccess = false; | |||
int nRC = ::mkdir(path.c_str(), 0775); | |||
if(nRC == -1) | |||
{ | |||
switch(errno) | |||
{ | |||
case ENOENT: | |||
//parent didn't exist, try to create it | |||
if( _makeDirectoryParents(path.substr(0, path.find_last_of('/')))) | |||
//Now, try to create again. | |||
bSuccess = 0 == ::mkdir(path.c_str(), 0775); | |||
else | |||
bSuccess = false; | |||
break; | |||
case EEXIST: | |||
//Done! | |||
bSuccess = true; | |||
break;std::string getHost(); | |||
default: | |||
bSuccess = false; | |||
break; | |||
} | |||
} | |||
else | |||
bSuccess = true; | |||
return bSuccess; | |||
} | |||
int FileManager::_extractFile(const char * path, unzFile unz, unz_file_info_s * fileInfo) { | |||
//check to make sure filepath or fileInfo isnt null | |||
if (path == NULL || fileInfo == NULL) | |||
return -1; | |||
if (unzOpenCurrentFile(unz) != UNZ_OK) | |||
return -2; | |||
char folderPath[strlen(path) + 1]; | |||
strcpy(folderPath, path); | |||
char * pos = strrchr(folderPath, '/'); | |||
if (pos != NULL) { | |||
*pos = '\0'; | |||
_makeDirectoryParents(string(folderPath)); | |||
} | |||
u32 blocksize = 0x8000; | |||
u8 * buffer = (u8*) malloc(blocksize); | |||
if (buffer == NULL) | |||
return -3; | |||
u32 done = 0; | |||
int writeBytes = 0; | |||
FILE * fp = fopen(path, "w"); | |||
if (fp == NULL) { | |||
free(buffer); | |||
return -4; | |||
} | |||
while (done < fileInfo->uncompressed_size) { | |||
if (done + blocksize > fileInfo->uncompressed_size) { | |||
blocksize = fileInfo->uncompressed_size - done; | |||
} | |||
unzReadCurrentFile(unz, buffer, blocksize); | |||
writeBytes = write(fileno(fp), buffer, blocksize); | |||
if (writeBytes <= 0) { | |||
break; | |||
} | |||
done += writeBytes; | |||
} | |||
fflush(fp); | |||
fsync(fileno(fp)); | |||
fclose(fp); | |||
free(buffer); | |||
if (done != fileInfo->uncompressed_size) | |||
return -4; | |||
unzCloseCurrentFile(unz); | |||
return 0; | |||
} | |||
} |
@ -0,0 +1,53 @@ | |||
// Kosmos Updater | |||
// Copyright (C) 2019 Steven Mattera | |||
// | |||
// This program is free software; you can redistribute it and/or | |||
// modify it under the terms of the GNU General Public License | |||
// as published by the Free Software Foundation; either version 2 | |||
// of the License, or (at your option) any later version. | |||
// | |||
// This program 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 General Public License for more details. | |||
// | |||
// You should have received a copy of the GNU General Public License | |||
// along with this program; if not, write to the Free Software | |||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
#pragma once | |||
#include <minizip/unzip.h> | |||
#include <string> | |||
#include <vector> | |||
#include "models/NetRequest.hpp" | |||
#include "models/ThreadObj.hpp" | |||
#include "models/Zip.hpp" | |||
namespace ku { | |||
class FileManager { | |||
public: | |||
static bool writeFile(std::string filename, models::NetRequest * request); | |||
static bool deleteFile(std::string filename); | |||
static bool fileExists(std::string filename); | |||
static bool createSubfolder(std::string path); | |||
static void extract(models::Zip * zip); | |||
static void cleanUpFiles(models::ThreadObj * status); | |||
static void applyNoGC(models::ThreadObj * status); | |||
private: | |||
static inline std::vector<Thread> _threads; | |||
static Result _createThread(ThreadFunc func, models::ThreadObj * arg); | |||
static void _extract(void * ptr); | |||
static void _cleanUpFiles(void * ptr); | |||
static void _applyNoGC(void * ptr); | |||
static unz_file_info_s * _getFileInfo(unzFile unz); | |||
static std::string _getFullFileName(unzFile unz, unz_file_info_s * fileInfo); | |||
static bool _makeDirectoryParents(std::string path); | |||
static int _extractFile(const char * path, unzFile unz, unz_file_info_s * fileInfo); | |||
static inline const std::string HEKATE_FILE = "sdmc:/bootloader/hekate_ipl.ini"; | |||
}; | |||
} |
@ -0,0 +1,180 @@ | |||
// Kosmos Updater | |||
// Copyright (C) 2019 Steven Mattera | |||
// | |||
// This program is free software; you can redistribute it and/or | |||
// modify it under the terms of the GNU General Public License | |||
// as published by the Free Software Foundation; either version 2 | |||
// of the License, or (at your option) any later version. | |||
// | |||
// This program 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 General Public License for more details. | |||
// | |||
// You should have received a copy of the GNU General Public License | |||
// along with this program; if not, write to the Free Software | |||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
#include "NetManager.hpp" | |||
#include "ConfigManager.hpp" | |||
using namespace std; | |||
using namespace ku::models; | |||
namespace ku { | |||
void NetManager::initialize() { | |||
#ifndef DEBUG | |||
socketInitializeDefault(); | |||
#endif | |||
curl_global_init(CURL_GLOBAL_ALL); | |||
_hostname = ConfigManager::getHost(); | |||
_shouldUseProxy = ConfigManager::shouldUseProxy(); | |||
_proxyURL = ConfigManager::getProxy(); | |||
_proxyUsername = ConfigManager::getProxyUsername(); | |||
_proxyPassword = ConfigManager::getProxyPassword(); | |||
} | |||
void NetManager::dealloc() { | |||
curl_global_cleanup(); | |||
#ifndef DEBUG | |||
socketExit(); | |||
#endif | |||
} | |||
NetRequest * NetManager::getLatestAppVersion() { | |||
NetRequest * request = new NetRequest("GET", _hostname + "/" + API_VERSION + "/app/version-number"); | |||
_createThread(_request, request); | |||
return request; | |||
} | |||
NetRequest * NetManager::getLatestApp() { | |||
NetRequest * request = new NetRequest("GET", _hostname + "/" + API_VERSION + "/app"); | |||
_createThread(_request, request); | |||
return request; | |||
} | |||
NetRequest * NetManager::getLatestKosmosVersion() { | |||
NetRequest * request = new NetRequest("GET", _hostname + "/" + API_VERSION + "/package/version-number"); | |||
_createThread(_request, request); | |||
return request; | |||
} | |||
NetRequest * NetManager::getLatestKosmos() { | |||
NetRequest * request = new NetRequest("GET", _hostname + "/" + API_VERSION + "/package"); | |||
_createThread(_request, request); | |||
return request; | |||
} | |||
Result NetManager::_createThread(ThreadFunc func, NetRequest * request) { | |||
Thread thread; | |||
Result res; | |||
if (R_FAILED( res = threadCreate(&thread, func, (void *) request, 0x2000, 0x2B, -2))) | |||
return res; | |||
if (R_FAILED( res = threadStart(&thread))) | |||
return res; | |||
request->thread = thread; | |||
_threads.push_back(thread); | |||
return 0; | |||
} | |||
void NetManager::_request(void * ptr) { | |||
NetRequest * request = (NetRequest *) ptr; | |||
CURL * curl; | |||
CURLcode res; | |||
string userAgent = string("kosmos-updater/") + VERSION; | |||
curl = curl_easy_init(); | |||
if (curl) { | |||
string url = request->getURL(); | |||
struct curl_slist * headers = NULL; | |||
headers = curl_slist_append(headers, "Cache-Control: no-cache"); | |||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); | |||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); | |||
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, request->getMethod().c_str()); | |||
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION , _headerFunction); | |||
curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *) request); | |||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _writeFunction); | |||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) request); | |||
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, _progressFunction); | |||
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, (void *) request); | |||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); | |||
curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.c_str()); | |||
if (_shouldUseProxy && _proxyURL.size() > 0) { | |||
curl_easy_setopt(curl, CURLOPT_PROXY, _proxyURL.c_str()); | |||
if (_proxyUsername.size() > 0) | |||
curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, _proxyUsername.c_str()); | |||
if (_proxyPassword.size() > 0) | |||
curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, _proxyPassword.c_str()); | |||
} | |||
res = curl_easy_perform(curl); | |||
if (res != CURLE_OK) { | |||
mutexLock(&request->mutexRequest); | |||
request->hasError = true; | |||
request->errorMessage = string(curl_easy_strerror(res)); | |||
mutexUnlock(&request->mutexRequest); | |||
curl_easy_cleanup(curl); | |||
curl_slist_free_all(headers); | |||
return; | |||
} | |||
long http_code = 0; | |||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); | |||
if (http_code != 200) { | |||
mutexLock(&request->mutexRequest); | |||
request->hasError = true; | |||
request->errorMessage = "There was an error on the server, please try again later."; | |||
mutexUnlock(&request->mutexRequest); | |||
curl_easy_cleanup(curl); | |||
curl_slist_free_all(headers); | |||
return; | |||
} | |||
curl_easy_cleanup(curl); | |||
curl_slist_free_all(headers); | |||
} | |||
mutexLock(&request->mutexRequest); | |||
request->isComplete = true; | |||
mutexUnlock(&request->mutexRequest); | |||
} | |||
int NetManager::_progressFunction(void *ptr, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { | |||
NetRequest * request = (NetRequest *) ptr; | |||
mutexLock(&request->mutexRequest); | |||
request->progress = (double) dlnow / (double) dltotal; | |||
mutexUnlock(&request->mutexRequest); | |||
return 0; | |||
} | |||
size_t NetManager::_writeFunction(void *contents, size_t size, size_t nmemb, void * ptr) { | |||
NetRequest * request = (NetRequest *) ptr; | |||
return request->appendData(contents, size, nmemb); | |||
} | |||
size_t NetManager::_headerFunction(void *contents, size_t size, size_t nmemb, void * ptr) { | |||
NetRequest * request = (NetRequest *) ptr; | |||
return request->appendHeaderData(contents, size, nmemb); | |||
} | |||
} |
@ -0,0 +1,52 @@ | |||
// Kosmos Updater | |||
// Copyright (C) 2019 Steven Mattera | |||
// | |||
// This program is free software; you can redistribute it and/or | |||
// modify it under the terms of the GNU General Public License | |||
// as published by the Free Software Foundation; either version 2 | |||
// of the License, or (at your option) any later version. | |||
// | |||
// This program 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 General Public License for more details. | |||
// | |||
// You should have received a copy of the GNU General Public License | |||
// along with this program; if not, write to the Free Software | |||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
#pragma once | |||
#include <curl/curl.h> | |||
#include <string> | |||
#include <switch.h> | |||
#include <vector> | |||
#include "models/NetRequest.hpp" | |||
namespace ku { | |||
class NetManager { | |||
public: | |||
static void initialize(); | |||
static void dealloc(); | |||
static models::NetRequest * getLatestAppVersion(); | |||
static models::NetRequest * getLatestApp(); | |||
static models::NetRequest * getLatestKosmosVersion(); | |||
static models::NetRequest * getLatestKosmos(); | |||
private: | |||
static inline bool _shouldUseProxy; | |||
static inline std::string _proxyURL; | |||
static inline std::string _proxyUsername; | |||
static inline std::string _proxyPassword; | |||
static inline std::string _hostname; | |||
static inline std::vector<Thread> _threads; | |||
static Result _createThread(ThreadFunc func, models::NetRequest * ptr); | |||
static void _request(void * ptr); | |||
static int _progressFunction(void *ptr, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); | |||
static size_t _writeFunction(void *contents, size_t size, size_t nmemb, void * ptr); | |||
static size_t _headerFunction(void *contents, size_t size, size_t nmemb, void * ptr); | |||
}; | |||
} |
@ -0,0 +1,65 @@ | |||
// Kosmos Updater | |||
// Copyright (C) 2019 Steven Mattera | |||
// | |||
// This program is free software; you can redistribute it and/or | |||
// modify it under the terms of the GNU General Public License | |||
// as published by the Free Software Foundation; either version 2 | |||
// of the License, or (at your option) any later version. | |||
// | |||
// This program 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 General Public License for more details. | |||
// | |||
// You should have received a copy of the GNU General Public License | |||
// along with this program; if not, write to the Free Software | |||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
#include "Scene.hpp" | |||
namespace ku { | |||
Scene::Scene() { | |||
_touchedView = NULL; | |||
} | |||
void Scene::render(SDL_Rect rect, double dTime) { | |||
for (auto const& view : subviews) { | |||
if (!view->hidden) { | |||
SDL_Rect subviewFrame = view->frame; | |||
view->render({ rect.x + subviewFrame.x, rect.y + subviewFrame.y, subviewFrame.w, subviewFrame.h }, dTime); | |||
} | |||
} | |||
} | |||
void Scene::touchStarted() { | |||
for (auto const& view : subviews) { | |||
// TODO: Check if touch is within the view | |||
if (view->isTouchable) { | |||
_touchedView = view; | |||
_touchedView->touchStarted(); | |||
} | |||
} | |||
} | |||
void Scene::touchMoved() { | |||
if (_touchedView != NULL) { | |||
_touchedView->touchMoved(); | |||
} | |||
} | |||
void Scene::touchEnded() { | |||
if (_touchedView != NULL) { | |||
_touchedView->touchEnded(); | |||
} | |||
} | |||
void Scene::addSubView(View * view) { | |||
view->superview = NULL; | |||
subviews.push_back(view); | |||
} | |||
void Scene::removeSubView(View * view) { | |||
view->superview = NULL; | |||
subviews.remove(view); | |||
} | |||
} |