all repos — mgba @ 73947766defe48d122796c602b553af507cdb6d5

mGBA Game Boy Advance Emulator

src/core/video-logger.c (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 <mgba/core/video-logger.h>
  7
  8#include <mgba/core/core.h>
  9#include <mgba-util/memory.h>
 10#include <mgba-util/vfs.h>
 11
 12const char mVL_MAGIC[] = "mVL\0";
 13
 14static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length);
 15static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block);
 16
 17static inline size_t _roundUp(size_t value, int shift) {
 18	value += (1 << shift) - 1;
 19	return value >> shift;
 20}
 21
 22void mVideoLoggerRendererCreate(struct mVideoLogger* logger) {
 23	logger->writeData = _writeData;
 24	logger->readData = _readData;
 25	logger->vf = NULL;
 26}
 27
 28void mVideoLoggerRendererInit(struct mVideoLogger* logger) {
 29	logger->palette = anonymousMemoryMap(logger->paletteSize);
 30	logger->vram = anonymousMemoryMap(logger->vramSize);
 31	logger->oam = anonymousMemoryMap(logger->oamSize);
 32
 33	logger->vramDirtyBitmap = calloc(_roundUp(logger->vramSize, 17), sizeof(uint32_t));
 34	logger->oamDirtyBitmap = calloc(_roundUp(logger->oamSize, 6), sizeof(uint32_t));
 35}
 36
 37void mVideoLoggerRendererDeinit(struct mVideoLogger* logger) {
 38	mappedMemoryFree(logger->palette, logger->paletteSize);
 39	mappedMemoryFree(logger->vram, logger->vramSize);
 40	mappedMemoryFree(logger->oam, logger->oamSize);
 41
 42	free(logger->vramDirtyBitmap);
 43	free(logger->oamDirtyBitmap);
 44}
 45
 46void mVideoLoggerRendererReset(struct mVideoLogger* logger) {
 47	memset(logger->vramDirtyBitmap, 0, sizeof(uint32_t) * _roundUp(logger->vramSize, 17));
 48	memset(logger->oamDirtyBitmap, 0, sizeof(uint32_t) * _roundUp(logger->oamSize, 6));
 49}
 50
 51void mVideoLoggerRendererWriteVideoRegister(struct mVideoLogger* logger, uint32_t address, uint16_t value) {
 52	struct mVideoLoggerDirtyInfo dirty = {
 53		DIRTY_REGISTER,
 54		address,
 55		value,
 56		0xDEADBEEF,
 57	};
 58	logger->writeData(logger, &dirty, sizeof(dirty));
 59}
 60
 61void mVideoLoggerRendererWriteVRAM(struct mVideoLogger* logger, uint32_t address) {
 62	int bit = 1 << (address >> 12);
 63	if (logger->vramDirtyBitmap[address >> 17] & bit) {
 64		return;
 65	}
 66	logger->vramDirtyBitmap[address >> 17] |= bit;
 67}
 68
 69void mVideoLoggerRendererWritePalette(struct mVideoLogger* logger, uint32_t address, uint16_t value) {
 70	struct mVideoLoggerDirtyInfo dirty = {
 71		DIRTY_PALETTE,
 72		address,
 73		value,
 74		0xDEADBEEF,
 75	};
 76	logger->writeData(logger, &dirty, sizeof(dirty));
 77}
 78
 79void mVideoLoggerRendererWriteOAM(struct mVideoLogger* logger, uint32_t address, uint16_t value) {
 80	struct mVideoLoggerDirtyInfo dirty = {
 81		DIRTY_OAM,
 82		address,
 83		value,
 84		0xDEADBEEF,
 85	};
 86	logger->writeData(logger, &dirty, sizeof(dirty));
 87}
 88
 89void mVideoLoggerRendererDrawScanline(struct mVideoLogger* logger, int y) {
 90	size_t i;
 91	for (i = 0; i < _roundUp(logger->vramSize, 17); ++i) {
 92		if (logger->vramDirtyBitmap[i]) {
 93			uint32_t bitmap = logger->vramDirtyBitmap[i];
 94			logger->vramDirtyBitmap[i] = 0;
 95			int j;
 96			for (j = 0; j < 32; ++j) {
 97				if (!(bitmap & (1 << j))) {
 98					continue;
 99				}
100				struct mVideoLoggerDirtyInfo dirty = {
101					DIRTY_VRAM,
102					j * 0x1000,
103					0xABCD,
104					0xDEADBEEF,
105				};
106				logger->writeData(logger, &dirty, sizeof(dirty));
107				logger->writeData(logger, logger->vramBlock(logger, j * 0x1000), 0x1000);
108			}
109		}
110	}
111	struct mVideoLoggerDirtyInfo dirty = {
112		DIRTY_SCANLINE,
113		y,
114		0,
115		0xDEADBEEF,
116	};
117	logger->writeData(logger, &dirty, sizeof(dirty));
118}
119
120void mVideoLoggerRendererFlush(struct mVideoLogger* logger) {
121	struct mVideoLoggerDirtyInfo dirty = {
122		DIRTY_FLUSH,
123		0,
124		0,
125		0xDEADBEEF,
126	};
127	logger->writeData(logger, &dirty, sizeof(dirty));
128}
129
130bool mVideoLoggerRendererRun(struct mVideoLogger* logger) {
131	struct mVideoLoggerDirtyInfo item = {0};
132	while (logger->readData(logger, &item, sizeof(item), false)) {
133		switch (item.type) {
134		case DIRTY_REGISTER:
135		case DIRTY_PALETTE:
136		case DIRTY_OAM:
137		case DIRTY_VRAM:
138		case DIRTY_SCANLINE:
139		case DIRTY_FLUSH:
140			if (!logger->parsePacket(logger, &item)) {
141				return true;
142			}
143			break;
144		default:
145			return false;
146		}
147	}
148	return true;
149}
150
151static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) {
152	return logger->vf->write(logger->vf, data, length) == (ssize_t) length;
153}
154
155static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) {
156	return logger->vf->read(logger->vf, data, length) == (ssize_t) length || !block;
157}
158
159struct mVideoLogContext* mVideoLoggerCreate(struct mCore* core) {
160	struct mVideoLogContext* context = malloc(sizeof(*context));
161	core->startVideoLog(core, context);
162	return context;
163}
164
165void mVideoLoggerDestroy(struct mCore* core, struct mVideoLogContext* context) {
166	if (core) {
167		core->endVideoLog(core);
168	}
169	free(context);
170}
171
172void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext* context, struct VFile* vf) {
173	struct mVideoLogHeader header = {{0}};
174	memcpy(header.magic, mVL_MAGIC, sizeof(mVL_MAGIC));
175
176	enum mPlatform platform = core->platform(core);
177	STORE_32LE(platform, 0, &header.platform);
178	STORE_32LE(context->nChannels, 0, &header.nChannels);
179
180	ssize_t pointer = vf->seek(vf, sizeof(header), SEEK_SET);
181	if (context->initialStateSize) {
182		ssize_t written = vf->write(vf, context->initialState, context->initialStateSize);
183		if (written > 0) {
184			STORE_32LE(pointer, 0, &header.initialStatePointer);
185			pointer += written;
186		} else {
187			header.initialStatePointer = 0;
188		}
189	} else {
190		header.initialStatePointer = 0;
191	}
192
193	size_t i;
194	for (i = 0; i < context->nChannels && i < 32; ++i) {
195		struct VFile* channel = context->channels[i].channelData;
196		void* block = channel->map(channel, channel->size(channel), MAP_READ);
197
198		struct mVideoLogChannelHeader chHeader = {0};
199		STORE_32LE(context->channels[i].type, 0, &chHeader.type);
200		STORE_32LE(channel->size(channel), 0, &chHeader.channelSize);
201
202		if (context->channels[i].initialStateSize) {
203			ssize_t written = vf->write(vf, context->channels[i].initialState, context->channels[i].initialStateSize);
204			if (written > 0) {
205				STORE_32LE(pointer, 0, &chHeader.channelInitialStatePointer);
206				pointer += written;
207			} else {
208				chHeader.channelInitialStatePointer = 0;
209			}
210		}
211		STORE_32LE(pointer, 0, &header.channelPointers[i]);
212		ssize_t written = vf->write(vf, &chHeader, sizeof(chHeader));
213		if (written != sizeof(chHeader)) {
214			continue;
215		}
216		pointer += written;
217		written = vf->write(vf, block, channel->size(channel));
218		if (written != channel->size(channel)) {
219			break;
220		}
221		pointer += written;
222	}
223	vf->seek(vf, 0, SEEK_SET);
224	vf->write(vf, &header, sizeof(header));
225}