SameBoy/Core/cheat_search.c

143 lines
4.7 KiB
C

#include "gb.h"
void GB_cheat_search_reset(GB_gameboy_t *gb)
{
if (gb->cheat_search_data) {
free(gb->cheat_search_data);
gb->cheat_search_data = NULL;
}
if (gb->cheat_search_bitmap) {
free(gb->cheat_search_bitmap);
gb->cheat_search_bitmap = NULL;
}
gb->cheat_search_count = 0;
}
bool GB_cheat_search_filter(GB_gameboy_t *gb, const char *expression, GB_cheat_search_data_type_t data_type)
{
GB_ASSERT_NOT_RUNNING(gb)
// Make sure the expression is valid first
if (GB_debugger_evaluate_cheat_filter(gb, expression, NULL, 0, 0)) {
return false;
}
gb->cheat_search_data_type = data_type;
if (gb->cheat_search_count == 0) {
GB_cheat_search_reset(gb);
gb->cheat_search_count = gb->ram_size + gb->mbc_ram_size + sizeof(gb->hram);
gb->cheat_search_data = malloc(gb->cheat_search_count);
gb->cheat_search_bitmap = malloc((gb->cheat_search_count + 7) / 8);
memset(gb->cheat_search_data, 0, gb->cheat_search_count);
memset(gb->cheat_search_bitmap, 0, (gb->cheat_search_count + 7) / 8);
}
uint8_t mask = 1;
uint8_t *old_data = gb->cheat_search_data;
uint8_t *bitmap = gb->cheat_search_bitmap;
uint8_t *new_data = gb->ram;
for (unsigned i = gb->ram_size + gb->mbc_ram_size + sizeof(gb->hram); i--;) {
if (*bitmap & mask) {
goto skip;
}
bool result = false;
if (data_type & GB_CHEAT_SEARCH_DATA_TYPE_16BIT) {
// The last byte of each section always fails on 16-bit searches
if ((new_data != gb->ram + gb->ram_size - 1 &&
new_data != gb->mbc_ram + gb->mbc_ram_size - 1 &&
new_data != gb->hram + sizeof(gb->hram) - 1)) {
uint16_t old = old_data[0] | (old_data[1] << 8);
uint16_t new = new_data[0] | (new_data[1] << 8);
if (data_type & GB_CHEAT_SEARCH_DATA_TYPE_BE_BIT) {
old = __builtin_bswap16(old);
new = __builtin_bswap16(new);
}
GB_debugger_evaluate_cheat_filter(gb, expression, &result, old, new);
}
}
else {
GB_debugger_evaluate_cheat_filter(gb, expression, &result, *old_data, *new_data);
}
if (result) {
// Filter passed, update old value
*old_data = *new_data;
if (data_type & GB_CHEAT_SEARCH_DATA_TYPE_16BIT) {
old_data[1] = new_data[1];
}
}
else {
// Did not pass filter, remove address
*bitmap |= mask;
gb->cheat_search_count--;
}
skip:;
old_data++;
if (new_data == gb->ram + gb->ram_size - 1) {
new_data = gb->mbc_ram;
}
else if (new_data == gb->mbc_ram + gb->mbc_ram_size - 1) {
new_data = gb->hram;
}
else {
new_data++;
}
mask <<= 1;
if (mask == 0) {
mask = 1;
bitmap++;
}
}
return true;
}
size_t GB_cheat_search_result_count(GB_gameboy_t *gb)
{
return gb->cheat_search_count;
}
void GB_cheat_search_get_results(GB_gameboy_t *gb, GB_cheat_search_result_t *results)
{
uint8_t mask = 1;
uint8_t *old_data = gb->cheat_search_data;
uint8_t *bitmap = gb->cheat_search_bitmap;
size_t count = gb->cheat_search_count;
while (count) {
if (!(*bitmap & mask)) {
count--;
if (gb->cheat_search_data_type & GB_CHEAT_SEARCH_DATA_TYPE_16BIT) {
// Do not check for end of section, data_type is required to be the same as the last filter call
uint16_t old = old_data[0] | (old_data[1] << 8);
if (gb->cheat_search_data_type & GB_CHEAT_SEARCH_DATA_TYPE_BE_BIT) {
old = __builtin_bswap16(old);
}
results->value = old;
}
else {
results->value = *old_data;
}
size_t offset = old_data - gb->cheat_search_data;
if (offset < gb->ram_size) {
results->bank = offset / 0x1000;
results->addr = (offset & 0xfff) + (results->bank? 0xd000 : 0xc000);
}
else if (offset < gb->ram_size + gb->mbc_ram_size) {
results->addr = (offset & 0x1fff) + 0xa000;
results->bank = (offset - gb->ram_size) / 0x2000;
}
else {
results->addr = (offset & 0x7f) + 0xff80;
results->bank = 0;
}
results++;
}
old_data++;
mask <<= 1;
if (mask == 0) {
mask = 1;
bitmap++;
}
}
}