mirror of https://github.com/mgba-emu/mgba.git
Qt: Memory viewer now supports load/paste
This commit is contained in:
parent
57894955a2
commit
27545462e4
1
CHANGES
1
CHANGES
|
@ -5,6 +5,7 @@ Features:
|
||||||
- GB: Tile viewer
|
- GB: Tile viewer
|
||||||
- Sprite viewer
|
- Sprite viewer
|
||||||
- Debugging console
|
- Debugging console
|
||||||
|
- Improved memory viewer
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
- LR35902: Fix core never exiting with certain event patterns
|
- LR35902: Fix core never exiting with certain event patterns
|
||||||
- GB Timer: Improve DIV reset behavior
|
- GB Timer: Improve DIV reset behavior
|
||||||
|
|
|
@ -56,6 +56,16 @@ MemoryModel::MemoryModel(QWidget* parent)
|
||||||
connect(save, SIGNAL(triggered()), this, SLOT(save()));
|
connect(save, SIGNAL(triggered()), this, SLOT(save()));
|
||||||
addAction(save);
|
addAction(save);
|
||||||
|
|
||||||
|
QAction* paste = new QAction(tr("Paste"), this);
|
||||||
|
paste->setShortcut(QKeySequence::Paste);
|
||||||
|
connect(paste, SIGNAL(triggered()), this, SLOT(paste()));
|
||||||
|
addAction(paste);
|
||||||
|
|
||||||
|
QAction* load = new QAction(tr("Load"), this);
|
||||||
|
load->setShortcut(QKeySequence::Open);
|
||||||
|
connect(load, SIGNAL(triggered()), this, SLOT(load()));
|
||||||
|
addAction(load);
|
||||||
|
|
||||||
static QString arg("%0");
|
static QString arg("%0");
|
||||||
for (int i = 0; i < 256; ++i) {
|
for (int i = 0; i < 256; ++i) {
|
||||||
QStaticText str(arg.arg(i, 2, 16, QChar('0')).toUpper());
|
QStaticText str(arg.arg(i, 2, 16, QChar('0')).toUpper());
|
||||||
|
@ -144,9 +154,7 @@ void MemoryModel::copy() {
|
||||||
if (!clipboard) {
|
if (!clipboard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QByteArray bytestring;
|
QByteArray bytestring(serialize());
|
||||||
QDataStream stream(&bytestring, QIODevice::WriteOnly);
|
|
||||||
serialize(&stream);
|
|
||||||
QString string;
|
QString string;
|
||||||
string.reserve(bytestring.size() * 2);
|
string.reserve(bytestring.size() * 2);
|
||||||
static QString arg("%0");
|
static QString arg("%0");
|
||||||
|
@ -157,6 +165,20 @@ void MemoryModel::copy() {
|
||||||
clipboard->setText(string);
|
clipboard->setText(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MemoryModel::paste() {
|
||||||
|
QClipboard* clipboard = QApplication::clipboard();
|
||||||
|
if (!clipboard) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString string = clipboard->text();
|
||||||
|
if (string.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QByteArray bytestring(QByteArray::fromHex(string.toLocal8Bit()));
|
||||||
|
deserialize(bytestring);
|
||||||
|
viewport()->update();
|
||||||
|
}
|
||||||
|
|
||||||
void MemoryModel::save() {
|
void MemoryModel::save() {
|
||||||
QString filename = GBAApp::app()->getSaveFileName(this, tr("Save selected memory"));
|
QString filename = GBAApp::app()->getSaveFileName(this, tr("Save selected memory"));
|
||||||
if (filename.isNull()) {
|
if (filename.isNull()) {
|
||||||
|
@ -167,25 +189,78 @@ void MemoryModel::save() {
|
||||||
LOG(QT, WARN) << tr("Failed to open output file: %1").arg(filename);
|
LOG(QT, WARN) << tr("Failed to open output file: %1").arg(filename);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QDataStream stream(&outfile);
|
QByteArray out(serialize());
|
||||||
serialize(&stream);
|
outfile.write(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryModel::serialize(QDataStream* stream) {
|
void MemoryModel::load() {
|
||||||
|
QString filename = GBAApp::app()->getOpenFileName(this, tr("Load memory"));
|
||||||
|
if (filename.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QFile infile(filename);
|
||||||
|
if (!infile.open(QIODevice::ReadOnly)) {
|
||||||
|
LOG(QT, WARN) << tr("Failed to open input file: %1").arg(filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QByteArray bytestring(infile.readAll());
|
||||||
|
deserialize(bytestring);
|
||||||
|
viewport()->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray MemoryModel::serialize() {
|
||||||
|
QByteArray bytes;
|
||||||
|
bytes.reserve(m_selection.second - m_selection.first);
|
||||||
switch (m_align) {
|
switch (m_align) {
|
||||||
case 1:
|
case 1:
|
||||||
for (uint32_t i = m_selection.first; i < m_selection.second; i += m_align) {
|
for (uint32_t i = m_selection.first; i < m_selection.second; i += m_align) {
|
||||||
*stream << m_core->rawRead8(m_core, i, m_currentBank);
|
char datum = m_core->rawRead8(m_core, i, m_currentBank);
|
||||||
|
bytes.append(datum);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
for (uint32_t i = m_selection.first; i < m_selection.second; i += m_align) {
|
for (uint32_t i = m_selection.first; i < m_selection.second; i += m_align) {
|
||||||
*stream << m_core->rawRead16(m_core, i, m_currentBank);
|
quint16 datum = m_core->rawRead16(m_core, i, m_currentBank);
|
||||||
|
char leDatum[2];
|
||||||
|
STORE_16LE(datum, 0, (uint16_t*) leDatum);
|
||||||
|
bytes.append(leDatum, 2);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
for (uint32_t i = m_selection.first; i < m_selection.second; i += m_align) {
|
for (uint32_t i = m_selection.first; i < m_selection.second; i += m_align) {
|
||||||
*stream << m_core->rawRead32(m_core, i, m_currentBank);
|
quint32 datum = m_core->rawRead32(m_core, i, m_currentBank);
|
||||||
|
char leDatum[4];
|
||||||
|
STORE_32LE(datum, 0, (uint16_t*) leDatum);
|
||||||
|
bytes.append(leDatum, 4);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryModel::deserialize(const QByteArray& bytes) {
|
||||||
|
uint32_t addr = m_selection.first;
|
||||||
|
switch (m_align) {
|
||||||
|
case 1:
|
||||||
|
for (int i = 0; i < bytes.size(); i += m_align, addr += m_align) {
|
||||||
|
uint8_t datum = bytes[i];
|
||||||
|
m_core->rawWrite8(m_core, addr, m_currentBank, datum);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
for (int i = 0; i < bytes.size(); i += m_align, addr += m_align) {
|
||||||
|
char leDatum[2]{ bytes[i], bytes[i + 1] };
|
||||||
|
uint16_t datum;
|
||||||
|
LOAD_16LE(datum, 0, leDatum);
|
||||||
|
m_core->rawWrite16(m_core, addr, m_currentBank, datum);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
for (int i = 0; i < bytes.size(); i += m_align, addr += m_align) {
|
||||||
|
char leDatum[4]{ bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3] };
|
||||||
|
uint32_t datum;
|
||||||
|
LOAD_32LE(datum, 0, leDatum);
|
||||||
|
m_core->rawWrite32(m_core, addr, m_currentBank, datum);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,9 @@ public slots:
|
||||||
void jumpToAddress(uint32_t);
|
void jumpToAddress(uint32_t);
|
||||||
|
|
||||||
void copy();
|
void copy();
|
||||||
|
void paste();
|
||||||
void save();
|
void save();
|
||||||
|
void load();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void selectionChanged(uint32_t start, uint32_t end);
|
void selectionChanged(uint32_t start, uint32_t end);
|
||||||
|
@ -59,7 +61,8 @@ private:
|
||||||
|
|
||||||
void adjustCursor(int adjust, bool shift);
|
void adjustCursor(int adjust, bool shift);
|
||||||
|
|
||||||
void serialize(QDataStream* stream);
|
QByteArray serialize();
|
||||||
|
void deserialize(const QByteArray&);
|
||||||
|
|
||||||
mCore* m_core;
|
mCore* m_core;
|
||||||
QFont m_font;
|
QFont m_font;
|
||||||
|
|
Loading…
Reference in New Issue