This repository has been archived on 2021-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
modetw/src/installationmanager.cpp

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();
}