502 lines
12 KiB
C++
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;
|
|
}
|