diff --git a/lib/external/libwolv b/lib/external/libwolv index 7806c1939..8698c778c 160000 --- a/lib/external/libwolv +++ b/lib/external/libwolv @@ -1 +1 @@ -Subproject commit 7806c1939d5aa6c9af794e4b2ff9116bb89d54d8 +Subproject commit 8698c778c02b0e29cf681f7a3d65e020fd26a643 diff --git a/lib/libimhex/include/hex/providers/memory_provider.hpp b/lib/libimhex/include/hex/providers/memory_provider.hpp index 33bfb7aed..a0ddc1c07 100644 --- a/lib/libimhex/include/hex/providers/memory_provider.hpp +++ b/lib/libimhex/include/hex/providers/memory_provider.hpp @@ -35,8 +35,6 @@ namespace hex::prv { [[nodiscard]] u64 getActualSize() const override { return m_data.size(); } void resizeRaw(u64 newSize) override; - void insertRaw(u64 offset, u64 size) override; - void removeRaw(u64 offset, u64 size) override; [[nodiscard]] std::string getName() const override { return m_name; } diff --git a/lib/libimhex/include/hex/providers/provider.hpp b/lib/libimhex/include/hex/providers/provider.hpp index 86bb376ee..2fdf9d290 100644 --- a/lib/libimhex/include/hex/providers/provider.hpp +++ b/lib/libimhex/include/hex/providers/provider.hpp @@ -166,8 +166,8 @@ namespace hex::prv { void remove(u64 offset, u64 size); virtual void resizeRaw(u64 newSize) { hex::unused(newSize); } - virtual void insertRaw(u64 offset, u64 size) { hex::unused(offset, size); } - virtual void removeRaw(u64 offset, u64 size) { hex::unused(offset, size); } + virtual void insertRaw(u64 offset, u64 size); + virtual void removeRaw(u64 offset, u64 size); virtual void save(); virtual void saveAs(const std::fs::path &path); diff --git a/lib/libimhex/include/hex/providers/undo_redo/stack.hpp b/lib/libimhex/include/hex/providers/undo_redo/stack.hpp index 8355724b3..aa5b8eaba 100644 --- a/lib/libimhex/include/hex/providers/undo_redo/stack.hpp +++ b/lib/libimhex/include/hex/providers/undo_redo/stack.hpp @@ -27,6 +27,7 @@ namespace hex::prv::undo { void groupOperations(u32 count, const UnlocalizedString &unlocalizedName); void apply(const Stack &otherStack); + void reapply(); [[nodiscard]] bool canUndo() const; [[nodiscard]] bool canRedo() const; diff --git a/lib/libimhex/source/providers/memory_provider.cpp b/lib/libimhex/source/providers/memory_provider.cpp index 37cb6547e..311e3fac6 100644 --- a/lib/libimhex/source/providers/memory_provider.cpp +++ b/lib/libimhex/source/providers/memory_provider.cpp @@ -31,41 +31,4 @@ namespace hex::prv { m_data.resize(newSize); } - void MemoryProvider::insertRaw(u64 offset, u64 size) { - auto oldSize = this->getActualSize(); - this->resizeRaw(oldSize + size); - - std::vector buffer(0x1000); - const std::vector zeroBuffer(0x1000); - - auto position = oldSize; - while (position > offset) { - const auto readSize = std::min(position - offset, buffer.size()); - - position -= readSize; - - this->readRaw(position, buffer.data(), readSize); - this->writeRaw(position, zeroBuffer.data(), readSize); - this->writeRaw(position + size, buffer.data(), readSize); - } - } - - void MemoryProvider::removeRaw(u64 offset, u64 size) { - auto oldSize = this->getActualSize(); - std::vector buffer(0x1000); - - const auto newSize = oldSize - size; - auto position = offset; - while (position < newSize) { - const auto readSize = std::min(newSize - position, buffer.size()); - - this->readRaw(position + size, buffer.data(), readSize); - this->writeRaw(position, buffer.data(), readSize); - - position += readSize; - } - - this->resizeRaw(oldSize - size); - } - } diff --git a/lib/libimhex/source/providers/provider.cpp b/lib/libimhex/source/providers/provider.cpp index 46918908e..5bda5906e 100644 --- a/lib/libimhex/source/providers/provider.cpp +++ b/lib/libimhex/source/providers/provider.cpp @@ -102,6 +102,52 @@ namespace hex::prv { this->markDirty(); } + void Provider::insertRaw(u64 offset, u64 size) { + auto oldSize = this->getActualSize(); + this->resizeRaw(oldSize + size); + + std::vector buffer(0x1000); + const std::vector zeroBuffer(0x1000); + + auto position = oldSize; + while (position > offset) { + const auto readSize = std::min(position - offset, buffer.size()); + + position -= readSize; + + this->readRaw(position, buffer.data(), readSize); + this->writeRaw(position, zeroBuffer.data(), readSize); + this->writeRaw(position + size, buffer.data(), readSize); + } + } + + void Provider::removeRaw(u64 offset, u64 size) { + if (offset > this->getActualSize() || size == 0) + return; + + if ((offset + size) > this->getActualSize()) + size = this->getActualSize() - offset; + + auto oldSize = this->getActualSize(); + + std::vector buffer(0x1000); + + const auto newSize = oldSize - size; + auto position = offset; + while (position < newSize) { + const auto readSize = std::min(newSize - position, buffer.size()); + + this->readRaw(position + size, buffer.data(), readSize); + this->writeRaw(position, buffer.data(), readSize); + + position += readSize; + } + + this->resizeRaw(newSize); + } + + + void Provider::applyOverlays(u64 offset, void *buffer, size_t size) const { for (auto &overlay : m_overlays) { auto overlayOffset = overlay->getAddress(); diff --git a/lib/libimhex/source/providers/undo/stack.cpp b/lib/libimhex/source/providers/undo/stack.cpp index 1e5f3501b..efbda320c 100644 --- a/lib/libimhex/source/providers/undo/stack.cpp +++ b/lib/libimhex/source/providers/undo/stack.cpp @@ -93,6 +93,13 @@ namespace hex::prv::undo { } } + void Stack::reapply() { + for (const auto &operation : m_undoStack) { + operation->redo(m_provider); + } + } + + bool Stack::add(std::unique_ptr &&operation) { diff --git a/plugins/builtin/include/content/providers/file_provider.hpp b/plugins/builtin/include/content/providers/file_provider.hpp index 24c510a55..0d9bbb90d 100644 --- a/plugins/builtin/include/content/providers/file_provider.hpp +++ b/plugins/builtin/include/content/providers/file_provider.hpp @@ -11,6 +11,8 @@ namespace hex::plugin::builtin { class FileProvider : public hex::prv::Provider { public: + constexpr static u64 MaxMemoryFileSize = 128 * 1024 * 1024; + FileProvider() = default; ~FileProvider() override = default; @@ -21,8 +23,6 @@ namespace hex::plugin::builtin { [[nodiscard]] bool isSavable() const override; void resizeRaw(u64 newSize) override; - void insertRaw(u64 offset, u64 size) override; - void removeRaw(u64 offset, u64 size) override; void readRaw(u64 offset, void *buffer, size_t size) override; void writeRaw(u64 offset, const void *buffer, size_t size) override; @@ -57,12 +57,17 @@ namespace hex::plugin::builtin { private: void convertToMemoryFile(); + void handleFileChange(); protected: std::fs::path m_path; wolv::io::File m_file; size_t m_fileSize = 0; + wolv::io::ChangeTracker m_changeTracker; + std::vector m_data; + bool m_loadedIntoMemory = false; + std::optional m_fileStats; bool m_readable = false, m_writable = false; diff --git a/plugins/builtin/include/content/providers/memory_file_provider.hpp b/plugins/builtin/include/content/providers/memory_file_provider.hpp index 1ad05f1a4..876194aeb 100644 --- a/plugins/builtin/include/content/providers/memory_file_provider.hpp +++ b/plugins/builtin/include/content/providers/memory_file_provider.hpp @@ -24,8 +24,6 @@ namespace hex::plugin::builtin { [[nodiscard]] u64 getActualSize() const override { return m_data.size(); } void resizeRaw(u64 newSize) override; - void insertRaw(u64 offset, u64 size) override; - void removeRaw(u64 offset, u64 size) override; void save() override; diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 7e877223d..84fe1c2a1 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -402,6 +402,8 @@ "hex.builtin.provider.file.size": "Size", "hex.builtin.provider.file.menu.open_file": "Open file externally", "hex.builtin.provider.file.menu.open_folder": "Open containing folder", + "hex.builtin.provider.file.too_large": "This file is too large to be loaded into memory. Opening it anyways will result in modifications to be written directly to the file. Would you like to open it as Read-Only instead?", + "hex.builtin.provider.file.reload_changes": "The file has been modified by an external source. Do you want to reload it?", "hex.builtin.provider.gdb": "GDB Server Provider", "hex.builtin.provider.gdb.ip": "IP Address", "hex.builtin.provider.gdb.name": "GDB Server <{0}:{1}>", diff --git a/plugins/builtin/source/content/providers/file_provider.cpp b/plugins/builtin/source/content/providers/file_provider.cpp index f3ad0a70c..3b7580f91 100644 --- a/plugins/builtin/source/content/providers/file_provider.cpp +++ b/plugins/builtin/source/content/providers/file_provider.cpp @@ -16,6 +16,7 @@ #include #include +#include #if defined(OS_WINDOWS) #include @@ -42,25 +43,37 @@ namespace hex::plugin::builtin { } bool FileProvider::isSavable() const { - return m_undoRedoStack.canUndo(); + return m_loadedIntoMemory; } void FileProvider::readRaw(u64 offset, void *buffer, size_t size) { if (m_fileSize == 0 || (offset + size) > m_fileSize || buffer == nullptr || size == 0) return; - m_file.readBufferAtomic(offset, static_cast(buffer), size); + if (m_loadedIntoMemory) + std::memcpy(buffer, m_data.data() + offset, size); + else + m_file.readBufferAtomic(offset, static_cast(buffer), size); } void FileProvider::writeRaw(u64 offset, const void *buffer, size_t size) { if ((offset + size) > this->getActualSize() || buffer == nullptr || size == 0) return; - m_file.writeBufferAtomic(offset, static_cast(buffer), size); + if (m_loadedIntoMemory) + std::memcpy(m_data.data() + offset, buffer, size); + else + m_file.writeBufferAtomic(offset, static_cast(buffer), size); } void FileProvider::save() { - m_file.flush(); + if (m_loadedIntoMemory) { + m_file.open(); + m_file.writeVectorAtomic(0x00, m_data); + m_file.setSize(m_data.size()); + } else { + m_file.flush(); + } #if defined(OS_WINDOWS) FILETIME ft; @@ -75,6 +88,9 @@ namespace hex::plugin::builtin { } #endif + if (m_loadedIntoMemory) + m_file.close(); + Provider::save(); } @@ -86,54 +102,14 @@ namespace hex::plugin::builtin { } void FileProvider::resizeRaw(u64 newSize) { - m_file.setSize(newSize); + if (m_loadedIntoMemory) + m_data.resize(newSize); + else + m_file.setSize(newSize); + m_fileSize = newSize; } - void FileProvider::insertRaw(u64 offset, u64 size) { - auto oldSize = this->getActualSize(); - this->resizeRaw(oldSize + size); - - std::vector buffer(0x1000); - const std::vector zeroBuffer(0x1000); - - auto position = oldSize; - while (position > offset) { - const auto readSize = std::min(position - offset, buffer.size()); - - position -= readSize; - - this->readRaw(position, buffer.data(), readSize); - this->writeRaw(position, zeroBuffer.data(), readSize); - this->writeRaw(position + size, buffer.data(), readSize); - } - } - - void FileProvider::removeRaw(u64 offset, u64 size) { - if (offset > this->getActualSize() || size == 0) - return; - - if ((offset + size) > this->getActualSize()) - size = this->getActualSize() - offset; - - auto oldSize = this->getActualSize(); - - std::vector buffer(0x1000); - - const auto newSize = oldSize - size; - auto position = offset; - while (position < newSize) { - const auto readSize = std::min(newSize - position, buffer.size()); - - this->readRaw(position + size, buffer.data(), readSize); - this->writeRaw(position, buffer.data(), readSize); - - position += readSize; - } - - this->resizeRaw(newSize); - } - u64 FileProvider::getActualSize() const { return m_fileSize; } @@ -246,12 +222,36 @@ namespace hex::plugin::builtin { } } + if (m_writable) { + if (m_fileSize < MaxMemoryFileSize && !m_writable) { + m_data = m_file.readVectorAtomic(0x00, m_fileSize); + if (!m_data.empty()) { + m_changeTracker = wolv::io::ChangeTracker(m_file); + m_changeTracker.startTracking(std::bind_front(FileProvider::handleFileChange, this)); + m_file.close(); + m_loadedIntoMemory = true; + } + } else { + m_writable = false; + ui::PopupQuestion::open("hex.builtin.provider.file.too_large"_lang, + [this] { + m_writable = false; + }, + [this] { + m_writable = true; + RequestUpdateWindowTitle::post(); + }); + } + } + return true; } void FileProvider::close() { m_file.close(); + m_data.clear(); s_openedFiles.erase(this); + m_changeTracker.stopTracking(); } void FileProvider::loadSettings(const nlohmann::json &settings) { @@ -332,4 +332,14 @@ namespace hex::plugin::builtin { } } + void FileProvider::handleFileChange() { + ui::PopupQuestion::open("hex.builtin.provider.file.reload_changes"_lang, [this] { + this->close(); + (void)this->open(); + getUndoStack().reapply(); + }, + []{}); + } + + } diff --git a/plugins/builtin/source/content/providers/memory_file_provider.cpp b/plugins/builtin/source/content/providers/memory_file_provider.cpp index c432e630c..ac23c3127 100644 --- a/plugins/builtin/source/content/providers/memory_file_provider.cpp +++ b/plugins/builtin/source/content/providers/memory_file_provider.cpp @@ -69,43 +69,6 @@ namespace hex::plugin::builtin { m_data.resize(newSize); } - void MemoryFileProvider::insertRaw(u64 offset, u64 size) { - auto oldSize = this->getActualSize(); - this->resizeRaw(oldSize + size); - - std::vector buffer(0x1000); - const std::vector zeroBuffer(0x1000); - - auto position = oldSize; - while (position > offset) { - const auto readSize = std::min(position - offset, buffer.size()); - - position -= readSize; - - this->readRaw(position, buffer.data(), readSize); - this->writeRaw(position, zeroBuffer.data(), readSize); - this->writeRaw(position + size, buffer.data(), readSize); - } - } - - void MemoryFileProvider::removeRaw(u64 offset, u64 size) { - auto oldSize = this->getActualSize(); - std::vector buffer(0x1000); - - const auto newSize = oldSize - size; - auto position = offset; - while (position < newSize) { - const auto readSize = std::min(newSize - position, buffer.size()); - - this->readRaw(position + size, buffer.data(), readSize); - this->writeRaw(position, buffer.data(), readSize); - - position += readSize; - } - - this->resizeRaw(oldSize - size); - } - [[nodiscard]] std::string MemoryFileProvider::getName() const { if (m_name.empty()) return Lang("hex.builtin.provider.mem_file.unsaved"); diff --git a/plugins/builtin/source/content/views/view_patches.cpp b/plugins/builtin/source/content/views/view_patches.cpp index c5ddf2349..a4bb8b1a0 100644 --- a/plugins/builtin/source/content/views/view_patches.cpp +++ b/plugins/builtin/source/content/views/view_patches.cpp @@ -50,6 +50,8 @@ namespace hex::plugin::builtin { return std::nullopt; auto provider = ImHexApi::Provider::get(); + if (!provider->isSavable()) + return std::nullopt; offset -= provider->getBaseAddress();