all repos — mgba @ 3eb6a92265cb32c64e01fb163db9c37f134b47a4

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/cheats.h>
  9#include <mgba/core/log.h>
 10#include <mgba/core/serialize.h>
 11#include <mgba-util/vfs.h>
 12#include <mgba/internal/debugger/symbols.h>
 13
 14#ifdef USE_ELF
 15#include <mgba-util/elf-read.h>
 16#endif
 17
 18#ifdef M_CORE_GB
 19#include <mgba/gb/core.h>
 20#include <mgba/gb/interface.h>
 21#endif
 22#ifdef M_CORE_GBA
 23#include <mgba/gba/core.h>
 24#include <mgba/gba/interface.h>
 25#endif
 26#ifdef M_CORE_DS
 27#include <mgba/ds/core.h>
 28#include <mgba/internal/ds/ds.h>
 29#endif
 30#ifndef MINIMAL_CORE
 31#include <mgba/feature/video-logger.h>
 32#endif
 33
 34static const struct mCoreFilter {
 35	bool (*filter)(struct VFile*);
 36	struct mCore* (*open)(void);
 37	enum mPlatform platform;
 38} _filters[] = {
 39#ifdef M_CORE_DS
 40	{ DSIsROM, DSCoreCreate, PLATFORM_DS },
 41#endif
 42#ifdef M_CORE_GBA
 43	{ GBAIsROM, GBACoreCreate, PLATFORM_GBA },
 44#endif
 45#ifdef M_CORE_GB
 46	{ GBIsROM, GBCoreCreate, PLATFORM_GB },
 47#endif
 48	{ 0, 0, PLATFORM_NONE }
 49};
 50
 51struct mCore* mCoreFindVF(struct VFile* vf) {
 52	if (!vf) {
 53		return NULL;
 54	}
 55	const struct mCoreFilter* filter;
 56	for (filter = &_filters[0]; filter->filter; ++filter) {
 57		if (filter->filter(vf)) {
 58			break;
 59		}
 60	}
 61	if (filter->open) {
 62		return filter->open();
 63	}
 64#ifndef MINIMAL_CORE
 65	return mVideoLogCoreFind(vf);
 66#endif
 67	return NULL;
 68}
 69
 70enum mPlatform mCoreIsCompatible(struct VFile* vf) {
 71	if (!vf) {
 72		return PLATFORM_NONE;
 73	}
 74	const struct mCoreFilter* filter;
 75	for (filter = &_filters[0]; filter->filter; ++filter) {
 76		if (filter->filter(vf)) {
 77			return filter->platform;
 78		}
 79	}
 80	return PLATFORM_NONE;
 81}
 82
 83#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 84#include <mgba-util/png-io.h>
 85
 86#ifdef PSP2
 87#include <psp2/photoexport.h>
 88#endif
 89
 90struct mCore* mCoreFind(const char* path) {
 91	struct VDir* archive = VDirOpenArchive(path);
 92	struct mCore* core = NULL;
 93	if (archive) {
 94		struct VDirEntry* dirent = archive->listNext(archive);
 95		while (dirent) {
 96			struct VFile* vf = archive->openFile(archive, dirent->name(dirent), O_RDONLY);
 97			if (!vf) {
 98				dirent = archive->listNext(archive);
 99				continue;
100			}
101			core = mCoreFindVF(vf);
102			vf->close(vf);
103			if (core) {
104				break;
105			}
106			dirent = archive->listNext(archive);
107		}
108		archive->close(archive);
109	} else {
110		struct VFile* vf = VFileOpen(path, O_RDONLY);
111		if (!vf) {
112			return NULL;
113		}
114		core = mCoreFindVF(vf);
115		vf->close(vf);
116	}
117	if (core) {
118		return core;
119	}
120	return NULL;
121}
122
123bool mCoreLoadFile(struct mCore* core, const char* path) {
124#ifdef FIXED_ROM_BUFFER
125	return mCorePreloadFile(core, path);
126#else
127	struct VFile* rom = mDirectorySetOpenPath(&core->dirs, path, core->isROM);
128	if (!rom) {
129		return false;
130	}
131
132	bool ret = core->loadROM(core, rom);
133	if (!ret) {
134		rom->close(rom);
135	}
136	return ret;
137#endif
138}
139
140bool mCorePreloadVF(struct mCore* core, struct VFile* vf) {
141	return mCorePreloadVFCB(core, vf, NULL, NULL);
142}
143
144bool mCorePreloadFile(struct mCore* core, const char* path) {
145	return mCorePreloadFileCB(core, path, NULL, NULL);
146}
147
148bool mCorePreloadVFCB(struct mCore* core, struct VFile* vf, void (cb)(size_t, size_t, void*), void* context) {
149	struct VFile* vfm;
150	size_t size = vf->size(vf);
151
152#ifdef FIXED_ROM_BUFFER
153	extern uint32_t* romBuffer;
154	extern size_t romBufferSize;
155	vfm = VFileFromMemory(romBuffer, romBufferSize);
156#else
157	vfm = VFileMemChunk(NULL, size);
158#endif
159
160	uint8_t buffer[2048];
161	ssize_t read;
162	size_t total = 0;
163	vf->seek(vf, 0, SEEK_SET);
164	while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) {
165		vfm->write(vfm, buffer, read);
166		total += read;
167		if (cb) {
168			cb(total, size, context);
169		}
170	}
171	vf->close(vf);
172	if (read < 0) {
173		vfm->close(vfm);
174		return false;
175	}
176	bool ret = core->loadROM(core, vfm);
177	if (!ret) {
178		vfm->close(vfm);
179	}
180	return ret;
181}
182
183bool mCorePreloadFileCB(struct mCore* core, const char* path, void (cb)(size_t, size_t, void*), void* context) {
184	struct VFile* rom = mDirectorySetOpenPath(&core->dirs, path, core->isROM);
185	if (!rom) {
186		return false;
187	}
188
189	bool ret = mCorePreloadVFCB(core, rom, cb, context);
190	if (!ret) {
191		rom->close(rom);
192	}
193	return ret;
194}
195
196bool mCoreAutoloadSave(struct mCore* core) {
197	if (!core->dirs.save) {
198		return false;
199	}
200	return core->loadSave(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.save, ".sav", O_CREAT | O_RDWR));
201}
202
203bool mCoreAutoloadPatch(struct mCore* core) {
204	if (!core->dirs.patch) {
205		return false;
206	}
207	return core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ups", O_RDONLY)) ||
208	       core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ips", O_RDONLY)) ||
209	       core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".bps", O_RDONLY));
210}
211
212bool mCoreAutoloadCheats(struct mCore* core) {
213	bool success = true;
214	int cheatAuto;
215	if (!mCoreConfigGetIntValue(&core->config, "cheatAutoload", &cheatAuto) || cheatAuto) {
216		struct VFile* vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.cheats, ".cheats", O_RDONLY);
217		if (vf) {
218			struct mCheatDevice* device = core->cheatDevice(core);
219			if (!device) {
220				success = false;
221			} else {
222				success = mCheatParseFile(device, vf);
223			}
224			vf->close(vf);
225		}
226	}
227	if (!mCoreConfigGetIntValue(&core->config, "cheatAutosave", &cheatAuto) || cheatAuto) {
228		struct mCheatDevice* device = core->cheatDevice(core);
229		if (device) {
230			device->autosave = true;
231		}
232	}
233	return success;
234}
235
236bool mCoreSaveState(struct mCore* core, int slot, int flags) {
237	struct VFile* vf = mCoreGetState(core, slot, true);
238	if (!vf) {
239		return false;
240	}
241	bool success = mCoreSaveStateNamed(core, vf, flags);
242	vf->close(vf);
243	if (success) {
244		mLOG(STATUS, INFO, "State %i saved", slot);
245	} else {
246		mLOG(STATUS, INFO, "State %i failed to save", slot);
247	}
248
249	return success;
250}
251
252bool mCoreLoadState(struct mCore* core, int slot, int flags) {
253	struct VFile* vf = mCoreGetState(core, slot, false);
254	if (!vf) {
255		return false;
256	}
257	bool success = mCoreLoadStateNamed(core, vf, flags);
258	vf->close(vf);
259	if (success) {
260		mLOG(STATUS, INFO, "State %i loaded", slot);
261	} else {
262		mLOG(STATUS, INFO, "State %i failed to load", slot);
263	}
264
265	return success;
266}
267
268struct VFile* mCoreGetState(struct mCore* core, int slot, bool write) {
269	if (!core->dirs.state) {
270		return NULL;
271	}
272	char name[PATH_MAX + 14]; // Quash warning
273	snprintf(name, sizeof(name), "%s.ss%i", core->dirs.baseName, slot);
274	return core->dirs.state->openFile(core->dirs.state, name, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
275}
276
277void mCoreDeleteState(struct mCore* core, int slot) {
278	char name[PATH_MAX + 14]; // Quash warning
279	snprintf(name, sizeof(name), "%s.ss%i", core->dirs.baseName, slot);
280	core->dirs.state->deleteFile(core->dirs.state, name);
281}
282
283void mCoreTakeScreenshot(struct mCore* core) {
284#ifdef USE_PNG
285	size_t stride;
286	const void* pixels = 0;
287	unsigned width, height;
288	core->desiredVideoDimensions(core, &width, &height);
289	struct VFile* vf;
290#ifndef PSP2
291	vf = VDirFindNextAvailable(core->dirs.screenshot, core->dirs.baseName, "-", ".png", O_CREAT | O_TRUNC | O_WRONLY);
292#else
293	vf = VFileMemChunk(0, 0);
294#endif
295	bool success = false;
296	if (vf) {
297		core->getPixels(core, &pixels, &stride);
298		png_structp png = PNGWriteOpen(vf);
299		png_infop info = PNGWriteHeader(png, width, height);
300		success = PNGWritePixels(png, width, height, stride, pixels);
301		PNGWriteClose(png, info);
302#ifdef PSP2
303		void* data = vf->map(vf, 0, 0);
304		PhotoExportParam param = {
305			0,
306			NULL,
307			NULL,
308			NULL,
309			{ 0 }
310		};
311		scePhotoExportFromData(data, vf->size(vf), &param, NULL, NULL, NULL, NULL, 0);
312#endif
313		vf->close(vf);
314	}
315	if (success) {
316		mLOG(STATUS, INFO, "Screenshot saved");
317		return;
318	}
319#else
320	UNUSED(core);
321#endif
322	mLOG(STATUS, WARN, "Failed to take screenshot");
323}
324#endif
325
326void mCoreInitConfig(struct mCore* core, const char* port) {
327	mCoreConfigInit(&core->config, port);
328}
329
330void mCoreLoadConfig(struct mCore* core) {
331#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
332	mCoreConfigLoad(&core->config);
333#endif
334	mCoreLoadForeignConfig(core, &core->config);
335}
336
337void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config) {
338	mCoreConfigMap(config, &core->opts);
339#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
340	mDirectorySetMapOptions(&core->dirs, &core->opts);
341#endif
342	if (core->opts.audioBuffers) {
343		core->setAudioBufferSize(core, core->opts.audioBuffers);
344	}
345
346	mCoreConfigCopyValue(&core->config, config, "cheatAutosave");
347	mCoreConfigCopyValue(&core->config, config, "cheatAutoload");
348
349	core->loadConfig(core, config);
350}
351
352void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) {
353	core->rtc.custom = rtc;
354	core->rtc.override = RTC_CUSTOM_START;
355}
356
357void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size) {
358	return mCoreGetMemoryBlockMasked(core, start, size, mCORE_MEMORY_MAPPED);
359}
360
361void* mCoreGetMemoryBlockMasked(struct mCore* core, uint32_t start, size_t* size, uint32_t mask) {
362	const struct mCoreMemoryBlock* blocks;
363	size_t nBlocks = core->listMemoryBlocks(core, &blocks);
364	size_t i;
365	for (i = 0; i < nBlocks; ++i) {
366		if (!(blocks[i].flags & mCORE_MEMORY_MAPPED)) {
367			continue;
368		}
369		if (!(blocks[i].flags & mask)) {
370			continue;
371		}
372		if (start < blocks[i].start) {
373			continue;
374		}
375		if (start >= blocks[i].start + blocks[i].size) {
376			continue;
377		}
378		uint8_t* out = core->getMemoryBlock(core, blocks[i].id, size);
379		out += start - blocks[i].start;
380		*size -= start - blocks[i].start;
381		return out;
382	}
383	return NULL;
384}
385
386#ifdef USE_ELF
387bool mCoreLoadELF(struct mCore* core, struct ELF* elf) {
388	struct ELFProgramHeaders ph;
389	ELFProgramHeadersInit(&ph, 0);
390	ELFGetProgramHeaders(elf, &ph);
391	size_t i;
392	for (i = 0; i < ELFProgramHeadersSize(&ph); ++i) {
393		size_t bsize, esize;
394		Elf32_Phdr* phdr = ELFProgramHeadersGetPointer(&ph, i);
395		void* block = mCoreGetMemoryBlockMasked(core, phdr->p_paddr, &bsize, mCORE_MEMORY_WRITE | mCORE_MEMORY_WORM);
396		char* bytes = ELFBytes(elf, &esize);
397		if (block && bsize >= phdr->p_filesz && esize > phdr->p_offset && esize >= phdr->p_filesz + phdr->p_offset) {
398			memcpy(block, &bytes[phdr->p_offset], phdr->p_filesz);
399		} else {
400			return false;
401		}
402	}
403	return true;
404}
405
406#ifdef USE_DEBUGGERS
407void mCoreLoadELFSymbols(struct mDebuggerSymbols* symbols, struct ELF* elf) {
408	size_t symIndex = ELFFindSection(elf, ".symtab");
409	size_t names = ELFFindSection(elf, ".strtab");
410	Elf32_Shdr* symHeader = ELFGetSectionHeader(elf, symIndex);
411	char* bytes = ELFBytes(elf, NULL);
412
413	Elf32_Sym* syms = (Elf32_Sym*) &bytes[symHeader->sh_offset];
414	size_t i;
415	for (i = 0; i * sizeof(*syms) < symHeader->sh_size; ++i) {
416		if (!syms[i].st_name || ELF32_ST_TYPE(syms[i].st_info) == STT_FILE) {
417			continue;
418		}
419		const char* name = ELFGetString(elf, names, syms[i].st_name);
420		if (name[0] == '$') {
421			continue;
422		}
423		mDebuggerSymbolAdd(symbols, name, syms[i].st_value, -1);
424	}
425}
426#endif
427#endif