Compare commits
6 commits
1851867b7e
...
389dabfc77
Author | SHA1 | Date | |
---|---|---|---|
389dabfc77 | |||
07cbaefeac | |||
f9aa4bc817 | |||
e634ef037d | |||
bb85c3d6b0 | |||
b19c3e0025 |
23 changed files with 364 additions and 194 deletions
|
@ -20,7 +20,10 @@ set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
set(SAVETOOL_PROJECT_VERSION 1.5.0-pre)
|
set(SAVETOOL_PROJECT_VERSION 1.5.0-pre)
|
||||||
|
|
||||||
find_package(Corrade REQUIRED Main Containers Utility)
|
find_package(Corrade REQUIRED Containers Utility)
|
||||||
|
if(CORRADE_TARGET_WINDOWS)
|
||||||
|
find_package(Corrade REQUIRED Main)
|
||||||
|
endif()
|
||||||
find_package(Magnum REQUIRED GL Sdl2Application)
|
find_package(Magnum REQUIRED GL Sdl2Application)
|
||||||
find_package(MagnumIntegration REQUIRED ImGui)
|
find_package(MagnumIntegration REQUIRED ImGui)
|
||||||
find_package(CURL REQUIRED HTTPS)
|
find_package(CURL REQUIRED HTTPS)
|
||||||
|
@ -34,6 +37,10 @@ corrade_add_resource(Assets assets.conf)
|
||||||
add_subdirectory(Logger EXCLUDE_FROM_ALL)
|
add_subdirectory(Logger EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(UESaveFile EXCLUDE_FROM_ALL)
|
add_subdirectory(UESaveFile EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
|
if(CORRADE_TARGET_WINDOWS)
|
||||||
|
set(SAVETOOL_RC_FILE resource.rc)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(MassBuilderSaveTool WIN32
|
add_executable(MassBuilderSaveTool WIN32
|
||||||
main.cpp
|
main.cpp
|
||||||
SaveTool/SaveTool.h
|
SaveTool/SaveTool.h
|
||||||
|
@ -90,10 +97,13 @@ add_executable(MassBuilderSaveTool WIN32
|
||||||
Maps/WeaponTypes.hpp
|
Maps/WeaponTypes.hpp
|
||||||
ToastQueue/ToastQueue.h
|
ToastQueue/ToastQueue.h
|
||||||
ToastQueue/ToastQueue.cpp
|
ToastQueue/ToastQueue.cpp
|
||||||
|
UpdateChecker/UpdateChecker.h
|
||||||
|
UpdateChecker/UpdateChecker.cpp
|
||||||
Utilities/Crc32.h
|
Utilities/Crc32.h
|
||||||
|
Version/Version.h
|
||||||
FontAwesome/IconsFontAwesome5.h
|
FontAwesome/IconsFontAwesome5.h
|
||||||
FontAwesome/IconsFontAwesome5Brands.h
|
FontAwesome/IconsFontAwesome5Brands.h
|
||||||
resource.rc
|
${SAVETOOL_RC_FILE}
|
||||||
${Assets}
|
${Assets}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -102,9 +112,13 @@ if(CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||||
target_compile_definitions(MassBuilderSaveTool PRIVATE SAVETOOL_DEBUG_BUILD)
|
target_compile_definitions(MassBuilderSaveTool PRIVATE SAVETOOL_DEBUG_BUILD)
|
||||||
endif()
|
endif()
|
||||||
target_compile_definitions(MassBuilderSaveTool PRIVATE
|
target_compile_definitions(MassBuilderSaveTool PRIVATE
|
||||||
SAVETOOL_VERSION="${SAVETOOL_PROJECT_VERSION}"
|
SAVETOOL_VERSION_STRING="${SAVETOOL_PROJECT_VERSION}"
|
||||||
|
SAVETOOL_VERSION_MAJOR=1
|
||||||
|
SAVETOOL_VERSION_MINOR=5
|
||||||
|
SAVETOOL_VERSION_PATCH=0
|
||||||
|
SAVETOOL_VERSION_PRERELEASE=true
|
||||||
SAVETOOL_CODENAME="Friendly Valkyrie"
|
SAVETOOL_CODENAME="Friendly Valkyrie"
|
||||||
SUPPORTED_GAME_VERSION="0.9.x"
|
SAVETOOL_SUPPORTED_GAME_VERSION="0.10.x"
|
||||||
)
|
)
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL Release)
|
if(CMAKE_BUILD_TYPE STREQUAL Release)
|
||||||
|
@ -120,7 +134,6 @@ endif()
|
||||||
target_link_libraries(MassBuilderSaveTool PRIVATE
|
target_link_libraries(MassBuilderSaveTool PRIVATE
|
||||||
Corrade::Containers
|
Corrade::Containers
|
||||||
Corrade::Utility
|
Corrade::Utility
|
||||||
Corrade::Main
|
|
||||||
Magnum::Magnum
|
Magnum::Magnum
|
||||||
Magnum::GL
|
Magnum::GL
|
||||||
Magnum::Sdl2Application
|
Magnum::Sdl2Application
|
||||||
|
@ -130,6 +143,12 @@ target_link_libraries(MassBuilderSaveTool PRIVATE
|
||||||
efsw::efsw
|
efsw::efsw
|
||||||
libzip::zip
|
libzip::zip
|
||||||
CURL::libcurl_static
|
CURL::libcurl_static
|
||||||
|
)
|
||||||
|
|
||||||
|
if(CORRADE_TARGET_WINDOWS)
|
||||||
|
target_link_libraries(MassBuilderSaveTool PRIVATE
|
||||||
|
Corrade::Main
|
||||||
imm32
|
imm32
|
||||||
wtsapi32
|
wtsapi32
|
||||||
)
|
)
|
||||||
|
endif()
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
class MagnumLogBuffer : public std::stringbuf {
|
class MagnumLogBuffer : public std::stringbuf {
|
||||||
public:
|
public:
|
||||||
explicit MagnumLogBuffer(EntryType type);
|
explicit MagnumLogBuffer(EntryType type);
|
||||||
~MagnumLogBuffer();
|
~MagnumLogBuffer() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int sync() override;
|
int sync() override;
|
||||||
|
|
|
@ -23,9 +23,9 @@ using namespace Corrade;
|
||||||
using namespace Containers::Literals;
|
using namespace Containers::Literals;
|
||||||
|
|
||||||
struct StoryProgressPoint {
|
struct StoryProgressPoint {
|
||||||
std::int32_t id;
|
std::int32_t id{};
|
||||||
Containers::StringView chapter;
|
Containers::StringView chapter = nullptr;
|
||||||
Containers::StringView point;
|
Containers::StringView point = nullptr;
|
||||||
Containers::StringView after = nullptr;
|
Containers::StringView after = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -185,7 +185,7 @@ Mass::writeArmourPart(ArmourSlot slot) {
|
||||||
auto decals_array = part_prop->at<ArrayProperty>(MASS_ARMOUR_DECALS);
|
auto decals_array = part_prop->at<ArrayProperty>(MASS_ARMOUR_DECALS);
|
||||||
writeDecals(part.decals, decals_array);
|
writeDecals(part.decals, decals_array);
|
||||||
|
|
||||||
if(part.accessories.size() != 0) {
|
if(!part.accessories.isEmpty()) {
|
||||||
auto accs_array = part_prop->at<ArrayProperty>(MASS_ARMOUR_ACCESSORIES);
|
auto accs_array = part_prop->at<ArrayProperty>(MASS_ARMOUR_ACCESSORIES);
|
||||||
writeAccessories(part.accessories, accs_array);
|
writeAccessories(part.accessories, accs_array);
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,7 +269,7 @@ Profile::setMaterial(MaterialID id, std::int32_t amount) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto predicate = [&id](UnrealPropertyBase::ptr& prop){
|
auto predicate = [&id](UnrealPropertyBase::ptr& prop){
|
||||||
auto res_prop = static_cast<ResourceItemValue*>(prop.get());
|
auto res_prop = dynamic_cast<ResourceItemValue*>(prop.get());
|
||||||
return res_prop->id == id;
|
return res_prop->id == id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -286,7 +286,7 @@ Profile::setMaterial(MaterialID id, std::int32_t amount) {
|
||||||
arrayAppend(mats_prop->items, std::move(prop));
|
arrayAppend(mats_prop->items, std::move(prop));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res_prop = static_cast<ResourceItemValue*>(it->get());
|
res_prop = dynamic_cast<ResourceItemValue*>(it->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
res_prop->quantity = amount;
|
res_prop->quantity = amount;
|
||||||
|
@ -308,10 +308,10 @@ Profile::getResource(Containers::StringView container, MaterialID id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto predicate = [&id](UnrealPropertyBase::ptr& prop){
|
auto predicate = [&id](UnrealPropertyBase::ptr& prop){
|
||||||
auto res_prop = static_cast<ResourceItemValue*>(prop.get());
|
auto res_prop = dynamic_cast<ResourceItemValue*>(prop.get());
|
||||||
return res_prop->id == id;
|
return res_prop->id == id;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto it = std::find_if(mats_prop->items.begin(), mats_prop->items.end(), predicate);
|
auto it = std::find_if(mats_prop->items.begin(), mats_prop->items.end(), predicate);
|
||||||
return it != mats_prop->items.end() ? static_cast<ResourceItemValue*>(it->get())->quantity : 0;
|
return it != mats_prop->items.end() ? dynamic_cast<ResourceItemValue*>(it->get())->quantity : 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,7 +235,7 @@ ProfileManager::refreshBackups() {
|
||||||
auto files_view = files->exceptSuffix(files->end() - std::remove_if(files->begin(), files->end(), predicate));
|
auto files_view = files->exceptSuffix(files->end() - std::remove_if(files->begin(), files->end(), predicate));
|
||||||
|
|
||||||
int error_code = 0;
|
int error_code = 0;
|
||||||
zip_t* zip = nullptr;
|
zip_t* zip;
|
||||||
for(Containers::StringView file : files_view) {
|
for(Containers::StringView file : files_view) {
|
||||||
Backup backup;
|
Backup backup;
|
||||||
backup.filename = file;
|
backup.filename = file;
|
||||||
|
@ -324,7 +324,7 @@ ProfileManager::restoreBackup(std::size_t index) {
|
||||||
auto error_format = "Extraction of file {} failed: {}"_s;
|
auto error_format = "Extraction of file {} failed: {}"_s;
|
||||||
|
|
||||||
int error_code = 0;
|
int error_code = 0;
|
||||||
zip_t* zip = nullptr;
|
zip_t* zip;
|
||||||
|
|
||||||
zip = zip_open(Utility::Path::join(_backupsDirectory, backup.filename).data(), ZIP_RDONLY, &error_code);
|
zip = zip_open(Utility::Path::join(_backupsDirectory, backup.filename).data(), ZIP_RDONLY, &error_code);
|
||||||
if(zip == nullptr) {
|
if(zip == nullptr) {
|
||||||
|
@ -358,8 +358,8 @@ ProfileManager::restoreBackup(std::size_t index) {
|
||||||
|
|
||||||
Containers::StaticArray<8192, char> buf{ValueInit};
|
Containers::StaticArray<8192, char> buf{ValueInit};
|
||||||
|
|
||||||
auto bytes_read = 0l;
|
std::int64_t bytes_read;
|
||||||
while((bytes_read = zip_fread(zf, buf.data(), buf.size())) > 0) {
|
while((bytes_read = zip_fread(zf, buf.data(), buf.size())) > 0ll) {
|
||||||
if(std::fwrite(buf.data(), sizeof(char), bytes_read, out) < static_cast<std::size_t>(bytes_read)) {
|
if(std::fwrite(buf.data(), sizeof(char), bytes_read, out) < static_cast<std::size_t>(bytes_read)) {
|
||||||
_lastError = Utility::format(error_format.data(), file, "not enough bytes written.");
|
_lastError = Utility::format(error_format.data(), file, "not enough bytes written.");
|
||||||
LOG_ERROR(_lastError);
|
LOG_ERROR(_lastError);
|
||||||
|
|
|
@ -29,8 +29,7 @@
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
#include <curl/curl.h>
|
#include <wtypesbase.h>
|
||||||
|
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#include <wtsapi32.h>
|
#include <wtsapi32.h>
|
||||||
|
|
||||||
|
@ -48,7 +47,7 @@ Utility::Tweakable tweak;
|
||||||
|
|
||||||
SaveTool::SaveTool(const Arguments& arguments):
|
SaveTool::SaveTool(const Arguments& arguments):
|
||||||
Platform::Sdl2Application{arguments,
|
Platform::Sdl2Application{arguments,
|
||||||
Configuration{}.setTitle("M.A.S.S. Builder Save Tool " SAVETOOL_VERSION " (\"" SAVETOOL_CODENAME "\")")
|
Configuration{}.setTitle("M.A.S.S. Builder Save Tool " SAVETOOL_VERSION_STRING " (\"" SAVETOOL_CODENAME "\")")
|
||||||
.setSize({960, 720})}
|
.setSize({960, 720})}
|
||||||
{
|
{
|
||||||
#ifdef SAVETOOL_DEBUG_BUILD
|
#ifdef SAVETOOL_DEBUG_BUILD
|
||||||
|
@ -131,8 +130,6 @@ SaveTool::SaveTool(const Arguments& arguments):
|
||||||
|
|
||||||
initialiseConfiguration();
|
initialiseConfiguration();
|
||||||
|
|
||||||
LOG_INFO("Initialising update checker.");
|
|
||||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
||||||
if(conf().checkUpdatesOnStartup()) {
|
if(conf().checkUpdatesOnStartup()) {
|
||||||
_queue.addToast(Toast::Type::Default, "Checking for updates..."_s);
|
_queue.addToast(Toast::Type::Default, "Checking for updates..."_s);
|
||||||
_updateThread = std::thread{[this]{ checkForUpdates(); }};
|
_updateThread = std::thread{[this]{ checkForUpdates(); }};
|
||||||
|
@ -155,9 +152,6 @@ SaveTool::SaveTool(const Arguments& arguments):
|
||||||
SaveTool::~SaveTool() {
|
SaveTool::~SaveTool() {
|
||||||
LOG_INFO("Cleaning up.");
|
LOG_INFO("Cleaning up.");
|
||||||
|
|
||||||
LOG_INFO("Shutting libcurl down.");
|
|
||||||
curl_global_cleanup();
|
|
||||||
|
|
||||||
SDL_RemoveTimer(_gameCheckTimerId);
|
SDL_RemoveTimer(_gameCheckTimerId);
|
||||||
|
|
||||||
LOG_INFO("Saving the configuration.");
|
LOG_INFO("Saving the configuration.");
|
||||||
|
@ -339,7 +333,7 @@ SaveTool::drawDisclaimer() {
|
||||||
|
|
||||||
ImGui::Bullet();
|
ImGui::Bullet();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::TextUnformatted("This version of the application was tested on M.A.S.S. Builder early access version " SUPPORTED_GAME_VERSION ". It may or may not work with other versions of the game.");
|
ImGui::TextUnformatted("This version of the application was tested on M.A.S.S. Builder early access version " SAVETOOL_SUPPORTED_GAME_VERSION ". It may or may not work with other versions of the game.");
|
||||||
|
|
||||||
if(conf().isRunningInWine()) {
|
if(conf().isRunningInWine()) {
|
||||||
ImGui::Bullet();
|
ImGui::Bullet();
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
@ -40,6 +41,7 @@
|
||||||
#include "../ProfileManager/ProfileManager.h"
|
#include "../ProfileManager/ProfileManager.h"
|
||||||
#include "../MassManager/MassManager.h"
|
#include "../MassManager/MassManager.h"
|
||||||
#include "../ToastQueue/ToastQueue.h"
|
#include "../ToastQueue/ToastQueue.h"
|
||||||
|
#include "../UpdateChecker/UpdateChecker.h"
|
||||||
|
|
||||||
#ifdef SAVETOOL_DEBUG_BUILD
|
#ifdef SAVETOOL_DEBUG_BUILD
|
||||||
#define tw CORRADE_TWEAKABLE
|
#define tw CORRADE_TWEAKABLE
|
||||||
|
@ -83,11 +85,6 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
||||||
};
|
};
|
||||||
void initEvent(SDL_Event& event);
|
void initEvent(SDL_Event& event);
|
||||||
|
|
||||||
enum UpdateCheckStatus : std::int32_t {
|
|
||||||
CurlInitFailed = 0,
|
|
||||||
CurlError = 1,
|
|
||||||
CurlTimeout = 2,
|
|
||||||
};
|
|
||||||
void updateCheckEvent(SDL_Event& event);
|
void updateCheckEvent(SDL_Event& event);
|
||||||
|
|
||||||
enum FileEventType: std::int32_t {
|
enum FileEventType: std::int32_t {
|
||||||
|
@ -260,10 +257,8 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
||||||
};
|
};
|
||||||
Containers::StaticArray<2, efsw::WatchID> _watchIDs;
|
Containers::StaticArray<2, efsw::WatchID> _watchIDs;
|
||||||
|
|
||||||
bool _updateAvailable{false};
|
Containers::Optional<UpdateChecker> _checker{Containers::NullOpt};
|
||||||
Containers::String _latestVersion;
|
std::mutex _checkerMutex;
|
||||||
Containers::String _releaseLink;
|
|
||||||
Containers::String _downloadLink;
|
|
||||||
|
|
||||||
bool _modifiedBySaveTool{false};
|
bool _modifiedBySaveTool{false};
|
||||||
bool _jointsDirty{false};
|
bool _jointsDirty{false};
|
||||||
|
|
|
@ -387,7 +387,7 @@ SaveTool::drawUnavailableMaterialRow(Containers::StringView name, std::int32_t t
|
||||||
ImGui::TableSetColumnIndex(1);
|
ImGui::TableSetColumnIndex(1);
|
||||||
ImGui::TextUnformatted(name.begin(), name.end());
|
ImGui::TextUnformatted(name.begin(), name.end());
|
||||||
ImGui::TableSetColumnIndex(2);
|
ImGui::TableSetColumnIndex(2);
|
||||||
ImGui::TextDisabled("Unavailable as of game version " SUPPORTED_GAME_VERSION);
|
ImGui::TextDisabled("Unavailable as of game version " SAVETOOL_SUPPORTED_GAME_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -134,7 +134,7 @@ SaveTool::drawMassViewer() {
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_currentMass->globalStyles().size() != 0 && ImGui::BeginTabItem("Global styles")) {
|
if(!_currentMass->globalStyles().isEmpty() && ImGui::BeginTabItem("Global styles")) {
|
||||||
drawGlobalStyles();
|
drawGlobalStyles();
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,7 @@ SaveTool::drawArmour() {
|
||||||
|
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
|
|
||||||
if(part.accessories.size() != 0) {
|
if(!part.accessories.isEmpty()) {
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
ImGui::PushID("Accessory");
|
ImGui::PushID("Accessory");
|
||||||
|
|
|
@ -28,7 +28,7 @@ SaveTool::drawWeapons() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
|
const float footer_height_to_reserve = ImGui::GetFrameHeightWithSpacing();
|
||||||
|
|
||||||
ImGui::BeginGroup();
|
ImGui::BeginGroup();
|
||||||
|
|
||||||
|
@ -51,13 +51,11 @@ SaveTool::drawWeapons() {
|
||||||
|
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
bool dirty = _meleeDirty || _shieldsDirty || _bShootersDirty || _eShootersDirty || _bLaunchersDirty || _eLaunchersDirty;
|
bool dirty = _meleeDirty || _shieldsDirty || _bShootersDirty || _eShootersDirty || _bLaunchersDirty || _eLaunchersDirty;
|
||||||
|
|
||||||
ImGui::BeginDisabled(!dirty);
|
ImGui::BeginDisabled(!dirty);
|
||||||
|
|
||||||
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
|
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save order"); })) {
|
||||||
if(_meleeDirty) {
|
if(_meleeDirty) {
|
||||||
_modifiedBySaveTool = true;
|
_modifiedBySaveTool = true;
|
||||||
if(!_currentMass->writeMeleeWeapons()) {
|
if(!_currentMass->writeMeleeWeapons()) {
|
||||||
|
@ -158,6 +156,8 @@ SaveTool::drawWeapons() {
|
||||||
|
|
||||||
ImGui::EndGroup();
|
ImGui::EndGroup();
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
if(!_currentWeapon) {
|
if(!_currentWeapon) {
|
||||||
|
@ -176,8 +176,6 @@ SaveTool::drawWeapons() {
|
||||||
|
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
if(drawUnsafeWidget([](){ return ImGui::Button(ICON_FA_SAVE " Save changes to weapon category"); })) {
|
if(drawUnsafeWidget([](){ return ImGui::Button(ICON_FA_SAVE " Save changes to weapon category"); })) {
|
||||||
_modifiedBySaveTool = true;
|
_modifiedBySaveTool = true;
|
||||||
switch(_currentWeapon->type) {
|
switch(_currentWeapon->type) {
|
||||||
|
@ -544,7 +542,7 @@ SaveTool::drawWeaponEditor(Weapon& weapon) {
|
||||||
|
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
|
|
||||||
if(part.accessories.size() != 0) {
|
if(!part.accessories.isEmpty()) {
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
ImGui::PushID("Accessory");
|
ImGui::PushID("Accessory");
|
||||||
|
|
|
@ -18,8 +18,6 @@
|
||||||
|
|
||||||
#include <SDL_events.h>
|
#include <SDL_events.h>
|
||||||
|
|
||||||
#include <curl/curl.h>
|
|
||||||
|
|
||||||
#include "../Logger/Logger.h"
|
#include "../Logger/Logger.h"
|
||||||
|
|
||||||
#include "SaveTool.h"
|
#include "SaveTool.h"
|
||||||
|
@ -28,108 +26,51 @@ void
|
||||||
SaveTool::updateCheckEvent(SDL_Event& event) {
|
SaveTool::updateCheckEvent(SDL_Event& event) {
|
||||||
_updateThread.join();
|
_updateThread.join();
|
||||||
|
|
||||||
if(event.user.code == CurlInitFailed) {
|
LOG_INFO("in updateCheckEvent().");
|
||||||
_queue.addToast(Toast::Type::Error, "Couldn't initialise libcurl. Update check aborted."_s);
|
|
||||||
LOG_ERROR("Couldn't initialise libcurl. Update check aborted.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(event.user.code == CurlError) {
|
|
||||||
Containers::String error{static_cast<char*>(event.user.data2), CURL_ERROR_SIZE, nullptr};
|
|
||||||
_queue.addToast(Toast::Type::Error, error, std::chrono::milliseconds{5000});
|
|
||||||
_queue.addToast(Toast::Type::Error, static_cast<char*>(event.user.data1),
|
|
||||||
std::chrono::milliseconds{5000});
|
|
||||||
LOG_ERROR_FORMAT("{}: {}", static_cast<char*>(event.user.data1), static_cast<char*>(event.user.data2));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(event.user.code == CurlTimeout) {
|
|
||||||
_queue.addToast(Toast::Type::Error, "The request timed out."_s);
|
|
||||||
LOG_ERROR("The request timed out.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(event.user.code != 200) {
|
|
||||||
_queue.addToast(Toast::Type::Error,
|
|
||||||
Utility::format("The request failed with error code {}.", event.user.code));
|
|
||||||
LOG_ERROR_FORMAT("The request failed with error code {}.", event.user.code);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Version {
|
switch(static_cast<UpdateChecker::Result>(event.user.code)) {
|
||||||
explicit Version(Containers::StringView str) {
|
case UpdateChecker::Success:
|
||||||
std::size_t start_point = 0;
|
_checkerMutex.lock();
|
||||||
|
if(_checker->updateAvailable()) {
|
||||||
if(str[0] == 'v') {
|
using namespace std::chrono_literals;
|
||||||
start_point++;
|
_queue.addToast(Toast::Type::Warning,
|
||||||
}
|
"Your version is out of date and thus unsupported.\nCheck the settings for more information."_s, 5s);
|
||||||
|
|
||||||
auto components = Containers::StringView{str.data() + start_point}.split('.');
|
|
||||||
|
|
||||||
major = std::strtol(components[0].data(), nullptr, 10);
|
|
||||||
minor = std::strtol(components[1].data(), nullptr, 10);
|
|
||||||
patch = std::strtol(components[2].data(), nullptr, 10);
|
|
||||||
|
|
||||||
fullVersion = major * 10000 + minor * 100 + patch;
|
|
||||||
|
|
||||||
if(str.hasSuffix("-pre")) {
|
|
||||||
prerelease = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::int32_t fullVersion;
|
|
||||||
std::int32_t major = 0;
|
|
||||||
std::int32_t minor = 0;
|
|
||||||
std::int32_t patch = 0;
|
|
||||||
bool prerelease = false;
|
|
||||||
|
|
||||||
bool operator==(const Version& other) const {
|
|
||||||
return fullVersion == other.fullVersion && prerelease == other.prerelease;
|
|
||||||
}
|
|
||||||
bool operator>(const Version& other) const {
|
|
||||||
if((fullVersion > other.fullVersion) ||
|
|
||||||
(fullVersion == other.fullVersion && !prerelease && other.prerelease))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return false;
|
if(_checker->version() == current_version || (current_version > _checker->version() && current_version.prerelease)) {
|
||||||
}
|
|
||||||
}
|
|
||||||
explicit operator Containers::String() const {
|
|
||||||
return Utility::format("{}.{}.{}{}", major, minor, patch, prerelease ? "-pre" : "");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const Version current_ver{SAVETOOL_VERSION};
|
|
||||||
|
|
||||||
auto str = static_cast<char*>(event.user.data1);
|
|
||||||
Containers::String response{str, strlen(str), nullptr};
|
|
||||||
auto components = response.splitOnAnyWithoutEmptyParts("\r\n");
|
|
||||||
|
|
||||||
Version latest_ver{components.front()};
|
|
||||||
|
|
||||||
if(latest_ver > current_ver) {
|
|
||||||
_queue.addToast(Toast::Type::Warning,
|
|
||||||
"Your version is out of date.\nCheck the settings for more information."_s,
|
|
||||||
std::chrono::milliseconds{5000});
|
|
||||||
_updateAvailable = true;
|
|
||||||
_latestVersion = Containers::String{latest_ver};
|
|
||||||
_releaseLink = Utility::format("https://williamjcm.ovh/git/williamjcm/MassBuilderSaveTool/releases/tag/v{}",
|
|
||||||
components.front());
|
|
||||||
_downloadLink = components.back();
|
|
||||||
}
|
|
||||||
else if(latest_ver == current_ver || (current_ver > latest_ver && current_ver.prerelease)) {
|
|
||||||
_queue.addToast(Toast::Type::Success, "The application is already up to date."_s);
|
_queue.addToast(Toast::Type::Success, "The application is already up to date."_s);
|
||||||
}
|
}
|
||||||
else if(current_ver > latest_ver && !current_ver.prerelease) {
|
else if(_checker->version() > current_version && !current_version.prerelease) {
|
||||||
_queue.addToast(Toast::Type::Warning,
|
_queue.addToast(Toast::Type::Warning,
|
||||||
"Your version is more recent than the latest one in the repo. How???"_s);
|
"Your version is more recent than the latest one in the repo. How???"_s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_checkerMutex.unlock();
|
||||||
inline
|
break;
|
||||||
std::size_t
|
case UpdateChecker::HttpError:
|
||||||
writeData(char* ptr, std::size_t size, std::size_t nmemb, Containers::String* buf) {
|
_checkerMutex.lock();
|
||||||
if(!ptr || !buf) return 0;
|
_queue.addToast(Toast::Type::Error, _checker->error());
|
||||||
(*buf) = Utility::format("{}{}", *buf, Containers::StringView{ptr, size * nmemb});
|
LOG_ERROR(_checker->error());
|
||||||
return size * nmemb;
|
_checkerMutex.unlock();
|
||||||
|
break;
|
||||||
|
case UpdateChecker::CurlInitFailed:
|
||||||
|
_queue.addToast(Toast::Type::Error, "Couldn't initialise libcurl. Update check aborted."_s);
|
||||||
|
LOG_ERROR("Couldn't initialise libcurl. Update check aborted.");
|
||||||
|
break;
|
||||||
|
case UpdateChecker::CurlError:
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
_checkerMutex.lock();
|
||||||
|
_queue.addToast(Toast::Type::Error, _checker->error(), 10s);
|
||||||
|
LOG_ERROR(_checker->error());
|
||||||
|
_checkerMutex.unlock();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case UpdateChecker::CurlTimeout:
|
||||||
|
_queue.addToast(Toast::Type::Error, "The request timed out."_s);
|
||||||
|
LOG_ERROR("The request timed out.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -138,43 +79,15 @@ SaveTool::checkForUpdates() {
|
||||||
SDL_zero(event);
|
SDL_zero(event);
|
||||||
event.type = _updateEventId;
|
event.type = _updateEventId;
|
||||||
|
|
||||||
auto curl = curl_easy_init();
|
_checkerMutex.lock();
|
||||||
if(!curl) {
|
|
||||||
event.user.code = CurlInitFailed;
|
if(!_checker) {
|
||||||
|
_checker.emplace();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(curl) {
|
event.user.code = _checker->check();
|
||||||
Containers::String response_body{Containers::AllocatedInit, ""};
|
|
||||||
Containers::String error_buffer{ValueInit, CURL_ERROR_SIZE * 2};
|
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, "https://williamjcm.ovh/mbst/version");
|
_checkerMutex.unlock();
|
||||||
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_body);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer.data());
|
|
||||||
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 0L);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 10000L);
|
|
||||||
|
|
||||||
auto code = curl_easy_perform(curl);
|
|
||||||
|
|
||||||
if(code == CURLE_OK) {
|
|
||||||
long status = 0;
|
|
||||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
|
|
||||||
event.user.code = std::int32_t(status);
|
|
||||||
event.user.data1 = response_body.release();
|
|
||||||
}
|
|
||||||
else if(code == CURLE_OPERATION_TIMEDOUT) {
|
|
||||||
event.user.code = CurlTimeout;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
event.user.code = CurlError;
|
|
||||||
event.user.data1 = const_cast<char*>(curl_easy_strerror(code));
|
|
||||||
event.user.data2 = Containers::String{error_buffer}.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_PushEvent(&event);
|
SDL_PushEvent(&event);
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,15 +132,16 @@ SaveTool::drawMainMenu() {
|
||||||
_updateThread = std::thread{[this]{ checkForUpdates(); }};
|
_updateThread = std::thread{[this]{ checkForUpdates(); }};
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_updateAvailable) {
|
if(_checker && (_checkerMutex.try_lock() && _checker->updateAvailable())) {
|
||||||
drawAlignedText("Version %s is available.", _latestVersion.data());
|
drawAlignedText("Version %s is available.", Containers::String{_checker->version()}.data());
|
||||||
if(ImGui::Button(ICON_FA_FILE_SIGNATURE " Release notes")) {
|
if(ImGui::Button(ICON_FA_FILE_SIGNATURE " Release notes")) {
|
||||||
openUri(_releaseLink);
|
openUri("https://williamjcm.ovh/mbst");
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if(ImGui::Button(ICON_FA_DOWNLOAD " Download now")) {
|
if(ImGui::Button(ICON_FA_DOWNLOAD " Download now")) {
|
||||||
openUri(_downloadLink);
|
openUri(_checker->downloadLink());
|
||||||
}
|
}
|
||||||
|
_checkerMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(drawCheckbox("Skip disclaimer", conf().skipDisclaimer())) {
|
if(drawCheckbox("Skip disclaimer", conf().skipDisclaimer())) {
|
||||||
|
|
|
@ -126,7 +126,7 @@ BinaryWriter::writeDouble(double value) {
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BinaryWriter::writeArray(Containers::ArrayView<const char> array) {
|
BinaryWriter::writeArray(Containers::ArrayView<const char> array) {
|
||||||
if(array.size() == 0) {
|
if(array.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -198,6 +198,8 @@ StructProperty::ptr
|
||||||
StructSerialiser::readStructValue(Containers::StringView name, Containers::StringView type, std::size_t value_length,
|
StructSerialiser::readStructValue(Containers::StringView name, Containers::StringView type, std::size_t value_length,
|
||||||
BinaryReader& reader, PropertySerialiser& serialiser)
|
BinaryReader& reader, PropertySerialiser& serialiser)
|
||||||
{
|
{
|
||||||
|
static_cast<void>(value_length);
|
||||||
|
|
||||||
auto st_prop = Containers::pointer<GenericStructProperty>();
|
auto st_prop = Containers::pointer<GenericStructProperty>();
|
||||||
st_prop->structType = type;
|
st_prop->structType = type;
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ TextPropertySerialiser::deserialiseProperty(Containers::StringView name, Contain
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto interval = reader.position() - start_position;
|
std::int64_t interval;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
Containers::String str;
|
Containers::String str;
|
||||||
|
|
|
@ -114,10 +114,8 @@ UESaveFile::saveToFile() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(std::size_t i = 0; i < _customFormatData.size(); i++) {
|
for(auto& i : _customFormatData) {
|
||||||
if(!writer.writeStaticArray(Containers::StaticArrayView<16, const char>{_customFormatData[i].id}) ||
|
if(!writer.writeStaticArray<16>(staticArrayView(i.id)) || !writer.writeUint32(i.value)) {
|
||||||
!writer.writeUint32(_customFormatData[i].value))
|
|
||||||
{
|
|
||||||
_lastError = "Couldn't write the custom format data."_s;
|
_lastError = "Couldn't write the custom format data."_s;
|
||||||
LOG_ERROR(_lastError);
|
LOG_ERROR(_lastError);
|
||||||
return false;
|
return false;
|
||||||
|
|
112
src/UpdateChecker/UpdateChecker.cpp
Normal file
112
src/UpdateChecker/UpdateChecker.cpp
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
// MassBuilderSaveTool
|
||||||
|
// Copyright (C) 2021-2023 Guillaume Jacquemin
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <Corrade/Containers/ScopeGuard.h>
|
||||||
|
#include <Corrade/Containers/String.h>
|
||||||
|
#include <Corrade/Containers/StringView.h>
|
||||||
|
#include <Corrade/Utility/Format.h>
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#include "../Logger/Logger.h"
|
||||||
|
|
||||||
|
#include "UpdateChecker.h"
|
||||||
|
|
||||||
|
using namespace Corrade;
|
||||||
|
|
||||||
|
UpdateChecker::UpdateChecker() {
|
||||||
|
LOG_INFO("Initialising update checker.");
|
||||||
|
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateChecker::~UpdateChecker() {
|
||||||
|
LOG_INFO("Shutting libcurl down.");
|
||||||
|
curl_global_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateChecker::Result
|
||||||
|
UpdateChecker::check() {
|
||||||
|
auto curl = curl_easy_init();
|
||||||
|
if(!curl) {
|
||||||
|
return Result::CurlInitFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
Containers::ScopeGuard guard(curl, curl_easy_cleanup);
|
||||||
|
|
||||||
|
Containers::String response_body{Containers::AllocatedInit, ""};
|
||||||
|
Containers::String error_buffer{ValueInit, CURL_ERROR_SIZE};
|
||||||
|
static auto write_data = [](char* ptr, std::size_t size, std::size_t nmemb, Containers::String* buf){
|
||||||
|
if(!ptr || !buf) return std::size_t{};
|
||||||
|
(*buf) = Utility::format("{}{}", *buf, Containers::StringView{ptr, size * nmemb});
|
||||||
|
return size * nmemb;
|
||||||
|
};
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, "https://williamjcm.ovh/mbst/version");
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_body);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer.data());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 0L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
|
||||||
|
|
||||||
|
auto code = curl_easy_perform(curl);
|
||||||
|
|
||||||
|
if(code == CURLE_OK) {
|
||||||
|
long status = 0;
|
||||||
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
|
||||||
|
|
||||||
|
if(status != 200) {
|
||||||
|
_error = Utility::format("The request failed with error code {}.", status);
|
||||||
|
return Result::HttpError;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto parts = response_body.splitOnAnyWithoutEmptyParts("\r\n");
|
||||||
|
|
||||||
|
_foundVersion = Version{parts.front()};
|
||||||
|
_downloadLink = parts.back();
|
||||||
|
LOG_INFO_FORMAT("Found version: {}", Containers::String{_foundVersion});
|
||||||
|
|
||||||
|
return Result::Success;
|
||||||
|
}
|
||||||
|
else if(code == CURLE_OPERATION_TIMEDOUT) {
|
||||||
|
return Result::CurlTimeout;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_error = Utility::format("{}: {}", curl_easy_strerror(code), error_buffer);
|
||||||
|
return Result::CurlError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Containers::StringView
|
||||||
|
UpdateChecker::error() const {
|
||||||
|
return _error;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
UpdateChecker::updateAvailable() const {
|
||||||
|
return _foundVersion > current_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Version&
|
||||||
|
UpdateChecker::version() const {
|
||||||
|
return _foundVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
Containers::StringView
|
||||||
|
UpdateChecker::downloadLink() const {
|
||||||
|
return _downloadLink;
|
||||||
|
}
|
49
src/UpdateChecker/UpdateChecker.h
Normal file
49
src/UpdateChecker/UpdateChecker.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// MassBuilderSaveTool
|
||||||
|
// Copyright (C) 2021-2023 Guillaume Jacquemin
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../Version/Version.h"
|
||||||
|
|
||||||
|
class UpdateChecker {
|
||||||
|
public:
|
||||||
|
explicit UpdateChecker();
|
||||||
|
~UpdateChecker();
|
||||||
|
|
||||||
|
enum Result: std::int32_t {
|
||||||
|
Success,
|
||||||
|
HttpError,
|
||||||
|
CurlInitFailed,
|
||||||
|
CurlError,
|
||||||
|
CurlTimeout
|
||||||
|
};
|
||||||
|
|
||||||
|
auto check() -> Result;
|
||||||
|
|
||||||
|
auto error() const -> Containers::StringView;
|
||||||
|
|
||||||
|
bool updateAvailable() const;
|
||||||
|
|
||||||
|
auto version() const -> const Version&;
|
||||||
|
|
||||||
|
auto downloadLink() const -> Containers::StringView;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Containers::String _error;
|
||||||
|
|
||||||
|
Version _foundVersion{};
|
||||||
|
Containers::String _downloadLink;
|
||||||
|
};
|
89
src/Version/Version.h
Normal file
89
src/Version/Version.h
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// MassBuilderSaveTool
|
||||||
|
// Copyright (C) 2021-2023 Guillaume Jacquemin
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <Corrade/Containers/Array.h>
|
||||||
|
#include <Corrade/Containers/String.h>
|
||||||
|
#include <Corrade/Containers/StringView.h>
|
||||||
|
#include <Corrade/Utility/Format.h>
|
||||||
|
|
||||||
|
using namespace Corrade;
|
||||||
|
|
||||||
|
struct Version {
|
||||||
|
explicit Version() = default;
|
||||||
|
|
||||||
|
explicit Version(Containers::StringView str) {
|
||||||
|
std::size_t start_point = 0;
|
||||||
|
|
||||||
|
if(str[0] == 'v') {
|
||||||
|
start_point++;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto components = Containers::StringView{str.data() + start_point}.split('.');
|
||||||
|
|
||||||
|
major = std::strtol(components[0].data(), nullptr, 10);
|
||||||
|
minor = std::strtol(components[1].data(), nullptr, 10);
|
||||||
|
patch = std::strtol(components[2].data(), nullptr, 10);
|
||||||
|
|
||||||
|
fullVersion = major * 10000 + minor * 100 + patch;
|
||||||
|
|
||||||
|
if(str.hasSuffix("-pre")) {
|
||||||
|
prerelease = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr explicit Version(long major_, long minor_, long patch_, bool prerelease_):
|
||||||
|
major{major_}, minor{minor_}, patch{patch_}, prerelease{prerelease_},
|
||||||
|
fullVersion{major_ * 10000 + minor_ * 100 + patch_}
|
||||||
|
{
|
||||||
|
//ctor
|
||||||
|
}
|
||||||
|
|
||||||
|
long major = 0;
|
||||||
|
long minor = 0;
|
||||||
|
long patch = 0;
|
||||||
|
bool prerelease = false;
|
||||||
|
long fullVersion = 0;
|
||||||
|
|
||||||
|
bool operator==(const Version& other) const {
|
||||||
|
return fullVersion == other.fullVersion && prerelease == other.prerelease;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const Version& other) const {
|
||||||
|
if((fullVersion > other.fullVersion) ||
|
||||||
|
(fullVersion == other.fullVersion && !prerelease && other.prerelease))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator Containers::String() const {
|
||||||
|
return Utility::format("{}.{}.{}{}", major, minor, patch, prerelease ? "-pre" : "");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr Version current_version{
|
||||||
|
SAVETOOL_VERSION_MAJOR,
|
||||||
|
SAVETOOL_VERSION_MINOR,
|
||||||
|
SAVETOOL_VERSION_PATCH,
|
||||||
|
SAVETOOL_VERSION_PRERELEASE
|
||||||
|
};
|
|
@ -39,7 +39,7 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
logger().initialise();
|
logger().initialise();
|
||||||
|
|
||||||
LOG_INFO("Initialising M.A.S.S. Builder Save Tool version " SAVETOOL_VERSION ".");
|
LOG_INFO("Initialising M.A.S.S. Builder Save Tool version " SAVETOOL_VERSION_STRING ".");
|
||||||
|
|
||||||
auto str = setlocale(LC_ALL, ".utf-8");
|
auto str = setlocale(LC_ALL, ".utf-8");
|
||||||
if(str) {
|
if(str) {
|
||||||
|
|
Loading…
Reference in a new issue