443 lines
12 KiB
C++
443 lines
12 KiB
C++
#include "../include/installationmanager.hpp"
|
|
|
|
#include <unistd.h>
|
|
#include <sstream>
|
|
#include <deque>
|
|
#include <curl/curl.h>
|
|
#include <curl/easy.h>
|
|
#include <sys/stat.h> // fstat
|
|
#include <sys/types.h> // fstat
|
|
#include <cstdio>
|
|
#include <fstream>
|
|
|
|
#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<bool> 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<bool> 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<std::string> 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<int> query_campaign = {"Please select your campaign", "campaigns"};
|
|
_campaigns = query_campaign.execute(_cfg) - 1;
|
|
/* TURN SELECTION */
|
|
const Query<int> 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<bool> 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<std::string> print;
|
|
switch (i)
|
|
{
|
|
case 3:
|
|
{
|
|
std::stringstream ss;
|
|
std::vector<std::string> 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<std::string> 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<OStreamWrapper> writer(osw);
|
|
json.Accept(writer);*/
|
|
|
|
const std::string fp = _hd + INSTALLED.GetPath();
|
|
|
|
CSV csv(std::move(fp));
|
|
csv.Write();
|
|
csv.Update();
|
|
}
|