GUI: close patch dialog when debugging stops + do not show patch dialog when not debugging + fixed a bug with the checking of patches + added patch import/export (with undo support and option to not apply patches with a different oldbyte value)
This commit is contained in:
parent
e4101a0d79
commit
704994df12
|
@ -749,6 +749,14 @@ void MainWindow::getStrWindow(const QString title, QString *text)
|
|||
|
||||
void MainWindow::patchWindow()
|
||||
{
|
||||
if(!DbgIsDebugging())
|
||||
{
|
||||
QMessageBox msg(QMessageBox::Critical, "Error!", QString("Patches cannot be shown when not debugging..."));
|
||||
msg.setWindowIcon(QIcon(":/icons/images/compile-error.png"));
|
||||
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
|
||||
msg.exec();
|
||||
return;
|
||||
}
|
||||
GuiUpdatePatches();
|
||||
mPatchDialog->showNormal();
|
||||
mPatchDialog->setFocus();
|
||||
|
|
|
@ -12,6 +12,7 @@ PatchDialog::PatchDialog(QWidget *parent) :
|
|||
setModal(false); //non-modal window
|
||||
|
||||
connect(Bridge::getBridge(), SIGNAL(updatePatches()), this, SLOT(updatePatches()));
|
||||
connect(Bridge::getBridge(), SIGNAL(dbgStateChanged(DBGSTATE)), this, SLOT(dbgStateChanged(DBGSTATE)));
|
||||
|
||||
mGroupSelector = new PatchDialogGroupSelector(parent);
|
||||
mGroupSelector->setGroup(0);
|
||||
|
@ -74,6 +75,15 @@ int_t PatchDialog::getGroupAddress(const PatchInfoList & patchList, int group)
|
|||
return -1;
|
||||
}
|
||||
|
||||
void PatchDialog::dbgStateChanged(DBGSTATE state)
|
||||
{
|
||||
if(state==stopped)
|
||||
{
|
||||
mGroupSelector->hide();
|
||||
reject();
|
||||
}
|
||||
}
|
||||
|
||||
void PatchDialog::updatePatches()
|
||||
{
|
||||
if(this->isVisible())
|
||||
|
@ -104,12 +114,14 @@ void PatchDialog::updatePatches()
|
|||
//fill the patch list
|
||||
STATUSINFO defaultStatus;
|
||||
defaultStatus.group=0;
|
||||
defaultStatus.checked=false;
|
||||
defaultStatus.checked=true;
|
||||
for(int i=0; i<numPatches; i++)
|
||||
{
|
||||
if(!patches[i].addr)
|
||||
continue;
|
||||
QString mod(patches[i].mod);
|
||||
if(!*patches[i].mod)
|
||||
continue;
|
||||
QString mod = patches[i].mod;
|
||||
PatchMap::iterator found = mPatches->find(mod);
|
||||
if(found != mPatches->end()) //found
|
||||
(*mPatches)[mod].append(PatchPair(patches[i], defaultStatus));
|
||||
|
@ -238,7 +250,8 @@ void PatchDialog::on_listModules_itemSelectionChanged()
|
|||
QString addrText = QString("%1").arg(curPatch.addr, sizeof(int_t)*2, 16, QChar('0')).toUpper();
|
||||
QListWidgetItem* item = new QListWidgetItem(QString().sprintf("%d", patchList.at(i).second.group).rightJustified(4, ' ', true) + "|" + addrText + QString().sprintf(":%.2X->%.2X", curPatch.oldbyte, curPatch.newbyte), ui->listPatches);
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(Qt::Unchecked);
|
||||
Qt::CheckState state = patchList.at(i).second.checked ? Qt::Checked : Qt::Unchecked;
|
||||
item->setCheckState(state);
|
||||
}
|
||||
mIsWorking=false;
|
||||
}
|
||||
|
@ -452,3 +465,194 @@ void PatchDialog::on_btnPatchFile_clicked()
|
|||
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
|
||||
msg.exec();
|
||||
}
|
||||
|
||||
void PatchDialog::on_btnImport_clicked()
|
||||
{
|
||||
QString filename = QFileDialog::getOpenFileName(this, tr("Open patch"), QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation), tr("Patch files (*.1337)"));
|
||||
if(!filename.length())
|
||||
return;
|
||||
filename=QDir::toNativeSeparators(filename); //convert to native path format (with backlashes)
|
||||
QFile file(filename);
|
||||
file.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream in(&file);
|
||||
QString patch=in.readAll();
|
||||
file.close();
|
||||
patch=patch.replace("\r\n", "\n");
|
||||
QStringList lines=patch.split("\n", QString::SkipEmptyParts);
|
||||
if(!lines.size())
|
||||
{
|
||||
QMessageBox msg(QMessageBox::Critical, "Error!", QString("The patch file is empty..."));
|
||||
msg.setWindowIcon(QIcon(":/icons/images/compile-error.png"));
|
||||
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
|
||||
msg.exec();
|
||||
return;
|
||||
}
|
||||
typedef struct IMPORTSTATUS
|
||||
{
|
||||
bool nomatchoriginal;
|
||||
bool matchold;
|
||||
};
|
||||
|
||||
QList<QPair<DBGPATCHINFO, IMPORTSTATUS>> patchList;
|
||||
DBGPATCHINFO curPatch;
|
||||
int_t modbase=0;
|
||||
bool bBadOriginal = false;
|
||||
bool bAlreadyDone = false;
|
||||
for(int i=0; i<lines.size(); i++)
|
||||
{
|
||||
ULONGLONG rva;
|
||||
unsigned int oldbyte;
|
||||
unsigned int newbyte;
|
||||
QString curLine=lines.at(i);
|
||||
if(curLine.startsWith(">")) //module
|
||||
{
|
||||
strcpy(curPatch.mod, curLine.toUtf8().constData()+1);
|
||||
modbase=DbgFunctions()->ModBaseFromName(curPatch.mod);
|
||||
continue;
|
||||
}
|
||||
if(!modbase)
|
||||
continue;
|
||||
curLine=curLine.replace(" ", "");
|
||||
if(sscanf(curLine.toUtf8().constData(), "%llX:%X->%X", &rva, &oldbyte, &newbyte)!=3)
|
||||
{
|
||||
QMessageBox msg(QMessageBox::Critical, "Error!", QString("Patch file format is incorrect..."));
|
||||
msg.setWindowIcon(QIcon(":/icons/images/compile-error.png"));
|
||||
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
|
||||
msg.exec();
|
||||
return;
|
||||
}
|
||||
oldbyte&=0xFF;
|
||||
newbyte&=0xFF;
|
||||
curPatch.addr=rva+modbase;
|
||||
if(!DbgMemIsValidReadPtr(curPatch.addr))
|
||||
continue;
|
||||
unsigned char checkbyte=0;
|
||||
DbgMemRead(curPatch.addr, &checkbyte, sizeof(checkbyte));
|
||||
if(checkbyte==newbyte)
|
||||
bAlreadyDone=true;
|
||||
else if(checkbyte!=oldbyte)
|
||||
bBadOriginal=true;
|
||||
curPatch.oldbyte=oldbyte;
|
||||
curPatch.newbyte=newbyte;
|
||||
IMPORTSTATUS status = {checkbyte!=oldbyte && !checkbyte==newbyte, checkbyte==newbyte};
|
||||
patchList.push_back(QPair<DBGPATCHINFO, IMPORTSTATUS>(curPatch, status));
|
||||
}
|
||||
|
||||
if(!patchList.size())
|
||||
{
|
||||
QMessageBox msg(QMessageBox::Information, "Information", QString().sprintf("No patches to apply in the current process."));
|
||||
msg.setWindowIcon(QIcon(":/icons/images/information.png"));
|
||||
msg.setParent(this, Qt::Dialog);
|
||||
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
|
||||
msg.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
bool bUndoPatched = false;
|
||||
if(bAlreadyDone)
|
||||
{
|
||||
QMessageBox msg(QMessageBox::Question, "Question", "Some patches are already applied.\n\nDo you want to remove these patches?", QMessageBox::Yes|QMessageBox::No);
|
||||
msg.setWindowIcon(QIcon(":/icons/images/question.png"));
|
||||
msg.setParent(this, Qt::Dialog);
|
||||
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
|
||||
if(msg.exec()==QMessageBox::Yes)
|
||||
bUndoPatched = true;
|
||||
}
|
||||
|
||||
bool bPatchBadOriginals = false;
|
||||
if(bBadOriginal)
|
||||
{
|
||||
QMessageBox msg(QMessageBox::Question, "Question", "Some bytes do not match the original in the patch file.\n\nDo you want to apply these patches anyway?", QMessageBox::Yes|QMessageBox::No);
|
||||
msg.setWindowIcon(QIcon(":/icons/images/question.png"));
|
||||
msg.setParent(this, Qt::Dialog);
|
||||
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
|
||||
if(msg.exec()==QMessageBox::Yes)
|
||||
bPatchBadOriginals = true;
|
||||
}
|
||||
|
||||
int patched=0;
|
||||
for(int i=0; i<patchList.size(); i++)
|
||||
{
|
||||
if(!bPatchBadOriginals && patchList.at(i).second.nomatchoriginal)
|
||||
continue;
|
||||
curPatch=patchList.at(i).first;
|
||||
if(bUndoPatched && patchList.at(i).second.matchold)
|
||||
{
|
||||
GuiAddStatusBarMessage("undo!");
|
||||
if(DbgFunctions()->MemPatch(curPatch.addr, &curPatch.oldbyte, 1))
|
||||
patched++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(DbgFunctions()->MemPatch(curPatch.addr, &curPatch.newbyte, 1))
|
||||
patched++;
|
||||
}
|
||||
}
|
||||
|
||||
updatePatches();
|
||||
GuiUpdateAllViews();
|
||||
|
||||
QMessageBox msg(QMessageBox::Information, "Information", QString().sprintf("%d/%d patch(es) applied!", patched, patchList.size()));
|
||||
msg.setWindowIcon(QIcon(":/icons/images/information.png"));
|
||||
msg.setParent(this, Qt::Dialog);
|
||||
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
|
||||
msg.exec();
|
||||
}
|
||||
|
||||
void PatchDialog::on_btnExport_clicked()
|
||||
{
|
||||
if(!mPatches->size())
|
||||
return;
|
||||
|
||||
QString filename = QFileDialog::getSaveFileName(this, tr("Save patch"), QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation), tr("Patch files (*.1337)"));
|
||||
if(!filename.length())
|
||||
return;
|
||||
filename=QDir::toNativeSeparators(filename); //convert to native path format (with backlashes)
|
||||
|
||||
QStringList lines;
|
||||
|
||||
int patches=0;
|
||||
for(PatchMap::iterator i=mPatches->begin(); i!=mPatches->end(); ++i)
|
||||
{
|
||||
const PatchInfoList & curPatchList = i.value();
|
||||
bool bModPlaced=false;
|
||||
int_t modbase=DbgFunctions()->ModBaseFromName(i.key().toUtf8().constData());
|
||||
if(!modbase)
|
||||
continue;
|
||||
for(int j=0; j<curPatchList.size(); j++)
|
||||
{
|
||||
if(!curPatchList.at(j).second.checked) //skip unchecked patches
|
||||
continue;
|
||||
if(!bModPlaced)
|
||||
{
|
||||
lines.push_back(">" + i.key());
|
||||
bModPlaced=true;
|
||||
}
|
||||
QString addrText = QString("%1").arg(curPatchList.at(j).first.addr-modbase, sizeof(int_t)*2, 16, QChar('0')).toUpper();
|
||||
lines.push_back(addrText+QString().sprintf(":%.2X->%.2X", curPatchList.at(j).first.oldbyte, curPatchList.at(j).first.newbyte));
|
||||
patches++;
|
||||
}
|
||||
}
|
||||
|
||||
if(!lines.size())
|
||||
{
|
||||
QMessageBox msg(QMessageBox::Information, "Information", QString().sprintf("No patches to export."));
|
||||
msg.setWindowIcon(QIcon(":/icons/images/information.png"));
|
||||
msg.setParent(this, Qt::Dialog);
|
||||
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
|
||||
msg.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(filename);
|
||||
file.open(QFile::WriteOnly | QFile::Text);
|
||||
QString text = lines.join("\n");
|
||||
file.write(text.toUtf8().constData(), text.length());
|
||||
file.close();
|
||||
|
||||
QMessageBox msg(QMessageBox::Information, "Information", QString().sprintf("%d patch(es) exported!", patches));
|
||||
msg.setWindowIcon(QIcon(":/icons/images/information.png"));
|
||||
msg.setParent(this, Qt::Dialog);
|
||||
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
|
||||
msg.exec();
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ private:
|
|||
int_t getGroupAddress(const PatchInfoList & patchList, int group);
|
||||
|
||||
private slots:
|
||||
void dbgStateChanged(DBGSTATE state);
|
||||
void updatePatches();
|
||||
void groupToggle();
|
||||
void groupPrevious();
|
||||
|
@ -57,6 +58,8 @@ private slots:
|
|||
void on_listPatches_itemSelectionChanged();
|
||||
void on_btnPickGroups_clicked();
|
||||
void on_btnPatchFile_clicked();
|
||||
void on_btnImport_clicked();
|
||||
void on_btnExport_clicked();
|
||||
};
|
||||
|
||||
#endif // PATCHDIALOG_H
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>511</width>
|
||||
<height>452</height>
|
||||
<height>451</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -51,7 +51,7 @@
|
|||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>211</width>
|
||||
<height>431</height>
|
||||
<height>391</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="title">
|
||||
|
@ -63,7 +63,7 @@
|
|||
<x>10</x>
|
||||
<y>20</y>
|
||||
<width>191</width>
|
||||
<height>401</height>
|
||||
<height>361</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
|
@ -76,8 +76,8 @@
|
|||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>230</x>
|
||||
<y>380</y>
|
||||
<x>231</x>
|
||||
<y>381</y>
|
||||
<width>271</width>
|
||||
<height>58</height>
|
||||
</rect>
|
||||
|
@ -88,21 +88,21 @@
|
|||
<item>
|
||||
<widget class="QPushButton" name="btnSelectAll">
|
||||
<property name="text">
|
||||
<string>Select All</string>
|
||||
<string>&Select All</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnDeselectAll">
|
||||
<property name="text">
|
||||
<string>Deselect All</string>
|
||||
<string>&Deselect All</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnRestoreSelected">
|
||||
<property name="text">
|
||||
<string>Restore Selected</string>
|
||||
<string>&Restore Selected</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -116,7 +116,7 @@
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Pick Groups</string>
|
||||
<string>Pick &Groups</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -126,7 +126,7 @@
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Patch File</string>
|
||||
<string>&Patch File</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -134,6 +134,32 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>7</x>
|
||||
<y>410</y>
|
||||
<width>211</width>
|
||||
<height>25</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnImport">
|
||||
<property name="text">
|
||||
<string>&Import</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnExport">
|
||||
<property name="text">
|
||||
<string>&Export</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>listModules</tabstop>
|
||||
|
|
|
@ -20,7 +20,9 @@ bool MemoryPage::read(byte_t* parDest, uint_t parRVA, uint_t parSize)
|
|||
|
||||
bool MemoryPage::write(const void* parDest, uint_t parRVA, uint_t parSize)
|
||||
{
|
||||
return DbgFunctions()->MemPatch(mBase + parRVA, (unsigned char*)parDest, parSize);
|
||||
bool ret = DbgFunctions()->MemPatch(mBase + parRVA, (unsigned char*)parDest, parSize);
|
||||
GuiUpdatePatches();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MemoryPage::write(const byte_t* parDest, uint_t parRVA, uint_t parSize)
|
||||
|
|
Loading…
Reference in New Issue