impr: Load small files into memory, open larger files as read-only by default
#841, #1585
This commit is contained in:
parent
f050c69ccd
commit
45a3bdffe0
2
lib/external/libwolv
vendored
2
lib/external/libwolv
vendored
@ -1 +1 @@
|
||||
Subproject commit 7806c1939d5aa6c9af794e4b2ff9116bb89d54d8
|
||||
Subproject commit 8698c778c02b0e29cf681f7a3d65e020fd26a643
|
@ -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; }
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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<u8> buffer(0x1000);
|
||||
const std::vector<u8> zeroBuffer(0x1000);
|
||||
|
||||
auto position = oldSize;
|
||||
while (position > offset) {
|
||||
const auto readSize = std::min<size_t>(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<u8> buffer(0x1000);
|
||||
|
||||
const auto newSize = oldSize - size;
|
||||
auto position = offset;
|
||||
while (position < newSize) {
|
||||
const auto readSize = std::min<size_t>(newSize - position, buffer.size());
|
||||
|
||||
this->readRaw(position + size, buffer.data(), readSize);
|
||||
this->writeRaw(position, buffer.data(), readSize);
|
||||
|
||||
position += readSize;
|
||||
}
|
||||
|
||||
this->resizeRaw(oldSize - size);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<u8> buffer(0x1000);
|
||||
const std::vector<u8> zeroBuffer(0x1000);
|
||||
|
||||
auto position = oldSize;
|
||||
while (position > offset) {
|
||||
const auto readSize = std::min<size_t>(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<u8> buffer(0x1000);
|
||||
|
||||
const auto newSize = oldSize - size;
|
||||
auto position = offset;
|
||||
while (position < newSize) {
|
||||
const auto readSize = std::min<size_t>(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();
|
||||
|
@ -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> &&operation) {
|
||||
|
@ -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<u8> m_data;
|
||||
bool m_loadedIntoMemory = false;
|
||||
|
||||
std::optional<struct stat> m_fileStats;
|
||||
|
||||
bool m_readable = false, m_writable = false;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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}>",
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <cstring>
|
||||
#include <popups/popup_question.hpp>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <windows.h>
|
||||
@ -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<u8*>(buffer), size);
|
||||
if (m_loadedIntoMemory)
|
||||
std::memcpy(buffer, m_data.data() + offset, size);
|
||||
else
|
||||
m_file.readBufferAtomic(offset, static_cast<u8*>(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<const u8*>(buffer), size);
|
||||
if (m_loadedIntoMemory)
|
||||
std::memcpy(m_data.data() + offset, buffer, size);
|
||||
else
|
||||
m_file.writeBufferAtomic(offset, static_cast<const u8*>(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<u8> buffer(0x1000);
|
||||
const std::vector<u8> zeroBuffer(0x1000);
|
||||
|
||||
auto position = oldSize;
|
||||
while (position > offset) {
|
||||
const auto readSize = std::min<size_t>(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<u8> buffer(0x1000);
|
||||
|
||||
const auto newSize = oldSize - size;
|
||||
auto position = offset;
|
||||
while (position < newSize) {
|
||||
const auto readSize = std::min<size_t>(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();
|
||||
},
|
||||
[]{});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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<u8> buffer(0x1000);
|
||||
const std::vector<u8> zeroBuffer(0x1000);
|
||||
|
||||
auto position = oldSize;
|
||||
while (position > offset) {
|
||||
const auto readSize = std::min<size_t>(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<u8> buffer(0x1000);
|
||||
|
||||
const auto newSize = oldSize - size;
|
||||
auto position = offset;
|
||||
while (position < newSize) {
|
||||
const auto readSize = std::min<size_t>(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");
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user