all repos — mgba @ 1e76ae31d62931c2c2d1aaa07741dda926d27b46

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