/****************************************************************************
*                                                                           *
* Project64 - A Nintendo 64 emulator.                                       *
* http://www.pj64-emu.com/                                                  *
* Copyright (C) 2012 Project64. All rights reserved.                        *
*                                                                           *
* License:                                                                  *
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html                        *
*                                                                           *
****************************************************************************/
#include "stdafx.h"
#include "MemoryScanner.h"

CMixed::TypeNameEntry CMixed::TypeNames[] = {
    { "uint8",   ValueType_uint8 },
    { "int8",    ValueType_int8 },
    { "uint16",  ValueType_uint16 },
    { "int16",   ValueType_int16 },
    { "uint32",  ValueType_uint32 },
    { "int32",   ValueType_int32 },
    { "uint64",  ValueType_uint64 },
    { "int64",   ValueType_int64 },
    { "float",   ValueType_float },
    { "double",  ValueType_double },
    { "char",    ValueType_string },
    { "char",    ValueType_unkstring },
    { "char",    ValueType_unkstring },
    { NULL,      ValueType_invalid}
};

const char* CMixed::GetTypeName(void)
{
    switch (m_Type)
    {
    case ValueType_uint8:  return "uint8";
    case ValueType_int8:   return "int8";
    case ValueType_uint16: return "uint16";
    case ValueType_int16:  return "int16";
    case ValueType_uint32: return "uint32";
    case ValueType_int32:  return "int32";
    case ValueType_uint64: return "uint64";
    case ValueType_int64:  return "int64";
    case ValueType_float:  return "float";
    case ValueType_double: return "double";
    case ValueType_string:
    case ValueType_istring:
    case ValueType_unkstring:
        return "char";
    }

    return NULL;
}

ValueType CMixed::GetTypeFromString(const char* name, int* charArrayLength)
{
    for (int i = 0; TypeNames[i].name != NULL; i++)
    {
        if (strcmp(name, TypeNames[i].name) == 0)
        {
            *charArrayLength = 0;
            return TypeNames[i].type;
        }
    }

    if (sscanf(name, "char[%d]", charArrayLength) != 0)
    {
        return ValueType_string;
    }

    return ValueType_invalid;
}

int CMixed::GetTypeSize(void)
{
    switch (m_Type)
    {
    case ValueType_uint8:  return sizeof(uint8_t);
    case ValueType_int8:   return sizeof(int8_t);
    case ValueType_uint16: return sizeof(uint16_t);
    case ValueType_int16:  return sizeof(int16_t);
    case ValueType_uint32: return sizeof(uint32_t);
    case ValueType_int32:  return sizeof(int32_t);
    case ValueType_uint64: return sizeof(uint64_t);
    case ValueType_int64:  return sizeof(int64_t);
    case ValueType_float:  return sizeof(float);
    case ValueType_double: return sizeof(double);
    case ValueType_string:
    case ValueType_istring:
    case ValueType_unkstring:
        return m_StrLength;
    default:
        return 0;
    }
}

bool CMixed::IsStringType(void)
{
    switch (m_Type)
    {
    case ValueType_string:
    case ValueType_istring:
    case ValueType_unkstring:
        return true;
    }

    return false;
}

int CMixed::ToString(char* buffer, bool bHex, size_t size)
{
    if (bHex)
    {
        switch (m_Type)
        {
        case ValueType_uint8:
        case ValueType_int8:
            return snprintf(buffer, size, "0x%02X", m_Value._uint8);
        case ValueType_uint16:
        case ValueType_int16:
            return snprintf(buffer, size, "0x%04X", m_Value._uint16);
        case ValueType_uint32:
        case ValueType_int32:
        case ValueType_float:
            return snprintf(buffer, size, "0x%08X", m_Value._uint32);
        case ValueType_uint64:
        case ValueType_int64:
        case ValueType_double:
            return snprintf(buffer, size, "0x%016llX", m_Value._uint64);
        default:
            return snprintf(buffer, size, "?");
        }
    }

    switch (m_Type)
    {
    case ValueType_uint8:  return snprintf(buffer, size, "%d", m_Value._uint8);
    case ValueType_int8:   return snprintf(buffer, size, "%d", m_Value._sint8);
    case ValueType_uint16: return snprintf(buffer, size, "%d", m_Value._uint16);
    case ValueType_int16:  return snprintf(buffer, size, "%d", m_Value._sint16);
    case ValueType_uint32: return snprintf(buffer, size, "%lu", m_Value._uint32);
    case ValueType_int32:  return snprintf(buffer, size, "%d", m_Value._sint32);
    case ValueType_uint64: return snprintf(buffer, size, "%llu", m_Value._uint64);
    case ValueType_int64:  return snprintf(buffer, size, "%lld", m_Value._sint64);
    case ValueType_float:  return snprintf(buffer, size, "%f", m_Value._float);
    case ValueType_double: return snprintf(buffer, size, "%f", m_Value._double);
    default: return snprintf(buffer, size, "?");
    }
}

CScanResult::CScanResult(AddressType addressType, DisplayFormat displayFormat) :
    m_AddressType(addressType),
    m_Address(0),
    m_DisplayFormat(displayFormat),
    m_bSelected(false),
    m_Description(NULL)
{
}

CScanResult::~CScanResult(void)
{
}

void CScanResult::SetDescription(const char* str)
{
    if (m_Description != NULL)
    {
        free(m_Description);
    }

    size_t len = strlen(str);
    m_Description = (char*)malloc(len + 1);
    strcpy(m_Description, str);
    m_Description[len] = '\0';
}

void CScanResult::DeleteDescription(void)
{
    if (m_Description != NULL)
    {
        free(m_Description);
        m_Description = NULL;
    }
}

const char* CScanResult::GetDescription(void)
{
    if (m_Description == NULL)
    {
        return "";
    }
    return m_Description;
}

int CScanResult::GetValueString(char *buffer, size_t size)
{
    bool bHex = (m_DisplayFormat == DisplayHex);
    return ToString(buffer, bHex, size);
}

bool CScanResult::GetMemoryValue(CMixed* v)
{
    if (g_MMU == NULL)
    {
        return false;
    }

    uint32_t paddr = m_Address & 0x1FFFFFFF;

    if (!CMemoryScanner::PAddrValid(paddr))
    {
        return false;
    }

    uint8_t* mem = CMemoryScanner::GetMemoryPool(paddr);

    uint64_t raw64 = 0;

    if (GetTypeSize() == 8)
    {
        raw64 = ((uint64_t)*(uint32_t*)&mem[paddr] << 32) | *(uint32_t*)&mem[paddr + 4];
    }

    switch (m_Type)
    {
    case ValueType_uint8:  v->Set(*(uint8_t*) &mem[paddr ^ 3]); break;
    case ValueType_int8:   v->Set(*(int8_t*)  &mem[paddr ^ 3]); break;
    case ValueType_uint16: v->Set(*(uint16_t*)&mem[paddr ^ 2]); break;
    case ValueType_int16:  v->Set(*(int16_t*) &mem[paddr ^ 2]); break;
    case ValueType_uint32: v->Set(*(uint32_t*)&mem[paddr]); break;
    case ValueType_int32:  v->Set(*(int32_t*)&mem[paddr]); break;
    case ValueType_uint64: v->Set(*(uint64_t*)&raw64); break;
    case ValueType_int64:  v->Set(*(int64_t*)&raw64); break;
    case ValueType_float:  v->Set(*(float*)&mem[paddr]); break;
    case ValueType_double: v->Set(*(double*)&raw64); break;
    default: return false; // (primitives only)
    }

    return true;
}

int CScanResult::GetMemoryValueString(char* buffer, size_t size, bool bIgnoreHex)
{
    if (g_MMU == NULL)
    {
        sprintf(buffer, "?");
        return 1;
    }
    
    bool bHex = (m_DisplayFormat == DisplayHex) && !bIgnoreHex;

    uint32_t paddr = m_Address & 0x1FFFFFFF;

    if (!CMemoryScanner::PAddrValid(paddr))
    {
        return sprintf(buffer, "?");
    }

    uint8_t* mem = CMemoryScanner::GetMemoryPool(paddr);

    if (m_Type == ValueType_istring ||
        m_Type == ValueType_string  ||
        m_Type == ValueType_unkstring)
    {
        if (bHex)
        {
            char* out = buffer;

            for (int i = 0; i < m_StrLength; i++)
            {
                uint32_t ipaddr = (paddr + i) ^ 3;
                if (i != 0) out += sprintf(out, " ");
                out += sprintf(out, "%02X", mem[ipaddr]);
            }
            *out = '\0';
            return out - buffer;
        }
        else
        {
            for (int i = 0; i < m_StrLength; i++)
            {
                uint32_t ipaddr = (paddr + i) ^ 3;
                buffer[i] = mem[ipaddr];
            }
            buffer[m_StrLength] = '\0';
            return m_StrLength;
        }
    }

    CMixed memVal;
    if (!GetMemoryValue(&memVal))
    {
        return 0;
    }

    return memVal.ToString(buffer, bHex, size);
}

int CScanResult::GetAddressString(char *buffer)
{
    return sprintf(buffer, "0x%08X", m_Address);
}

uint32_t CScanResult::GetVirtualAddress(void)
{
    if (m_AddressType == AddressType_Virtual)
    {
        return m_Address;
    }
    else
    {
        // convert physical to virtual kseg0
        return (m_Address | 0x80000000);
    }
}

bool CScanResult::SetMemoryValueFromString(const char* str)
{
    if (g_MMU == NULL)
    {
        //sprintf(buffer, "?");
        return false;
    }

    bool bHex = (m_DisplayFormat == DisplayHex);

    uint32_t paddr = m_Address & 0x1FFFFFFF;

    if (!CMemoryScanner::PAddrValid(paddr))
    {
        return false;
    }

    uint8_t* mem = CMemoryScanner::GetMemoryPool(m_Address & 0x1FFFFFFF);

    char* endptr;
    uint64_t intVal = strtoull(str, &endptr, 0);
    double doubleVal = strtod(str, &endptr);

    switch (m_Type)
    {
    case ValueType_uint8:
    case ValueType_int8:
        mem[paddr ^ 3] = intVal & 0xFF;
        break;
    case ValueType_uint16:
    case ValueType_int16:
        *(uint16_t*)&mem[paddr ^ 2] = intVal & 0xFFFF;
        break;
    case ValueType_uint32:
    case ValueType_int32:
        *(uint32_t*)&mem[paddr] = intVal & 0xFFFFFFFF;
        break;
    case ValueType_uint64:
    case ValueType_int64:
        *(uint64_t*)&mem[paddr] = (intVal << 32) | (intVal >> 32);
        break;
    case ValueType_float:
        if (bHex)
        {
            *(uint32_t*)&mem[paddr] = intVal & 0xFFFFFFFF;
            break;
        }
        *(float*)&mem[paddr] = (float)doubleVal;
        break;
    case ValueType_double:
        if (bHex)
        {
            *(uint64_t*)&mem[paddr] = (intVal << 32) | (intVal >> 32);
            break;
        }
        intVal = *(uint64_t*)&doubleVal;
        *(uint64_t*)&mem[paddr] = (intVal << 32) | (intVal >> 32);
        break;
    case ValueType_string:
    case ValueType_istring:
    case ValueType_unkstring:
        if (bHex)
        {
            int size = CMemoryScanner::ParseHexString(NULL, str);
            if (size == 0)
            {
                return false;
            }

            char* buff = new char[size];
            CMemoryScanner::ParseHexString(buff, str);

            for (int i = 0; i < m_StrLength; i++)
            {
                uint32_t ipaddr = (paddr + i) ^ 3;
                mem[ipaddr] = buff[i];
            }

            delete[] buff;
        }
        else
        {
            for (int i = 0; i < m_StrLength; i++)
            {
                uint32_t ipaddr = (paddr + i) ^ 3;
                mem[ipaddr] = str[i];
            }
        }
        break;
    }

    return true;
}

bool CScanResult::SetAddressSafe(uint32_t address)
{
    if (!g_MMU || !g_Rom)
    {
        return false;
    }

    uint32_t ramSize = g_MMU->RdramSize();
    uint32_t romSize = g_Rom->GetRomSize();

    uint32_t paddrStart = address & 0x1FFFFFFF;
    uint32_t paddrEnd = (paddrStart + GetTypeSize()) - 1;

    if (m_AddressType == AddressType_Virtual)
    {
        if (!CMemoryScanner::AddrCheck(address, 0x80000000, 0xBFFFFFFF))
        {
            return false;
        }
    }

    if (!CMemoryScanner::PAddrRangeValid(paddrStart, paddrEnd))
    {
        return false;
    }

    if (!CMemoryScanner::RangeCheck(paddrStart, paddrEnd, 0x00000000, ramSize - 1) &&
        !CMemoryScanner::RangeCheck(paddrStart, paddrEnd, 0x10000000, 0x10000000 + romSize - 1) &&
        !CMemoryScanner::RangeCheck(paddrStart, paddrEnd, 0x04000000, 0x04001FFF))
    {
        return false;
    }

    m_Address = address;

    return true;
}

bool CScanResult::SetStrLengthSafe(int length)
{
    if (!g_MMU || !g_Rom)
    {
        return false;
    }

    uint32_t ramSize = g_MMU->RdramSize();
    uint32_t romSize = g_Rom->GetRomSize();

    uint32_t paddrStart = m_Address & 0x1FFFFFFF;
    uint32_t paddrEnd = (paddrStart + length) - 1;

    if (!CMemoryScanner::RangeCheck(paddrStart, paddrEnd, 0x00000000, ramSize - 1) &&
        !CMemoryScanner::RangeCheck(paddrStart, paddrEnd, 0x10000000, 0x10000000 + romSize - 1) &&
        !CMemoryScanner::RangeCheck(paddrStart, paddrEnd, 0x04000000, 0x04001FFF))
    {
        return false;
    }

    m_StrLength = length;

    return true;
}

//bool CScanResult::IsSelected(void)
//{
//    return m_bSelected;
//}
//
//void CScanResult::SetSelected(bool bSelected)
//{
//    m_bSelected = bSelected;
//}


/*********************/

CMemoryScanner::CMemoryScanner(void) :
    m_DidFirstScan(false),
    m_ValueType(ValueType_uint8),
    m_StringValueLength(false),
    m_bDataTypePrimitive(true),
    m_SearchType(SearchType_ExactValue),
    m_AddressType(AddressType_Virtual),
    m_VAddrBits(0x80000000),
    m_Memory(NULL)
{
    m_Value._uint64 = 0;
    SetAddressRange(0x80000000, 0x803FFFFF);
}

bool CMemoryScanner::RangeCheck(uint32_t addrStart, uint32_t addrEnd, uint32_t rangeStart, uint32_t rangeEnd)
{
    return (addrStart <= addrEnd) && (addrStart >= rangeStart) && (addrEnd <= rangeEnd);
}

bool CMemoryScanner::AddrCheck(uint32_t addr, uint32_t rangeStart, uint32_t rangeEnd)
{
    return (addr >= rangeStart) && (addr <= rangeEnd);
}

bool CMemoryScanner::PAddrValid(uint32_t physAddr)
{
    if (g_MMU == NULL || g_Rom == NULL)
    {
        g_Notify->BreakPoint(__FILE__, __LINE__);
    }

    uint32_t ramSize = g_MMU->RdramSize();
    uint32_t romSize = g_Rom->GetRomSize();

    return (AddrCheck(physAddr, 0x00000000, 0x00000000 + ramSize - 1) ||
        AddrCheck(physAddr, 0x10000000, 0x10000000 + romSize - 1) ||
        AddrCheck(physAddr, 0x04000000, 0x04001FFF));
}

bool CMemoryScanner::PAddrRangeValid(uint32_t physAddrStart, uint32_t physAddrEnd)
{
    return (RangeCheck(physAddrStart, physAddrEnd, 0x00000000, g_MMU->RdramSize()) ||
        RangeCheck(physAddrStart, physAddrEnd, 0x04000000, 0x04001FFF) ||
        RangeCheck(physAddrStart, physAddrEnd, 0x10000000, 0x15FFFFFF));
}

void CMemoryScanner::SetAddressType(AddressType addressType)
{
    m_AddressType = addressType;
}

void CMemoryScanner::Reset(void)
{
    m_DidFirstScan = false;

    m_ValueType = ValueType_uint8;
    m_SearchType = SearchType_ExactValue;
    
    m_Results.clear();
}

bool CMemoryScanner::SetAddressRange(uint32_t startAddress, uint32_t endAddress)
{
    if (!g_MMU || !g_Rom)
    {
        return false;
    }

    if(m_DidFirstScan)
    {
        return false;
    }

    if (m_AddressType == AddressType_Virtual)
    {
        m_VAddrBits = startAddress & 0xE0000000;

        // don't allow TLB
        if (!RangeCheck(startAddress, endAddress, 0x80000000, 0xBFFFFFFF))
        {
            return false;
        }

        // use physical addresses internally
        startAddress = startAddress & 0x1FFFFFFF;
        endAddress = endAddress & 0x1FFFFFFF;
    }
    else
    {
        m_VAddrBits = 0;
    }

    if (RangeCheck(startAddress, endAddress, 0x00000000, 0x007FFFFF))
    {
        if (endAddress >= g_MMU->RdramSize())
        {
            return false;
        }

        m_Memory = g_MMU->Rdram();
    }
    else if (RangeCheck(startAddress, endAddress, 0x04000000, 0x04001FFF))
    {
        m_Memory = g_MMU->Rdram();
    }
    else if (RangeCheck(startAddress, endAddress, 0x10000000, 0x10FFFFFF))
    {
        if ((endAddress - 0x10000000) >= g_Rom->GetRomSize())
        {
            return false;
        }

        m_Memory = (g_Rom->GetRomAddress() - 0x10000000);
    }
    else
    {
        return false; // invalid range
    }
    
    m_Memory = GetMemoryPool(startAddress);

    m_RangeStartAddress = startAddress;
    m_RangeEndAddress = endAddress;
    return true;
}

uint8_t* CMemoryScanner::GetMemoryPool(uint32_t physAddr)
{
    if (!g_MMU || !g_Rom)
    {
        return NULL;
    }

    if ((physAddr >= 0x00000000 && physAddr < g_MMU->RdramSize()) ||
        (physAddr >= 0x04000000 && physAddr <= 0x04001FFF))
    {
        return g_MMU->Rdram();
    }
    else if (physAddr >= 0x10000000 && physAddr <= 0x18000000)
    {
        return (g_Rom->GetRomAddress() - 0x10000000);
    }
    else
    {
        return NULL;
    }

}

bool CMemoryScanner::SetValueType(ValueType type)
{
    if(m_DidFirstScan)
    {
        return false;
    }
    
    switch(type)
    {
    case ValueType_string:
    case ValueType_istring:
    case ValueType_unkstring:
        m_bDataTypePrimitive = false;
        break;
    default:
        m_bDataTypePrimitive = true;
        break;
    }
    
    m_ValueType = type;
    return true;
}

void CMemoryScanner::SetStringValueLength(int length)
{
    m_StringValueLength = length;
}

bool CMemoryScanner::SetSearchType(SearchType searchType)
{
    if(!m_bDataTypePrimitive)
    {
        return false;
    }
    
    switch(searchType)
    {
    case SearchType_UnknownValue:
    case SearchType_JalTo:
        if (m_DidFirstScan)
        {
            return false;
        }
        break;
    case SearchType_ChangedValue:
    case SearchType_UnchangedValue:
    case SearchType_IncreasedValue:
    case SearchType_DecreasedValue:
        if(!m_DidFirstScan)
        {
            return false;
        }
        break;
    }
    
    m_SearchType = searchType;
    return true;
}

bool CMemoryScanner::DidFirstScan(void)
{
    return m_DidFirstScan;
}

size_t CMemoryScanner::GetNumResults(void)
{
    return m_Results.size();
}

CScanResult* CMemoryScanner::GetResult(size_t index)
{
    if (index >= m_Results.size())
    {
        return NULL;
    }

    return &m_Results[index];
}    

void CMemoryScanner::RemoveResult(size_t index)
{
    if (index >= m_Results.size())
    {
        return;
    }

    m_Results.erase(m_Results.begin() + index);
}

// scan for text or hex array
void CMemoryScanner::FirstScanLoopString(DisplayFormat resultDisplayFormat)
{
    int length = m_StringValueLength;

    uint32_t startAddr = m_RangeStartAddress;
    uint32_t endAddr = (m_RangeEndAddress - length) + 1;

    CScanResult result(m_AddressType, resultDisplayFormat);
    result.SetStrLength(length);

    for (uint32_t addr = startAddr; addr <= endAddr; addr++)
    {
        for (int i = 0; i < length; i++)
        {
            uint32_t leAddr = (addr + i) ^ 3;
            if ((uint8_t)m_Value._string[i] != m_Memory[leAddr])
            {
                goto next_addr;
            }
        }

        result.m_Address = addr | m_VAddrBits;
        result.Set((const wchar_t*)NULL);
        m_Results.push_back(result);
    next_addr:;
    }
}

// scan for text (case-insensitive)
void CMemoryScanner::FirstScanLoopIString(DisplayFormat resultDisplayFormat)
{
    int length = m_StringValueLength;

    uint32_t startAddr = m_RangeStartAddress;
    uint32_t endAddr = m_RangeEndAddress - length;

    CScanResult result(m_AddressType, resultDisplayFormat);
    result.SetStrLength(length);

    for (uint32_t addr = startAddr; addr <= endAddr; addr++)
    {
        for (int i = 0; i < length; i++)
        {
            uint32_t leAddr = (addr + i) ^ 3;
            if (toupper((uint8_t)m_Value._string[i]) != toupper(m_Memory[leAddr]))
            {
                goto next_addr;
            }
        }

        result.m_Address = addr | m_VAddrBits;
        result.Set((const wchar_t*)NULL);
        m_Results.push_back(result);
    next_addr:;
    }
}

// scan for text of unknown single-byte encoding
void CMemoryScanner::FirstScanLoopUnkString(void)
{
    const char* str = stdstr().FromUTF16(m_Value._string).c_str();
    int length = m_StringValueLength;
    
    uint32_t startAddr = m_RangeStartAddress;
    uint32_t endAddr = m_RangeEndAddress - length;

    CScanResult result(m_AddressType, DisplayHex);
    result.SetStrLength(length);

    for (uint32_t addr = startAddr; addr <= endAddr; addr++)
    {
        uint32_t leAddr = addr ^ 3;

        char numberDiff = 0, lowercaseDiff = 0, uppercaseDiff = 0;
        bool haveNumberDiff = false, haveLowercaseDiff = false, haveUppercaseDiff = false;

        for (int i = 0; i < length; i++)
        {
            leAddr = (addr + i) ^ 3;

            if (!isalnum(str[i]))
            {
                continue;
            }

            if (str[i] >= 'a' && str[i] <= 'z')
            {
                if (!haveLowercaseDiff)
                {
                    lowercaseDiff = str[i] - m_Memory[leAddr];
                    haveLowercaseDiff = true;
                }
                else if (m_Memory[leAddr] + lowercaseDiff != str[i])
                {
                    goto next_addr;
                }
            }
            else if (str[i] >= 'A' && str[i] <= 'Z')
            {
                if (!haveUppercaseDiff)
                {
                    uppercaseDiff = str[i] - m_Memory[leAddr];
                    haveUppercaseDiff = true;
                }
                else if (m_Memory[leAddr] + uppercaseDiff != str[i])
                {
                    goto next_addr;
                }
            }
            else if (str[i] >= '0' && str[i] <= '9')
            {
                if (!haveNumberDiff)
                {
                    numberDiff = str[i] - m_Memory[leAddr];
                    haveNumberDiff = true;
                }
                else if (m_Memory[leAddr] + numberDiff != str[i])
                {
                    goto next_addr;
                }
            }
        }

        result.m_Address = addr | m_VAddrBits;
        result.Set((const wchar_t*)NULL);
        m_Results.push_back(result);

    next_addr:;
    }
}

#define _FirstScanLoopPrimitive(T, Compare, resDisplayFormat) FirstScanLoopPrimitive<T>(Compare<T>, resDisplayFormat)
#define _FirstScanLoopPrimitive64(T, Compare, resDisplayFormat) FirstScanLoopPrimitive64<T>(Compare<T>, resDisplayFormat)

#define FIRST_SCAN_PRIMITIVES(CompareFunc) \
    switch(m_ValueType)                    \
    {                                      \
    case ValueType_uint8:  _FirstScanLoopPrimitive(uint8_t,  CompareFunc, resDisplayFormat); break; \
    case ValueType_int8:   _FirstScanLoopPrimitive(int8_t,   CompareFunc, resDisplayFormat); break; \
    case ValueType_uint16: _FirstScanLoopPrimitive(uint16_t, CompareFunc, resDisplayFormat); break; \
    case ValueType_int16:  _FirstScanLoopPrimitive(int16_t,  CompareFunc, resDisplayFormat); break; \
    case ValueType_uint32: _FirstScanLoopPrimitive(uint32_t, CompareFunc, resDisplayFormat); break; \
    case ValueType_int32:  _FirstScanLoopPrimitive(int32_t,  CompareFunc, resDisplayFormat); break; \
    case ValueType_uint64: _FirstScanLoopPrimitive64(uint64_t, CompareFunc, resDisplayFormat); break; \
    case ValueType_int64:  _FirstScanLoopPrimitive64(int64_t,  CompareFunc, resDisplayFormat); break; \
    case ValueType_float:  _FirstScanLoopPrimitive(float,    CompareFunc, resDisplayFormat); break; \
    case ValueType_double: _FirstScanLoopPrimitive64(double,   CompareFunc, resDisplayFormat); break; \
    }

bool CMemoryScanner::FirstScan(DisplayFormat resDisplayFormat)
{
    if (!g_MMU)
    {
        return false;
    }

    if (m_bDataTypePrimitive)
    {
        switch (m_SearchType)
        {
        case SearchType_UnknownValue:
            FIRST_SCAN_PRIMITIVES(NoCompare);
            break;
        case SearchType_ExactValue:
            FIRST_SCAN_PRIMITIVES(CompareEqual);
            break;
        case SearchType_JalTo:
            m_Value._uint32 = 0x0C000000 | ((m_Value._uint32 & 0x3FFFFFF) >> 2);
            FIRST_SCAN_PRIMITIVES(CompareEqual);
            break;
        case SearchType_LessThanValue:
            FIRST_SCAN_PRIMITIVES(CompareLessThan);
            break;
        case SearchType_GreaterThanValue:
            FIRST_SCAN_PRIMITIVES(CompareGreaterThan);
            break;
        case SearchType_LessThanOrEqualToValue:
            FIRST_SCAN_PRIMITIVES(CompareLessThanOrEqual);
            break;
        case SearchType_GreaterThanOrEqualToValue:
            FIRST_SCAN_PRIMITIVES(CompareGreaterThanOrEqual);
            break;
        }
    }
    else
    {
        switch (m_ValueType)
        {
        case ValueType_string:
            FirstScanLoopString(resDisplayFormat);
            break;
        case ValueType_istring:
            FirstScanLoopIString(resDisplayFormat);
            break;
        case ValueType_unkstring:
            FirstScanLoopUnkString();
            break;
        }
    }

    m_DidFirstScan = true;
    return true;
}

#define _NextScanLoopPrimitive(T, Compare) NextScanLoopPrimitive<T>(Compare<T>)
#define _NextScanLoopPrimitiveResults(T, Compare) NextScanLoopPrimitiveResults<T>(Compare<T>)
#define _NextScanLoopPrimitive64(T, Compare) NextScanLoopPrimitive64<T>(Compare<T>)
#define _NextScanLoopPrimitiveResults64(T, Compare) NextScanLoopPrimitiveResults64<T>(Compare<T>)

// compare result's current value in memory against m_Value
#define NEXT_SCAN_PRIMITIVES_AGAINST_VALUE(CompareFunc) \
    switch(m_ValueType)                                 \
    {                                                   \
    case ValueType_uint8:  _NextScanLoopPrimitive(uint8_t,  CompareFunc); break; \
    case ValueType_int8:   _NextScanLoopPrimitive(int8_t,   CompareFunc); break; \
    case ValueType_uint16: _NextScanLoopPrimitive(uint16_t, CompareFunc); break; \
    case ValueType_int16:  _NextScanLoopPrimitive(int16_t,  CompareFunc); break; \
    case ValueType_uint32: _NextScanLoopPrimitive(uint32_t, CompareFunc); break; \
    case ValueType_int32:  _NextScanLoopPrimitive(int32_t,  CompareFunc); break; \
    case ValueType_uint64: _NextScanLoopPrimitive64(uint64_t, CompareFunc); break; \
    case ValueType_int64:  _NextScanLoopPrimitive64(int64_t,  CompareFunc); break; \
    case ValueType_float:  _NextScanLoopPrimitive(float,    CompareFunc); break; \
    case ValueType_double: _NextScanLoopPrimitive64(double,   CompareFunc); break; \
    }

// compare result's current value in memory against result's old value
#define NEXT_SCAN_PRIMITIVES_AGAINST_RESULTS(CompareFunc) \
    switch(m_ValueType)                                   \
    {                                                     \
    case ValueType_uint8:  _NextScanLoopPrimitiveResults(uint8_t,  CompareFunc); break; \
    case ValueType_int8:   _NextScanLoopPrimitiveResults(int8_t,   CompareFunc); break; \
    case ValueType_uint16: _NextScanLoopPrimitiveResults(uint16_t, CompareFunc); break; \
    case ValueType_int16:  _NextScanLoopPrimitiveResults(int16_t,  CompareFunc); break; \
    case ValueType_uint32: _NextScanLoopPrimitiveResults(uint32_t, CompareFunc); break; \
    case ValueType_int32:  _NextScanLoopPrimitiveResults(int32_t,  CompareFunc); break; \
    case ValueType_uint64: _NextScanLoopPrimitiveResults64(uint64_t, CompareFunc); break; \
    case ValueType_int64:  _NextScanLoopPrimitiveResults64(int64_t,  CompareFunc); break; \
    case ValueType_float:  _NextScanLoopPrimitiveResults(float,    CompareFunc); break; \
    case ValueType_double: _NextScanLoopPrimitiveResults64(double,   CompareFunc); break; \
    }

bool CMemoryScanner::NextScan()
{
    if (!g_MMU || !m_DidFirstScan || !m_bDataTypePrimitive)
    {
        // NextScan does not support complex data
        return false;
    }
    
    switch(m_SearchType)
    {
    case SearchType_ExactValue:
        NEXT_SCAN_PRIMITIVES_AGAINST_VALUE(CompareEqual);
        break;
    case SearchType_LessThanValue:
        NEXT_SCAN_PRIMITIVES_AGAINST_VALUE(CompareLessThan);
        break;
    case SearchType_GreaterThanValue:
        NEXT_SCAN_PRIMITIVES_AGAINST_VALUE(CompareGreaterThan);
        break;
    case SearchType_LessThanOrEqualToValue:
        NEXT_SCAN_PRIMITIVES_AGAINST_VALUE(CompareLessThanOrEqual);
        break;
    case SearchType_GreaterThanOrEqualToValue:
        NEXT_SCAN_PRIMITIVES_AGAINST_VALUE(CompareGreaterThanOrEqual);
        break;

    case SearchType_ChangedValue:
        NEXT_SCAN_PRIMITIVES_AGAINST_RESULTS(CompareNotEqual);
        break;
    case SearchType_UnchangedValue:
        NEXT_SCAN_PRIMITIVES_AGAINST_RESULTS(CompareEqual);
        break;
    case SearchType_IncreasedValue:
        NEXT_SCAN_PRIMITIVES_AGAINST_RESULTS(CompareGreaterThan);
        break;
    case SearchType_DecreasedValue:
        NEXT_SCAN_PRIMITIVES_AGAINST_RESULTS(CompareLessThan);
        break;
    }
    
    return true;
}


int CMemoryScanner::HexDigitVal(char c)
{
    if (c >= '0' && c <= '9') return (c - '0');
    if (c >= 'A' && c <= 'F') return (c - 'A') + 0x0A;
    if (c >= 'a' && c <= 'f') return (c - 'a') + 0x0A;
    return 0;
}

int CMemoryScanner::ParseHexString(char *dst, const char* src)
{
    bool bHiNibble = true;
    uint8_t curByte = 0;
    int size = 0;

    for (int i = 0; src[i] != '\0'; i++)
    {
        if (!isxdigit(src[i]))
        {
            if (!bHiNibble)
            {
                return 0;
            }

            if (isspace(src[i]))
            {
                continue;
            }

            return 0;
        }

        if (bHiNibble)
        {
            curByte = (HexDigitVal(src[i]) << 4) & 0xF0;
            bHiNibble = false;
        }
        else
        {
            curByte |= HexDigitVal(src[i]);
            if (dst != NULL)
            {
                dst[size] = curByte;
            }
            size++;
            bHiNibble = true;
        }
    }

    if (!bHiNibble)
    {
        return 0;
    }

    return size;
}
