mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-04-02 21:47:40 -05:00
impr: Load small files into memory, open larger files as read-only by default
#841, #1585
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user