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}