#pragma once // MassBuilderSaveTool // Copyright (C) 2021-2022 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 . #include #include #include #include #include #include #ifdef SAVETOOL_DEBUG_BUILD #include #endif #include #include #include #include #include #include #include #include "../ProfileManager/ProfileManager.h" #include "../MassManager/MassManager.h" #include "../ToastQueue/ToastQueue.h" #ifdef SAVETOOL_DEBUG_BUILD #define tw CORRADE_TWEAKABLE #endif using namespace Corrade; using namespace Containers::Literals; using namespace Magnum; class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener { public: explicit SaveTool(const Arguments& arguments); ~SaveTool() override; void handleFileAction(efsw::WatchID watch_id, const std::string& dir, const std::string& filename, efsw::Action action, std::string old_filename = "") override; private: // Events void drawEvent() override; void viewportEvent(ViewportEvent& event) override; void keyPressEvent(KeyEvent& event) override; void keyReleaseEvent(KeyEvent& event) override; void mousePressEvent(MouseEvent& event) override; void mouseReleaseEvent(MouseEvent& event) override; void mouseMoveEvent(MouseMoveEvent& event) override; void mouseScrollEvent(MouseScrollEvent& event) override; void textInputEvent(TextInputEvent& event) override; void anyEvent(SDL_Event& event) override; enum InitStatus: Int { InitSuccess, ProfileManagerFailure }; void initEvent(SDL_Event& event); enum UpdateCheckStatus : Int { CurlInitFailed = 0, CurlError = 1, CurlTimeout = 2, }; void updateCheckEvent(SDL_Event& event); enum FileEventType: Int { FileAdded = efsw::Action::Add, FileDeleted = efsw::Action::Delete, FileModified = efsw::Action::Modified, FileMoved = efsw::Action::Moved, StagedUpdate = 1 << 3 }; void fileUpdateEvent(SDL_Event& event); // Initialisation methods void initialiseConfiguration(); void initialiseGui(); void initialiseManager(); auto initialiseToolDirectories() -> bool; auto findGameDataDirectory() -> bool; void initialiseMassManager(); void initialiseFileWatcher(); // GUI-related methods void drawImGui(); void drawGui(); void drawMainMenu(); void drawDisclaimer(); void drawInitialisation(); void drawProfileManager(); auto drawBackupListPopup() -> ImGuiID; auto drawBackupProfilePopup(std::size_t profile_index) -> ImGuiID; auto drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID; void drawManager(); auto drawIntEditPopup(int* value_to_edit, int max) -> bool; auto drawRenamePopup(Containers::ArrayView name_view) -> bool; void drawGeneralInfo(); void drawResearchInventory(); template void drawMaterialRow(Containers::StringView name, Int tier, Getter getter, Setter setter); void drawUnavailableMaterialRow(Containers::StringView name, Int tier); void drawMassManager(); auto drawDeleteMassPopup(int mass_index) -> ImGuiID; auto drawDeleteStagedMassPopup(Containers::StringView filename) -> ImGuiID; void drawMassViewer(); void drawFrameInfo(); void drawJointSliders(); void drawFrameStyles(); void drawEyeColourPicker(); void drawCustomFrameStyles(); void drawArmour(); void drawCustomArmourStyles(); void drawWeapons(); void drawWeaponCategory(Containers::StringView name, Containers::ArrayView weapons_view, bool& dirty, Containers::StringView payload_type, Containers::StringView payload_tooltip); void drawWeaponEditor(Weapon& weapon); void drawGlobalStyles(); void drawTuning(); void drawDecalEditor(Decal& decal); void drawAccessoryEditor(Accessory& accessory, Containers::ArrayView style_view); auto getStyleName(Int id, Containers::ArrayView view) -> Containers::StringView; enum DCSResult { DCS_Fail, DCS_ResetStyle, DCS_Save }; auto drawCustomStyle(CustomStyle& style) -> DCSResult; void drawAbout(); void drawGameState(); // Convenience wrappers over ImGui stuff void drawHelpMarker(Containers::StringView text, Float wrap_pos = 0.0f); void drawTooltip(Containers::StringView text, Float wrap_pos = 0.0f); template auto drawUnsafeWidget(Functor func, Args... args) -> bool { GameState game_state = _gameState; // Copying the value to reduce the risk of a data race. ImGui::BeginDisabled(game_state != GameState::NotRunning); bool result = func(std::forward(args)...); ImGui::EndDisabled(); return result; } // Obviously, should only be used with ImGui widgets that return a bool. // Also, func should be a lambda if there are any default arguments, like ImGui::Button(), etc... template void drawUnsafeText(const char* text, Args... args) { // Alternative to the above, for ImGui::Text*() variants. if(_gameState != GameState::NotRunning) { ImGui::TextDisabled(text, std::forward(args)...); } else { ImGui::Text(text, std::forward(args)...); } } template void drawAlignedText(Containers::StringView text, Args... args) { ImGui::AlignTextToFramePadding(); ImGui::Text(text.data(), std::forward(args)...); } void openUri(Containers::StringView uri); void checkGameState(); void checkForUpdates(); Utility::Configuration _conf{"MassBuilderSaveTool.ini"_s}; Utility::Resource _rs{"assets"_s}; // GUI-related members ImGuiIntegration::Context _imgui{NoCreate}; enum class UiState: uint8_t { Disclaimer, Initialising, ProfileManager, MainManager, MassViewer } _uiState{UiState::Disclaimer}; bool _aboutPopup{false}; #ifdef SAVETOOL_DEBUG_BUILD bool _demoWindow{false}; bool _styleEditor{false}; bool _metricsWindow{false}; #endif ToastQueue _queue; std::thread _initThread; std::thread _updateThread; UnsignedInt _initEventId; UnsignedInt _updateEventId; UnsignedInt _fileEventId; Containers::String _lastError; Containers::String _gameDataDir; Containers::String _configDir; Containers::String _saveDir; Containers::String _screenshotsDir; Containers::String _backupsDir; Containers::String _stagingDir; //Containers::String _armouryDir; //Containers::String _armoursDir; //Containers::String _weaponsDir; //Containers::String _stylesDir; enum class GameState : UnsignedByte { Unknown, NotRunning, Running } _gameState{GameState::Unknown}; SDL_TimerID _gameCheckTimerId = 0; Containers::Pointer _profileManager; Profile* _currentProfile{nullptr}; Containers::Pointer _massManager; Mass* _currentMass{nullptr}; Weapon* _currentWeapon = nullptr; Containers::Pointer _fileWatcher; enum watchID { SaveDir = 0, StagingDir = 1 }; Containers::StaticArray<2, efsw::WatchID> _watchIDs; int _swapInterval = 1; float _fpsCap = 60.0f; bool _skipDisclaimer{false}; bool _checkUpdatesOnStartup{true}; bool _updateAvailable{false}; Containers::String _latestVersion; Containers::String _releaseLink; Containers::String _downloadLink; bool _modifiedBySaveTool{false}; bool _jointsDirty{false}; bool _stylesDirty{false}; bool _eyeFlareDirty{false}; Containers::StaticArray<38, Int> _selectedArmourDecals{ValueInit}; Containers::StaticArray<38, Int> _selectedArmourAccessories{ValueInit}; Int _selectedBLPlacement{0}; Int _selectedWeaponPart{0}; Int _selectedWeaponDecal{0}; Int _selectedWeaponAccessory{0}; bool _meleeDirty{false}; bool _shieldsDirty{false}; bool _bShootersDirty{false}; bool _eShootersDirty{false}; bool _bLaunchersDirty{false}; bool _eLaunchersDirty{false}; bool _cheatMode{false}; bool _advancedMode{false}; Timeline _timeline; };