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}