#include "../include/installationmanager.hpp" #include #include #include #include #include #include // fstat #include // fstat #include #include #include "../include/exceptionhandler.hpp" #include "../include/query.hpp" #include "../include/paths.hpp" 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 campaign, turn, and pack selection */ _print_selection(3); std::cout << "\n"; /* Final confirmation */ const Query query_final = {"Are you sure you want to install this selection?"}; if (!query_final.execute(_cfg)) { exit(EXIT_SUCCESS); } /* * INSTALLATION */ std::cout << "Installing " << _modname << " version " << _ver << "...\n"; _installall(); std::cout << "Installation complete!\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: "; /* List 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 */ /* Name and version confirmation */ 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)) { /* Set mod name and version */ _modname = modlist["name"].GetString(); _ver = modlist["versions"][0].GetString(); } else { /* If more than 1 version, request for version */ if (modlist["versions"].Size() > 1) { 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(EXIT_SUCCESS); } } } 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; } size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) { size_t written = fwrite(ptr, size, nmemb, stream); return written; } void InstallationManager::_download(const std::string &tmp, const std::string &dwn) { CURL *curl; FILE *fp; CURLcode res; curl = curl_easy_init(); if (curl) { fp = fopen(tmp.c_str(), "wb"); curl_easy_setopt(curl, CURLOPT_URL, dwn.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); res = curl_easy_perform(curl); /* always cleanup */ curl_easy_cleanup(curl); fclose(fp); } else { throw ExceptionHandler(CUSTOM_ERROR, "Failed to download" + dwn); } } /* * Downloads from a url: MOD + file * To a temporary location: TMP + path + file * To be moved to path + file */ void InstallationManager::_install(const std::string &file, const std::string &path, const std::string &fp) { /* Constant strings */ static const std::string PREFIX = "https://gitlab.com/tw-lmm/etw/"; static const std::string BINDER = "/raw/master/"; static const std::string MOD = PREFIX + _mod + BINDER + _ver + "/"; static const std::string TMP = "/tmp/etw_lmm/"; const std::string temp = TMP + file; const std::string download = MOD + path + file; std::cout << "temp: " << temp << "\n" << "download: " << download << "\n" << "fp: " << fp << "\n"; /* Checks if directory /tmp/etw_lmm/ exists */ struct stat tmp; if (stat(TMP.c_str(), &tmp) != 0) { mkdir(TMP.c_str(), 0700); } _download(temp, download); if (access(fp.c_str(), F_OK) < 0) { if (std::remove(fp.c_str())) { throw ExceptionHandler(CUSTOM_ERROR, "Cannot remove file " + fp + "!"); } } if (rename(temp.c_str(), fp.c_str()) < 0) { throw ExceptionHandler(CUSTOM_ERROR, "Cannot move file " + file + "!"); } if (std::remove(temp.c_str()) < 0) { throw ExceptionHandler(CUSTOM_ERROR, "Cannot remove file " + temp + "!"); } } void InstallationManager::_installall() { /* Install manifest */ std::vector installed; /* * CAMPAIGN - DEPENDENCIES */ std::cout << "Installing campaign dependencies...\n"; for (rapidjson::SizeType i = 0; _cfg["campaign_default"].Size(); i++) { const std::string fp = _hd + STEAM_PREFIX + std::string(_cfg["campaign_default"][i]["path"].GetString()) + std::string(_cfg["campaign_default"][i]["file"].GetString()); std::cout << "Downloading " << std::string(_cfg["campaign_default"][i]["file"].GetString()) << "\n"; _install(std::string(_cfg["campaign_default"][i]["file"].GetString()), std::string(_cfg["campaigns"][_campaigns]["id"].GetString()) + "/default/", fp); installed.push_back(std::move(fp)); } /* * CAMPAIGN - TURNS */ std::cout << "Installing campaign preferences...\n"; const std::string LUA = "scripting.lua"; const std::string ESF = "startpos.esf"; // const std::string CAMPAIGN = std::string(_cfg["campaigns"][_campaigns]["id"].GetString()) + "/" + std::to_string((_turns+1)*2) + "_turns/main/"; const std::string fp_lua = _hd + STEAM_PREFIX + MAIN_CAMPAIGN + "/" + LUA; const std::string fp_esf = _hd + STEAM_PREFIX + MAIN_CAMPAIGN + "/" + ESF; _install(LUA, std::string(_cfg["campaigns"][_campaigns]["id"].GetString()) + "/" + std::string(_cfg["turns"][_turns]["id"].GetString()) + "/main/", fp_lua); installed.push_back(std::move(fp_lua)); _install(ESF, std::string(_cfg["campaigns"][_campaigns]["id"].GetString()) + "/" + std::string(_cfg["turns"][_turns]["id"].GetString()) + "/main/", fp_esf); installed.push_back(std::move(fp_esf)); /* * DATA PACKS */ std::cout << "Installing packs...\n"; for (auto b : _packs) { rapidjson::SizeType i = 0; if (b) { if (_cfg["packs"][i]["files"].Size() > 1) { for (rapidjson::SizeType j = 0; j < _cfg["packs"][i]["files"].Size(); j++) { const std::string fp = _hd + STEAM_PREFIX + "data/" + std::string(_cfg["packs"][i]["files"][j].GetString()); _install(std::string(_cfg["packs"][i]["files"][j].GetString()), "data/", fp); installed.push_back(std::move(fp)); } } else { const std::string fp = _hd + STEAM_PREFIX + "data/" + std::string(_cfg["packs"][i]["files"][0].GetString()); _install(std::string(_cfg["packs"][i]["files"][0].GetString()), "data/", fp); installed.push_back(std::move(fp)); } } i++; } /* Logging installed files into installed.json */ /*rapidjson::Document json; json.Parse(_hd + INSTALLED.GetPath()); rapidjson::Value json_paths(kArrayType); rapidjson::Document::AllocatorType& allocator = json.GetAllocator(); for (auto &i : installed) { json_paths.PushBack(i, allocator); } std::ofstream ofs(_hd + INSTALLED.GetPath()); rapidjson::OStreamWrapper osw(ofs); rapidjson::Writer writer(osw); json.Accept(writer);*/ const std::string fp = _hd + INSTALLED.GetPath(); CSV csv(std::move(fp)); csv.Write(); csv.Update(); }