mirror of
https://github.com/rizinorg/cutter.git
synced 2025-02-20 21:56:06 +00:00
Add menu options for changing IO mode (#2187)
This commit is contained in:
parent
3545f059f1
commit
6f75fa1a71
@ -24,6 +24,33 @@ Import a PDB file
|
||||
|
||||
**Steps:** File -> Import PDB
|
||||
|
||||
Enable Write mode
|
||||
-----------------------
|
||||
**Description:** This option will enable Write mode and allow you to patch and edit the binary on disk. **Please note** that when Write mode is enabled, each change you make to the binary in Cutter will be applied to the original file on disk. In order not to take unnecessary risks, consider using Cache mode instead.
|
||||
|
||||
**Steps:** File -> Set mode -> Write mode
|
||||
|
||||
|
||||
Enable Cache mode
|
||||
-----------------------
|
||||
**Description:** This option will enable Cache mode and allow you to patch and edit the binary **without** applying the changes to the file on disk. Unlike in Write mode, in Cache mode, the changes you make to the binary in Cutter will not be applied to the original file on disk unless you specifically committing them using the "Commit changes" menu item. This is safer than using Write mode because there is no risk to lose important data.
|
||||
|
||||
**Steps:** File -> Set mode -> Cache mode
|
||||
|
||||
|
||||
Enable Read-Only mode
|
||||
------------------------
|
||||
**Description:** This option is available when files are opened in Write or Cache modes. When Read-Only mode is enabled, no patches and editions to the file are allowed. This is the default mode for files in Cutter, unless specified otherwise by the user, by either enabling Write or Cache mode.
|
||||
|
||||
**Steps:** File -> Set mode -> Read-Only mode
|
||||
|
||||
Commit changes from cache
|
||||
----------------------------
|
||||
**Description:** Apply the changes performed in Cache mode to the file on disk. Cache mode will not apply the changes and patches made unless the user clicks "Commit changes". To automatically apply every change to the file on disk, use the less-safer Write mode.
|
||||
|
||||
**Steps:** File -> Commit changes
|
||||
|
||||
|
||||
Save project
|
||||
----------------------------------------
|
||||
**Description:** Save your session to a project. If no project file assigned to your session, the "Save as..." dialog will open.
|
||||
|
@ -27,8 +27,11 @@ void AnalTask::runTask()
|
||||
openFailed = false;
|
||||
|
||||
int perms = R_PERM_RX;
|
||||
if (options.writeEnabled)
|
||||
if (options.writeEnabled) {
|
||||
perms |= R_PERM_W;
|
||||
emit Core()->ioModeChanged();
|
||||
|
||||
}
|
||||
|
||||
// Demangle (must be before file Core()->loadFile)
|
||||
Core()->setConfig("bin.demangle", options.demangle);
|
||||
|
@ -1,14 +1,47 @@
|
||||
#include "IOModesController.h"
|
||||
#include "Cutter.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QPushButton>
|
||||
#include <QObject>
|
||||
#include <QMessageBox>
|
||||
#include <QJsonObject>
|
||||
|
||||
bool IOModesController::canWrite()
|
||||
{
|
||||
return Core()->isIOCacheEnabled() || Core()->isWriteModeEnabled();
|
||||
}
|
||||
|
||||
IOModesController::Mode IOModesController::getIOMode()
|
||||
{
|
||||
if (Core()->isWriteModeEnabled()) {
|
||||
return Mode::WRITE;
|
||||
} else if (Core()->isIOCacheEnabled()) {
|
||||
return Mode::CACHE;
|
||||
} else {
|
||||
return Mode::READ_ONLY;
|
||||
}
|
||||
}
|
||||
|
||||
void IOModesController::setIOMode(IOModesController::Mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case Mode::READ_ONLY:
|
||||
if (askCommitUnsavedChanges()) {
|
||||
Core()->setWriteMode(false);
|
||||
}
|
||||
break;
|
||||
case Mode::CACHE:
|
||||
Core()->setIOCache(true);
|
||||
break;
|
||||
case Mode::WRITE:
|
||||
if (askCommitUnsavedChanges()) {
|
||||
Core()->setWriteMode(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool IOModesController::prepareForWriting()
|
||||
{
|
||||
if (canWrite()) {
|
||||
@ -20,15 +53,15 @@ bool IOModesController::prepareForWriting()
|
||||
msgBox.setWindowTitle(QObject::tr("Write error"));
|
||||
msgBox.setText(
|
||||
QObject::tr("Your file is opened in read-only mode. "
|
||||
"Editing is only available when the file is opened in either Write or Cache modes.\n\n"
|
||||
"WARNING: In Write mode, any changes will be committed to the file on disk. "
|
||||
"For safety, please consider using Cache mode and then commit the changes manually "
|
||||
"via File -> Commit modifications to disk."));
|
||||
"Editing is only available when the file is opened in either Write or Cache modes.\n\n"
|
||||
"WARNING: In Write mode, any changes will be committed to the file on disk. "
|
||||
"For safety, please consider using Cache mode and then commit the changes manually "
|
||||
"via File -> Commit modifications to disk."));
|
||||
msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole);
|
||||
QAbstractButton *reopenButton = msgBox.addButton(QObject::tr("Reopen in Write mode"),
|
||||
QMessageBox::YesRole);
|
||||
QAbstractButton *iocacheButton = msgBox.addButton(QObject::tr("Enable Cache mode"),
|
||||
QMessageBox::YesRole);
|
||||
QMessageBox::YesRole);
|
||||
|
||||
msgBox.exec();
|
||||
|
||||
@ -41,3 +74,40 @@ bool IOModesController::prepareForWriting()
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IOModesController::allChangesComitted()
|
||||
{
|
||||
// Get a list of available write changes
|
||||
QJsonArray changes = Core()->cmdj("wcj").array();
|
||||
|
||||
// Check if there is a change which isn't written to the file
|
||||
for (const QJsonValue &value : changes) {
|
||||
QJsonObject changeObject = value.toObject();
|
||||
if (!changeObject["written"].toBool()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IOModesController::askCommitUnsavedChanges()
|
||||
{
|
||||
// Check if there are uncommitted changes
|
||||
if (!allChangesComitted()) {
|
||||
QMessageBox::StandardButton ret = QMessageBox::question(NULL, QObject::tr("Uncomitted changes"),
|
||||
QObject::tr("It seems that you have changes or patches that are not committed to the file.\n"
|
||||
"Do you want to commit them now?"),
|
||||
(QMessageBox::StandardButtons)(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel));
|
||||
if (ret == QMessageBox::Save) {
|
||||
Core()->commitWriteCache();
|
||||
} else if (ret == QMessageBox::Discard) {
|
||||
Core()->cmdRaw("wcr");
|
||||
emit Core()->refreshCodeViews();
|
||||
} else if (ret == QMessageBox::Cancel) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3,13 +3,20 @@
|
||||
|
||||
#include "core/Cutter.h"
|
||||
|
||||
class IOModesController
|
||||
class IOModesController : public QObject
|
||||
|
||||
{
|
||||
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class Mode { READ_ONLY, CACHE, WRITE };
|
||||
bool prepareForWriting();
|
||||
bool canWrite();
|
||||
bool allChangesComitted();
|
||||
Mode getIOMode();
|
||||
void setIOMode(Mode mode);
|
||||
|
||||
public slots:
|
||||
bool askCommitUnsavedChanges();
|
||||
};
|
||||
|
||||
#endif // IOMODESCONTROLLER_H
|
||||
|
@ -3786,9 +3786,15 @@ BasicInstructionHighlighter* CutterCore::getBIHighlighter()
|
||||
|
||||
void CutterCore::setIOCache(bool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
// disable write mode when cache is enabled
|
||||
setWriteMode(false);
|
||||
}
|
||||
setConfig("io.cache", enabled);
|
||||
this->iocache = enabled;
|
||||
|
||||
emit ioCacheChanged(enabled);
|
||||
emit ioModeChanged();
|
||||
}
|
||||
|
||||
bool CutterCore::isIOCacheEnabled() const
|
||||
@ -3798,8 +3804,11 @@ bool CutterCore::isIOCacheEnabled() const
|
||||
|
||||
void CutterCore::commitWriteCache()
|
||||
{
|
||||
// Temporarily disable cache mode
|
||||
TempConfig tempConfig;
|
||||
tempConfig.set("io.cache", false);
|
||||
if (!isWriteModeEnabled()) {
|
||||
setWriteMode (true);
|
||||
cmdRaw("oo+");
|
||||
cmdRaw("wci");
|
||||
cmdRaw("oo");
|
||||
} else {
|
||||
@ -3812,8 +3821,8 @@ void CutterCore::setWriteMode(bool enabled)
|
||||
{
|
||||
bool writeModeState = isWriteModeEnabled();
|
||||
|
||||
if (writeModeState == enabled) {
|
||||
// New mode is the same as current. Do nothing.
|
||||
if (writeModeState == enabled && !this->iocache) {
|
||||
// New mode is the same as current and IO Cache is disabled. Do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3824,7 +3833,11 @@ void CutterCore::setWriteMode(bool enabled)
|
||||
} else {
|
||||
cmdRaw("oo");
|
||||
}
|
||||
// Disable cache mode because we specifically set write or
|
||||
// read-only modes.
|
||||
setIOCache(false);
|
||||
writeModeChanged (enabled);
|
||||
emit ioModeChanged();
|
||||
}
|
||||
|
||||
bool CutterCore::isWriteModeEnabled()
|
||||
|
@ -657,6 +657,7 @@ signals:
|
||||
|
||||
void ioCacheChanged(bool newval);
|
||||
void writeModeChanged(bool newval);
|
||||
void ioModeChanged();
|
||||
|
||||
/**
|
||||
* emitted when debugTask started or finished running
|
||||
|
@ -205,12 +205,35 @@ void MainWindow::initUI()
|
||||
connect(core->getAsyncTaskManager(), &AsyncTaskManager::tasksChanged, this,
|
||||
&MainWindow::updateTasksIndicator);
|
||||
|
||||
//Undo and redo seek
|
||||
// Undo and redo seek
|
||||
ui->actionBackward->setShortcut(QKeySequence::Back);
|
||||
ui->actionForward->setShortcut(QKeySequence::Forward);
|
||||
|
||||
initBackForwardMenu();
|
||||
|
||||
connect(core, &CutterCore::ioModeChanged, this, &MainWindow::setAvailableIOModeOptions);
|
||||
|
||||
QActionGroup *ioModeActionGroup = new QActionGroup(this);
|
||||
|
||||
ioModeActionGroup->addAction(ui->actionCacheMode);
|
||||
ioModeActionGroup->addAction(ui->actionWriteMode);
|
||||
ioModeActionGroup->addAction(ui->actionReadOnly);
|
||||
|
||||
connect(ui->actionCacheMode, &QAction::triggered, this, [this]() {
|
||||
ioModesController.setIOMode(IOModesController::Mode::CACHE);
|
||||
setAvailableIOModeOptions();
|
||||
});
|
||||
|
||||
connect(ui->actionWriteMode, &QAction::triggered, this, [this]() {
|
||||
ioModesController.setIOMode(IOModesController::Mode::WRITE);
|
||||
setAvailableIOModeOptions();
|
||||
});
|
||||
|
||||
connect(ui->actionReadOnly, &QAction::triggered, this, [this]() {
|
||||
ioModesController.setIOMode(IOModesController::Mode::READ_ONLY);
|
||||
setAvailableIOModeOptions();
|
||||
});
|
||||
|
||||
connect(ui->actionSaveLayout, &QAction::triggered, this, &MainWindow::saveNamedLayout);
|
||||
|
||||
/* Setup plugins interfaces */
|
||||
@ -651,20 +674,10 @@ void MainWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
|
||||
// Check if there are uncommitted changes
|
||||
if (core->isIOCacheEnabled() && !core->cmdj("wcj").array().isEmpty()) {
|
||||
|
||||
QMessageBox::StandardButton ret = QMessageBox::question(this, APPNAME,
|
||||
tr("It seems that you have changes or patches that are not committed to the file.\n"
|
||||
"Do you want to commit them now?"),
|
||||
(QMessageBox::StandardButtons)(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel));
|
||||
if (ret == QMessageBox::Cancel) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret == QMessageBox::Save) {
|
||||
core->commitWriteCache();
|
||||
}
|
||||
if (!ioModesController.askCommitUnsavedChanges()) {
|
||||
// if false, Cancel was chosen
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton ret = QMessageBox::question(this, APPNAME,
|
||||
@ -1765,3 +1778,17 @@ QMenu *MainWindow::getContextMenuExtensions(ContextMenuType type)
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setAvailableIOModeOptions()
|
||||
{
|
||||
switch (ioModesController.getIOMode()) {
|
||||
case IOModesController::Mode::WRITE:
|
||||
ui->actionWriteMode->setChecked(true);
|
||||
break;
|
||||
case IOModesController::Mode::CACHE:
|
||||
ui->actionCacheMode->setChecked(true);
|
||||
break;
|
||||
default:
|
||||
ui->actionReadOnly->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "dialogs/WelcomeDialog.h"
|
||||
#include "common/Configuration.h"
|
||||
#include "common/InitialOptions.h"
|
||||
#include "common/IOModesController.h"
|
||||
#include "MemoryDockWidget.h"
|
||||
|
||||
#include <memory>
|
||||
@ -219,6 +220,7 @@ private slots:
|
||||
void onZoomOut();
|
||||
void onZoomReset();
|
||||
|
||||
void setAvailableIOModeOptions();
|
||||
private:
|
||||
CutterCore *core;
|
||||
|
||||
@ -234,6 +236,7 @@ private:
|
||||
Omnibar *omnibar;
|
||||
ProgressIndicator *tasksProgressIndicator;
|
||||
QByteArray emptyState;
|
||||
IOModesController ioModesController;
|
||||
|
||||
Configuration *configuration;
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1013</width>
|
||||
<height>29</height>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="defaultUp">
|
||||
@ -51,20 +51,32 @@
|
||||
<widget class="QMenu" name="menuFile">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>328</x>
|
||||
<y>99</y>
|
||||
<width>265</width>
|
||||
<height>364</height>
|
||||
<x>2024</x>
|
||||
<y>127</y>
|
||||
<width>151</width>
|
||||
<height>312</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuSetMode">
|
||||
<property name="toolTip">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Set mode</string>
|
||||
</property>
|
||||
<addaction name="actionWriteMode"/>
|
||||
<addaction name="actionCacheMode"/>
|
||||
<addaction name="actionReadOnly"/>
|
||||
</widget>
|
||||
<addaction name="actionNew"/>
|
||||
<addaction name="actionMap"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionImportPDB"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menuSetMode"/>
|
||||
<addaction name="actionCommitChanges"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSave"/>
|
||||
@ -799,6 +811,39 @@
|
||||
<string>Commit changes</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionWriteMode">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Write mode</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open the file in write mode. Every change to the file will change the original file on disk.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCacheMode">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cache mode</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Enable cache mode. Changes to the file would not be applied to disk unless you specifically commit them. This is a safer option.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionReadOnly">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Read-Only mode</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSaveLayout">
|
||||
<property name="text">
|
||||
<string>Save layout</string>
|
||||
|
@ -86,6 +86,7 @@ HexdumpWidget::HexdumpWidget(MainWindow *main) :
|
||||
|
||||
connect(Config(), &Configuration::fontsUpdated, this, &HexdumpWidget::fontsUpdated);
|
||||
connect(Core(), &CutterCore::refreshAll, this, [this]() { refresh(); });
|
||||
connect(Core(), &CutterCore::refreshCodeViews, this, [this]() { refresh(); });
|
||||
connect(Core(), &CutterCore::instructionChanged, this, [this]() { refresh(); });
|
||||
connect(Core(), &CutterCore::stackChanged, this, [this]() { refresh(); });
|
||||
connect(Core(), &CutterCore::registersChanged, this, [this]() { refresh(); });
|
||||
|
Loading…
Reference in New Issue
Block a user