void CheatEngine::addAddressesWithValue(const value_t& value, value_size_t size) {
  assert(size > 0);

  MEMORY_BASIC_INFORMATION info;
  for (address_t address = nullptr;
      VirtualQueryEx(m_process, address, &info, sizeof(info)) == sizeof(info);
      address = static_cast<unsigned char*>(info.BaseAddress) + info.RegionSize) {
    if (can_modify_page(info)) {
      chunk_t chunk(info.RegionSize);
      DWORD bytesRead;
      ReadProcessMemory(m_process, address, chunk.data(), info.RegionSize, &bytesRead);
      chunk.resize(bytesRead);

      offsets_t matches;
      for (chunk_t::size_type i = 0; i <= chunk.size() - size; ++i) {
        if (std::equal(chunk.cbegin() + i, chunk.cbegin() + i + size, value.cbegin())) {
          matches.push_back(i);
        }
      }
      if (!matches.empty()) {
        MemoryBlock block;
        block.matches = matches;
        block.baseAddress = info.BaseAddress;
        block.size = bytesRead;
        m_blocks.push_back(block);
      }
    }
  }
}
void CheatEngine::keepAddressesWithValue(const value_t& value, value_size_t size) {
  assert(!m_blocks.empty());

  for (auto blockIt = begin(m_blocks); blockIt != end(m_blocks);) {
    MEMORY_BASIC_INFORMATION info;
    const auto len = VirtualQueryEx(m_process, blockIt->baseAddress, &info, sizeof(info));
    assert(len == sizeof(info));

    if (can_modify_page(info)) {
      chunk_t chunk(blockIt->size);
      DWORD bytesRead;
      ReadProcessMemory(m_process, blockIt->baseAddress, chunk.data(), blockIt->size, &bytesRead);
      chunk.resize(bytesRead);
      blockIt->size = bytesRead;

      // from the offsets that matched before within that page, only keep the ones that still match
      offsets_t stillMatches;
      for (auto offsetIt = blockIt->matches.cbegin(); offsetIt != blockIt->matches.cend(); ++offsetIt) {
        assert(*offsetIt + size <= chunk.size());
        if (std::equal(chunk.cbegin() + *offsetIt, chunk.cbegin() + *offsetIt + size, value.cbegin())) {
          stillMatches.push_back(*offsetIt);
        }
      }
      blockIt->matches = stillMatches;

      // memory page no longer has matching addresses: drop it
      if (blockIt->matches.empty()) {
        blockIt = m_blocks.erase(blockIt);
      }
      else {
        ++blockIt;
      }
    }
    else {
      ++blockIt;
    }
  }
}