feat: Calibration and mod option querying are functional, as well as basic commands

This commit is contained in:
aumars 2017-12-29 17:11:11 +01:00
parent 403735ab4e
commit 535253f910
32 changed files with 1275 additions and 848 deletions

3
.gitignore vendored
View File

@ -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

View File

@ -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)

17
include/backup.hpp Normal file
View File

@ -0,0 +1,17 @@
#ifndef BACKUP_HPP
#define BACKUP_HPP
#include <string>
#include <vector>
class Backup
{
public:
Backup(const std::string&, const std::vector<std::string>&);
void Execute(const std::string&) const;
private:
std::string _prefix;
std::vector<std::string> _vec;
};
#endif

55
include/calibrate.hpp Normal file
View File

@ -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

23
include/calibratetext.hpp Normal file
View File

@ -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

View File

@ -0,0 +1,21 @@
#ifndef CALIBRATIONMANAGER_HPP
#define CALIBRATIONMANAGER_HPP
#include <string>
#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

View File

@ -1,2 +0,0 @@
#define etw_lmm_VERSION_MAJOR @etw-lmm_VERSION_MAJOR@
#define etw_lmm_VERSION_MINOR @etw-lmm_VERSION_MINOR@

View File

@ -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

25
include/etw_lmm.hpp Normal file
View File

@ -0,0 +1,25 @@
#ifndef ETW_LMM_HPP
#define ETW_LMM_HPP
#include <vector>
#include <string>
class ETW_LMM
{
public:
ETW_LMM(int argc, char **argv);
unsigned int execute();
private:
std::vector<std::string> _args;
std::string _hd;
void _set_homedir();
void _help();
void _menu();
void _list();
void _calibrate();
void _install();
void _uninstall(const std::string&);
};
#endif

View File

@ -0,0 +1,28 @@
#ifndef EXCEPTION_HANDLER_HPP
#define EXCEPTION_HANDLER_HPP
#include <string>
#include <iostream>
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

20
include/generic.hpp Normal file
View File

@ -0,0 +1,20 @@
#ifndef GENERIC_HPP
#define GENERIC_HPP
#include <string>
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

View File

@ -0,0 +1,28 @@
#ifndef INSTALLATIONMANAGER_HPP
#define INSTALLATIONMANAGER_HPP
#include <string>
#include <vector>
#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<bool> _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

36
include/md5_wrapper.hpp Normal file
View File

@ -0,0 +1,36 @@
#ifndef MD5_WRAPPER_HPP
#define MD5_WRAPPER_HPP
#include <string>
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

158
include/paths.hpp Normal file
View File

@ -0,0 +1,158 @@
#ifndef PATHS_HPP
#define PATHS_HPP
#include <vector>
#include <array>
#include <map>
#include <string>
#include <openssl/md5.h> /* 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<std::string, std::string> 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<CalibrateText, 2> CALIB_VERIFY_ARR =
{
EMPIRE_SCRIPT,
PREFERENCES
};
const std::vector<std::string> MAIN_CAMPAIGN_PATH_VEC =
{
"scripting.lua",
"startpos.esf"
};
const std::vector<std::string> 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, 2> BACKUP_ARR =
{
MAIN_CAMPAIGN_BACKUP,
GLOBAL_MAP_BACKUP
};
#endif

26
include/query.hpp Normal file
View File

@ -0,0 +1,26 @@
#ifndef QUERY_HPP
#define QUERY_HPP
#include <string>
#include <limits>
#include <iostream>
#include "rapidjson/document.h"
template <class OUTPUT>
class Query
{
public:
Query<OUTPUT>(const std::string&);
Query<OUTPUT>(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

View File

@ -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"
]
}

View File

@ -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/"
}
}

View File

@ -1,182 +0,0 @@
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <pwd.h>
#include <vector>
#include <cstring>
#include <openssl/md5.h> /* 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<std::string>& 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;
}

53
src/backup.cpp Normal file
View File

@ -0,0 +1,53 @@
#include <sys/stat.h>
#include <iostream>
#include <fstream>
#include <sys/sendfile.h> // sendfile
#include <fcntl.h> // open
#include <unistd.h> // close
#include <sys/stat.h> // fstat
#include <sys/types.h> // fstat
#include "../include/exceptionhandler.hpp"
#include "../include/backup.hpp"
Backup::Backup(const std::string &prefix, const std::vector<std::string> &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!");
}
}
}

View File

@ -1,63 +1,67 @@
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <iostream>
#include <openssl/md5.h> /* 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<unsigned char*>(mmap(static_cast<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);
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;
}

68
src/calibratetext.cpp Normal file
View File

@ -0,0 +1,68 @@
#include <unistd.h>
#include <pwd.h>
#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";
}*/
}

View File

@ -0,0 +1,87 @@
#include "../include/calibrationmanager.hpp"
#include <unistd.h>
#include <pwd.h>
#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";
}

139
src/etw_lmm.cpp Normal file
View File

@ -0,0 +1,139 @@
#include <unistd.h>
#include <pwd.h>
#include <iostream>
#include <fstream>
#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();
}

27
src/exceptionhandler.cpp Normal file
View File

@ -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";
}

4
src/generic.cpp Normal file
View File

@ -0,0 +1,4 @@
#include "../include/generic.hpp"
Generic::Generic(const std::string &path)
: _path(path) {};

View File

@ -1,268 +0,0 @@
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#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<std::streamsize>::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<std::streamsize>::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<std::streamsize>::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<std::streamsize>::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<bool> pack_vector;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// std::cin.ignore(std::numeric_limits<std::streamsize>::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;
}

247
src/installationmanager.cpp Normal file
View File

@ -0,0 +1,247 @@
#include "../include/installationmanager.hpp"
#include <unistd.h>
#include <sstream>
#include <deque>
#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<bool> 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<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))
{
_modname = modlist["name"].GetString();
_ver = modlist["versions"][0].GetString();
}
else
{
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);
}
}
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<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;
}

View File

@ -1,72 +1,7 @@
#include <iostream>
#include <vector>
#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<std::string> 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();
}

View File

@ -1,89 +0,0 @@
#include <iostream>
#include <openssl/md5.h> /* md5sum */
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <cstring>
/**
* 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);
}

17
src/md5_wrapper.cpp Normal file
View File

@ -0,0 +1,17 @@
#include <openssl/md5.h> /* 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) {}

116
src/query.cpp Normal file
View File

@ -0,0 +1,116 @@
#include "../include/query.hpp"
template <>
Query<bool>::Query(const std::string &query)
: _query(std::move(query)) {}
template <>
Query<int>::Query(const std::string &query, char *list)
: _query(std::move(query)), _list(std::move(list)) {}
template <>
Query<std::string>::Query(const std::string &query, char *list)
: _query(std::move(query)), _list(std::move(list)) {}
/*template <class OUTPUT>
OUTPUT Query<OUTPUT>::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<bool>::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<std::streamsize>::max(), '\n'); return false;
case 'Y':
case 'y': std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
case '\n': return true;
default:
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cerr << "ERROR: \"" << in << "\" is not a valid input!\n";
}
} while (true);
}
template <>
std::string Query<std::string>::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<int>::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<int>(cfg[_list].Size()))
{
std::cout << "Error! \"" << input << "\" is not a valid input." << "\n";
}
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
} while (output < 1 || output > static_cast<int>(cfg[_list].Size()));
return output;
}

View File

@ -1,91 +0,0 @@
#include <unistd.h>
#include <pwd.h>
#include <string>
#include <fstream>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <algorithm>
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<std::streamsize>::max(), '\n'); return 1;
case 'N':
case 'n': std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); return 0;
case '\n': return b_yes ? 1 : 0;
default:
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cerr << "Error! \"" << yn << "\" is not a valid input!\n" << query << str_yn;
std::cin.get(yn);
}
} while (1);
}