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}