feat: Add more features

This commit is contained in:
aumars 2018-02-22 14:31:59 +01:00
parent 535253f910
commit 57b2d75593
8 changed files with 420 additions and 73 deletions

View File

@ -17,6 +17,7 @@ set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
# Find necessary OpenSSL libraries
find_package (OpenSSL REQUIRED)
find_package (CURL REQUIRED)
# Create uninstall target
configure_file(
@ -29,6 +30,7 @@ add_custom_target(uninstall
# Pre-build: include libraries
include_directories (${OPENSSL_INCLUDE_DIR})
include_directories (${PROJECT_BINARY_DIR})
include_directories (${CURL_INCLUDE_DIRS})
# Pre-build: set source files
set (SOURCE_FILES src/main.cpp
@ -41,13 +43,15 @@ src/generic.cpp
src/exceptionhandler.cpp
src/calibrationmanager.cpp
src/installationmanager.cpp
src/query.cpp)
src/query.cpp
src/csv.cpp)
# Build: set binary to source files
add_executable (etw-lmm ${SOURCE_FILES})
# Post-build: target libraries
target_link_libraries (etw-lmm ${OPENSSL_LIBRARIES})
target_link_libraries (etw-lmm ${CURL_LIBRARIES})
# Pre-install: create config directory
file(MAKE_DIRECTORY $ENV{HOME}/.local/share/etw-lmm)

View File

@ -22,66 +22,12 @@ query (available mods)
list (installed mods)
list ~/.config/etw-lmm/INSTALLED
calibrate (game files) md5sum files
CHECK IF MODS ARE INSTALLED
~/.local/share/feral-interactive/Empire/AppData/scripts/preferences_empire_scripts.txt
fbf65ef80563e0bd76836c150e49ecd0 preferences_empire_scripts.txt
if not
set preferences_empire_scripts.txt to preferences_empire_scripts.txt_utf8
SET TO "write_preferences_at_exit false"
REMOVE "\nmod [mod];"
preferences_empire_scripts.txt.backup
7edb7bc5b92a230bb1bcf0f5e9d28ef9 preferences.empire_script.txt_utf8 FALSE
ef1834b11d0cd72fece41236a1005c00 preferences.empire_script.txt_utf8 TRUE
5d064a6cfdc35f4714fd31d5d99735d0 preferences.empire_script.txt FALSE
fbf65ef80563e0bd76836c150e49ecd0 preferences.empire_script.txt TRUE
.txt_backup is same
TEXT BUG FIX
~/.local/share/feral-interactive/Empire/preferences
SET TO "<value name="UsePBOSurfaces" type="integer">0</value>"
data/campaigns/main
89b18f2c2c3f98b6fed7ed7ae4e3e4f9 scripting.lua
084b14b4174b93c6489422fe33dc7b2b startpos.esf
create main_backup/
data/campaign_maps/global_map
086f52fb74d2d0c03ef4e0a969fac4d9 america_lookup.tga
92569145991bb8c1937769f023264fbb america_map.tga
c77b3468113aae3e1ebf43d0a2279986 brazil_map.tga
98f9a1c82a9fbf2ce15aa9c37eb5735c east_indies_lookup.tga
c15c8e4db971585dce18f764db084f2c east_indies_map.tga
62c929e8849b77b3d3060bdd448fe0db europe_lookup.tga
dd31679ed17096692475dad452c2d2c5 europe_map.tga
a731bf4b7574ad3abf4cbbff0873880d india_lookup.tga
6a69c2b16c421aabc642396a8f5ecf48 india_map.tga
e03faec4549366c5d6df0e37e37e9c28 ivory_coast_map.tga
46788119db0b0c3e79d307f7005dbac4 madagascar_map.tga
84648966d1ad44ae072b698dc0e311d4 stratradar_america.tga
c77b3468113aae3e1ebf43d0a2279986 stratradar_brazil.tga
c15c8e4db971585dce18f764db084f2c stratradar_east_indies.tga
b95431493f9a1078c013efb627cfe12a stratradar_europe.tga
e25d189f93fa3761a48a033d0818176a stratradar_india.tga
e03faec4549366c5d6df0e37e37e9c28 stratradar_ivory_coast.tga
46788119db0b0c3e79d307f7005dbac4 stratradar_madagascar.tga
d3cd17af453c9aaa5dd64dfeb8499585 pathfinding.esf
57d6dac4b3c39587819607fce460e00e poi.esf
e30d0b9873fad972d490a7cacb039ea9 regions.esf
ebd02a0e4f39e07816054445cbd59d3d sea_grids.esf
dd853d15992a5c0f15da0cdd6ac443c1 trade_routes.esf
create global_map_backup/
add support for c++17 <filesystem> and c++11 if users dont want
permanent status list of mods: basically what the user had chosen for the mod
in installed.csv or something
====
refactor code from C++ to C
int
use enumerated error codes with char
breakup the functions/DRY everything
use functions for sed iconv cp rm

32
include/csv.hpp Normal file
View File

@ -0,0 +1,32 @@
#ifndef CSV_HPP
#define CSV_HPP
#include <fstream>
#include "generic.hpp"
class CSV : public Generic
{
public:
CSV(const std::string&);
~CSV();
void Write();
void Update();
void Close();
unsigned int Width();
std::string GetValue(unsigned int, unsigned int);
std::string GetColumnValue(unsigned int, const std::string&);
void SetValue(const std::string&, unsigned int, unsigned int);
void SetColumnValue(const std::string&, unsigned int, const std::string&);
void AddColumnValue(const std::string&, const std::string&);
unsigned int ValuesInRow(unsigned int);
unsigned int ValuesInColumn(unsigned int);
unsigned int ValuesInColumn(const std::string&);
private:
std::fstream _csv;
std::string _temp;
std::string _GetRow(unsigned int);
};
#endif

View File

@ -23,6 +23,9 @@ private:
void _version_query();
void _campaign_query();
void _pack_query();
void _download(const std::string&, const std::string&);
void _install(const std::string&, const std::string&, const std::string&);
void _installall();
};
#endif

View File

@ -77,7 +77,7 @@ const std::map<std::string, std::string> PATH_MD5_MAP =
const Generic INSTALLED =
{
"/.local/share/etw-lmm/installed"
"/.local/share/etw-lmm/installed.csv"
};
const CalibrateText EMPIRE_SCRIPT =

View File

@ -1,4 +1,3 @@
#include <sys/stat.h>
#include <iostream>
#include <fstream>
#include <sys/sendfile.h> // sendfile

164
src/csv.cpp Normal file
View File

@ -0,0 +1,164 @@
#include "../include/csv.hpp"
#include <unistd.h>
#include <sys/sendfile.h> // sendfile
#include <fcntl.h> // open
#include <sys/stat.h> // fstat
#include <limits>
#include "../include/exceptionhandler.hpp"
CSV::CSV(const std::string &fp)
: Generic(fp), _temp(fp + "~")
{
}
CSV::~CSV()
{
}
unsigned int CSV::Width()
{
/* check if file exists, returns 0 if otherwise */
if (access(_path.c_str(), F_OK) < 0)
{
return 0;
}
std::string comma;
getline(_csv, comma); // grab first line
/* check if first line is empty, return 0 if otherwise */
if (comma == "\n")
{
return 0;
}
unsigned int n = 1;
for (unsigned int i = 0; i < comma.length(); i++) // count number of commas
{
if (comma.at(i) == ',') n++;
}
return n;
}
std::string CSV::GetValue(unsigned int row, unsigned int column)
{
std::string str = _GetRow(row);
/* find first column, column 0 */
if (column == 0)
{
/* find position of first comma */
std::size_t st = str.find_first_of(",");
/* returns substring from position zero of string to position of comma,
omitting comma */
return str.substr(0, st);
}
/* number of commas removed */
unsigned int n = 0;
/* width, i.e. number of values width-wise, stored as const */
const unsigned int width = Width();
do
{
/* find last comma */
std::size_t st = str.find_last_of(","); // returns npos if none
/* remove comma and word following comma to length */
str.erase(st, str.length());
/* increment no of commas omitted */
n++;
/* if number of commas is equal to width - column,that is the position of
desired value */
} while ((width - column) < n);
/* find comma before wanted value */
std::size_t st = str.find_last_of(",");
/* make substring that starts after comma */
return str.substr(st + 1);
}
std::string CSV::GetColumnValue(unsigned int row, const std::string &column)
{
std::string const str = _GetRow(row);
std::size_t st = str.find(column);
return str.substr(st, column.length());
}
void CSV::SetColumnValue(const std::string &value, unsigned int row, const std::string &column)
{
}
void CSV::AddColumnValue(const std::string &value, const std::string &column)
{
/*
1. find column number
2. put preceding rows into a string vector until a empty value is found
** add commas if necessary
3.
*/
}
void CSV::Write()
{
_csv.open(_temp); // write
}
void CSV::Update()
{
int source = open(_temp.c_str(), O_RDONLY, 0);
int dest = open(_path.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(_path.c_str(), F_OK) != 0)
{
throw ExceptionHandler(CUSTOM_ERROR, "Backing up " + _path + " failed!");
}*/
Close();
}
void CSV::Close()
{
_csv.close();
std::remove(_temp.c_str());
}
std::string CSV::_GetRow(unsigned int n)
{
/* Set position to very start */
_csv.seekg(std::ios::beg);
/* Loop skipping every new line, finding row */
for (unsigned int i = 0; i < n; i++)
{
_csv.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
}
/* columns */
std::string str;
getline(_csv, str);
return str;
}

View File

@ -3,12 +3,24 @@
#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"
#include "../include/csv.hpp"
#include "../include/rapidjson/istreamwrapper.h"
#include "../include/rapidjson/error/en.h"
#include "../include/rapidjson/filereadstream.h"
#include "../include/rapidjson/filewritestream.h"
#include "../include/rapidjson/ostreamwrapper.h"
#include "../include/rapidjson/writer.h"
InstallationManager::InstallationManager(const std::string &hd, const std::string &mod)
: _hd(hd), _mod(std::move(mod)) {}
@ -28,24 +40,36 @@ void InstallationManager::execute()
std::cout << "\n" << "You have selected:" << "\n";
_print_selection(2);
/* PACKS SELECTION */
/*
* PACKS SELECTION
*/
_pack_query();
/* SELECTION SUMMARY */
/*
* 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(0);
exit(EXIT_SUCCESS);
}
// INSTALLATION
/*
* INSTALLATION
*/
std::cout << "Installing " << _modname << " version " << _ver << "...\n";
_installall();
std::cout << "Installation complete!\n";
}
void InstallationManager::_version_query()
@ -62,6 +86,7 @@ void InstallationManager::_version_query()
<< "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)
@ -74,33 +99,36 @@ void InstallationManager::_version_query()
std::cout << "\n";
/* PRE-INSTALLATION PROCESS */
/* --VERSION */
/*
* 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)
{
// std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
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(0);
exit(EXIT_SUCCESS);
}
}
std::cout << "Preparing to install " << _modname << " version " << _ver << "..." << "\n";
}
void InstallationManager::_campaign_query()
@ -134,8 +162,6 @@ void InstallationManager::_pack_query()
std::cin.clear();
}
}
void InstallationManager::_grab_json(const std::string &path, rapidjson::Document &d)
@ -245,3 +271,176 @@ void InstallationManager::_print_selection(const int i)
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();
}