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}