mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-04-02 13:37:42 -05:00
feat: Implement better and more complete undo/redo stack (#1433)
This PR aims to implement a more complete undo/redo stack that, unlike the old one, also supports undoing insertions, deletions and resize operations
This commit is contained in:
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
#include <hex/helpers/magic.hpp>
|
||||
@@ -20,9 +19,8 @@ namespace hex::prv {
|
||||
}
|
||||
|
||||
|
||||
Provider::Provider() : m_id(s_idCounter++) {
|
||||
this->m_patches.emplace_back();
|
||||
this->m_currPatches = this->m_patches.begin();
|
||||
Provider::Provider() : m_undoRedoStack(this), m_id(s_idCounter++) {
|
||||
|
||||
}
|
||||
|
||||
Provider::~Provider() {
|
||||
@@ -39,7 +37,7 @@ namespace hex::prv {
|
||||
}
|
||||
|
||||
void Provider::write(u64 offset, const void *buffer, size_t size) {
|
||||
this->writeRaw(offset - this->getBaseAddress(), buffer, size);
|
||||
EventManager::post<EventProviderDataModified>(this, offset, size, static_cast<const u8*>(buffer));
|
||||
this->markDirty();
|
||||
}
|
||||
|
||||
@@ -68,53 +66,29 @@ namespace hex::prv {
|
||||
file.writeBuffer(buffer.data(), bufferSize);
|
||||
}
|
||||
|
||||
for (auto &[patchAddress, patch] : getPatches()) {
|
||||
file.seek(patchAddress - this->getBaseAddress());
|
||||
file.writeBuffer(&patch, 1);
|
||||
}
|
||||
|
||||
EventManager::post<EventProviderSaved>(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Provider::resize(size_t newSize) {
|
||||
hex::unused(newSize);
|
||||
i64 difference = newSize - this->getActualSize();
|
||||
|
||||
if (difference > 0)
|
||||
EventManager::post<EventProviderDataInserted>(this, this->getActualSize(), difference);
|
||||
else if (difference < 0)
|
||||
EventManager::post<EventProviderDataRemoved>(this, this->getActualSize(), -difference);
|
||||
|
||||
this->markDirty();
|
||||
}
|
||||
|
||||
void Provider::insert(u64 offset, size_t size) {
|
||||
auto &patches = getPatches();
|
||||
|
||||
std::vector<std::pair<u64, u8>> patchesToMove;
|
||||
|
||||
for (auto &[address, value] : patches) {
|
||||
if (address > offset)
|
||||
patchesToMove.emplace_back(address, value);
|
||||
}
|
||||
|
||||
for (const auto &[address, value] : patchesToMove)
|
||||
patches.erase(address);
|
||||
for (const auto &[address, value] : patchesToMove)
|
||||
patches.insert({ address + size, value });
|
||||
EventManager::post<EventProviderDataInserted>(this, offset, size);
|
||||
|
||||
this->markDirty();
|
||||
}
|
||||
|
||||
void Provider::remove(u64 offset, size_t size) {
|
||||
auto &patches = getPatches();
|
||||
|
||||
std::vector<std::pair<u64, u8>> patchesToMove;
|
||||
|
||||
for (auto &[address, value] : patches) {
|
||||
if (address > offset)
|
||||
patchesToMove.emplace_back(address, value);
|
||||
}
|
||||
|
||||
for (const auto &[address, value] : patchesToMove)
|
||||
patches.erase(address);
|
||||
for (const auto &[address, value] : patchesToMove)
|
||||
patches.insert({ address - size, value });
|
||||
EventManager::post<EventProviderDataRemoved>(this, offset, size);
|
||||
|
||||
this->markDirty();
|
||||
}
|
||||
@@ -131,38 +105,6 @@ namespace hex::prv {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::map<u64, u8> &Provider::getPatches() {
|
||||
return *this->m_currPatches;
|
||||
}
|
||||
|
||||
const std::map<u64, u8> &Provider::getPatches() const {
|
||||
return *this->m_currPatches;
|
||||
}
|
||||
|
||||
void Provider::applyPatches() {
|
||||
if (!this->isWritable())
|
||||
return;
|
||||
|
||||
this->m_patches.emplace_back();
|
||||
|
||||
for (auto &[patchAddress, patch] : getPatches()) {
|
||||
u8 value = 0x00;
|
||||
this->readRaw(patchAddress - this->getBaseAddress(), &value, 1);
|
||||
this->m_patches.back().insert({ patchAddress, value });
|
||||
}
|
||||
|
||||
for (auto &[patchAddress, patch] : getPatches()) {
|
||||
this->writeRaw(patchAddress - this->getBaseAddress(), &patch, 1);
|
||||
}
|
||||
|
||||
this->markDirty();
|
||||
|
||||
this->m_patches.emplace_back();
|
||||
this->m_currPatches = std::prev(this->m_patches.end());
|
||||
}
|
||||
|
||||
|
||||
Overlay *Provider::newOverlay() {
|
||||
return this->m_overlays.emplace_back(std::make_unique<Overlay>()).get();
|
||||
}
|
||||
@@ -235,54 +177,20 @@ namespace hex::prv {
|
||||
return { };
|
||||
}
|
||||
|
||||
void Provider::addPatch(u64 offset, const void *buffer, size_t size, bool createUndo) {
|
||||
if (createUndo) {
|
||||
// Delete all patches after the current one if a modification is made while
|
||||
// the current patch list is not at the end of the undo stack
|
||||
if (std::next(this->m_currPatches) != this->m_patches.end())
|
||||
this->m_patches.erase(std::next(this->m_currPatches), this->m_patches.end());
|
||||
|
||||
createUndoPoint();
|
||||
}
|
||||
|
||||
for (u64 i = 0; i < size; i++) {
|
||||
u8 patch = static_cast<const u8 *>(buffer)[i];
|
||||
u8 originalValue = 0x00;
|
||||
this->readRaw((offset + i) - this->getBaseAddress(), &originalValue, sizeof(u8));
|
||||
|
||||
if (patch == originalValue)
|
||||
getPatches().erase(offset + i);
|
||||
else
|
||||
getPatches()[offset + i] = patch;
|
||||
|
||||
EventManager::post<EventPatchCreated>(offset, originalValue, patch);
|
||||
}
|
||||
|
||||
this->markDirty();
|
||||
|
||||
}
|
||||
|
||||
void Provider::createUndoPoint() {
|
||||
this->m_patches.push_back(getPatches());
|
||||
this->m_currPatches = std::prev(this->m_patches.end());
|
||||
}
|
||||
|
||||
void Provider::undo() {
|
||||
if (canUndo())
|
||||
--this->m_currPatches;
|
||||
this->m_undoRedoStack.undo();
|
||||
}
|
||||
|
||||
void Provider::redo() {
|
||||
if (canRedo())
|
||||
++this->m_currPatches;
|
||||
this->m_undoRedoStack.redo();
|
||||
}
|
||||
|
||||
bool Provider::canUndo() const {
|
||||
return this->m_currPatches != this->m_patches.begin();
|
||||
return this->m_undoRedoStack.canUndo();
|
||||
}
|
||||
|
||||
bool Provider::canRedo() const {
|
||||
return std::next(this->m_currPatches) != this->m_patches.end();
|
||||
return this->m_undoRedoStack.canRedo();
|
||||
}
|
||||
|
||||
bool Provider::hasFilePicker() const {
|
||||
@@ -341,14 +249,6 @@ namespace hex::prv {
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &[patchAddress, value] : this->m_patches.back()) {
|
||||
if (!nextRegionAddress.has_value() || patchAddress < nextRegionAddress)
|
||||
nextRegionAddress = patchAddress;
|
||||
|
||||
if (address == patchAddress)
|
||||
insideValidRegion = true;
|
||||
}
|
||||
|
||||
if (!nextRegionAddress.has_value())
|
||||
return { Region::Invalid(), false };
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user