project64/Source/Project64/UserInterface/Debugger/Symbols.cpp

502 lines
12 KiB
C++

/****************************************************************************
* *
* 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 "Symbols.h"
CSymbolTable::CSymbolTable(CDebuggerUI* debugger) :
m_Debugger(debugger),
m_NextSymbolId(0),
m_SymFileBuffer(NULL),
m_SymFileSize(0),
m_ParserToken(NULL),
m_ParserTokenLength(0),
m_ParserDelimeter(0),
m_SymFileParseBuffer(NULL),
m_bHaveFirstToken(false),
m_TokPos(NULL)
{
}
CSymbolTable::~CSymbolTable()
{
delete[] m_SymFileBuffer;
delete[] m_SymFileParseBuffer;
}
symbol_type_info_t CSymbolTable::m_SymbolTypes[] = {
{ SYM_CODE, "code", 1 },
{ SYM_DATA, "data", 1 },
{ SYM_U8, "u8", 1 },
{ SYM_U16, "u16", 2 },
{ SYM_U32, "u32", 4 },
{ SYM_U64, "u64", 8 },
{ SYM_S8, "s8", 1 },
{ SYM_S16, "s16", 2 },
{ SYM_S32, "s32", 4 },
{ SYM_S64, "s64", 8 },
{ SYM_FLOAT, "float", 4 },
{ SYM_DOUBLE, "double", 8 },
{ SYM_VECTOR2, "v2", 8 },
{ SYM_VECTOR3, "v3", 12 },
{ SYM_VECTOR4, "v4", 16 },
{ SYM_INVALID, NULL, 0 }
};
symbol_type_id_t CSymbolTable::GetTypeId(char* typeName)
{
const char* name;
for (int i = 0; (name = m_SymbolTypes[i].name) != NULL; i++)
{
if (strcmp(typeName, name) == 0)
{
return (symbol_type_id_t)i;
}
}
return SYM_INVALID;
}
const char* CSymbolTable::GetTypeName(int typeId)
{
if (typeId >= NUM_SYM_TYPES)
{
return NULL;
}
return m_SymbolTypes[typeId].name;
}
int CSymbolTable::GetTypeSize(int typeId)
{
if (typeId >= NUM_SYM_TYPES)
{
return NULL;
}
return m_SymbolTypes[typeId].size;
}
// Open symbols file for game and parse into list
CPath CSymbolTable::GetSymFilePath()
{
stdstr symFileName;
symFileName.Format("%s.sym", g_Settings->LoadStringVal(Game_GameName).c_str());
CPath symFilePath(g_Settings->LoadStringVal(Directory_NativeSave).c_str(), symFileName.c_str());
if (g_Settings->LoadBool(Setting_UniqueSaveDir))
{
symFilePath.AppendDirectory(g_Settings->LoadStringVal(Game_UniqueSaveDir).c_str());
}
symFilePath.NormalizePath(CPath(CPath::MODULE_DIRECTORY));
if (!symFilePath.DirectoryExists())
{
symFilePath.DirectoryCreate();
}
return symFilePath;
}
void CSymbolTable::ParserFetchToken(const char* delim)
{
if (!m_bHaveFirstToken)
{
m_TokPos = NULL;
m_ParserToken = strtok_s(m_SymFileParseBuffer, delim, &m_TokPos);
m_bHaveFirstToken = true;
}
else
{
m_ParserToken = strtok_s(NULL, delim, &m_TokPos);
}
if (m_ParserToken != NULL)
{
m_ParserTokenLength = strlen(m_ParserToken);
m_ParserDelimeter = m_SymFileBuffer[m_ParserToken - m_SymFileParseBuffer + m_ParserTokenLength];
}
else
{
m_ParserTokenLength = 0;
m_ParserDelimeter = '\0';
}
}
void CSymbolTable::Load()
{
CGuard guard(m_CS);
m_NextSymbolId = 0;
m_Symbols.clear();
if (g_Settings->LoadStringVal(Game_GameName).length() == 0)
{
MessageBox(NULL, L"Game must be loaded", L"Symbols", MB_ICONWARNING | MB_OK);
return;
}
CPath symFilePath = GetSymFilePath();
bool bOpened = m_SymFileHandle.Open(symFilePath, CFileBase::modeRead);
if (!bOpened)
{
return;
}
if (m_SymFileBuffer != NULL)
{
delete[] m_SymFileBuffer;
}
if (m_SymFileParseBuffer != NULL)
{
delete[] m_SymFileParseBuffer;
}
m_SymFileSize = m_SymFileHandle.GetLength();
m_SymFileBuffer = new char[m_SymFileSize + 1];
m_SymFileParseBuffer = new char[m_SymFileSize + 1];
m_SymFileHandle.Read(m_SymFileBuffer, m_SymFileSize);
m_SymFileHandle.Close();
m_SymFileBuffer[m_SymFileSize] = '\0';
strcpy(m_SymFileParseBuffer, m_SymFileBuffer);
m_bHaveFirstToken = false;
symbol_parse_error_t errorCode = ERR_SUCCESS;
int lineNumber = 1;
while (true)
{
uint32_t address = 0;
int type = 0;
char* name = NULL;
char* description = NULL;
// Address
ParserFetchToken(",\n\0");
if (m_ParserToken == NULL || m_ParserTokenLength == 0)
{
// Empty line @EOF
errorCode = ERR_SUCCESS;
break;
}
char* endptr;
address = (uint32_t)strtoull(m_ParserToken, &endptr, 16);
if (endptr == m_ParserToken)
{
errorCode = ERR_INVALID_ADDR;
break;
}
// Type
if (m_ParserDelimeter != ',')
{
errorCode = ERR_MISSING_FIELDS;
break;
}
ParserFetchToken(",\n\0");
type = GetTypeId(m_ParserToken);
if (type == -1)
{
errorCode = ERR_INVALID_TYPE;
break;
}
// Name
if (m_ParserDelimeter != ',')
{
errorCode = ERR_MISSING_FIELDS;
break;
}
ParserFetchToken(",\n\0");
name = m_ParserToken;
// Optional description
if (m_ParserDelimeter == ',')
{
ParserFetchToken("\n\0");
description = m_ParserToken;
}
// Add symbol object to the vector
AddSymbol(type, address, name, description);
if (m_ParserDelimeter == '\0')
{
errorCode = ERR_SUCCESS;
break;
}
lineNumber++;
}
delete[] m_SymFileParseBuffer;
m_SymFileParseBuffer = NULL;
delete[] m_SymFileBuffer;
m_SymFileBuffer = NULL;
switch (errorCode)
{
case ERR_SUCCESS:
break;
case ERR_INVALID_ADDR:
ParseErrorAlert("Invalid address", lineNumber);
break;
case ERR_INVALID_TYPE:
ParseErrorAlert("Invalid type", lineNumber);
break;
case ERR_INVALID_NAME:
ParseErrorAlert("Invalid name", lineNumber);
break;
case ERR_MISSING_FIELDS:
ParseErrorAlert("Missing required field(s)", lineNumber);
break;
}
}
void CSymbolTable::Save()
{
CGuard guard(m_CS);
m_SymFileHandle.Open(GetSymFilePath(), CFileBase::modeCreate | CFileBase::modeReadWrite);
m_SymFileHandle.SeekToBegin();
for (size_t i = 0; i < m_Symbols.size(); i++)
{
CSymbol& symbol = m_Symbols[i];
stdstr strLine = stdstr_f("%08X,%s,%s", symbol.m_Address, symbol.TypeName(), symbol.m_Name);
if (symbol.m_Description != NULL)
{
strLine += stdstr_f(",%s", symbol.m_Description);
}
strLine += "\n";
m_SymFileHandle.Write(strLine.c_str(), strLine.length());
}
m_SymFileHandle.SetEndOfFile();
m_SymFileHandle.Close();
}
void CSymbolTable::GetValueString(char* dst, CSymbol* symbol)
{
union
{
uint8_t u8;
int8_t s8;
uint16_t u16;
int16_t s16;
uint32_t u32;
int32_t s32;
uint64_t u64;
int64_t s64;
float f32;
double f64;
} value;
uint32_t address = symbol->m_Address;
float xyzw[4];
switch (symbol->m_Type)
{
case SYM_CODE:
case SYM_DATA:
sprintf(dst, "");
break;
case SYM_U8:
m_Debugger->DebugLoad_VAddr(address, value.u8);
sprintf(dst, "%u", value.u8);
break;
case SYM_U16:
m_Debugger->DebugLoad_VAddr(address, value.u16);
sprintf(dst, "%u", value.u16);
break;
case SYM_U32:
m_Debugger->DebugLoad_VAddr(address, value.u32);
sprintf(dst, "%u", value.u32);
break;
case SYM_U64:
m_Debugger->DebugLoad_VAddr(address, value.u64);
sprintf(dst, "%I64u", value.u64);
break;
case SYM_S8:
m_Debugger->DebugLoad_VAddr(address, value.s8);
sprintf(dst, "%ihh", value.s8);
break;
case SYM_S16:
m_Debugger->DebugLoad_VAddr(address, value.s16);
sprintf(dst, "%i", value.s16);
break;
case SYM_S32:
m_Debugger->DebugLoad_VAddr(address, value.s32);
sprintf(dst, "%i", value.s32);
break;
case SYM_S64:
m_Debugger->DebugLoad_VAddr(address, value.s64);
sprintf(dst, "%I64i", value.s64);
break;
case SYM_FLOAT:
m_Debugger->DebugLoad_VAddr(address, value.f32);
sprintf(dst, "%f", value.f32);
break;
case SYM_DOUBLE:
m_Debugger->DebugLoad_VAddr(address, value.f64);
sprintf(dst, "%f", value.f64);
break;
case SYM_VECTOR2:
for (int i = 0; i < 2; i++) {
m_Debugger->DebugLoad_VAddr(address + (i * sizeof(float)), value.f32);
xyzw[i] = value.f32;
}
sprintf(dst, "%f, %f", xyzw[0], xyzw[1]);
break;
case SYM_VECTOR3:
for (int i = 0; i < 3; i++) {
m_Debugger->DebugLoad_VAddr(address + (i * sizeof(float)), value.f32);
xyzw[i] = value.f32;
}
sprintf(dst, "%f, %f, %f", xyzw[0], xyzw[1], xyzw[2]);
break;
case SYM_VECTOR4:
for (int i = 0; i < 4; i++) {
m_Debugger->DebugLoad_VAddr(address + (i * sizeof(float)), value.f32);
xyzw[i] = value.f32;
}
sprintf(dst, "%f, %f, %f, %f", xyzw[0], xyzw[1], xyzw[2], xyzw[3]);
break;
default:
g_Notify->BreakPoint(__FILE__, __LINE__);
break;
}
}
void CSymbolTable::ParseErrorAlert(char* message, int lineNumber)
{
stdstr messageFormatted = stdstr_f("%s\nLine %d", message, lineNumber);
MessageBox(NULL, messageFormatted.ToUTF16().c_str(), L"Parse error", MB_OK | MB_ICONWARNING);
}
void CSymbolTable::Reset()
{
CGuard guard(m_CS);
m_Symbols.clear();
}
bool CSymbolTable::CmpSymbolAddresses(CSymbol& a, CSymbol& b)
{
return (a.m_Address < b.m_Address);
}
void CSymbolTable::AddSymbol(int type, uint32_t address, const char* name, const char* description)
{
CGuard guard(m_CS);
if (name == NULL || strlen(name) == 0)
{
return;
}
if (description == NULL || strlen(description) == 0)
{
description = NULL;
}
int id = m_NextSymbolId++;
CSymbol symbol = CSymbol(id, type, address, name, description);
m_Symbols.push_back(symbol);
sort(m_Symbols.begin(), m_Symbols.end(), CmpSymbolAddresses);
}
int CSymbolTable::GetCount()
{
CGuard guard(m_CS);
return m_Symbols.size();
}
bool CSymbolTable::GetSymbolByIndex(size_t index, CSymbol* symbol)
{
CGuard guard(m_CS);
if (index < 0 || index >= m_Symbols.size())
{
return false;
}
*symbol = m_Symbols[index];
return true;
}
bool CSymbolTable::GetSymbolByAddress(uint32_t address, CSymbol* symbol)
{
CGuard guard(m_CS);
for (size_t i = 0; i < m_Symbols.size(); i++)
{
if (m_Symbols[i].m_Address == address)
{
*symbol = m_Symbols[i];
return true;
}
}
return false;
}
bool CSymbolTable::GetSymbolByOverlappedAddress(uint32_t address, CSymbol* symbol)
{
CGuard guard(m_CS);
for (size_t i = 0; i < m_Symbols.size(); i++)
{
if (address >= m_Symbols[i].m_Address &&
address < m_Symbols[i].m_Address + m_Symbols[i].TypeSize())
{
*symbol = m_Symbols[i];
return true;
}
}
return false;
}
bool CSymbolTable::GetSymbolById(int id, CSymbol* symbol)
{
CGuard guard(m_CS);
for (size_t i = 0; i < m_Symbols.size(); i++)
{
if (m_Symbols[i].m_Id == id)
{
*symbol = m_Symbols[i];
return true;
}
}
return false;
}
bool CSymbolTable::RemoveSymbolById(int id)
{
CGuard guard(m_CS);
for (size_t i = 0; i < m_Symbols.size(); i++)
{
if (m_Symbols[i].m_Id == id)
{
m_Symbols.erase(m_Symbols.begin() + i);
return true;
}
}
return false;
}