all repos — mgba @ 633449dfd4dcb9e70646cccf217cea3d4fbb3191

mGBA Game Boy Advance Emulator

src/gba/sharkport.c (view raw)

  1/* Copyright (c) 2013-2015 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/internal/gba/sharkport.h>
  7
  8#include <mgba/internal/arm/macros.h>
  9#include <mgba/internal/gba/gba.h>
 10#include <mgba-util/vfs.h>
 11
 12static const char* const SHARKPORT_HEADER = "SharkPortSave";
 13
 14bool GBASavedataImportSharkPort(struct GBA* gba, struct VFile* vf, bool testChecksum) {
 15	union {
 16		char c[0x1C];
 17		int32_t i;
 18	} buffer;
 19	if (vf->read(vf, &buffer.i, 4) < 4) {
 20		return false;
 21	}
 22	int32_t size;
 23	LOAD_32(size, 0, &buffer.i);
 24	if (size != (int32_t) strlen(SHARKPORT_HEADER)) {
 25		return false;
 26	}
 27	if (vf->read(vf, buffer.c, size) < size) {
 28		return false;
 29	}
 30	if (memcmp(SHARKPORT_HEADER, buffer.c, size) != 0) {
 31		return false;
 32	}
 33	if (vf->read(vf, &buffer.i, 4) < 4) {
 34		return false;
 35	}
 36	LOAD_32(size, 0, &buffer.i);
 37	if (size != 0x000F0000) {
 38		// What is this value?
 39		return false;
 40	}
 41
 42	// Skip first three fields
 43	if (vf->read(vf, &buffer.i, 4) < 4) {
 44		return false;
 45	}
 46	LOAD_32(size, 0, &buffer.i);
 47	if (vf->seek(vf, size, SEEK_CUR) < 0) {
 48		return false;
 49	}
 50
 51	if (vf->read(vf, &buffer.i, 4) < 4) {
 52		return false;
 53	}
 54	LOAD_32(size, 0, &buffer.i);
 55	if (vf->seek(vf, size, SEEK_CUR) < 0) {
 56		return false;
 57	}
 58
 59	if (vf->read(vf, &buffer.i, 4) < 4) {
 60		return false;
 61	}
 62	LOAD_32(size, 0, &buffer.i);
 63	if (vf->seek(vf, size, SEEK_CUR) < 0) {
 64		return false;
 65	}
 66
 67	// Read payload
 68	if (vf->read(vf, &buffer.i, 4) < 4) {
 69		return false;
 70	}
 71	LOAD_32(size, 0, &buffer.i);
 72	if (size < 0x1C || size > SIZE_CART_FLASH1M + 0x1C) {
 73		return false;
 74	}
 75	char* payload = malloc(size);
 76	if (vf->read(vf, payload, size) < size) {
 77		goto cleanup;
 78	}
 79
 80	struct GBACartridge* cart = (struct GBACartridge*) gba->memory.rom;
 81	memcpy(buffer.c, &cart->title, 16);
 82	buffer.c[0x10] = 0;
 83	buffer.c[0x11] = 0;
 84	buffer.c[0x12] = cart->checksum;
 85	buffer.c[0x13] = cart->maker;
 86	buffer.c[0x14] = 1;
 87	buffer.c[0x15] = 0;
 88	buffer.c[0x16] = 0;
 89	buffer.c[0x17] = 0;
 90	buffer.c[0x18] = 0;
 91	buffer.c[0x19] = 0;
 92	buffer.c[0x1A] = 0;
 93	buffer.c[0x1B] = 0;
 94	if (memcmp(buffer.c, payload, 0x1C) != 0) {
 95		goto cleanup;
 96	}
 97
 98	uint32_t checksum;
 99	if (vf->read(vf, &buffer.i, 4) < 4) {
100		goto cleanup;
101	}
102	LOAD_32(checksum, 0, &buffer.i);
103
104	if (testChecksum) {
105		uint32_t calcChecksum = 0;
106		int i;
107		for (i = 0; i < size; ++i) {
108			calcChecksum += ((int32_t) payload[i]) << (calcChecksum % 24);
109		}
110
111		if (calcChecksum != checksum) {
112			goto cleanup;
113		}
114	}
115
116	uint32_t copySize = size - 0x1C;
117	switch (gba->memory.savedata.type) {
118	case SAVEDATA_FLASH512:
119		if (copySize > SIZE_CART_FLASH512) {
120			GBASavedataForceType(&gba->memory.savedata, SAVEDATA_FLASH1M, gba->memory.savedata.realisticTiming);
121		}
122	// Fall through
123	default:
124		if (copySize > GBASavedataSize(&gba->memory.savedata)) {
125			copySize = GBASavedataSize(&gba->memory.savedata);
126		}
127		break;
128	case SAVEDATA_FORCE_NONE:
129	case SAVEDATA_AUTODETECT:
130		goto cleanup;
131	}
132
133	if (gba->memory.savedata.type == SAVEDATA_EEPROM) {
134		size_t i;
135		for (i = 0; i < copySize; i += 8) {
136			uint32_t lo, hi;
137			LOAD_32BE(lo, i + 0x1C, payload);
138			LOAD_32BE(hi, i + 0x20, payload);
139			STORE_32LE(hi, i, gba->memory.savedata.data);
140			STORE_32LE(lo, i + 4, gba->memory.savedata.data);
141		}
142	} else {
143		memcpy(gba->memory.savedata.data, &payload[0x1C], copySize);
144	}
145	if (gba->memory.savedata.vf) {
146		gba->memory.savedata.vf->sync(gba->memory.savedata.vf, gba->memory.savedata.data, size);
147	}
148
149	free(payload);
150	return true;
151
152cleanup:
153	free(payload);
154	return false;
155}
156
157bool GBASavedataExportSharkPort(const struct GBA* gba, struct VFile* vf) {
158	union {
159		char c[0x1C];
160		int32_t i;
161	} buffer;
162	uint32_t size = strlen(SHARKPORT_HEADER);
163	STORE_32(size, 0, &buffer.i);
164	if (vf->write(vf, &buffer.i, 4) < 4) {
165		return false;
166	}
167	if (vf->write(vf, SHARKPORT_HEADER, size) < size) {
168		return false;
169	}
170
171	size = 0x000F0000;
172	STORE_32(size, 0, &buffer.i);
173	if (vf->write(vf, &buffer.i, 4) < 4) {
174		return false;
175	}
176
177	const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
178	size = sizeof(cart->title);
179	STORE_32(size, 0, &buffer.i);
180	if (vf->write(vf, &buffer.i, 4) < 4) {
181		return false;
182	}
183	if (vf->write(vf, cart->title, size) < 4) {
184		return false;
185	}
186
187	time_t t = time(0);
188	struct tm* tm = localtime(&t);
189	size = strftime(&buffer.c[4], sizeof(buffer.c) - 4, "%m/%d/%Y %I:%M:%S %p", tm);
190	STORE_32(size, 0, &buffer.i);
191	if (vf->write(vf, buffer.c, size + 4) < size + 4) {
192		return false;
193	}
194
195	// Last field is blank
196	size = 0;
197	STORE_32(size, 0, &buffer.i);
198	if (vf->write(vf, &buffer.i, 4) < 4) {
199		return false;
200	}
201
202	// Write payload
203	size = 0x1C;
204	switch (gba->memory.savedata.type) {
205	case SAVEDATA_SRAM:
206		size += SIZE_CART_SRAM;
207		break;
208	case SAVEDATA_FLASH512:
209		size += SIZE_CART_FLASH512;
210		break;
211	case SAVEDATA_FLASH1M:
212		size += SIZE_CART_FLASH1M;
213		break;
214	case SAVEDATA_EEPROM:
215		size += SIZE_CART_EEPROM;
216		break;
217	case SAVEDATA_FORCE_NONE:
218	case SAVEDATA_AUTODETECT:
219		return false;
220	}
221	STORE_32(size, 0, &buffer.i);
222	if (vf->write(vf, &buffer.i, 4) < 4) {
223		return false;
224	}
225	size -= 0x1C;
226
227	memcpy(buffer.c, &cart->title, 16);
228	buffer.c[0x10] = 0;
229	buffer.c[0x11] = 0;
230	buffer.c[0x12] = cart->checksum;
231	buffer.c[0x13] = cart->maker;
232	buffer.c[0x14] = 1;
233	buffer.c[0x15] = 0;
234	buffer.c[0x16] = 0;
235	buffer.c[0x17] = 0;
236	buffer.c[0x18] = 0;
237	buffer.c[0x19] = 0;
238	buffer.c[0x1A] = 0;
239	buffer.c[0x1B] = 0;
240	if (vf->write(vf, buffer.c, 0x1C) < 0x1C) {
241		return false;
242	}
243
244	uint32_t checksum = 0;
245	size_t i;
246	for (i = 0; i < 0x1C; ++i) {
247		checksum += buffer.c[i] << (checksum % 24);
248	}
249
250
251	if (gba->memory.savedata.type == SAVEDATA_EEPROM) {
252		for (i = 0; i < size; ++i) {
253			char byte = gba->memory.savedata.data[i ^ 7];
254			checksum += byte << (checksum % 24);
255			vf->write(vf, &byte, 1);
256		}
257	} else if (vf->write(vf, gba->memory.savedata.data, size) < size) {
258		return false;
259	} else {
260		for (i = 0; i < size; ++i) {
261			checksum += ((char) gba->memory.savedata.data[i]) << (checksum % 24);
262		}
263	}
264
265	STORE_32(checksum, 0, &buffer.i);
266	if (vf->write(vf, &buffer.i, 4) < 4) {
267		return false;
268	}
269
270	return true;
271}