Skip to content

Commit

Permalink
support for indirect code notes via overflow math (#1066)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jamiras authored Feb 10, 2024
1 parent 9255dd0 commit 2a7c61b
Show file tree
Hide file tree
Showing 5 changed files with 556 additions and 77 deletions.
175 changes: 113 additions & 62 deletions src/data/models/CodeNotesModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,20 +278,12 @@ void CodeNotesModel::ExtractSize(CodeNote& pNote)
}
}

static unsigned ReadPointer(const ra::data::context::EmulatorContext& pEmulatorContext,
ra::ByteAddress nPointerAddress, MemSize nSize)
static ra::ByteAddress ConvertPointer(ra::ByteAddress nAddress)
{
auto nAddress = pEmulatorContext.ReadMemory(nPointerAddress, nSize);

// assume anything annotated as a 32-bit pointer is providing a real (non-translated) address and
// attempt to do the translation ourself.
if (nSize == MemSize::ThirtyTwoBit)
{
const auto& pConsoleContext = ra::services::ServiceLocator::Get<ra::data::context::ConsoleContext>();
const auto nConvertedAddress = pConsoleContext.ByteAddressFromRealAddress(nAddress);
if (nConvertedAddress != 0xFFFFFFFF)
nAddress = nConvertedAddress;
}
const auto& pConsoleContext = ra::services::ServiceLocator::Get<ra::data::context::ConsoleContext>();
const auto nConvertedAddress = pConsoleContext.ByteAddressFromRealAddress(nAddress);
if (nConvertedAddress != 0xFFFFFFFF)
nAddress = nConvertedAddress;

return nAddress;
}
Expand Down Expand Up @@ -322,9 +314,9 @@ void CodeNotesModel::AddCodeNote(ra::ByteAddress nAddress, const std::string& sA
try
{
if (sNextNote.length() > 2 && sNextNote.at(1) == 'x')
offsetNote.Offset = gsl::narrow_cast<int>(std::wcstol(sNextNote.c_str() + 2, &pEnd, 16));
offsetNote.Offset = gsl::narrow_cast<int>(std::wcstoll(sNextNote.c_str() + 2, &pEnd, 16));
else
offsetNote.Offset = gsl::narrow_cast<int>(std::wcstol(sNextNote.c_str(), &pEnd, 10));
offsetNote.Offset = gsl::narrow_cast<int>(std::wcstoll(sNextNote.c_str(), &pEnd, 10));
}
catch (const std::exception&)
{
Expand Down Expand Up @@ -367,9 +359,33 @@ void CodeNotesModel::AddCodeNote(ra::ByteAddress nAddress, const std::string& sA
pointerNote.Bytes = 4;
}

// capture the initial value of the pointer
const auto& pEmulatorContext = ra::services::ServiceLocator::Get<ra::data::context::EmulatorContext>();
const auto nPointerValue = ReadPointer(pEmulatorContext, nAddress, pointerNote.MemSize);

// assume anything annotated as a 32-bit pointer will read a real (non-translated) address and
// flag it to be converted to an RA address when evaluating indirect notes in DoFrame()
if (pointerNote.MemSize == MemSize::ThirtyTwoBit ||
pointerNote.MemSize == MemSize::ThirtyTwoBitBigEndian)
{
const auto nMaxAddress = pEmulatorContext.TotalMemorySize();

pointerData->OffsetType = OffsetType::Converted;

// if any offset exceeds the available memory for the system, assume the user is leveraging
// overflow math instead of masking, and don't attempt to translate the addresses.
for (const auto& pNote : pointerData->OffsetNotes)
{
if (ra::to_unsigned(pNote.Offset) >= nMaxAddress)
{
pointerData->OffsetType = OffsetType::Overflow;
break;
}
}
}

// capture the initial value of the pointer
pointerData->RawPointerValue = pEmulatorContext.ReadMemory(nAddress, pointerNote.MemSize);
const auto nPointerValue = (pointerData->OffsetType == OffsetType::Converted)
? ConvertPointer(pointerData->RawPointerValue) : pointerData->RawPointerValue;
pointerData->PointerValue = nPointerValue;
pointerNote.PointerData = std::move(pointerData);

Expand Down Expand Up @@ -451,14 +467,18 @@ ra::ByteAddress CodeNotesModel::FindCodeNoteStart(ra::ByteAddress nAddress) cons
if (pCodeNote.second.PointerData == nullptr)
continue;

if (nAddress >= pCodeNote.second.PointerData->PointerValue &&
nAddress < pCodeNote.second.PointerData->PointerValue + pCodeNote.second.PointerData->OffsetRange)
const auto nPointerValue = pCodeNote.second.PointerData->PointerValue;
const auto nConvertedPointerValue = (pCodeNote.second.PointerData->OffsetType == OffsetType::Overflow)
? ConvertPointer(nPointerValue) : nPointerValue;

if (nAddress >= nConvertedPointerValue &&
nAddress < nConvertedPointerValue + pCodeNote.second.PointerData->OffsetRange)
{
const auto nOffset = ra::to_signed(nAddress - pCodeNote.second.PointerData->PointerValue);
const auto nOffset = ra::to_signed(nAddress - nPointerValue);
for (const auto& pOffsetNote : pCodeNote.second.PointerData->OffsetNotes)
{
if (pOffsetNote.Offset <= nOffset && pOffsetNote.Offset + ra::to_signed(pOffsetNote.Bytes) > nOffset)
return pCodeNote.second.PointerData->PointerValue + pOffsetNote.Offset;
return nPointerValue + pOffsetNote.Offset;
}
}
}
Expand Down Expand Up @@ -540,9 +560,13 @@ std::wstring CodeNotesModel::FindCodeNote(ra::ByteAddress nAddress, MemSize nSiz
if (!pIter2.second.PointerData)
continue;

if (nLastAddress >= pIter2.second.PointerData->PointerValue)
const auto nPointerValue = pIter2.second.PointerData->PointerValue;
const auto nConvertedPointerValue = (pIter2.second.PointerData->OffsetType == OffsetType::Overflow)
? ConvertPointer(nPointerValue) : nPointerValue;

if (nLastAddress >= nConvertedPointerValue)
{
const auto nOffset = ra::to_signed(nAddress - pIter2.second.PointerData->PointerValue);
const auto nOffset = ra::to_signed(nAddress - nPointerValue);
const auto nLastOffset = nOffset + ra::to_signed(nCheckBytes) - 1;
for (const auto& pNote : pIter2.second.PointerData->OffsetNotes)
{
Expand Down Expand Up @@ -657,29 +681,40 @@ const CodeNotesModel::CodeNote* CodeNotesModel::FindCodeNoteInternal(ra::ByteAdd
return &pIter->second;

if (m_bHasPointers)
return FindIndirectCodeNoteInternal(nAddress).second;

return nullptr;
}

std::pair<ra::ByteAddress, const CodeNotesModel::CodeNote*>
CodeNotesModel::FindIndirectCodeNoteInternal(ra::ByteAddress nAddress) const
{
for (const auto& pCodeNote : m_mCodeNotes)
{
for (const auto& pCodeNote : m_mCodeNotes)
{
if (pCodeNote.second.PointerData == nullptr)
continue;
if (pCodeNote.second.PointerData == nullptr)
continue;

if (nAddress >= pCodeNote.second.PointerData->PointerValue &&
nAddress < pCodeNote.second.PointerData->PointerValue + pCodeNote.second.PointerData->OffsetRange)
// if the pointer address was not converted, do so now.
const auto nPointerValue = pCodeNote.second.PointerData->PointerValue;
const auto nConvertedPointerValue = (pCodeNote.second.PointerData->OffsetType == OffsetType::Overflow)
? ConvertPointer(nPointerValue) : nPointerValue;

if (nAddress >= nConvertedPointerValue &&
nAddress < nConvertedPointerValue + pCodeNote.second.PointerData->OffsetRange)
{
const auto nOffset = ra::to_signed(nAddress - nPointerValue);
for (const auto& pOffsetNote : pCodeNote.second.PointerData->OffsetNotes)
{
const auto nOffset = ra::to_signed(nAddress - pCodeNote.second.PointerData->PointerValue);
for (const auto& pOffsetNote : pCodeNote.second.PointerData->OffsetNotes)
{
if (pOffsetNote.Offset == nOffset)
return &pOffsetNote;
}
if (pOffsetNote.Offset == nOffset)
return {pCodeNote.first, &pOffsetNote};
}
}
}

return nullptr;
return {0, nullptr};
}

const std::wstring* CodeNotesModel::FindIndirectCodeNote(ra::ByteAddress nAddress, unsigned nOffset) const noexcept
const std::wstring* CodeNotesModel::FindIndirectCodeNote(ra::ByteAddress nAddress, unsigned nOffset) const
{
if (!m_bHasPointers)
return nullptr;
Expand All @@ -691,39 +726,40 @@ const std::wstring* CodeNotesModel::FindIndirectCodeNote(ra::ByteAddress nAddres

if (nAddress == pCodeNote.first)
{
// look for the offset directly
for (const auto& pOffsetNote : pCodeNote.second.PointerData->OffsetNotes)
{
if (pOffsetNote.Offset == ra::to_signed(nOffset))
return &pOffsetNote.Note;
}

if (pCodeNote.second.PointerData->OffsetType == OffsetType::Overflow)
{
// direct offset not found, look for converted offset
const auto nConvertedAddress = ConvertPointer(pCodeNote.second.PointerData->RawPointerValue);
nOffset += nConvertedAddress - pCodeNote.second.PointerData->RawPointerValue;

for (const auto& pOffsetNote : pCodeNote.second.PointerData->OffsetNotes)
{
if (pOffsetNote.Offset == ra::to_signed(nOffset))
return &pOffsetNote.Note;
}
}

break;
}
}

return nullptr;
}

ra::ByteAddress CodeNotesModel::GetIndirectSource(ra::ByteAddress nAddress) const noexcept
ra::ByteAddress CodeNotesModel::GetIndirectSource(ra::ByteAddress nAddress) const
{
if (m_bHasPointers)
{
for (const auto& pCodeNote : m_mCodeNotes)
{
if (pCodeNote.second.PointerData == nullptr)
continue;

if (nAddress >= pCodeNote.second.PointerData->PointerValue &&
nAddress < pCodeNote.second.PointerData->PointerValue + pCodeNote.second.PointerData->OffsetRange)
{
const auto nOffset = ra::to_signed(nAddress - pCodeNote.second.PointerData->PointerValue);
for (const auto& pOffsetNote : pCodeNote.second.PointerData->OffsetNotes)
{
if (pOffsetNote.Offset == nOffset)
return pCodeNote.first;
}
}
}
const auto pCodeNote = FindIndirectCodeNoteInternal(nAddress);
if (pCodeNote.second != nullptr)
return pCodeNote.first;
}

return 0xFFFFFFFF;
Expand All @@ -745,15 +781,19 @@ ra::ByteAddress CodeNotesModel::GetNextNoteAddress(ra::ByteAddress nAfterAddress
if (!pNote.second.PointerData)
continue;

if (pNote.second.PointerData->PointerValue > nBestAddress)
const auto nPointerValue = pNote.second.PointerData->PointerValue;
const auto nConvertedPointerValue = (pNote.second.PointerData->OffsetType == OffsetType::Overflow)
? ConvertPointer(nPointerValue) : nPointerValue;

if (nConvertedPointerValue > nBestAddress)
continue;

if (pNote.second.PointerData->PointerValue + pNote.second.PointerData->OffsetRange < nAfterAddress)
if (nConvertedPointerValue + pNote.second.PointerData->OffsetRange < nAfterAddress)
continue;

for (const auto& pOffset : pNote.second.PointerData->OffsetNotes)
{
const auto pOffsetAddress = pNote.second.PointerData->PointerValue + pOffset.Offset;
const auto pOffsetAddress = nPointerValue + pOffset.Offset;
if (pOffsetAddress > nAfterAddress)
{
nBestAddress = std::min(nBestAddress, pOffsetAddress);
Expand Down Expand Up @@ -793,15 +833,19 @@ ra::ByteAddress CodeNotesModel::GetPreviousNoteAddress(ra::ByteAddress nBeforeAd
if (!pNote.second.PointerData)
continue;

if (pNote.second.PointerData->PointerValue > nBeforeAddress)
const auto nPointerValue = pNote.second.PointerData->PointerValue;
const auto nConvertedPointerValue = (pNote.second.PointerData->OffsetType == OffsetType::Overflow)
? ConvertPointer(nPointerValue) : nPointerValue;

if (nConvertedPointerValue > nBeforeAddress)
continue;

if (pNote.second.PointerData->PointerValue + pNote.second.PointerData->OffsetRange < nBestAddress)
if (nConvertedPointerValue + pNote.second.PointerData->OffsetRange < nBestAddress)
continue;

for (const auto& pOffset : pNote.second.PointerData->OffsetNotes)
{
const auto pOffsetAddress = pNote.second.PointerData->PointerValue + pOffset.Offset;
const auto pOffsetAddress = nPointerValue + pOffset.Offset;
if (pOffsetAddress >= nBeforeAddress)
break;

Expand Down Expand Up @@ -835,8 +879,9 @@ void CodeNotesModel::EnumerateCodeNotes(std::function<bool(ra::ByteAddress nAddr
if (!pIter.second.PointerData)
continue;

const auto nPointerValue = pIter.second.PointerData->PointerValue;
for (const auto& pNote : pIter.second.PointerData->OffsetNotes)
mNotes[pIter.second.PointerData->PointerValue + pNote.Offset] = &pNote;
mNotes[nPointerValue + pNote.Offset] = &pNote;
}

// merge in the non-pointer notes
Expand All @@ -863,7 +908,13 @@ void CodeNotesModel::DoFrame()
if (!pNote.second.PointerData)
continue;

const auto nNewAddress = ReadPointer(pEmulatorContext, pNote.first, pNote.second.MemSize);
const auto nNewRawAddress = pEmulatorContext.ReadMemory(pNote.first, pNote.second.MemSize);
if (nNewRawAddress == pNote.second.PointerData->RawPointerValue)
continue;
pNote.second.PointerData->RawPointerValue = nNewRawAddress;

const auto nNewAddress = (pNote.second.PointerData->OffsetType == OffsetType::Converted)
? ConvertPointer(nNewRawAddress) : nNewRawAddress;

const auto nOldAddress = pNote.second.PointerData->PointerValue;
if (nNewAddress == nOldAddress)
Expand Down
16 changes: 13 additions & 3 deletions src/data/models/CodeNotesModel.hh
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public:
/// Returns the note associated with the specified address.
/// </summary>
/// <returns>The note associated to the address, <c>nullptr</c> if no note is associated to the address.</returns>
const std::wstring* FindIndirectCodeNote(ra::ByteAddress nAddress, unsigned nOffset) const noexcept;
const std::wstring* FindIndirectCodeNote(ra::ByteAddress nAddress, unsigned nOffset) const;

/// <summary>
/// Returns the number of bytes associated to the code note at the specified address.
Expand Down Expand Up @@ -104,7 +104,7 @@ public:
/// <returns>
/// Returns 0xFFFFFFFF if not found, or not an indirect note.
/// </returns>
ra::ByteAddress GetIndirectSource(ra::ByteAddress nAddress) const noexcept;
ra::ByteAddress GetIndirectSource(ra::ByteAddress nAddress) const;

/// <summary>
/// Returns the address of the next code note after the provided address.
Expand Down Expand Up @@ -209,11 +209,19 @@ protected:
int Offset = 0;
};

enum OffsetType
{
None = 0,
Converted,
Overflow,
};

struct PointerData
{
ra::ByteAddress RawPointerValue = 0;
ra::ByteAddress PointerValue = 0;
unsigned int OffsetRange = 0;
OffsetType OffsetType = OffsetType::None;
std::vector<OffsetCodeNote> OffsetNotes;
};

Expand All @@ -223,7 +231,9 @@ protected:
std::map<ra::ByteAddress, std::wstring> m_mPendingCodeNotes;

const CodeNote* FindCodeNoteInternal(ra::ByteAddress nAddress) const;
void EnumerateCodeNotes(std::function<bool(ra::ByteAddress nAddress, const CodeNote& pCodeNote)> callback, bool bIncludeDerived) const;
std::pair<ra::ByteAddress, const CodeNotesModel::CodeNote*> FindIndirectCodeNoteInternal(ra::ByteAddress nAddress) const;
void EnumerateCodeNotes(std::function<bool(ra::ByteAddress nAddress, const CodeNote& pCodeNote)> callback,
bool bIncludeDerived) const;

unsigned int m_nGameId = 0;
bool m_bHasPointers = false;
Expand Down
Loading

0 comments on commit 2a7c61b

Please sign in to comment.