From 709149458319ec6bdf1a95c615a3ce5a6bbe1cb0 Mon Sep 17 00:00:00 2001
From: Vicki Pfau <vi@endrift.com>
Date: Sun, 23 Feb 2020 16:52:18 -0800
Subject: [PATCH] GBA e-Reader: Batch scanning

---
 include/mgba/gba/interface.h         |  2 ++
 include/mgba/internal/gba/hardware.h |  7 +++++
 src/gba/ereader.c                    | 39 ++++++++++++++++++++++++++++
 src/gba/hardware.c                   | 10 +++++++
 src/platform/qt/CoreController.cpp   |  2 +-
 src/platform/qt/GBAApp.cpp           | 11 ++++++++
 src/platform/qt/GBAApp.h             |  1 +
 src/platform/qt/Window.cpp           |  6 ++---
 8 files changed, 74 insertions(+), 4 deletions(-)

diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h
index 8c725e161..0c13834dd 100644
--- a/include/mgba/gba/interface.h
+++ b/include/mgba/gba/interface.h
@@ -88,6 +88,8 @@ struct GBASIOBattlechipGate {
 
 void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate*);
 
+void GBAEReaderQueueCard(struct GBA* gba, const void* data, size_t size);
+
 CXX_GUARD_END
 
 #endif
diff --git a/include/mgba/internal/gba/hardware.h b/include/mgba/internal/gba/hardware.h
index 42467db36..d46ca5494 100644
--- a/include/mgba/internal/gba/hardware.h
+++ b/include/mgba/internal/gba/hardware.h
@@ -18,6 +18,7 @@ mLOG_DECLARE_CATEGORY(GBA_HW);
 
 #define EREADER_DOTCODE_STRIDE 1200
 #define EREADER_DOTCODE_SIZE (EREADER_DOTCODE_STRIDE * 40 + 200)
+#define EREADER_CARDS_MAX 16
 
 #define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL)
 
@@ -133,6 +134,11 @@ enum EReaderCommand {
 	EREADER_COMMAND_READ_DATA = 0x23,
 };
 
+struct EReaderCard {
+	void* data;
+	size_t size;
+};
+
 struct GBACartridgeHardware {
 	struct GBA* p;
 	uint32_t devices;
@@ -177,6 +183,7 @@ struct GBACartridgeHardware {
 	int eReaderX;
 	int eReaderY;
 	uint8_t* eReaderDots;
+	struct EReaderCard eReaderCards[EREADER_CARDS_MAX];
 };
 
 void GBAHardwareInit(struct GBACartridgeHardware* gpio, uint16_t* gpioBase);
diff --git a/src/gba/ereader.c b/src/gba/ereader.c
index dd0b74dec..ad4f55d81 100644
--- a/src/gba/ereader.c
+++ b/src/gba/ereader.c
@@ -514,6 +514,19 @@ void _eReaderWriteControl0(struct GBACartridgeHardware* hw, uint8_t value) {
 	}
 	hw->eReaderRegisterControl0 = control;
 	if (!EReaderControl0IsScan(oldControl) && EReaderControl0IsScan(control)) {
+		if (hw->eReaderX > 1000) {
+			int i;
+			for (i = 0; i < EREADER_CARDS_MAX; ++i) {
+				if (!hw->eReaderCards[i].data) {
+					continue;
+				}
+				GBAHardwareEReaderScan(hw, hw->eReaderCards[i].data, hw->eReaderCards[i].size);
+				free(hw->eReaderCards[i].data);
+				hw->eReaderCards[i].data = NULL;
+				hw->eReaderCards[i].size = 0;
+				break;
+			}
+		}
 		hw->eReaderX = 0;
 		hw->eReaderY = 0;
 	} else if (EReaderControl0IsLedEnable(control) && EReaderControl0IsScan(control) && !EReaderControl1IsScanline(hw->eReaderRegisterControl1)) {
@@ -540,6 +553,19 @@ void _eReaderWriteControl1(struct GBACartridgeHardware* hw, uint8_t value) {
 
 void _eReaderReadData(struct GBACartridgeHardware* hw) {
 	memset(hw->eReaderData, 0, EREADER_BLOCK_SIZE);
+	if (!hw->eReaderDots) {
+		int i;
+		for (i = 0; i < EREADER_CARDS_MAX; ++i) {
+			if (!hw->eReaderCards[i].data) {
+				continue;
+			}
+			GBAHardwareEReaderScan(hw, hw->eReaderCards[i].data, hw->eReaderCards[i].size);
+			free(hw->eReaderCards[i].data);
+			hw->eReaderCards[i].data = NULL;
+			hw->eReaderCards[i].size = 0;
+			break;
+		}
+	}
 	if (hw->eReaderDots) {
 		int y = hw->eReaderY - 10;
 		if (y < 0 || y >= 120) {
@@ -579,3 +605,16 @@ void _eReaderReadData(struct GBACartridgeHardware* hw) {
 		GBARaiseIRQ(hw->p, IRQ_GAMEPAK, -led);
 	}
 }
+
+void GBAEReaderQueueCard(struct GBA* gba, const void* data, size_t size) {	
+	int i;
+	for (i = 0; i < EREADER_CARDS_MAX; ++i) {
+		if (gba->memory.hw.eReaderCards[i].data) {
+			continue;
+		}
+		gba->memory.hw.eReaderCards[i].data = malloc(size);
+		memcpy(gba->memory.hw.eReaderCards[i].data, data, size);
+		gba->memory.hw.eReaderCards[i].size = size;
+		return;
+	}
+}
\ No newline at end of file
diff --git a/src/gba/hardware.c b/src/gba/hardware.c
index 24c04733b..92c9fb284 100644
--- a/src/gba/hardware.c
+++ b/src/gba/hardware.c
@@ -51,6 +51,7 @@ static const int RTC_BYTES[8] = {
 void GBAHardwareInit(struct GBACartridgeHardware* hw, uint16_t* base) {
 	hw->gpioBase = base;
 	hw->eReaderDots = NULL;
+	memset(hw->eReaderCards, 0, sizeof(hw->eReaderCards));
 	GBAHardwareClear(hw);
 
 	hw->gbpCallback.d.readKeys = _gbpRead;
@@ -77,6 +78,15 @@ void GBAHardwareClear(struct GBACartridgeHardware* hw) {
 		mappedMemoryFree(hw->eReaderDots, EREADER_DOTCODE_SIZE);
 		hw->eReaderDots = NULL;
 	}
+	int i;
+	for (i = 0; i < EREADER_CARDS_MAX; ++i) {
+		if (!hw->eReaderCards[i].data) {
+			continue;
+		}
+		free(hw->eReaderCards[i].data);
+		hw->eReaderCards[i].data = NULL;
+		hw->eReaderCards[i].size = 0;
+	}
 
 	if (hw->p->sio.drivers.normal == &hw->gbpDriver.d) {
 		GBASIOSetDriver(&hw->p->sio, 0, SIO_NORMAL_32);
diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp
index 07adf2226..128015ec3 100644
--- a/src/platform/qt/CoreController.cpp
+++ b/src/platform/qt/CoreController.cpp
@@ -688,7 +688,7 @@ void CoreController::scanCard(const QString& path) {
 
 	mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* thread) {
 		CoreController* controller = static_cast<CoreController*>(thread->userData);
-		GBAHardwareEReaderScan(&static_cast<GBA*>(thread->core->board)->memory.hw, controller->m_eReaderData.constData(), controller->m_eReaderData.size());
+		GBAEReaderQueueCard(static_cast<GBA*>(thread->core->board), controller->m_eReaderData.constData(), controller->m_eReaderData.size());
 	});
 #endif
 }
diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp
index 32d4d4d9e..b1a058fe3 100644
--- a/src/platform/qt/GBAApp.cpp
+++ b/src/platform/qt/GBAApp.cpp
@@ -163,6 +163,17 @@ QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QStr
 	return filename;
 }
 
+QStringList GBAApp::getOpenFileNames(QWidget* owner, const QString& title, const QString& filter) {
+	QList<Window*> paused;
+	pauseAll(&paused);
+	QStringList filenames = QFileDialog::getOpenFileNames(owner, title, m_configController->getOption("lastDirectory"), filter);
+	continueAll(paused);
+	if (!filenames.isEmpty()) {
+		m_configController->setOption("lastDirectory", QFileInfo(filenames.at(0)).dir().canonicalPath());
+	}
+	return filenames;
+}
+
 QString GBAApp::getSaveFileName(QWidget* owner, const QString& title, const QString& filter) {
 	QList<Window*> paused;
 	pauseAll(&paused);
diff --git a/src/platform/qt/GBAApp.h b/src/platform/qt/GBAApp.h
index 82c25593c..805941f3b 100644
--- a/src/platform/qt/GBAApp.h
+++ b/src/platform/qt/GBAApp.h
@@ -59,6 +59,7 @@ public:
 	Window* newWindow();
 
 	QString getOpenFileName(QWidget* owner, const QString& title, const QString& filter = QString());
+	QStringList getOpenFileNames(QWidget* owner, const QString& title, const QString& filter = QString());
 	QString getSaveFileName(QWidget* owner, const QString& title, const QString& filter = QString());
 	QString getOpenDirectoryName(QWidget* owner, const QString& title);
 
diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp
index 02917ffbf..97fafd6c8 100644
--- a/src/platform/qt/Window.cpp
+++ b/src/platform/qt/Window.cpp
@@ -424,8 +424,8 @@ void Window::selectPatch() {
 }
 
 void Window::scanCard() {
-	QString filename = GBAApp::app()->getOpenFileName(this, tr("Select e-Reader dotcode"), tr("e-Reader card (*.raw *.bin)"));
-	if (!filename.isEmpty()) {
+	QStringList filenames = GBAApp::app()->getOpenFileNames(this, tr("Select e-Reader dotcode"), tr("e-Reader card (*.raw *.bin)"));
+	for (QString& filename : filenames) {
 		m_controller->scanCard(filename);
 	}
 }
@@ -1126,7 +1126,7 @@ void Window::setupMenu(QMenuBar* menubar) {
 
 	addGameAction(tr("Replace ROM..."), "replaceROM", this, &Window::replaceROM, "file");
 #ifdef M_CORE_GBA
-	Action* scanCard = addGameAction(tr("Scan e-Reader dotcode..."), "scanCard", this, &Window::scanCard, "file");
+	Action* scanCard = addGameAction(tr("Scan e-Reader dotcodes..."), "scanCard", this, &Window::scanCard, "file");
 	m_platformActions.insert(PLATFORM_GBA, scanCard);
 #endif