diff --git a/CMakeLists.txt b/CMakeLists.txt index d46a156..08d9184 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,8 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) project(Echo) -set(Echo_VERSION_MAJOR 0) -set(Echo_VERSION_MINOR 9) +set(Echo_VERSION_MAJOR 1) +set(Echo_VERSION_MINOR 0) set(CMAKE_SHARED_LIBRARY_SUFFIX " v${Echo_VERSION_MAJOR}.${Echo_VERSION_MINOR}.dll") @@ -20,7 +20,7 @@ file( src/*.hpp ) -add_library(Echo SHARED ${SOURCE_FILES} "src/Logic/speedhack.h" "src/Logic/speedhack.cpp" "src/Clickbot/clickbot.hpp" "src/Clickbot/clickbot.cpp" "src/Logic/Conversions/mhr.h" "src/GUI/implot/implot.h" "src/GUI/implot/implot_internal.h") +add_library(Echo SHARED ${SOURCE_FILES} "src/Logic/speedhack.h" "src/Logic/speedhack.cpp" "src/Clickbot/clickbot.hpp" "src/Clickbot/clickbot.cpp" "src/Logic/Conversions/mhr.h" "src/GUI/implot/implot.h" "src/GUI/implot/implot_internal.h" "src/Logic/Conversions/json.h") add_dependencies(Echo increment_version_number diff --git a/src/GUI/gui.cpp b/src/GUI/gui.cpp index 77fd84a..83f60ec 100644 --- a/src/GUI/gui.cpp +++ b/src/GUI/gui.cpp @@ -27,6 +27,7 @@ #include "../Logic/Conversions/tasbot.h" #include "../Logic/Conversions/mhr_json.h" #include "../Logic/Conversions/mhr.h" +#include "../Logic/Conversions/json.h" #include "../Logic/Conversions/osu.h" #include "../Logic/Conversions/plaintext.h" #include "../Hooks/hooks.hpp" @@ -181,7 +182,7 @@ double evalExpression(const std::vector& tokens, std::map> options = { {"Osu", std::make_shared()}, {"Mega Hack Replay JSON", std::make_shared()}, {"Mega Hack Replay", std::make_shared()}, - {"Plain Text", std::make_shared()} + {"Plain Text", std::make_shared<PlainText>()}, + {"JSON", std::make_shared<JSON>()} }; void GUI::tools() { @@ -2794,15 +2796,30 @@ void GUI::main() { ImGui::Separator(); - ImGui::Checkbox("Use JSON", &logic.use_json_for_files); - - CHECK_KEYBIND("useJSON"); + ImGui::Checkbox("File Browser", &logic.file_dialog); + CHECK_KEYBIND("useDialog"); ImGui::SameLine(); - ImGui::Checkbox("File Browser", &logic.file_dialog); + bool open_rename_modal = true; + + if (ImGui::Button("Overwrite Settings")) { + ImGui::OpenPopup("Overwrite Name Format"); + } + + if (ImGui::BeginPopupModal("Overwrite Name Format", &open_rename_modal, ImGuiWindowFlags_AlwaysAutoResize)) { + + ImGui::PushItemWidth(200); + ImGui::InputText("Renaming Format", &logic.rename_format); + ImGui::PopItemWidth(); + + ImGui::SameLine(); + + HelpMarker("The rename format prevents accidental deletion of important replays by renaming the ones that would otherwise overwrite each other.\nFor example, the format '_#' will rename ReplayName.echo to ReplayName_1.echo."); + + ImGui::EndPopup(); + } - CHECK_KEYBIND("useDialog"); ImGui::SameLine(); bool open_modal = true; diff --git a/src/Logic/Conversions/json.h b/src/Logic/Conversions/json.h new file mode 100644 index 0000000..a29350b --- /dev/null +++ b/src/Logic/Conversions/json.h @@ -0,0 +1,26 @@ +#ifndef JSON_H +#define JSON_H + +class JSON : public Convertible { +public: + void import(const std::string& filename) override { + auto& logic = Logic::get(); + + logic.read_file_json(filename, true); + } + + void export_to(const std::string& filename) override { + auto& logic = Logic::get(); + logic.write_file_json(filename); + } + + std::string get_type_filter() const override { + return ".json"; + } + + std::string get_directory() const override { + return ".echo/"; + } +}; + +#endif diff --git a/src/Logic/logic.cpp b/src/Logic/logic.cpp index 2b40e04..eaa4fbc 100644 --- a/src/Logic/logic.cpp +++ b/src/Logic/logic.cpp @@ -403,33 +403,41 @@ void Logic::set_replay_pos(unsigned idx) { #define w_b(var) file.write(reinterpret_cast<char*>(&var), sizeof(var)); #define r_b(var) file.read(reinterpret_cast<char*>(&var), sizeof(var)); -std::pair<std::string, std::string> generateNewFileName(const std::string& fileName, std::string ext) { +std::pair<std::string, std::string> generateNewFileName(const std::string& fileName, std::string ext, bool json = false) { + auto& logic = Logic::get(); + std::string rename_format = logic.rename_format; + std::string dir = ".echo\\"; std::string baseName = fs::path(fileName).stem().string(); + if (json) baseName = fs::path(fileName).stem().stem().string(); std::string extension = fs::path(fileName).extension().string(); + size_t hashPos = rename_format.find('#'); + size_t formatLength = rename_format.length(); + std::string newFileName = dir + baseName + ext; int count = 1; - size_t underscorePos = newFileName.find_last_of('_'); - if (underscorePos != std::string::npos) { - std::string suffix = newFileName.substr(underscorePos + 1); + while (fs::exists(newFileName)) { + size_t pos = newFileName.length() - ext.length() - formatLength; + std::string suffix = newFileName.substr(pos + hashPos + 1, formatLength - hashPos - 1); try { count = std::stoi(suffix) + 1; - newFileName.erase(underscorePos); // Remove the existing suffix - newFileName = newFileName + "_" + std::to_string(count) + ext; } catch (std::invalid_argument&) { // ignore and just use the default count value of 1 } - } - while (fs::exists(newFileName)) { - newFileName = dir + baseName + "_" + std::to_string(count) + ext; - count++; + std::stringstream ss; + ss << std::setw(formatLength - hashPos - 1) << std::setfill('0') << count; + std::string formattedCount = ss.str(); + newFileName.replace(pos + hashPos + 1, formatLength - hashPos - 1, formattedCount); } - return {newFileName, fs::path(newFileName).stem().string()}; + if (json) + return { newFileName, fs::path(newFileName).stem().stem().string() }; + + return { newFileName, fs::path(newFileName).stem().string() }; } void Logic::write_file(const std::string& filename) { @@ -593,7 +601,7 @@ void Logic::write_file_json(const std::string& filename) { std::string ext = ".echo.json"; std::string base = filename; - auto newFileName = generateNewFileName(base, ext); + auto newFileName = generateNewFileName(base, ext, true); std::string full_filename = newFileName.first; std::string newBase = newFileName.second; diff --git a/src/Logic/logic.hpp b/src/Logic/logic.hpp index 403c1e9..c1fb551 100644 --- a/src/Logic/logic.hpp +++ b/src/Logic/logic.hpp @@ -428,6 +428,8 @@ class Logic { std::chrono::duration<double> total_recording_time = std::chrono::duration<double>::zero(); int total_attempt_count = 1; + std::string rename_format = "_#"; + Recorder recorder; std::vector<Frame> inputs; diff --git a/src/main.cpp b/src/main.cpp index 7de0c8f..4bb2101 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -99,8 +99,6 @@ void writeConfig() { j["audio_speedhack"] = audiospeedhack.isEnabled(); - j["use_json"] = logic.use_json_for_files; - j["autoclicker"]["press_interval"] = Autoclicker::get().getFramesBetweenPresses(); j["autoclicker"]["release_interval"] = Autoclicker::get().getFramesBetweenReleases(); j["autoclicker"]["player_1"] = logic.autoclicker_player_1; @@ -237,8 +235,6 @@ void readConfig() { audiospeedhack.setEnabled(getOrDefault(j, "audio_speedhack", true)); - logic.use_json_for_files = getOrDefault(j, "use_json", false); - Autoclicker::get().setFramesBetweenPresses(getOrDefault(j["autoclicker"], "press_interval", 50)); Autoclicker::get().setFramesBetweenReleases(getOrDefault(j["autoclicker"], "release_interval", 50));