diff --git a/.gitignore b/.gitignore index adbee91..a850afe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ - *.o include/*.hpp.gch CMakeFiles/ -include/etw-lmm_config.h +include/etw-lmm_config.hpp cmake/CMake_Uninstall.cmake cmake_install.cmake CMakeCache.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index ef8b812..bb9945c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,8 @@ set (CMAKE_EXPORT_COMPILE_COMMANDS 0) # Generate header file with Caventure version configure_file ( - "${PROJECT_SOURCE_DIR}/include/etw-lmm_config.h.in" - "${PROJECT_BINARY_DIR}/include/etw-lmm_config.h") + "${PROJECT_SOURCE_DIR}/include/etw-lmm_config.hpp.in" + "${PROJECT_BINARY_DIR}/include/etw-lmm_config.hpp") # Set $CMAKE_MODULE_PATH to standard /cmake set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) @@ -32,11 +32,16 @@ include_directories (${PROJECT_BINARY_DIR}) # Pre-build: set source files set (SOURCE_FILES src/main.cpp -src/md5.cpp -src/args.cpp -src/utils.cpp +src/etw_lmm.cpp +src/backup.cpp src/calibrate.cpp -src/install.cpp) +src/calibratetext.cpp +src/md5_wrapper.cpp +src/generic.cpp +src/exceptionhandler.cpp +src/calibrationmanager.cpp +src/installationmanager.cpp +src/query.cpp) # Build: set binary to source files add_executable (etw-lmm ${SOURCE_FILES}) @@ -45,10 +50,8 @@ add_executable (etw-lmm ${SOURCE_FILES}) target_link_libraries (etw-lmm ${OPENSSL_LIBRARIES}) # Pre-install: create config directory -file(MAKE_DIRECTORY ~/.config/etw-lmm) -set (PROJECT_CONFIG_DIR ~/.config/etw-lmm) +file(MAKE_DIRECTORY $ENV{HOME}/.local/share/etw-lmm) +set (PROJECT_CONFIG_DIR $ENV{HOME}/.local/share/etw-lmm) -# Install: move reminaing files +# Install: move remianing files install(TARGETS etw-lmm DESTINATION bin) -install(DIRECTORY DESTINATION share/caventure) -install(DIRECTORY share/json DESTINATION share/caventure) diff --git a/include/backup.hpp b/include/backup.hpp new file mode 100644 index 0000000..fa1d942 --- /dev/null +++ b/include/backup.hpp @@ -0,0 +1,17 @@ +#ifndef BACKUP_HPP +#define BACKUP_HPP + +#include +#include + +class Backup +{ +public: + Backup(const std::string&, const std::vector&); + void Execute(const std::string&) const; +private: + std::string _prefix; + std::vector _vec; +}; + +#endif diff --git a/include/calibrate.hpp b/include/calibrate.hpp new file mode 100644 index 0000000..6eccafd --- /dev/null +++ b/include/calibrate.hpp @@ -0,0 +1,55 @@ +#ifndef CALIBRATE_HPP +#define CALIBRATE_HPP + +#include "generic.hpp" +#include "md5_wrapper.hpp" + +class Calibrate : public Generic +{ +public: + Calibrate(const std::string&, const std::string&); + inline static void PlusCount(); + inline static void ClearCount(); + inline static unsigned int GetCount(); + inline const MD5_wrapper& GetMD5Wrapper() const; + bool CompareMD5(unsigned char*) const; + + inline bool operator==(const Calibrate&) const; + inline bool operator!=(const Calibrate&) const; +private: + MD5_wrapper _md5; + static unsigned int _i; + unsigned long GetSizeByFD(const int) const; +}; + +inline void Calibrate::PlusCount() +{ + _i++; +} + +inline void Calibrate::ClearCount() +{ + _i = 0; +} + +inline unsigned int Calibrate::GetCount() +{ + return _i; +} + +inline const MD5_wrapper &Calibrate::GetMD5Wrapper() const +{ + return _md5; +} + +inline bool Calibrate::operator==(const Calibrate &rhs) const +{ + return this->GetMD5Wrapper().GetString() == rhs.GetMD5Wrapper().GetString(); +} + +inline bool Calibrate::operator!=(const Calibrate &rhs) const +{ + return this->GetMD5Wrapper().GetString() != rhs.GetMD5Wrapper().GetString(); +} + +#endif diff --git a/include/calibratetext.hpp b/include/calibratetext.hpp new file mode 100644 index 0000000..18445a7 --- /dev/null +++ b/include/calibratetext.hpp @@ -0,0 +1,23 @@ +#ifndef CALIBRATE_TEXT_HPP +#define CALIBRATE_TEXT_HPP + +#include "calibrate.hpp" + +enum CalibrateFlag +{ + NORMAL, + UCS2 +}; + +class CalibrateText : public Calibrate +{ +public: + CalibrateText(const std::string&, const std::string&, const std::string&, const std::string&, const CalibrateFlag); + void Execute() const; +private: + std::string _before; + std::string _after; + CalibrateFlag _cf; +}; + +#endif diff --git a/include/calibrationmanager.hpp b/include/calibrationmanager.hpp new file mode 100644 index 0000000..7eeaafe --- /dev/null +++ b/include/calibrationmanager.hpp @@ -0,0 +1,21 @@ +#ifndef CALIBRATIONMANAGER_HPP +#define CALIBRATIONMANAGER_HPP + +#include + +#include "calibratetext.hpp" + +class CalibrationManager +{ +public: + CalibrationManager(const std::string&); + void execute(); +private: + std::string _hd; + + void _verify(); + void _text(const std::string&, const CalibrateText&, const CalibrateFlag); + void _backup(); +}; + +#endif diff --git a/include/etw-lmm_config.h.in b/include/etw-lmm_config.h.in deleted file mode 100644 index a0783b2..0000000 --- a/include/etw-lmm_config.h.in +++ /dev/null @@ -1,2 +0,0 @@ -#define etw_lmm_VERSION_MAJOR @etw-lmm_VERSION_MAJOR@ -#define etw_lmm_VERSION_MINOR @etw-lmm_VERSION_MINOR@ diff --git a/include/etw-lmm_config.hpp.in b/include/etw-lmm_config.hpp.in new file mode 100644 index 0000000..0fa0ffd --- /dev/null +++ b/include/etw-lmm_config.hpp.in @@ -0,0 +1,7 @@ +#ifndef ETW_LMM_CONFIG_HPP +#define ETW_LMM_CONFIG_HPP + +const unsigned int etw_lmm_VERSION_MAJOR = @etw-lmm_VERSION_MAJOR@; +const unsigned int etw_lmm_VERSION_MINOR = @etw-lmm_VERSION_MINOR@; + +#endif diff --git a/include/etw_lmm.hpp b/include/etw_lmm.hpp new file mode 100644 index 0000000..ca0a2b8 --- /dev/null +++ b/include/etw_lmm.hpp @@ -0,0 +1,25 @@ +#ifndef ETW_LMM_HPP +#define ETW_LMM_HPP + +#include +#include + +class ETW_LMM +{ +public: + ETW_LMM(int argc, char **argv); + unsigned int execute(); +private: + std::vector _args; + std::string _hd; + + void _set_homedir(); + void _help(); + void _menu(); + void _list(); + void _calibrate(); + void _install(); + void _uninstall(const std::string&); +}; + +#endif diff --git a/include/exceptionhandler.hpp b/include/exceptionhandler.hpp new file mode 100644 index 0000000..93dc062 --- /dev/null +++ b/include/exceptionhandler.hpp @@ -0,0 +1,28 @@ +#ifndef EXCEPTION_HANDLER_HPP +#define EXCEPTION_HANDLER_HPP + +#include +#include + +enum ErrorCode +{ + UNKNOWN_ERROR, + CUSTOM_ERROR, + INVALID_PATH_ERROR, + INVALID_ARGUMENT_ERROR, + NO_ARGUMENT_ERROR, + PROCESS_ERROR +}; + +class ExceptionHandler +{ +public: + ExceptionHandler(const ErrorCode, const std::string&); + ExceptionHandler(const ErrorCode); + void GrabError(); +private: + ErrorCode _e; + std::string _s; +}; + +#endif diff --git a/include/generic.hpp b/include/generic.hpp new file mode 100644 index 0000000..a2bff70 --- /dev/null +++ b/include/generic.hpp @@ -0,0 +1,20 @@ +#ifndef GENERIC_HPP +#define GENERIC_HPP + +#include + +class Generic +{ +public: + Generic(const std::string&); + const inline std::string& GetPath() const; +protected: + std::string _path; +}; + +const inline std::string& Generic::GetPath() const +{ + return _path; +} + +#endif diff --git a/include/installationmanager.hpp b/include/installationmanager.hpp new file mode 100644 index 0000000..85dfce2 --- /dev/null +++ b/include/installationmanager.hpp @@ -0,0 +1,28 @@ +#ifndef INSTALLATIONMANAGER_HPP +#define INSTALLATIONMANAGER_HPP + +#include +#include + +#include "rapidjson/document.h" + +class InstallationManager +{ +public: + InstallationManager(const std::string&, const std::string&); + void execute(); +private: + std::string _hd, _mod, _modname, _ver; + int _campaigns, _turns; + std::vector _packs; + rapidjson::Document _cfg; + + void _print_title(const std::string&); + void _print_selection(const int); + void _grab_json(const std::string&, rapidjson::Document&); + void _version_query(); + void _campaign_query(); + void _pack_query(); +}; + +#endif diff --git a/include/md5_wrapper.hpp b/include/md5_wrapper.hpp new file mode 100644 index 0000000..ca9df75 --- /dev/null +++ b/include/md5_wrapper.hpp @@ -0,0 +1,36 @@ +#ifndef MD5_WRAPPER_HPP +#define MD5_WRAPPER_HPP + +#include + +class MD5_wrapper +{ +public: + MD5_wrapper(unsigned char*); + MD5_wrapper(const char*); + MD5_wrapper(const std::string&); + inline const std::string& GetString() const; + + inline bool operator==(const MD5_wrapper&) const; + inline bool operator!=(const MD5_wrapper&) const; +private: + unsigned long GetSizeByFD(const int) const; + std::string _md5; +}; + +inline const std::string &MD5_wrapper::GetString() const +{ + return _md5; +} + +inline bool MD5_wrapper::operator==(const MD5_wrapper &rhs) const +{ + return this->GetString() == rhs.GetString(); +} + +inline bool MD5_wrapper::operator!=(const MD5_wrapper &rhs) const +{ + return this->GetString() != rhs.GetString(); +} + +#endif diff --git a/include/paths.hpp b/include/paths.hpp new file mode 100644 index 0000000..0cbc955 --- /dev/null +++ b/include/paths.hpp @@ -0,0 +1,158 @@ +#ifndef PATHS_HPP +#define PATHS_HPP + +#include +#include +#include +#include +#include /* md5sum */ + +#include "generic.hpp" +#include "backup.hpp" +#include "calibrate.hpp" +#include "calibratetext.hpp" + +const std::string FERAL_PREFIX = "/.local/share/feral-interactive/Empire/"; +const std::string STEAM_PREFIX = "/.local/share/Steam/steamapps/common/Empire\ Total\ War/"; +const std::string MAIN_CAMPAIGN = "data/campaigns/main"; +const std::string GLOBAL_MAP = "data/campaign_maps/global_map"; + +const std::map PATH_MD5_MAP = +{ + {FERAL_PREFIX + "AppData/scripts/preferences.empire_script.txt", + "5d064a6cfdc35f4714fd31d5d99735d0"}, + {FERAL_PREFIX + "preferences", + "a0f283d89d97a00011f292cc8f9c0d24"}, + {STEAM_PREFIX + MAIN_CAMPAIGN + "/" + "scripting.lua", + "89b18f2c2c3f98b6fed7ed7ae4e3e4f9"}, + {STEAM_PREFIX + MAIN_CAMPAIGN + "/" + "startpos.esf", + "084b14b4174b93c6489422fe33dc7b2b"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "america_lookup.tga", + "086f52fb74d2d0c03ef4e0a969fac4d9"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "america_map.tga", + "92569145991bb8c1937769f023264fbb"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "brazil_map.tga", + "c77b3468113aae3e1ebf43d0a2279986"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "east_indies_lookup.tga", + "98f9a1c82a9fbf2ce15aa9c37eb5735c"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "east_indies_map.tga", + "c15c8e4db971585dce18f764db084f2c"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "europe_lookup.tga", + "62c929e8849b77b3d3060bdd448fe0db"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "europe_map.tga", + "dd31679ed17096692475dad452c2d2c5"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "india_lookup.tga", + "a731bf4b7574ad3abf4cbbff0873880d"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "india_map.tga", + "6a69c2b16c421aabc642396a8f5ecf48"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "ivory_coast_map.tga", + "e03faec4549366c5d6df0e37e37e9c28"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "madagascar_map.tga", + "46788119db0b0c3e79d307f7005dbac4"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "stratradar_america.tga", + "84648966d1ad44ae072b698dc0e311d4"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "stratradar_brazil.tga", + "c77b3468113aae3e1ebf43d0a2279986"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "stratradar_east_indies.tga", + "c15c8e4db971585dce18f764db084f2c"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "stratradar_europe.tga", + "b95431493f9a1078c013efb627cfe12a"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "stratradar_india.tga", + "e25d189f93fa3761a48a033d0818176a"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "stratradar_ivory_coast.tga", + "e03faec4549366c5d6df0e37e37e9c28"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "stratradar_madagascar.tga", + "46788119db0b0c3e79d307f7005dbac4"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "pathfinding.esf", + "d3cd17af453c9aaa5dd64dfeb8499585"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "poi.esf", + "57d6dac4b3c39587819607fce460e00e"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "regions.esf", + "e30d0b9873fad972d490a7cacb039ea9"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "sea_grids.esf", + "ebd02a0e4f39e07816054445cbd59d3d"}, + {STEAM_PREFIX + GLOBAL_MAP + "/" + "trade_routes.esf", + "dd853d15992a5c0f15da0cdd6ac443c1"} +}; + +const Generic INSTALLED = +{ + "/.local/share/etw-lmm/installed" +}; + +const CalibrateText EMPIRE_SCRIPT = +{ + "~" + FERAL_PREFIX + "AppData/scripts/preferences.empire_script.txt", + "fbf65ef80563e0bd76836c150e49ecd0", + "write_preferences_at_exit true", + "write_preferences_at_exit false", + UCS2 +}; + +const CalibrateText PREFERENCES = +{ + "~" + FERAL_PREFIX + "preferences", + "1d7c5d84f79a3d361bfa7c608b71d8c8", + "\"UsePBOSurfaces\" type=\"integer\">1", + "\"UsePBOSurfaces\" type=\"integer\">0", + NORMAL +}; + +const std::array CALIB_VERIFY_ARR = +{ + EMPIRE_SCRIPT, + PREFERENCES +}; + +const std::vector MAIN_CAMPAIGN_PATH_VEC = +{ + "scripting.lua", + "startpos.esf" +}; + +const std::vector GLOBAL_MAP_PATH_VEC = +{ + "america_lookup.tga", + "america_map.tga", + "brazil_map.tga", + "east_indies_lookup.tga", + "east_indies_map.tga", + "europe_lookup.tga", + "europe_map.tga", + "india_lookup.tga", + "india_map.tga", + "ivory_coast_map.tga", + "madagascar_map.tga", + "stratradar_america.tga", + "stratradar_brazil.tga", + "stratradar_east_indies.tga", + "stratradar_europe.tga", + "stratradar_india.tga", + "stratradar_ivory_coast.tga", + "stratradar_madagascar.tga", + "pathfinding.esf", + "poi.esf", + "regions.esf", + "sea_grids.esf", + "trade_routes.esf" +}; + +const Backup MAIN_CAMPAIGN_BACKUP = +{ + STEAM_PREFIX + "data/campaigns/main", + MAIN_CAMPAIGN_PATH_VEC +}; + +const Backup GLOBAL_MAP_BACKUP = +{ + STEAM_PREFIX + "data/campaign_maps/global_map", + GLOBAL_MAP_PATH_VEC +}; + +const std::array BACKUP_ARR = +{ + MAIN_CAMPAIGN_BACKUP, + GLOBAL_MAP_BACKUP +}; + +#endif diff --git a/include/query.hpp b/include/query.hpp new file mode 100644 index 0000000..a726a0f --- /dev/null +++ b/include/query.hpp @@ -0,0 +1,26 @@ +#ifndef QUERY_HPP +#define QUERY_HPP + +#include +#include +#include + +#include "rapidjson/document.h" + +template +class Query +{ +public: + Query(const std::string&); + Query(const std::string&, char*); + OUTPUT execute(const rapidjson::Document&) const; +private: + std::string _query; + char* _list; + + bool _bool(const rapidjson::Document&); + std::string _string(const rapidjson::Document&); + int _int(const rapidjson::Document&); +}; + +#endif diff --git a/share/json/md5.json b/share/json/md5.json deleted file mode 100644 index 9132321..0000000 --- a/share/json/md5.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "md5": - [ - "5d064a6cfdc35f4714fd31d5d99735d0", - "a0f283d89d97a00011f292cc8f9c0d24", - "89b18f2c2c3f98b6fed7ed7ae4e3e4f9", - "084b14b4174b93c6489422fe33dc7b2b", - "086f52fb74d2d0c03ef4e0a969fac4d9", - "92569145991bb8c1937769f023264fbb", - "c77b3468113aae3e1ebf43d0a2279986", - "98f9a1c82a9fbf2ce15aa9c37eb5735c", - "c15c8e4db971585dce18f764db084f2c", - "62c929e8849b77b3d3060bdd448fe0db", - "dd31679ed17096692475dad452c2d2c5", - "a731bf4b7574ad3abf4cbbff0873880d", - "6a69c2b16c421aabc642396a8f5ecf48", - "e03faec4549366c5d6df0e37e37e9c28", - "46788119db0b0c3e79d307f7005dbac4", - "84648966d1ad44ae072b698dc0e311d4", - "c77b3468113aae3e1ebf43d0a2279986", - "c15c8e4db971585dce18f764db084f2c", - "b95431493f9a1078c013efb627cfe12a", - "e25d189f93fa3761a48a033d0818176a", - "e03faec4549366c5d6df0e37e37e9c28", - "46788119db0b0c3e79d307f7005dbac4", - "d3cd17af453c9aaa5dd64dfeb8499585", - "57d6dac4b3c39587819607fce460e00e", - "e30d0b9873fad972d490a7cacb039ea9", - "ebd02a0e4f39e07816054445cbd59d3d", - "dd853d15992a5c0f15da0cdd6ac443c1" - ] -} diff --git a/share/json/path.json b/share/json/path.json deleted file mode 100644 index 9560d82..0000000 --- a/share/json/path.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "data": - [ - "/.local/share/feral-interactive/Empire/AppData/scripts/preferences.empire_script.txt", - "/.local/share/feral-interactive/Empire/preferences", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaigns/main/scripting.lua", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaigns/main/startpos.esf", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/america_lookup.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/america_map.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/brazil_map.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/east_indies_lookup.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/east_indies_map.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/europe_lookup.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/europe_map.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/india_lookup.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/india_map.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/ivory_coast_map.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/madagascar_map.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/stratradar_america.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/stratradar_brazil.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/stratradar_east_indies.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/stratradar_europe.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/stratradar_india.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/stratradar_ivory_coast.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/stratradar_madagascar.tga", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/pathfinding.esf", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/poi.esf", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/regions.esf", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/sea_grids.esf", - "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/trade_routes.esf" - ], - "preferences.empire_script": - { - "name": "feral-interactive/Empire/AppData/scripts/preferences.empire_script.txt", - "path": "~/.local/share/feral-interactive/Empire/AppData/scripts/preferences.empire_script.txt" - }, - "preferences": - { - "name": "feral-interactive/Empire/preferences", - "path": "~/.local/share/feral-interactive/Empire/preferences" - }, - "main_backup": - { - "name": "data/campaigns/main_backup/", - "path": "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaigns/main_backup/" - }, - "global_map_backup": - { - "name": "data/campaign_maps/global_map_backup/", - "path": "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map_backup/" - } -} diff --git a/src/args.cpp b/src/args.cpp deleted file mode 100644 index 2b1d568..0000000 --- a/src/args.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include /* md5sum */ - -#include "../include/etw-lmm_config.h" -#include "../include/rapidjson/document.h" - - -std::string to_hex(unsigned char*); -void md5sum(std::string, unsigned char [MD5_DIGEST_LENGTH]); -void print_md5_sum(unsigned char*); -int calibrate_preferences(); -int calibrate_text_bug_fix(); -int calibrate_backup(const std::string&); -int yn_query(const std::string&, bool); -int dir_not_exists(const std::string&); - -/** -* Brings up the command list/help menu -*/ -void help_menu() -{ - std::cout << "EMPIRE: TOTAL WAR -- LINUX MOD MANAGER " - << std::to_string(etw_lmm_VERSION_MAJOR) << "." << std::to_string(etw_lmm_VERSION_MINOR) << "\n" - << "Options:\n" - << "\thelp\t\tShow this help message\n" - << "\tinstall [MOD]\tInstall a mod\n" - << "\tuninstall [MOD]\tUninstall a mod\n" - << "\tcalibrate\tCalibrate original game files for modding\n" - << "\tlist\t\tList installed mods\n" - << "\tquery [MOD]\tQuery available mods" - << std::endl; -} - -/** -* Lists installed mods in mods filepath -* -* @param Path for file MODS -*/ -int list(const std::string& hd) -{ - const std::string mods = hd + "/.config/etw-lmm/MODS"; - - /* If file MODS doesn't exist */ - if (access(mods.c_str(), F_OK) == -1) - { - std::cout << "No mods installed!"; - return 0; - } - - std::ifstream ioMODS(mods); - std::string name; - - while (std::getline (ioMODS, name)) - { - if (name == "\0") - { - std::cout << "No mods installed!"; - } - std::cout << name << std::endl; - } - - ioMODS.close(); - - return 0; -} - -int calibrate(const std::string& hd, const std::vector& path, const char* path_md5[]) -{ - const std::string mods = hd + "/.config/etw-lmm/MODS"; - - /* If file MODS exists */ - if (access(mods.c_str(), F_OK) != -1) - { - std::ifstream ioMODS(mods); - std::string name; - - std::getline (ioMODS, name); - if (name != "\0") - { - std::cerr << "Either get a fresh install of Empire: Total War or uninstall the following mods: " << std::endl; - list(mods); - ioMODS.close(); - return 1; - } - - ioMODS.close(); - } - - unsigned int x = 0; - - std::cout << "No mods installed! Calibrating files now..." << std::endl; - for (unsigned int i = 0; i < path.size(); i++) - { - unsigned char md5_local[MD5_DIGEST_LENGTH]; - md5sum(path[i], md5_local); - if ((strcmp(to_hex(md5_local).c_str(), path_md5[i]) != 0)) - { - /* write_preferences_at_exit false in preferences.empire_script.txt */ - if (i == 0 && to_hex(md5_local) == "fbf65ef80563e0bd76836c150e49ecd0") - { - if (calibrate_preferences()) - { - return 1; - } - - continue; - } - - /* preferences has VBOSurfaces set to 1, TEXT BUG FIX */ - if (i == 1 && to_hex(md5_local) == "1d7c5d84f79a3d361bfa7c608b71d8c8") - { - if (calibrate_text_bug_fix()) - { - return 1; - } - - continue; - } - print_md5_sum(md5_local); - std::cerr << " != " << path_md5[i] << "\n" << path[i] << " is not calibrated!" << std::endl; - x++; - } - } - - if (x > 0) - { - std::cerr << "Number of files uncalibrated: " << x << std::endl; - return 1; - } - - std::cout << "All files are calibrated!" << std::endl; - - /* GAME FILE BACKUPS */ - - if (calibrate_backup(hd)) - { - if (yn_query("Would you like to backup your game files? This is recommended.", true)) - { - std::cout << "Backing up files now..." << std::endl; - - const std::string dir_main_backup = hd + "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaigns/main_backup"; - const std::string dir_global_map_backup = hd + "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map_backup"; - - if (dir_not_exists(dir_main_backup)) - { - const std::string dir_main = hd + "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaigns/main/"; - - const std::string backup_main = "cp -R " + dir_main + ". " + dir_main_backup; - - if (system(backup_main.c_str())) - { - std::cerr << "Failed to backup data/campaigns/main/!" << std::endl; - return 1; - } - } - - if (dir_not_exists(dir_global_map_backup)) - { - const std::string dir_global_map = hd + "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map/"; - - const std::string backup_global_map = "cp -R " + dir_global_map + ". " + dir_global_map_backup; - - if (system(backup_global_map.c_str())) - { - std::cerr << "Failed to backup data/campaign_map/global_map/!" << std::endl; - return 1; - } - } - - std::cout << "Critical game data is now backed up!" << std::endl; - } - } - - std::cout << "The game is now ready to install mods." << std::endl; - - return 0; -} diff --git a/src/backup.cpp b/src/backup.cpp new file mode 100644 index 0000000..e06b5ab --- /dev/null +++ b/src/backup.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include // sendfile +#include // open +#include // close +#include // fstat +#include // fstat + +#include "../include/exceptionhandler.hpp" +#include "../include/backup.hpp" + +Backup::Backup(const std::string &prefix, const std::vector &vec) +: _prefix(prefix), _vec(vec) {}; + +void Backup::Execute(const std::string &hd) const +{ + struct stat sb; + + const std::string bkp = hd + _prefix + "_backup/"; + + struct stat stat_dir; + if (stat(bkp.c_str(), &stat_dir) != 0) + { + mkdir(bkp.c_str(), 0700); + } + + for (auto &i : _vec) + { + const std::string source_file = hd + _prefix + "/" + i; + const std::string dest_file = bkp + i; + + std::cout << "Backing up " << _prefix << "/" << i << "\n"; + + int source = open(source_file.c_str(), O_RDONLY, 0); + int dest = open(dest_file.c_str(), O_WRONLY | O_CREAT /*| O_TRUNC/**/, 0644); + + // struct required, rationale: function stat() exists also + struct stat stat_source; + fstat(source, &stat_source); + + sendfile(dest, source, 0, stat_source.st_size); + + close(source); + close(dest); + + if (access(dest_file.c_str(), F_OK) != 0) + { + throw ExceptionHandler(CUSTOM_ERROR, "Backing up " + dest_file + " failed!"); + } + } + +} diff --git a/src/calibrate.cpp b/src/calibrate.cpp index f16291e..f90754b 100644 --- a/src/calibrate.cpp +++ b/src/calibrate.cpp @@ -1,63 +1,67 @@ +#include +#include +#include #include +#include /* md5sum */ -int replace_string(const std::string&, const std::string&, const std::string&); -int dir_not_exists(const std::string&); +#include "../include/calibrate.hpp" +#include "../include/exceptionhandler.hpp" -int calibrate_preferences() +unsigned int Calibrate::_i = 0; + +Calibrate::Calibrate(const std::string &path, const std::string &md5) +: Generic(std::move(path)), _md5(std::move(md5)) {} + +bool Calibrate::CompareMD5(unsigned char *md5) const { - std::cout << "Calibrating preferences.empire_script.txt..." << std::endl; - - /* Converting .txt file from UCS-2 to UTF-8 into a new .txt_utf8 file */ - if (system("iconv -f ucs-2 -t utf-8 -o ~/.local/share/feral-interactive/Empire/AppData/scripts/preferences.empire_script.txt_utf8 ~/.local/share/feral-interactive/Empire/AppData/scripts/preferences.empire_script.txt")) - { - return 1; - } - - /* Replacing string in utf_8 */ - if (replace_string("~/.local/share/feral-interactive/Empire/AppData/scripts/preferences.empire_script.txt_utf8", "write_preferences_at_exit true", "write_preferences_at_exit false")) - { - return 1; - } - - /* Converting back from UTF-8 to UCS-2 in .txt*/ - return system("iconv -f utf-8 -t ucs-2 -o ~/.local/share/feral-interactive/Empire/AppData/scripts/preferences.empire_script.txt ~/.local/share/feral-interactive/Empire/AppData/scripts/preferences.empire_script.txt_utf8"); - - /** NOT FUNCTIONAL - - // Verifying MD5 checksum - md5sum(path[i], md5_local); - if ((strcmp(to_hex(md5_local).c_str(), path_md5[i]) != 0)) - { - print_md5_sum(md5_local); - std::cerr << " != " << path_md5[i] << "\nFailed to calibrate preferences.empire_script.txt !" << std::endl; - } - + /* + * Variable initialisation */ -} + int file_descript; + unsigned long file_size; + unsigned char* file_buffer; -int calibrate_text_bug_fix() -{ - std::cout << "Calibrating preferences XML file..." << std::endl; - - /* Replacing string */ - return replace_string("~/.local/share/feral-interactive/Empire/preferences", "\"UsePBOSurfaces\" type=\"integer\">1", "\"UsePBOSurfaces\" type=\"integer\">0"); -} - -int calibrate_backup(const std::string& hd) -{ - std::cout << "Searching for backup files and directories..." << std::endl; - - if (dir_not_exists(hd + "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaigns/main_backup/")) + /* Checks if the file path can be opened. */ + file_descript = open(_path.c_str(), O_RDONLY); + if (file_descript < 0) { - std::cerr << "data/campaigns/main_backup/ not found!" << std::endl; - return 1; + throw ExceptionHandler(INVALID_PATH_ERROR, _path); } - if (dir_not_exists(hd + "/.local/share/Steam/steamapps/common/Empire\ Total\ War/data/campaign_maps/global_map_backup/")) + /* Grabs the size of file */ + file_size = GetSizeByFD(file_descript); + + /* Generates the buffer */ + file_buffer = static_cast(mmap(static_cast(0), file_size, PROT_READ, MAP_SHARED, file_descript, 0)); + + /* Computes the MD5 checksum to result */ + MD5(file_buffer, file_size, md5); + + /* Removes fime_buffer and file_size */ + munmap(file_buffer, file_size); + + MD5_wrapper md5_wrapper(md5); + + if (_md5.GetString() != md5_wrapper.GetString()) { - std::cerr << "data/campaign_maps/global_map_backup/ not found!" << std::endl; - return 1; + std::cout << "WARNING! " << _md5.GetString() << " != " << md5_wrapper.GetString() << "\n"; + return false; } - return 0; + return true; +} + +/** +* Get the size of the file by its file descriptor +* +* @param file_descript +*/ +unsigned long Calibrate::GetSizeByFD(const int fd) const +{ + struct stat statbuf; + if (fstat(fd, &statbuf) < 0) + { + exit(EXIT_FAILURE); + } + return statbuf.st_size; } diff --git a/src/calibratetext.cpp b/src/calibratetext.cpp new file mode 100644 index 0000000..df12527 --- /dev/null +++ b/src/calibratetext.cpp @@ -0,0 +1,68 @@ +#include +#include + +#include "../include/calibratetext.hpp" +#include "../include/exceptionhandler.hpp" + +CalibrateText::CalibrateText(const std::string &path, const std::string &md5, const std::string &before, const std::string &after, const CalibrateFlag cf) +: Calibrate(path, md5), _before(before), _after(after), _cf(cf) {} + +void CalibrateText::Execute() const +{ + std::cout << "Calibrating " << _path << "\n"; + + std::string cmd = "sed -i -- 's/" + _before + "/" + _after + "/g' " + _path; + std::string ucs2_to_utf8; + std::string utf8_to_ucs2; + + switch (_cf) + { + case UCS2: + cmd += "_utf8"; + ucs2_to_utf8 = "iconv -f ucs-2 -t utf-8 -o " + _path + "_utf8" + " " + _path; + utf8_to_ucs2 = "iconv -f utf-8 -t ucs-2 -o " + _path + " " + _path + "_utf8"; + + /* Converting .txt file from UCS-2 to UTF-8 into a new .txt_utf8 file */ + if (system(ucs2_to_utf8.c_str())) + { + throw ExceptionHandler(PROCESS_ERROR, _path); + } + break; + case NORMAL: + default: + break; + } + + if (system(cmd.c_str())) + { + throw ExceptionHandler(PROCESS_ERROR, _path); + } + + switch (_cf) + { + case UCS2: + /* Converting back from UTF-8 to UCS-2 in .txt*/ + if (system(utf8_to_ucs2.c_str())) + { + throw ExceptionHandler(PROCESS_ERROR, _path); + } + + + break; + case NORMAL: + default: + break; + } + + + + /** NOT FUNCTIONAL + + // Verifying MD5 checksum + md5sum(path[i], md5_local); + if ((strcmp(to_hex(md5_local).c_str(), path_md5[i]) != 0)) + { + print_md5_sum(md5_local); + std::cerr << " != " << path_md5[i] << "\nFailed to calibrate preferences.empire_script.txt !" << "\n"; + }*/ +} diff --git a/src/calibrationmanager.cpp b/src/calibrationmanager.cpp new file mode 100644 index 0000000..22f01a1 --- /dev/null +++ b/src/calibrationmanager.cpp @@ -0,0 +1,87 @@ +#include "../include/calibrationmanager.hpp" + +#include +#include + +#include "../include/exceptionhandler.hpp" +#include "../include/paths.hpp" + +CalibrationManager::CalibrationManager(const std::string &hd) +: _hd(hd) {} + +void CalibrationManager::execute() +{ + const std::string fp = _hd + INSTALLED.GetPath(); + + /* If file exists */ + if (access(std::move(fp).c_str(), F_OK) == 0) + { + std::cout << "Please uninstall any installed mods." << "\n"; + throw ExceptionHandler(UNKNOWN_ERROR); + } + + /* MD5 DIGEST VERIFICATION */ + + std::cout << "No mods installed! Calibrating files now..." << "\n"; + + /* Verifies the MD5 digest of each path */ + _verify(); + + if (Calibrate::GetCount() > 0) + { + std::cout << "Number of files uncalibrated: " << Calibrate::GetCount() << "\n"; + throw ExceptionHandler(CUSTOM_ERROR, "Please reinstall the game."); + } + + std::cout << "All files are calibrated!" << "\n"; + + /* GAME FILE BACKUPS */ + + _backup(); + + std::cout << "The game is now ready to install mods." << "\n"; +} + +void CalibrationManager::_verify() +{ + Calibrate::ClearCount(); + + for (auto &it : PATH_MD5_MAP) + { + const std::string fp = _hd + it.first; + unsigned char md5_local[MD5_DIGEST_LENGTH]; + + Calibrate calibrate_local(std::move(fp), it.second); + + if (!calibrate_local.CompareMD5(md5_local)) + { + bool blocal = true; + for (auto &i : CALIB_VERIFY_ARR) + { + if (i.CompareMD5(md5_local)) + { + i.Execute(); + blocal = false; + break; + } + } + + if (blocal) + { + Calibrate::PlusCount(); + } + } + } +} + +void CalibrationManager::_backup() +{ + std::cout << "Searching for backup files and directories..." << "\n"; + + for (auto &i : BACKUP_ARR) + { + i.Execute(_hd); + } + + std::cout << "Critical game data is now backed up!" << "\n"; +} diff --git a/src/etw_lmm.cpp b/src/etw_lmm.cpp new file mode 100644 index 0000000..4011b3a --- /dev/null +++ b/src/etw_lmm.cpp @@ -0,0 +1,139 @@ +#include +#include +#include +#include + +#include "../include/paths.hpp" +#include "../include/etw_lmm.hpp" +#include "../include/exceptionhandler.hpp" +#include "../include/etw-lmm_config.hpp" +#include "../include/calibrationmanager.hpp" +#include "../include/installationmanager.hpp" + +ETW_LMM::ETW_LMM(int argc, char **argv) +{ + for (unsigned int i = 0; i < argc; ++i) + { + _args.push_back(argv[i]); + } + + _set_homedir(); +} + +unsigned int ETW_LMM::execute() +{ + try + { + _menu(); + } + catch (ExceptionHandler& e) + { + std::cerr << _args[0] << ": "; + e.GrabError(); + return 1; + } + + return 0; +} + +void ETW_LMM::_menu() +{ + if (_args.size() > 1) + { + if (_args[1] == "help") { _help(); } + else if (_args[1] == "calibrate") { _calibrate(); } + else if (_args[1] == "list") { _list(); } + else if (_args[1] == "install") + { + if (_args.size() < 3) + { + throw ExceptionHandler(NO_ARGUMENT_ERROR); + } + + _install(); + } + else if (_args[1] == "uninstall") + { + if (_args.size() < 3) // TO DO + { + throw ExceptionHandler(NO_ARGUMENT_ERROR); + } + + // INSTALLATION + } + else + { + throw ExceptionHandler(INVALID_ARGUMENT_ERROR, _args[1]); + } + } + else + { + _help(); + } +} + +void ETW_LMM::_set_homedir() +{ + /* Retrieve home directory */ + char* char_homedir; + if ((char_homedir = getenv("HOME")) == NULL) + { + struct passwd *pw = getpwuid(getuid()); + char_homedir = pw->pw_dir; + } + + _hd = char_homedir; +} + +void ETW_LMM::_help() +{ + std::cout << "EMPIRE: TOTAL WAR -- LINUX MOD MANAGER " + << etw_lmm_VERSION_MAJOR << "." << etw_lmm_VERSION_MINOR << "\n" + << "Options:\n" + << "\thelp\t\tShow this help message\n" + << "\tinstall [MOD]\tInstall a mod\n" + << "\tuninstall [MOD]\tUninstall a mod\n" + << "\tcalibrate\tCalibrate original game files for modding\n" + << "\tlist\t\tList installed mods\n" + << "\n"; +} + +void ETW_LMM::_list() +{ + const std::string fp = _hd + INSTALLED.GetPath(); + + /* If file doesn't exist */ + if (access(std::move(fp).c_str(), F_OK) != 0) + { + std::cout << "No mods installed!"; + } + else + { + std::ifstream ifs(std::move(fp)); + std::string name; + + while (std::getline(ifs, name)) + { + if (name == "\0") + { + std::cout << "No mods installed!"; + break; + } + std::cout << name << "\n"; + } + + ifs.close(); + } +} + +void ETW_LMM::_calibrate() +{ + CalibrationManager cm(_hd); + cm.execute(); +} + +void ETW_LMM::_install() +{ + InstallationManager im(_hd, _args[2]); + im.execute(); +} diff --git a/src/exceptionhandler.cpp b/src/exceptionhandler.cpp new file mode 100644 index 0000000..3682f2e --- /dev/null +++ b/src/exceptionhandler.cpp @@ -0,0 +1,27 @@ +#include "../include/exceptionhandler.hpp" + +ExceptionHandler::ExceptionHandler(const ErrorCode e, const std::string &s) +: _e(e), _s(s) {} + +ExceptionHandler::ExceptionHandler(const ErrorCode e) +: _e(e) {} + +void ExceptionHandler::GrabError() +{ + std::string str; + + std::cerr << "Error code " << _e << "!" << "\n"; + + switch (_e) + { + case CUSTOM_ERROR: str += _s; break; + case INVALID_PATH_ERROR: str += "\"" + _s + "\" is an invalid path."; break; + case INVALID_ARGUMENT_ERROR: str += "\"" + _s + "\" is an invalid argument."; break; + case NO_ARGUMENT_ERROR: str += "No arguments found."; break; + case PROCESS_ERROR: str += "Failed to process " + _s; break; + case UNKNOWN_ERROR: + default: str += "Unknown error."; break; + } + + std::cerr << str << "\n"; +} diff --git a/src/generic.cpp b/src/generic.cpp new file mode 100644 index 0000000..5bcb093 --- /dev/null +++ b/src/generic.cpp @@ -0,0 +1,4 @@ +#include "../include/generic.hpp" + +Generic::Generic(const std::string &path) +: _path(path) {}; diff --git a/src/install.cpp b/src/install.cpp deleted file mode 100644 index e8f17db..0000000 --- a/src/install.cpp +++ /dev/null @@ -1,268 +0,0 @@ -#include -#include -#include -#include - -#include "../include/rapidjson/document.h" -#include "../include/rapidjson/istreamwrapper.h" -#include "../include/rapidjson/error/en.h" - -int file_not_exists(const std::string&); -int yn_query(const std::string&, bool); - -int install_query(const std::string &hd, const std::string &mod, std::string& ver) -{ - const std::string modjson_path(hd + "/modlist/" + mod + ".json"); - - if (file_not_exists(modjson_path)) - { - std::cerr << mod << " not found!" << std::endl; - return 1; - } - - rapidjson::Document modlist; - std::ifstream modjson_stream(modjson_path); - rapidjson::IStreamWrapper isw_modlist(modjson_stream); - modlist.ParseStream(isw_modlist); - - std::cout << "Name: " << modlist["name"].GetString() << std::endl - << "Author: " << modlist["author"].GetString() << std::endl - << "Last update: " << modlist["update"].GetString() << std::endl; - - const rapidjson::Value& versions = modlist["versions"]; - - std::cout << "Latest version: " << versions[0].GetString() << std::endl - << "Available versions: "; - - for (rapidjson::SizeType i = 0; i < versions.Size(); i++) - { - if (i != 0) - { - std::cout << ", "; - } - - std::cout << versions[i].GetString(); - } - - std::cout << std::endl; - - /* PRE-INSTALLATION PROCESS */ - /* --VERSION */ - - if (yn_query("Are you sure you want to install " + mod + " version " + versions[0].GetString() + "?", true)) - { - ver = versions[0].GetString(); - } - else - { - if (versions.Size() > 1) - { - // std::cin.ignore(std::numeric_limits::max(), '\n'); - do - { - std::string install_ver_input; - std::cout << "Please type in the version you would like to install [enter q to quit]: "; - std::cin.clear(); - - std::getline(std::cin, install_ver_input); - - if (install_ver_input == "q" || install_ver_input == "q\n") - { - std::cout << "Quitting!" << std::endl; - return 0; - } - - std::cout << install_ver_input << std::endl; - - bool install_ver_set = false; - - for (rapidjson::SizeType i = 0; i < versions.Size(); i++) - { - if (install_ver_input == versions[i].GetString()) - { - ver = install_ver_input; - install_ver_set = true; - break; - } - } - - if (install_ver_set) - { - break; - } - - std::cout << "Error! " << install_ver_input << " not found in versions list!" << std::endl; - } while (true); - } - else - { - std::cout << "Exiting installation!" << std::endl; - return 0; - } - } - - std::cout << "Preparing to install " << mod << " version " << ver << "..." << std::endl; - - return 0; -} - -int install(const std::string &hd, const std::string &mod) -{ - std::string install_ver; - if (install_query(hd, mod, install_ver)) - { - return 1; - } - /* --PACKPREINSTALLATION*/ - - const std::string modconfig_path(hd + "/" + mod + "/" + install_ver + "/modconfig.json"); - - std::cout << modconfig_path << std::endl; - - if (file_not_exists(modconfig_path)) - { - std::cerr << modconfig_path << " not found!" << std::endl; - return 1; - } - - rapidjson::Document modconfig; - std::ifstream modconfig_stream(modconfig_path); - rapidjson::IStreamWrapper isw_config(modconfig_stream); - if (modconfig.ParseStream<0>(isw_config).HasParseError()) - { - std::cerr << GetParseError_En(modconfig.GetParseError()) << std::endl; - return 1; - } - modconfig.ParseStream(isw_config); - - std::cout << "CAMPAIGN SELECTION" << std::endl - << "==================" << std::endl; - - for (rapidjson::SizeType i = 0; i < modconfig["campaigns"].Size(); i++) - { - std::cout << "[" << (i+1) << "] " << modconfig["campaigns"][i]["name"].GetString() << std::endl << "Description: " << modconfig["campaigns"][i]["desc"].GetString() << std::endl << std::endl; - } - - int campaign_selection, turn_selection; - - /* CAMPAIGN SELECTION */ - do - { - char campaign_selection_char; - std::cout << std::endl << "Please select a campaign:"; - - for (rapidjson::SizeType i = 0; i < modconfig["campaigns"].Size(); i++) - { - std::cout << " [" << (i+1) << "] " << modconfig["campaigns"][i]["name"].GetString(); - } - - std::cout << " [press q to exit]" << std::endl; - std::cin.get(campaign_selection_char); - campaign_selection = campaign_selection_char - '0'; - if (campaign_selection_char == 'q') - { - return 0; - } - else if (campaign_selection < 1 || campaign_selection > (int) modconfig["campaigns"].Size()) - { - std::cin.clear(); - std::cin.ignore(std::numeric_limits::max(), '\n'); - std::cout << "Error! \"" << campaign_selection_char << "\" is not a valid input." << std::endl; - } - else - { - break; - } - } while (true); - - std::cin.ignore(std::numeric_limits::max(), '\n'); - - /* TURN SELECTION */ - do - { - char turn_selection_char; - std::cout << std::endl << "Please select your preferred number of turns per campaign year:"; - - const rapidjson::Value& turns = modconfig["turns"]; - - for (rapidjson::SizeType i = 0; i < turns.Size(); i++) - { - std::cout << " [" << (i+1) << "] " << turns[i]["name"].GetString(); - } - - std::cout << " [press q to exit]" << std::endl; - std::cin.get(turn_selection_char); - turn_selection = turn_selection_char - '0'; - if (turn_selection_char == 'q') - { - return 0; - } - else if (turn_selection < 1 || turn_selection > (int) turns.Size()) - { - std::cin.clear(); - std::cin.ignore(std::numeric_limits::max(), '\n'); - std::cout << "Error! \"" << turn_selection_char << "\" is not a valid input." << std::endl; - } - else - { - break; - } - } while (true); - - std::cout << std::endl << "You have selected:" << std::endl - << "Campaign: " << modconfig["campaigns"][campaign_selection-1]["name"].GetString() << std::endl - << "Turns: " << modconfig["turns"][turn_selection-1]["name"].GetString() << std::endl << std::endl; - - /* PACKS SELECTION */ - - std::cout << "PACK SELECTION" << std::endl - << "==============" << std::endl; - - std::vector pack_vector; - - std::cin.ignore(std::numeric_limits::max(), '\n'); - // std::cin.ignore(std::numeric_limits::max(), '\n'); - for (rapidjson::SizeType i = 0; i < modconfig["packs"].Size(); i++) - { - std::cout << std::endl << modconfig["packs"][i]["name"].GetString() << std::endl - << modconfig["packs"][i]["desc"].GetString() << std::endl; - - if (yn_query("Do you want to install this pack?", true)) - { - pack_vector.insert(pack_vector.end(), true); - } - else pack_vector.insert(pack_vector.end(), false); - - std::cin.clear(); - } - - /* SELECTION SUMMARY */ - - std::cout << "SELECTION SUMMARY" << std::endl - << "=================" << std::endl; - - std::cout << "Campaign: " << modconfig["campaigns"][campaign_selection-1]["name"].GetString() << std::endl - << "Turns: " << modconfig["turns"][turn_selection-1]["name"].GetString() << std::endl - << "Packs: "; - - for (rapidjson::SizeType i = 0; i < modconfig["packs"].Size(); i++) - { - if (pack_vector[i]) - { - std::cout << modconfig["packs"][i]["name"].GetString(); - if (i != (modconfig["packs"].Size() - 1)) - { - std::cout << ", "; - } - } - } - - std::cout << std::endl << std::endl; - - if (!yn_query("Are you sure you want to install this selection?", true)) - { - return 0; - } - - return 0; -} diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp new file mode 100644 index 0000000..a855e52 --- /dev/null +++ b/src/installationmanager.cpp @@ -0,0 +1,247 @@ +#include "../include/installationmanager.hpp" + +#include +#include +#include + +#include "../include/exceptionhandler.hpp" +#include "../include/query.hpp" +#include "../include/rapidjson/istreamwrapper.h" +#include "../include/rapidjson/error/en.h" +#include "../include/rapidjson/filereadstream.h" + +InstallationManager::InstallationManager(const std::string &hd, const std::string &mod) +: _hd(hd), _mod(std::move(mod)) {} + +void InstallationManager::execute() +{ + _version_query(); + /* --PACKPREINSTALLATION*/ + + /* TEMPORARY JSON */ + const std::string fp = _hd + "/" + _mod + "/" + _ver + "/modconfig.json"; + _grab_json(std::move(fp), _cfg); + /* TEMPORARY JSON */ + + _campaign_query(); + + std::cout << "\n" << "You have selected:" << "\n"; + _print_selection(2); + + /* PACKS SELECTION */ + _pack_query(); + + /* SELECTION SUMMARY */ + _print_title("SELECTION SUMMARY"); + + _print_selection(3); + + std::cout << "\n"; + const Query query_final = {"Are you sure you want to install this selection?"}; + if (!query_final.execute(_cfg)) + { + exit(0); + } + + // INSTALLATION + + std::cout << "Installing " << _modname << " version " << _ver << "...\n"; +} + +void InstallationManager::_version_query() +{ + /* TEMPORARY JSON */ + const std::string fp = _hd + "/" + _mod + "/modinfo.json"; + rapidjson::Document modlist; + _grab_json(std::move(fp), modlist); + /* TEMPORARY JSON */ + + /* Printing mod info */ + std::cout << "Name: " << modlist["name"].GetString() << "\n" + << "Author: " << modlist["author"].GetString() << "\n" + << "Latest version: " << modlist["versions"][0].GetString() << "\n" + << "Available versions: "; + + for (rapidjson::SizeType i = 0; i < modlist["versions"].Size(); i++) + { + if (i != 0) + { + std::cout << ", "; + } + + std::cout << modlist["versions"][i].GetString(); + } + + std::cout << "\n"; + + /* PRE-INSTALLATION PROCESS */ + /* --VERSION */ + + const Query query_verify = {"Are you sure you want to install " + std::string(modlist["name"].GetString()) + " version " + modlist["versions"][0].GetString() + "?"}; + + /* Asks user for mod version */ + if (query_verify.execute(modlist)) + { + _modname = modlist["name"].GetString(); + _ver = modlist["versions"][0].GetString(); + } + else + { + if (modlist["versions"].Size() > 1) + { + // std::cin.ignore(std::numeric_limits::max(), '\n'); + const Query query_version = {"Please specify the version you would like to install", "versions"}; + _ver = query_version.execute(modlist); + } + else + { + std::cout << "Exiting installation!" << "\n"; + exit(0); + } + } + + std::cout << "Preparing to install " << _modname << " version " << _ver << "..." << "\n"; +} + +void InstallationManager::_campaign_query() +{ + _print_title("CAMPAIGN SELECTION"); + + for (rapidjson::SizeType i = 0; i < _cfg["campaigns"].Size(); i++) + { + std::cout << "[" << (i+1) << "] " << _cfg["campaigns"][i]["name"].GetString() << "\n" + << "Description: " << _cfg["campaigns"][i]["desc"].GetString() << "\n" << "\n"; + } + + /* CAMPAIGN SELECTION */ + const Query query_campaign = {"Please select your campaign", "campaigns"}; + _campaigns = query_campaign.execute(_cfg) - 1; + /* TURN SELECTION */ + const Query query_turns = {"Please select your preferred turns per year", "turns"}; + _turns = query_turns.execute(_cfg) - 1; +} + +void InstallationManager::_pack_query() +{ + _print_title("PACK SELECTION"); + const Query query_pack = {"Do you want to install this pack?"}; + for (rapidjson::SizeType i = 0; i < _cfg["packs"].Size(); i++) + { + std::cout << "\n" << _cfg["packs"][i]["name"].GetString() << "\n" + << _cfg["packs"][i]["desc"].GetString() << "\n"; + + _packs.push_back(query_pack.execute(_cfg)); + + std::cin.clear(); + } + + +} + +void InstallationManager::_grab_json(const std::string &path, rapidjson::Document &d) +{ + if ((access(path.c_str(), F_OK) != 0)) + { + throw ExceptionHandler(INVALID_ARGUMENT_ERROR); + } + + /* + std::ifstream ifs(path.c_str()); + rapidjson::IStreamWrapper isw(ifs); + d.ParseStream(isw); + + if (d.ParseStream<0>(isw).HasParseError()) + { + std::cerr << "Failed to parse via string stream!" << "\n"; + std::cerr << "RapidJson ERROR: "; + std::cerr << GetParseError_En(d.GetParseError()) << "\n"; + std::cerr << "Filepath: " << path << "\n"; + throw ExceptionHandler(CUSTOM_ERROR, "Failed to parse JSON file!"); + } + else + { + d.ParseStream(isw); + return 0; + } + if (d.Parse<0>(path.c_str()).HasParseError()) + { + std::cerr << "Failed to parse via string!" << "\n"; + std::cerr << "RapidJson ERROR: "; + std::cerr << GetParseError_En(d.GetParseError()) << "\n"; + std::cerr << "Filepath: " << path << "\n"; + throw ExceptionHandler(CUSTOM_ERROR, "Failed to parse JSON file!"); + } + { + d.Parse(path.c_str()); + return 0; + } + */ + + char readBuffer[65536]; + FILE *fp = fopen(path.c_str(), "r"); + rapidjson::FileReadStream is(fp, readBuffer, sizeof(readBuffer)); + + if (d.ParseStream<0>(is).HasParseError()) + { + std::cerr << "Failed to parse via file stream!" << "\n"; + std::cerr << "RapidJson ERROR: "; + std::cerr << GetParseError_En(d.GetParseError()) << "\n"; + std::cerr << "Filepath: " << path << "\n"; + throw ExceptionHandler(CUSTOM_ERROR, "Failed to parse JSON file!"); + } + else + { + d.ParseStream(is); + } +} + +void InstallationManager::_print_title(const std::string &str) +{ + std::cout << str << "\n"; + for (auto i : str) + { + std::cout << "="; + } + std::cout << "\n"; +} + +void InstallationManager::_print_selection(const int i) +{ + std::deque print; + switch (i) + { + case 3: + { + std::stringstream ss; + std::vector packs; + for (rapidjson::SizeType i = 0; i < _cfg["packs"].Size(); i++) + { + if (_packs[i]) + { + packs.push_back(_cfg["packs"][i]["name"].GetString()); + } + } + ss << "Packs: "; + for (auto it = packs.begin(); it != packs.end(); ++it) + { + ss << *it; + if (it != (packs.end() - 1)) + { + ss << ", "; + } + } + ss << "\n"; + print.push_front(ss.str()); + } + case 2: + { + print.push_front("Turns: " + std::string(_cfg["turns"][_turns]["name"].GetString()) + "\n"); + } + case 1: + { + print.push_front("Campaign: " + std::string(_cfg["campaigns"][_campaigns]["name"].GetString()) + "\n"); + } + } + + for (auto &i : print) std::cout << i; +} diff --git a/src/main.cpp b/src/main.cpp index 16c0b7b..fc9ddc4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,72 +1,7 @@ -#include -#include +#include "../include/etw_lmm.hpp" -/** -* Function prototypes -*/ -int list(const std::string&); -int calibrate(const std::string&); -std::string retrieve_path(); -void help_menu(); -int install(const std::string&, const std::string&); - -int main(int argc, char** argv) +int main(int argc, char *argv[]) { - /** - * Paths - */ - const std::string hd = retrieve_path(); - - for (unsigned int i = 0; i < path.size(); i++) - { - path[i].insert(0, hd); - } - /** - * Turning char arguments to string - */ - std::vector args(argv, argv + argc); - - /** - * Argument handler - */ - if (args[1] == "help") - { - help_menu(); - } - else if (args[1] == "calibrate") - { - return calibrate(hd); - } - else if (args[1] == "list") - { - return list(hd); - } - else if (args[1] == "install") - { - if (argc < 3) // TO DO - { - std::cerr << "Argument required!" << std::endl; - return 1; - } - - else if (install(hd, args[2])) - { - return 1; - } - } - else if (args[1] == "uninstall") - { - - } - else if (args[1] == "query") - { - - } - else - { - help_menu(); - return 1; - } - - return 0; + ETW_LMM lmm(argc, argv); + return lmm.execute(); } diff --git a/src/md5.cpp b/src/md5.cpp deleted file mode 100644 index b444c2d..0000000 --- a/src/md5.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include /* md5sum */ -#include -#include -#include -#include - - -/** -* Get the size of the file by its file descriptor -* -* @param file_descript -*/ -unsigned long get_size_by_fd(int fd) -{ - struct stat statbuf; - if (fstat(fd, &statbuf) < 0) - { - exit(EXIT_FAILURE); - } - return statbuf.st_size; -} - -/** -* Print the MD5 sum as hex-digits. -* -* @param MD5 checksum -*/ -void print_md5_sum(unsigned char* md5) -{ - for (unsigned int i = 0; i < MD5_DIGEST_LENGTH; i++) - { - printf("%02x", md5[i]); - } -} - -/** -* Converts MD5 checksum to hex. -* -* @param MD5 checksum -*/ -std::string to_hex(unsigned char* md5) -{ - static const char digits[] = "0123456789abcdef"; - std::string result; - - for (unsigned int i = 0; i < MD5_DIGEST_LENGTH; i++) - { - result += digits[md5[i] / MD5_DIGEST_LENGTH]; - result += digits[md5[i] % MD5_DIGEST_LENGTH]; - } - - return result; -} - -/** -* Computes a MD5 checksum and compares it to its corresponding calibrated checksum. -* -* @param File path to compute checksum, corresponding calibrated checksum -*/ -void md5sum(std::string file_path, unsigned char md5[MD5_DIGEST_LENGTH]) -{ - /* - * Variable initialisation - */ - int file_descript; - unsigned long file_size; - unsigned char* file_buffer; - - /* Checks if the file path can be opened. */ - file_descript = open(file_path.c_str(), O_RDONLY); - if (file_descript < 0) - { - std::cerr << "Unable to open " << file_path << std::endl; - exit(EXIT_FAILURE); - } - - /* Grabs the size of file */ - file_size = get_size_by_fd(file_descript); - - /* Generates the buffer */ - file_buffer = (unsigned char*) mmap((caddr_t)0, file_size, PROT_READ, MAP_SHARED, file_descript, 0); - - /* Computes the MD5 checksum to result */ - MD5(file_buffer, file_size, md5); - - /* Removes fime_buffer and file_size */ - munmap(file_buffer, file_size); -} diff --git a/src/md5_wrapper.cpp b/src/md5_wrapper.cpp new file mode 100644 index 0000000..84cba7a --- /dev/null +++ b/src/md5_wrapper.cpp @@ -0,0 +1,17 @@ +#include /* md5sum */ + +#include "../include/md5_wrapper.hpp" + +MD5_wrapper::MD5_wrapper(unsigned char *md5) +{ + static const char digits[] = "0123456789abcdef"; + + for (unsigned int i = 0; i < MD5_DIGEST_LENGTH; i++) + { + _md5 += digits[md5[i] / MD5_DIGEST_LENGTH]; + _md5 += digits[md5[i] % MD5_DIGEST_LENGTH]; + } +} + +MD5_wrapper::MD5_wrapper(const std::string &s) +: _md5(s) {} diff --git a/src/query.cpp b/src/query.cpp new file mode 100644 index 0000000..389c8cc --- /dev/null +++ b/src/query.cpp @@ -0,0 +1,116 @@ +#include "../include/query.hpp" + +template <> +Query::Query(const std::string &query) +: _query(std::move(query)) {} + +template <> +Query::Query(const std::string &query, char *list) +: _query(std::move(query)), _list(std::move(list)) {} + +template <> +Query::Query(const std::string &query, char *list) +: _query(std::move(query)), _list(std::move(list)) {} + +/*template +OUTPUT Query::execute() +{ + switch (_qf) + { + case BOOL: return _bool(); + case STRING: return _string(); + case INT: return _int(); + default: + throw ExceptionHandler(CUSTOM_ERROR, "No flag set for query."); + } +}*/ + +template <> +bool Query::execute(const rapidjson::Document &cfg) const +{ + char in; + + do + { + std::cout << _query << " [Y/n] "; + std::cin.get(in); + + switch (in) + { + case 'N': + case 'n': std::cin.ignore(std::numeric_limits::max(), '\n'); return false; + case 'Y': + case 'y': std::cin.ignore(std::numeric_limits::max(), '\n'); + case '\n': return true; + default: + std::cin.clear(); + std::cin.ignore(std::numeric_limits::max(), '\n'); + std::cerr << "ERROR: \"" << in << "\" is not a valid input!\n"; + } + } while (true); +} + +template <> +std::string Query::execute(const rapidjson::Document &cfg) const +{ + std::string input; + + do + { + std::cout << _query << " [enter q to quit]" << "\n"; + std::cin.clear(); + + std::cout << ">>> "; + std::getline(std::cin, input); + + if (input == "q" || input == "q\n") { exit(0); } + + for (rapidjson::SizeType i = 0; i < cfg[_list].Size(); i++) + { + if (input == cfg[_list][i].GetString()) + { + return std::move(input); + } + } + + std::cout << "Error! \"" << input << "\" not found in " << _list << " list!" << "\n"; + } while (true); +} + +template <> +int Query::execute(const rapidjson::Document &cfg) const +{ + char input; + int output; + + do + { + std::cout << "\n" << _query; + + for (rapidjson::SizeType i = 0; i < cfg[_list].Size(); i++) + { + std::cout << " [" << (i+1) << "] " << cfg[_list][i]["name"].GetString(); + } + + std::cout << " [press q to exit]" << "\n"; + + std::cout << ">>> "; + std::cin.get(input); + output = input - '0'; + + if (input == 'q') + { + exit(0); + } + + if (output < 1 || output > static_cast(cfg[_list].Size())) + { + std::cout << "Error! \"" << input << "\" is not a valid input." << "\n"; + } + + std::cin.clear(); + std::cin.ignore(std::numeric_limits::max(), '\n'); + } while (output < 1 || output > static_cast(cfg[_list].Size())); + + return output; +} diff --git a/src/utils.cpp b/src/utils.cpp deleted file mode 100644 index 389867b..0000000 --- a/src/utils.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -std::string retrieve_path() -{ - /* Retrieve home directory */ - const char* char_homedir; - if ((char_homedir = getenv("HOME")) == NULL) - { - struct passwd *pw = getpwuid(getuid()); - char_homedir = pw->pw_dir; - } - - /* Convert char pointer to string */ - const std::string s(char_homedir); - - return s; -} - -int replace_string(const std::string& file, const std::string& old_str, const std::string& new_str) -{ - std::string cmd = "sed -i -- 's/" + old_str + "/" + new_str + "/g' " + file; - - if (system(cmd.c_str())) - { - std::cerr << "Failed to calibrate: " << file; - return 1; - } - - return 0; -} - -/** -* 0 = true -* 1 = false -*/ -int dir_not_exists(const std::string& dir) -{ - struct stat sb; - - if (stat(dir.c_str(), &sb) != 0) - { - return 1; - } - - return !S_ISDIR(sb.st_mode); -} - -int file_not_exists(const std::string& dir) -{ - struct stat sb; - - if (stat(dir.c_str(), &sb) != 0) - { - return 1; - } - - return !S_ISREG(sb.st_mode); -} - -int yn_query(const std::string& query, bool b_yes) -{ - const std::string str_yn = b_yes ? " [Y/n] " : " [y/N] " ; - char yn; - - std::cout << query << str_yn; - std::cin.get(yn); - - do - { - switch (yn) - { - case 'Y': - case 'y': std::cin.ignore(std::numeric_limits::max(), '\n'); return 1; - case 'N': - case 'n': std::cin.ignore(std::numeric_limits::max(), '\n'); return 0; - case '\n': return b_yes ? 1 : 0; - default: - std::cin.clear(); - std::cin.ignore(std::numeric_limits::max(), '\n'); - std::cerr << "Error! \"" << yn << "\" is not a valid input!\n" << query << str_yn; - std::cin.get(yn); - } - } while (1); -}