all repos — mgba @ fb035eb0a3cddd2360a8e696c2dc6e3d77e6c19e

mGBA Game Boy Advance Emulator

src/core/core.c (view raw)

  1/* Copyright (c) 2013-2016 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 <mgba/core/core.h>
  7
  8#include <mgba/core/log.h>
  9#include <mgba/core/serialize.h>
 10#include <mgba-util/vfs.h>
 11
 12#ifdef M_CORE_GB
 13#include <mgba/gb/core.h>
 14// TODO: Fix layering violation
 15#include <mgba/internal/gb/gb.h>
 16#endif
 17#ifdef M_CORE_GBA
 18#include <mgba/gba/core.h>
 19#include <mgba/internal/gba/gba.h>
 20#endif
 21#ifdef M_CORE_DS
 22#include <mgba/ds/core.h>
 23#include <mgba/internal/ds/ds.h>
 24#endif
 25#ifndef MINIMAL_CORE
 26#include <mgba/feature/video-logger.h>
 27#endif
 28
 29static const struct mCoreFilter {
 30	bool (*filter)(struct VFile*);
 31	struct mCore* (*open)(void);
 32	enum mPlatform platform;
 33} _filters[] = {
 34#ifdef M_CORE_DS
 35	{ DSIsROM, DSCoreCreate, PLATFORM_DS },
 36#endif
 37#ifdef M_CORE_GBA
 38	{ GBAIsROM, GBACoreCreate, PLATFORM_GBA },
 39#endif
 40#ifdef M_CORE_GB
 41	{ GBIsROM, GBCoreCreate, PLATFORM_GB },
 42#endif
 43	{ 0, 0, PLATFORM_NONE }
 44};
 45
 46struct mCore* mCoreFindVF(struct VFile* vf) {
 47	if (!vf) {
 48		return NULL;
 49	}
 50	const struct mCoreFilter* filter;
 51	for (filter = &_filters[0]; filter->filter; ++filter) {
 52		if (filter->filter(vf)) {
 53			break;
 54		}
 55	}
 56	if (filter->open) {
 57		return filter->open();
 58	}
 59#ifndef MINIMAL_CORE
 60	return mVideoLogCoreFind(vf);
 61#endif
 62	return NULL;
 63}
 64
 65enum mPlatform mCoreIsCompatible(struct VFile* vf) {
 66	if (!vf) {
 67		return false;
 68	}
 69	const struct mCoreFilter* filter;
 70	for (filter = &_filters[0]; filter->filter; ++filter) {
 71		if (filter->filter(vf)) {
 72			return filter->platform;
 73		}
 74	}
 75	return PLATFORM_NONE;
 76}
 77
 78#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 79#include <mgba-util/png-io.h>
 80
 81#ifdef PSP2
 82#include <psp2/photoexport.h>
 83#endif
 84
 85struct mCore* mCoreFind(const char* path) {
 86	struct VDir* archive = VDirOpenArchive(path);
 87	struct mCore* core = NULL;
 88	if (archive) {
 89		struct VDirEntry* dirent = archive->listNext(archive);
 90		while (dirent) {
 91			struct VFile* vf = archive->openFile(archive, dirent->name(dirent), O_RDONLY);
 92			if (!vf) {
 93				dirent = archive->listNext(archive);
 94				continue;
 95			}
 96			core = mCoreFindVF(vf);
 97			vf->close(vf);
 98			if (core) {
 99				break;
100			}
101			dirent = archive->listNext(archive);
102		}
103		archive->close(archive);
104	} else {
105		struct VFile* vf = VFileOpen(path, O_RDONLY);
106		if (!vf) {
107			return NULL;
108		}
109		core = mCoreFindVF(vf);
110		vf->close(vf);
111	}
112	if (core) {
113		return core;
114	}
115	return NULL;
116}
117
118bool mCoreLoadFile(struct mCore* core, const char* path) {
119	struct VFile* rom = mDirectorySetOpenPath(&core->dirs, path, core->isROM);
120	if (!rom) {
121		return false;
122	}
123
124	bool ret = core->loadROM(core, rom);
125	if (!ret) {
126		rom->close(rom);
127	}
128	return ret;
129}
130
131bool mCorePreloadVF(struct mCore* core, struct VFile* vf) {
132	struct VFile* vfm = VFileMemChunk(NULL, vf->size(vf));
133	uint8_t buffer[2048];
134	ssize_t read;
135	vf->seek(vf, 0, SEEK_SET);
136	while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) {
137		vfm->write(vfm, buffer, read);
138	}
139	vf->close(vf);
140	bool ret = core->loadROM(core, vfm);
141	if (!ret) {
142		vfm->close(vfm);
143	}
144	return ret;
145}
146
147bool mCorePreloadFile(struct mCore* core, const char* path) {
148	struct VFile* rom = mDirectorySetOpenPath(&core->dirs, path, core->isROM);
149	if (!rom) {
150		return false;
151	}
152
153	bool ret = mCorePreloadVF(core, rom);
154	if (!ret) {
155		rom->close(rom);
156	}
157	return ret;
158}
159
160bool mCoreAutoloadSave(struct mCore* core) {
161	return core->loadSave(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.save, ".sav", O_CREAT | O_RDWR));
162}
163
164bool mCoreAutoloadPatch(struct mCore* core) {
165	return core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ups", O_RDONLY)) ||
166	       core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ips", O_RDONLY)) ||
167	       core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".bps", O_RDONLY));
168}
169
170bool mCoreSaveState(struct mCore* core, int slot, int flags) {
171	struct VFile* vf = mCoreGetState(core, slot, true);
172	if (!vf) {
173		return false;
174	}
175	bool success = mCoreSaveStateNamed(core, vf, flags);
176	vf->close(vf);
177	if (success) {
178		mLOG(STATUS, INFO, "State %i saved", slot);
179	} else {
180		mLOG(STATUS, INFO, "State %i failed to save", slot);
181	}
182
183	return success;
184}
185
186bool mCoreLoadState(struct mCore* core, int slot, int flags) {
187	struct VFile* vf = mCoreGetState(core, slot, false);
188	if (!vf) {
189		return false;
190	}
191	bool success = mCoreLoadStateNamed(core, vf, flags);
192	vf->close(vf);
193	if (success) {
194		mLOG(STATUS, INFO, "State %i loaded", slot);
195	} else {
196		mLOG(STATUS, INFO, "State %i failed to load", slot);
197	}
198
199	return success;
200}
201
202struct VFile* mCoreGetState(struct mCore* core, int slot, bool write) {
203	char name[PATH_MAX];
204	snprintf(name, sizeof(name), "%s.ss%i", core->dirs.baseName, slot);
205	return core->dirs.state->openFile(core->dirs.state, name, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
206}
207
208void mCoreDeleteState(struct mCore* core, int slot) {
209	char name[PATH_MAX];
210	snprintf(name, sizeof(name), "%s.ss%i", core->dirs.baseName, slot);
211	core->dirs.state->deleteFile(core->dirs.state, name);
212}
213
214void mCoreTakeScreenshot(struct mCore* core) {
215#ifdef USE_PNG
216	size_t stride;
217	const void* pixels = 0;
218	unsigned width, height;
219	core->desiredVideoDimensions(core, &width, &height);
220	struct VFile* vf;
221#ifndef PSP2
222	vf = VDirFindNextAvailable(core->dirs.screenshot, core->dirs.baseName, "-", ".png", O_CREAT | O_TRUNC | O_WRONLY);
223#else
224	vf = VFileMemChunk(0, 0);
225#endif
226	bool success = false;
227	if (vf) {
228		core->getPixels(core, &pixels, &stride);
229		png_structp png = PNGWriteOpen(vf);
230		png_infop info = PNGWriteHeader(png, width, height);
231		success = PNGWritePixels(png, width, height, stride, pixels);
232		PNGWriteClose(png, info);
233#ifdef PSP2
234		void* data = vf->map(vf, 0, 0);
235		PhotoExportParam param = {
236			0,
237			NULL,
238			NULL,
239			NULL,
240			{ 0 }
241		};
242		scePhotoExportFromData(data, vf->size(vf), &param, NULL, NULL, NULL, NULL, 0);
243#endif
244		vf->close(vf);
245	}
246	if (success) {
247		mLOG(STATUS, INFO, "Screenshot saved");
248		return;
249	}
250#else
251	UNUSED(core);
252#endif
253	mLOG(STATUS, WARN, "Failed to take screenshot");
254}
255#endif
256
257void mCoreInitConfig(struct mCore* core, const char* port) {
258	mCoreConfigInit(&core->config, port);
259}
260
261void mCoreLoadConfig(struct mCore* core) {
262#ifndef MINIMAL_CORE
263	mCoreConfigLoad(&core->config);
264#endif
265	mCoreLoadForeignConfig(core, &core->config);
266}
267
268void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config) {
269	mCoreConfigMap(config, &core->opts);
270#ifndef MINIMAL_CORE
271	mDirectorySetMapOptions(&core->dirs, &core->opts);
272#endif
273	if (core->opts.audioBuffers) {
274		core->setAudioBufferSize(core, core->opts.audioBuffers);
275	}
276	core->loadConfig(core, config);
277}
278
279void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) {
280	core->rtc.custom = rtc;
281	core->rtc.override = RTC_CUSTOM_START;
282}