all repos — mgba @ d571d8921fed313733803973629780633e3d5774

mGBA Game Boy Advance Emulator

src/core/mem-search.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/mem-search.h>
  7
  8#include <mgba/core/core.h>
  9#include <mgba/core/interface.h>
 10
 11DEFINE_VECTOR(mCoreMemorySearchResults, struct mCoreMemorySearchResult);
 12
 13static bool _op(int32_t value, int32_t match, enum mCoreMemorySearchOp op) {
 14	switch (op) {
 15	case mCORE_MEMORY_SEARCH_GREATER:
 16		return value > match;
 17	case mCORE_MEMORY_SEARCH_LESS:
 18		return value < match;
 19	case mCORE_MEMORY_SEARCH_EQUAL:
 20	case mCORE_MEMORY_SEARCH_DELTA:
 21		return value == match;
 22	}
 23}
 24
 25static size_t _search32(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint32_t value32, enum mCoreMemorySearchOp op, struct mCoreMemorySearchResults* out, size_t limit) {
 26	const uint32_t* mem32 = mem;
 27	size_t found = 0;
 28	uint32_t start = block->start;
 29	uint32_t end = size; // TODO: Segments
 30	size_t i;
 31	// TODO: Big endian
 32	for (i = 0; (!limit || found < limit) && i < end; i += 4) {
 33		if (_op(mem32[i >> 2], value32, op)) {
 34			struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
 35			res->address = start + i;
 36			res->type = mCORE_MEMORY_SEARCH_INT;
 37			res->width = 4;
 38			res->segment = -1; // TODO
 39			res->guessDivisor = 1;
 40			res->guessMultiplier = 1;
 41			res->oldValue = value32;
 42			++found;
 43		}
 44	}
 45	return found;
 46}
 47
 48static size_t _search16(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint16_t value16, enum mCoreMemorySearchOp op, struct mCoreMemorySearchResults* out, size_t limit) {
 49	const uint16_t* mem16 = mem;
 50	size_t found = 0;
 51	uint32_t start = block->start;
 52	uint32_t end = size; // TODO: Segments
 53	size_t i;
 54	// TODO: Big endian
 55	for (i = 0; (!limit || found < limit) && i < end; i += 2) {
 56		if (_op(mem16[i >> 1], value16, op)) {
 57			struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
 58			res->address = start + i;
 59			res->type = mCORE_MEMORY_SEARCH_INT;
 60			res->width = 2;
 61			res->segment = -1; // TODO
 62			res->guessDivisor = 1;
 63			res->guessMultiplier = 1;
 64			res->oldValue = value16;
 65			++found;
 66		}
 67	}
 68	return found;
 69}
 70
 71static size_t _search8(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint8_t value8, enum mCoreMemorySearchOp op, struct mCoreMemorySearchResults* out, size_t limit) {
 72	const uint8_t* mem8 = mem;
 73	size_t found = 0;
 74	uint32_t start = block->start;
 75	uint32_t end = size; // TODO: Segments
 76	size_t i;
 77	for (i = 0; (!limit || found < limit) && i < end; ++i) {
 78		if (_op(mem8[i], value8, op)) {
 79			struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
 80			res->address = start + i;
 81			res->type = mCORE_MEMORY_SEARCH_INT;
 82			res->width = 1;
 83			res->segment = -1; // TODO
 84			res->guessDivisor = 1;
 85			res->guessMultiplier = 1;
 86			res->oldValue = value8;
 87			++found;
 88		}
 89	}
 90	return found;
 91}
 92
 93static size_t _searchInt(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* out, size_t limit) {
 94	if (params->align == params->width || params->align == -1) {
 95		switch (params->width) {
 96		case 4:
 97			return _search32(mem, size, block, params->valueInt, params->op, out, limit);
 98		case 2:
 99			return _search16(mem, size, block, params->valueInt, params->op, out, limit);
100		case 1:
101			return _search8(mem, size, block, params->valueInt, params->op, out, limit);
102		}
103	}
104	return 0;
105}
106
107static size_t _searchStr(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const char* valueStr, int len, struct mCoreMemorySearchResults* out, size_t limit) {
108	const char* memStr = mem;
109	size_t found = 0;
110	uint32_t start = block->start;
111	uint32_t end = size; // TODO: Segments
112	size_t i;
113	for (i = 0; (!limit || found < limit) && i < end - len; ++i) {
114		if (!memcmp(valueStr, &memStr[i], len)) {
115			struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
116			res->address = start + i;
117			res->type = mCORE_MEMORY_SEARCH_STRING;
118			res->width = len;
119			res->segment = -1; // TODO
120			++found;
121		}
122	}
123	return found;
124}
125
126static size_t _searchGuess(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* out, size_t limit) {
127	// TODO: As str
128
129	char* end;
130	int64_t value;
131
132	size_t found = 0;
133
134	struct mCoreMemorySearchResults tmp;
135	mCoreMemorySearchResultsInit(&tmp, 0);
136
137	// Decimal:
138	value = strtoll(params->valueStr, &end, 10);
139	if (end && !end[0]) {
140		if ((params->width == -1 && value > 0x10000) || params->width == 4) {
141			found += _search32(mem, size, block, value, params->op, out, limit ? limit - found : 0);
142		} else if ((params->width == -1 && value > 0x100) || params->width == 2) {
143			found += _search16(mem, size, block, value, params->op, out, limit ? limit - found : 0);
144		} else {
145			found += _search8(mem, size, block, value, params->op, out, limit ? limit - found : 0);
146		}
147
148		uint32_t divisor = 1;
149		while (value && !(value % 10)) {
150			mCoreMemorySearchResultsClear(&tmp);
151			value /= 10;
152			divisor *= 10;
153
154			if ((params->width == -1 && value > 0x10000) || params->width == 4) {
155				found += _search32(mem, size, block, value, params->op, &tmp, limit ? limit - found : 0);
156			} else if ((params->width == -1 && value > 0x100) || params->width == 2) {
157				found += _search16(mem, size, block, value, params->op, &tmp, limit ? limit - found : 0);
158			} else {
159				found += _search8(mem, size, block, value, params->op, &tmp, limit ? limit - found : 0);
160			}
161			size_t i;
162			for (i = 0; i < mCoreMemorySearchResultsSize(&tmp); ++i) {
163				struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsGetPointer(&tmp, i);
164				res->guessDivisor = divisor;
165				*mCoreMemorySearchResultsAppend(out) = *res;
166			}
167		}
168	}
169
170	// Hex:
171	value = strtoll(params->valueStr, &end, 16);
172	if (end && !end[0]) {
173		if ((params->width == -1 && value > 0x10000) || params->width == 4) {
174			found += _search32(mem, size, block, value, params->op, out, limit ? limit - found : 0);
175		} else if ((params->width == -1 && value > 0x100) || params->width == 2) {
176			found += _search16(mem, size, block, value, params->op, out, limit ? limit - found : 0);
177		} else {
178			found += _search8(mem, size, block, value, params->op, out, limit ? limit - found : 0);
179		}
180
181		uint32_t divisor = 1;
182		while (value && !(value & 0xF)) {
183			mCoreMemorySearchResultsClear(&tmp);
184			value >>= 4;
185			divisor <<= 4;
186
187			if ((params->width == -1 && value > 0x10000) || params->width == 4) {
188				found += _search32(mem, size, block, value, params->op, &tmp, limit ? limit - found : 0);
189			} else if ((params->width == -1 && value > 0x100) || params->width == 2) {
190				found += _search16(mem, size, block, value, params->op, &tmp, limit ? limit - found : 0);
191			} else {
192				found += _search8(mem, size, block, value, params->op, &tmp, limit ? limit - found : 0);
193			}
194			size_t i;
195			for (i = 0; i < mCoreMemorySearchResultsSize(&tmp); ++i) {
196				struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsGetPointer(&tmp, i);
197				res->guessDivisor = divisor;
198				*mCoreMemorySearchResultsAppend(out) = *res;
199			}
200		}
201	}
202
203	mCoreMemorySearchResultsDeinit(&tmp);
204	return found;
205}
206
207static size_t _search(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* out, size_t limit) {
208	switch (params->type) {
209	case mCORE_MEMORY_SEARCH_INT:
210		return _searchInt(mem, size, block, params, out, limit);
211	case mCORE_MEMORY_SEARCH_STRING:
212		return _searchStr(mem, size, block, params->valueStr, params->width, out, limit);
213	case mCORE_MEMORY_SEARCH_GUESS:
214		return _searchGuess(mem, size, block, params, out, limit);
215	}
216}
217
218void mCoreMemorySearch(struct mCore* core, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* out, size_t limit) {
219	const struct mCoreMemoryBlock* blocks;
220	size_t nBlocks = core->listMemoryBlocks(core, &blocks);
221	size_t found = 0;
222
223	size_t b;
224	for (b = 0; (!limit || found < limit) && b < nBlocks; ++b) {
225		size_t size;
226		const struct mCoreMemoryBlock* block = &blocks[b];
227		if (!(block->flags & params->memoryFlags)) {
228			continue;
229		}
230		void* mem = core->getMemoryBlock(core, block->id, &size);
231		if (!mem) {
232			continue;
233		}
234		if (size > block->end - block->start) {
235			size = block->end - block->start; // TOOD: Segments
236		}
237		found += _search(mem, size, block, params, out, limit ? limit - found : 0);
238	}
239}
240
241bool _testGuess(struct mCore* core, struct mCoreMemorySearchResult* res, const struct mCoreMemorySearchParams* params) {
242	int64_t value;
243	int32_t offset = 0;
244	char* end;
245	if (params->op == mCORE_MEMORY_SEARCH_DELTA) {
246		offset = res->oldValue;
247	}
248
249	value = strtoll(params->valueStr, &end, 10);
250	if (end) {
251		res->oldValue += value;
252		if (_op(core->rawRead8(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) {
253			return true;
254		}
255		if (!(res->address & 1) && (res->width >= 2 || res->width == -1) && _op(core->rawRead16(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) {
256			return true;
257		}
258		if (!(res->address & 3) && (res->width >= 4 || res->width == -1) && _op(core->rawRead32(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) {
259			return true;
260		}
261		res->oldValue -= value;
262	}
263
264	value = strtoll(params->valueStr, &end, 16);
265	if (end) {
266		res->oldValue += value;
267		if (_op(core->rawRead8(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) {
268			return true;
269		}
270		if (!(res->address & 1) && (res->width >= 2 || res->width == -1) && _op(core->rawRead16(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) {
271			return true;
272		}
273		if (!(res->address & 3) && (res->width >= 4 || res->width == -1) && _op(core->rawRead32(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) {
274			return true;
275		}
276		res->oldValue -= value;
277	}
278	return false;
279}
280
281void mCoreMemorySearchRepeat(struct mCore* core, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* inout) {
282	size_t i;
283	for (i = 0; i < mCoreMemorySearchResultsSize(inout); ++i) {
284		struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsGetPointer(inout, i);
285		switch (res->type) {
286		case mCORE_MEMORY_SEARCH_INT:
287			if (params->type == mCORE_MEMORY_SEARCH_GUESS) {
288				if (!_testGuess(core, res, params)) {
289					*res = *mCoreMemorySearchResultsGetPointer(inout, mCoreMemorySearchResultsSize(inout) - 1);
290					mCoreMemorySearchResultsResize(inout, -1);
291					--i;
292				}
293			} else if (params->type == mCORE_MEMORY_SEARCH_INT) {
294				int32_t oldValue = params->valueInt;
295				if (params->op == mCORE_MEMORY_SEARCH_DELTA) {
296					oldValue += res->oldValue;
297				}
298				int32_t value = 0;
299				switch (params->width) {
300				case 1:
301					value = core->rawRead8(core, res->address, res->segment);
302					break;
303				case 2:
304					value = core->rawRead16(core, res->address, res->segment);
305					break;
306				case 4:
307					value = core->rawRead32(core, res->address, res->segment);
308					break;
309				default:
310					break;
311				}
312				if (!_op(value, oldValue, params->op)) {
313					*res = *mCoreMemorySearchResultsGetPointer(inout, mCoreMemorySearchResultsSize(inout) - 1);
314					mCoreMemorySearchResultsResize(inout, -1);
315					--i;
316				} else {
317					res->oldValue = value;
318				}
319			}
320			break;
321		case mCORE_MEMORY_SEARCH_STRING:
322		case mCORE_MEMORY_SEARCH_GUESS:
323			// TOOD
324			break;
325		}
326	}
327}