all repos — mgba @ 1089285b45c1213fff87e0518fc45a327b8ede8b

mGBA Game Boy Advance Emulator

src/platform/qt/CoreManager.cpp (view raw)

  1/* Copyright (c) 2013-2017 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "CoreManager.h"
  7
  8#include "CoreController.h"
  9#include "LogController.h"
 10#include "VFileDevice.h"
 11
 12#include <QDir>
 13
 14#ifdef M_CORE_GBA
 15#include <mgba/gba/core.h>
 16#endif
 17#ifdef M_CORE_GB
 18#include <mgba/gb/core.h>
 19#endif
 20
 21#include <mgba/core/core.h>
 22#include <mgba-util/vfs.h>
 23
 24using namespace QGBA;
 25
 26void CoreManager::setConfig(const mCoreConfig* config) {
 27	m_config = config;
 28}
 29
 30void CoreManager::setMultiplayerController(MultiplayerController* multiplayer) {
 31	m_multiplayer = multiplayer;
 32}
 33
 34QByteArray CoreManager::getExtdata(const QString& filename, mStateExtdataTag extdataType) {
 35	VFileDevice vf(filename, QIODevice::ReadOnly);
 36
 37	if (!vf.isOpen()) {
 38		return {};
 39	}
 40
 41	mStateExtdata extdata;
 42	mStateExtdataInit(&extdata);
 43
 44	QByteArray bytes;
 45	auto extract = [&bytes, &extdata, &vf, extdataType](mCore* core) -> bool {
 46		if (mCoreExtractExtdata(core, vf, &extdata)) {
 47			mStateExtdataItem extitem;
 48			if (!mStateExtdataGet(&extdata, extdataType, &extitem)) {
 49				return false;
 50			}
 51			if (extitem.size) {
 52				bytes = QByteArray::fromRawData(static_cast<const char*>(extitem.data), extitem.size);
 53			}
 54			return true;
 55		}
 56		return false;
 57	};
 58
 59	bool done = false;
 60	struct mCore* core = nullptr;
 61#ifdef USE_PNG
 62	done = extract(nullptr);
 63#endif
 64#ifdef M_CORE_GBA
 65	if (!done) {
 66		core = GBACoreCreate();
 67		core->init(core);
 68		done = extract(core);
 69		core->deinit(core);
 70	}
 71#endif
 72#ifdef M_CORE_GB
 73	if (!done) {
 74		core = GBCoreCreate();
 75		core->init(core);
 76		done = extract(core);
 77		core->deinit(core);
 78	}
 79#endif
 80
 81	mStateExtdataDeinit(&extdata);
 82	return bytes;
 83}
 84
 85CoreController* CoreManager::loadGame(const QString& path) {
 86	QFileInfo info(path);
 87	if (!info.isReadable()) {
 88		QString fname = info.fileName();
 89		QString base = info.path();
 90		if (base.endsWith("/") || base.endsWith(QDir::separator())) {
 91			base.chop(1);
 92		}
 93		VDir* dir = VDirOpenArchive(base.toUtf8().constData());
 94		if (dir) {
 95			VFile* vf = dir->openFile(dir, fname.toUtf8().constData(), O_RDONLY);
 96			if (vf) {
 97				struct VFile* vfclone = VFileMemChunk(NULL, vf->size(vf));
 98				uint8_t buffer[2048];
 99				ssize_t read;
100				while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) {
101					vfclone->write(vfclone, buffer, read);
102				}
103				vf->close(vf);
104				vf = vfclone;
105			}
106			dir->close(dir);
107			return loadGame(vf, fname, base);
108		} else {
109			LOG(QT, ERROR) << tr("Failed to open game file: %1").arg(path);
110		}
111		return nullptr;
112	}
113	VFile* vf = nullptr;
114	VDir* archive = VDirOpenArchive(path.toUtf8().constData());
115	if (archive) {
116		VFile* vfOriginal = VDirFindFirst(archive, [](VFile* vf) {
117			return mCoreIsCompatible(vf) != mPLATFORM_NONE;
118		});
119		ssize_t size;
120		if (vfOriginal && (size = vfOriginal->size(vfOriginal)) > 0) {
121			void* mem = vfOriginal->map(vfOriginal, size, MAP_READ);
122			vf = VFileMemChunk(mem, size);
123			vfOriginal->unmap(vfOriginal, mem, size);
124			vfOriginal->close(vfOriginal);
125		}
126	}
127	QDir dir(info.dir());
128	if (!vf) {
129		vf = VFileOpen(info.canonicalFilePath().toUtf8().constData(), O_RDONLY);
130	}
131	return loadGame(vf, info.fileName(), dir.canonicalPath());
132}
133
134CoreController* CoreManager::loadGame(VFile* vf, const QString& path, const QString& base) {
135	if (!vf) {
136		return nullptr;
137	}
138
139	mCore* core = mCoreFindVF(vf);
140	if (!core) {
141		vf->close(vf);
142		LOG(QT, ERROR) << tr("Could not load game. Are you sure it's in the correct format?");
143		return nullptr;
144	}
145
146	core->init(core);
147	mCoreInitConfig(core, nullptr);
148
149	if (m_config) {
150		mCoreLoadForeignConfig(core, m_config);
151	}
152
153	if (m_preload) {
154		mCorePreloadVF(core, vf);
155	} else {
156		core->loadROM(core, vf);
157	}
158
159	QByteArray bytes(path.toUtf8());
160	separatePath(bytes.constData(), nullptr, core->dirs.baseName, nullptr);
161
162	QFileInfo info(base);
163	if (info.isDir()) {
164		info = QFileInfo(base + "/" + path);
165	}
166	bytes = info.dir().canonicalPath().toUtf8();
167	mDirectorySetAttachBase(&core->dirs, VDirOpen(bytes.constData()));
168	if (!mCoreAutoloadSave(core)) {
169		LOG(QT, ERROR) << tr("Failed to open save file. Is the save directory writable?");
170	}
171	mCoreAutoloadCheats(core);
172
173	CoreController* cc = new CoreController(core);
174	if (m_multiplayer) {
175		cc->setMultiplayerController(m_multiplayer);
176	}
177	emit coreLoaded(cc);
178	return cc;
179}
180
181CoreController* CoreManager::loadBIOS(int platform, const QString& path) {
182	QFileInfo info(path);
183	VFile* vf = VFileOpen(info.canonicalFilePath().toUtf8().constData(), O_RDONLY);
184	if (!vf) {
185		return nullptr;
186	}
187
188	mCore* core = nullptr;
189	switch (platform) {
190#ifdef M_CORE_GBA
191	case mPLATFORM_GBA:
192		core = GBACoreCreate();
193		break;
194#endif
195	default:
196		vf->close(vf);
197		return nullptr;
198	}
199	if (!core) {
200		vf->close(vf);
201		return nullptr;
202	}
203
204	core->init(core);
205	mCoreInitConfig(core, nullptr);
206
207	if (m_config) {
208		mCoreLoadForeignConfig(core, m_config);
209	}
210
211	core->loadBIOS(core, vf, 0);
212
213	mCoreConfigSetOverrideIntValue(&core->config, "useBios", 1);
214	mCoreConfigSetOverrideIntValue(&core->config, "skipBios", 0);
215
216	QByteArray bytes(info.baseName().toUtf8());
217	strncpy(core->dirs.baseName, bytes.constData(), sizeof(core->dirs.baseName));
218
219	bytes = info.dir().canonicalPath().toUtf8();
220	mDirectorySetAttachBase(&core->dirs, VDirOpen(bytes.constData()));
221
222	CoreController* cc = new CoreController(core);
223	if (m_multiplayer) {
224		cc->setMultiplayerController(m_multiplayer);
225	}
226	emit coreLoaded(cc);
227	return cc;
228}