mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 07:47:03 -05:00
<!-- Please provide as much information as possible about what your PR aims to do. PRs with no description will most likely be closed until more information is provided. If you're planing on changing fundamental behaviour or add big new features, please open a GitHub Issue first before starting to work on it. If it's not something big and you still want to contact us about it, feel free to do so ! --> ### Problem description <!-- Describe the bug that you fixed/feature request that you implemented, or link to an existing issue describing it --> ### Implementation description <!-- Explain what you did to correct the problem --> ### Screenshots <!-- If your change is visual, take a screenshot showing it. Ideally, make before/after sceenshots --> ### Additional things <!-- Anything else you would like to say -->
343 lines
11 KiB
C++
343 lines
11 KiB
C++
#include <hex/providers/provider.hpp>
|
|
|
|
#include <hex.hpp>
|
|
#include <hex/api/events/events_provider.hpp>
|
|
#include <hex/api/events/events_interaction.hpp>
|
|
|
|
#include <cmath>
|
|
#include <cstring>
|
|
#include <optional>
|
|
#include <hex/api/content_registry/settings.hpp>
|
|
|
|
#include <hex/helpers/magic.hpp>
|
|
#include <wolv/io/file.hpp>
|
|
#include <wolv/literals.hpp>
|
|
|
|
#include <nlohmann/json.hpp>
|
|
#include <wolv/utils/string.hpp>
|
|
|
|
namespace hex::prv {
|
|
|
|
using namespace wolv::literals;
|
|
|
|
namespace {
|
|
|
|
u32 s_idCounter = 0;
|
|
|
|
}
|
|
|
|
IProviderDataBackupable::IProviderDataBackupable(Provider* provider) : m_provider(provider) {
|
|
m_shouldCreateBackups = ContentRegistry::Settings::read<bool>("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.enable", true);
|
|
m_maxSize = ContentRegistry::Settings::read<u32>("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.max_size", 1_MiB);
|
|
m_backupExtension = ContentRegistry::Settings::read<std::string>("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.extension", ".bak");
|
|
}
|
|
|
|
void IProviderDataBackupable::createBackupIfNeeded(const std::fs::path &inputFilePath) {
|
|
if (!m_shouldCreateBackups || m_backupCreated)
|
|
return;
|
|
|
|
if (m_provider->getActualSize() > m_maxSize)
|
|
return;
|
|
|
|
const std::fs::path backupFilePath = wolv::util::toUTF8String(inputFilePath) + m_backupExtension;
|
|
if (wolv::io::fs::copyFile(inputFilePath, backupFilePath, std::fs::copy_options::overwrite_existing)) {
|
|
if (wolv::io::fs::exists(backupFilePath)) {
|
|
m_backupCreated = true;
|
|
log::info("Created backup of provider data at '{}'", backupFilePath.string());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Provider::Provider() : m_undoRedoStack(this), m_id(s_idCounter++) {
|
|
|
|
}
|
|
|
|
Provider::~Provider() {
|
|
m_overlays.clear();
|
|
|
|
if (auto selection = ImHexApi::HexEditor::getSelection(); selection.has_value() && selection->provider == this)
|
|
EventRegionSelected::post(ImHexApi::HexEditor::ProviderRegion { { .address=0x00, .size=0x00 }, nullptr });
|
|
}
|
|
|
|
void Provider::read(u64 offset, void *buffer, size_t size, bool overlays) {
|
|
this->readRaw(offset - this->getBaseAddress(), buffer, size);
|
|
|
|
if (overlays)
|
|
this->applyOverlays(offset, buffer, size);
|
|
}
|
|
|
|
void Provider::write(u64 offset, const void *buffer, size_t size) {
|
|
if (!this->isWritable())
|
|
return;
|
|
|
|
EventProviderDataModified::post(this, offset, size, static_cast<const u8*>(buffer));
|
|
this->markDirty();
|
|
}
|
|
|
|
void Provider::save() {
|
|
if (!this->isWritable())
|
|
return;
|
|
|
|
EventProviderSaved::post(this);
|
|
}
|
|
void Provider::saveAs(const std::fs::path &path) {
|
|
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
|
|
|
if (file.isValid()) {
|
|
std::vector<u8> buffer(std::min<size_t>(2_MiB, this->getActualSize()), 0x00);
|
|
size_t bufferSize = 0;
|
|
|
|
for (u64 offset = 0; offset < this->getActualSize(); offset += bufferSize) {
|
|
bufferSize = std::min<size_t>(buffer.size(), this->getActualSize() - offset);
|
|
|
|
this->read(this->getBaseAddress() + offset, buffer.data(), bufferSize, true);
|
|
file.writeBuffer(buffer.data(), bufferSize);
|
|
}
|
|
|
|
EventProviderSaved::post(this);
|
|
}
|
|
}
|
|
|
|
bool Provider::resize(u64 newSize) {
|
|
if (newSize >> 63) {
|
|
log::error("new provider size is very large ({}). Is it a negative number ?", newSize);
|
|
return false;
|
|
}
|
|
i64 difference = newSize - this->getActualSize();
|
|
|
|
if (difference > 0)
|
|
EventProviderDataInserted::post(this, this->getActualSize(), difference);
|
|
else if (difference < 0)
|
|
EventProviderDataRemoved::post(this, this->getActualSize() + difference, -difference);
|
|
|
|
this->markDirty();
|
|
return true;
|
|
}
|
|
|
|
void Provider::insert(u64 offset, u64 size) {
|
|
EventProviderDataInserted::post(this, offset, size);
|
|
|
|
this->markDirty();
|
|
}
|
|
|
|
void Provider::remove(u64 offset, u64 size) {
|
|
EventProviderDataRemoved::post(this, offset, size);
|
|
|
|
this->markDirty();
|
|
}
|
|
|
|
void Provider::insertRaw(u64 offset, u64 size) {
|
|
auto oldSize = this->getActualSize();
|
|
auto newSize = oldSize + size;
|
|
this->resizeRaw(newSize);
|
|
|
|
std::vector<u8> buffer(0x1000, 0x00);
|
|
const std::vector<u8> zeroBuffer(0x1000, 0x00);
|
|
|
|
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(), newSize - oldSize);
|
|
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();
|
|
auto overlaySize = overlay->getSize();
|
|
|
|
u64 overlapMin = std::max(offset, overlayOffset);
|
|
u64 overlapMax = std::min(offset + size, overlayOffset + overlaySize);
|
|
if (overlapMax > overlapMin)
|
|
std::memcpy(static_cast<u8 *>(buffer) + std::max<u64>(0, overlapMin - offset), overlay->getData().data() + std::max<u64>(0, overlapMin - overlayOffset), overlapMax - overlapMin);
|
|
}
|
|
}
|
|
|
|
Overlay *Provider::newOverlay() {
|
|
return m_overlays.emplace_back(std::make_unique<Overlay>()).get();
|
|
}
|
|
|
|
void Provider::deleteOverlay(Overlay *overlay) {
|
|
m_overlays.remove_if([overlay](const auto &item) {
|
|
return item.get() == overlay;
|
|
});
|
|
}
|
|
|
|
const std::list<std::unique_ptr<Overlay>> &Provider::getOverlays() const {
|
|
return m_overlays;
|
|
}
|
|
|
|
|
|
u64 Provider::getPageSize() const {
|
|
return m_pageSize;
|
|
}
|
|
|
|
void Provider::setPageSize(u64 pageSize) {
|
|
if (pageSize > MaxPageSize)
|
|
pageSize = MaxPageSize;
|
|
if (pageSize == 0)
|
|
return;
|
|
|
|
m_pageSize = pageSize;
|
|
}
|
|
|
|
u32 Provider::getPageCount() const {
|
|
return (this->getActualSize() / this->getPageSize()) + (this->getActualSize() % this->getPageSize() != 0 ? 1 : 0);
|
|
}
|
|
|
|
u32 Provider::getCurrentPage() const {
|
|
return m_currPage;
|
|
}
|
|
|
|
void Provider::setCurrentPage(u32 page) {
|
|
if (page < getPageCount())
|
|
m_currPage = page;
|
|
}
|
|
|
|
|
|
void Provider::setBaseAddress(u64 address) {
|
|
m_baseAddress = address;
|
|
this->markDirty();
|
|
}
|
|
|
|
u64 Provider::getBaseAddress() const {
|
|
return m_baseAddress;
|
|
}
|
|
|
|
u64 Provider::getCurrentPageAddress() const {
|
|
return this->getPageSize() * this->getCurrentPage();
|
|
}
|
|
|
|
u64 Provider::getSize() const {
|
|
return std::min<u64>(this->getActualSize() - this->getPageSize() * m_currPage, this->getPageSize());
|
|
}
|
|
|
|
std::optional<u32> Provider::getPageOfAddress(u64 address) const {
|
|
u32 page = std::floor((address - this->getBaseAddress()) / double(this->getPageSize()));
|
|
|
|
if (page >= this->getPageCount())
|
|
return std::nullopt;
|
|
|
|
return page;
|
|
}
|
|
|
|
void Provider::undo() {
|
|
m_undoRedoStack.undo();
|
|
}
|
|
|
|
void Provider::redo() {
|
|
m_undoRedoStack.redo();
|
|
}
|
|
|
|
bool Provider::canUndo() const {
|
|
return m_undoRedoStack.canUndo();
|
|
}
|
|
|
|
bool Provider::canRedo() const {
|
|
return m_undoRedoStack.canRedo();
|
|
}
|
|
|
|
nlohmann::json Provider::storeSettings(nlohmann::json settings) const {
|
|
settings["displayName"] = this->getName();
|
|
settings["type"] = this->getTypeName();
|
|
|
|
settings["baseAddress"] = m_baseAddress;
|
|
settings["currPage"] = m_currPage;
|
|
|
|
return settings;
|
|
}
|
|
|
|
void Provider::loadSettings(const nlohmann::json &settings) {
|
|
m_baseAddress = settings["baseAddress"];
|
|
m_currPage = settings["currPage"];
|
|
}
|
|
|
|
std::pair<Region, bool> Provider::getRegionValidity(u64 address) const {
|
|
u64 absoluteAddress = address - this->getBaseAddress();
|
|
|
|
if (absoluteAddress < this->getActualSize())
|
|
return { Region { .address=this->getBaseAddress() + absoluteAddress, .size=this->getActualSize() - absoluteAddress }, true };
|
|
|
|
|
|
bool insideValidRegion = false;
|
|
|
|
std::optional<u64> nextRegionAddress;
|
|
for (const auto &overlay : m_overlays) {
|
|
Region overlayRegion = { .address=overlay->getAddress(), .size=overlay->getSize() };
|
|
if (!nextRegionAddress.has_value() || overlay->getAddress() < nextRegionAddress) {
|
|
nextRegionAddress = overlayRegion.getStartAddress();
|
|
}
|
|
|
|
if (Region { .address=address, .size=1 }.overlaps(overlayRegion)) {
|
|
insideValidRegion = true;
|
|
}
|
|
}
|
|
|
|
if (!nextRegionAddress.has_value())
|
|
return { Region::Invalid(), false };
|
|
else
|
|
return { Region { .address=address, .size=*nextRegionAddress - address }, insideValidRegion };
|
|
}
|
|
|
|
|
|
u32 Provider::getID() const {
|
|
return m_id;
|
|
}
|
|
|
|
void Provider::setID(u32 id) {
|
|
m_id = id;
|
|
if (id > s_idCounter)
|
|
s_idCounter = id + 1;
|
|
}
|
|
|
|
|
|
[[nodiscard]] std::variant<std::string, i128> Provider::queryInformation(const std::string &category, const std::string &) {
|
|
if (category == "mime")
|
|
return magic::getMIMEType(this);
|
|
else if (category == "description")
|
|
return magic::getDescription(this);
|
|
else if (category == "provider_type")
|
|
return this->getTypeName().get();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
[[nodiscard]] bool Provider::isDumpable() const {
|
|
return true;
|
|
}
|
|
|
|
}
|