all repos — mgba @ 00f5e2ea2a585cf71a545a2150e382d9129ab4fb

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