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}