diff --git a/src/RA_Integration.vcxproj b/src/RA_Integration.vcxproj index 5c873185..5744e6ac 100644 --- a/src/RA_Integration.vcxproj +++ b/src/RA_Integration.vcxproj @@ -70,6 +70,7 @@ + @@ -209,6 +210,7 @@ + diff --git a/src/RA_Integration.vcxproj.filters b/src/RA_Integration.vcxproj.filters index dd67d156..3bea2687 100644 --- a/src/RA_Integration.vcxproj.filters +++ b/src/RA_Integration.vcxproj.filters @@ -408,6 +408,9 @@ Services + + Data\Models + @@ -971,6 +974,9 @@ Services + + Data\Models + diff --git a/src/data/models/CodeNoteModel.cpp b/src/data/models/CodeNoteModel.cpp new file mode 100644 index 00000000..c94ae3f8 --- /dev/null +++ b/src/data/models/CodeNoteModel.cpp @@ -0,0 +1,569 @@ +#include "CodeNoteModel.hh" + +#include "data\context\ConsoleContext.hh" +#include "data\context\EmulatorContext.hh" + +#include "services\ServiceLocator.hh" + +#include "RA_StringUtils.h" +#include "ra_utility.h" + +namespace ra { +namespace data { +namespace models { + +struct CodeNoteModel::PointerData +{ + uint32_t RawPointerValue = 0xFFFFFFFF; // last raw value of pointer captured + ra::ByteAddress PointerAddress = 0xFFFFFFFF; // raw pointer value converted to RA address + unsigned int OffsetRange = 0; // highest offset captured within pointer block + + enum OffsetType + { + None = 0, + Converted, // PointerAddress will contain a converted address + Overflow, // offset exceeds RA address space, apply to RawPointerValue + }; + OffsetType OffsetType = OffsetType::None; + + struct OffsetCodeNote + { + CodeNoteModel CodeNote; + int Offset = 0; + }; + std::vector OffsetNotes; +}; + +// these must be defined here because of forward declaration of PointerData in header file. +CodeNoteModel::CodeNoteModel() noexcept {} +CodeNoteModel::~CodeNoteModel() {} +CodeNoteModel::CodeNoteModel(CodeNoteModel&& pOther) noexcept + : m_sAuthor(std::move(pOther.m_sAuthor)), + m_sNote(std::move(pOther.m_sNote)), + m_nBytes(pOther.m_nBytes), + m_nMemSize(pOther.m_nMemSize), + m_pPointerData(std::move(pOther.m_pPointerData)) +{ +} +CodeNoteModel& CodeNoteModel::operator=(CodeNoteModel&& pOther) noexcept +{ + m_sAuthor = std::move(pOther.m_sAuthor); + m_sNote = std::move(pOther.m_sNote); + m_nBytes = pOther.m_nBytes; + m_nMemSize = pOther.m_nMemSize; + m_pPointerData = std::move(pOther.m_pPointerData); + return *this; +} + +ra::ByteAddress CodeNoteModel::GetPointerAddress() const noexcept +{ + return m_pPointerData != nullptr ? m_pPointerData->PointerAddress : 0xFFFFFFFF; +} + +uint32_t CodeNoteModel::GetRawPointerValue() const noexcept +{ + return m_pPointerData != nullptr ? m_pPointerData->RawPointerValue : 0xFFFFFFFF; +} + +static ra::ByteAddress ConvertPointer(ra::ByteAddress nAddress) +{ + const auto& pConsoleContext = ra::services::ServiceLocator::Get(); + const auto nConvertedAddress = pConsoleContext.ByteAddressFromRealAddress(nAddress); + if (nConvertedAddress != 0xFFFFFFFF) + nAddress = nConvertedAddress; + + return nAddress; +} + +bool CodeNoteModel::SetRawPointerValue(uint32_t nValue) +{ + if (m_pPointerData == nullptr) + return false; + + if (nValue == m_pPointerData->RawPointerValue) + return false; + m_pPointerData->RawPointerValue = nValue; + + const auto nNewAddress = (m_pPointerData->OffsetType == PointerData::OffsetType::Converted) + ? ConvertPointer(nValue) : nValue; + + const auto nOldAddress = m_pPointerData->PointerAddress; + if (nNewAddress == nOldAddress) + return false; + + m_pPointerData->PointerAddress = nNewAddress; + return true; +} + +const CodeNoteModel* CodeNoteModel::GetPointerNoteAtOffset(int nOffset) const +{ + if (m_pPointerData == nullptr) + return nullptr; + + // look for explicit offset match + for (const auto& pOffsetNote : m_pPointerData->OffsetNotes) + { + if (pOffsetNote.Offset == nOffset) + return &pOffsetNote.CodeNote; + } + + if (m_pPointerData->OffsetType == PointerData::OffsetType::Overflow) + { + // direct offset not found, look for converted offset + const auto nConvertedAddress = ConvertPointer(m_pPointerData->RawPointerValue); + nOffset += nConvertedAddress - m_pPointerData->RawPointerValue; + + for (const auto& pOffsetNote : m_pPointerData->OffsetNotes) + { + if (pOffsetNote.Offset == nOffset) + return &pOffsetNote.CodeNote; + } + } + + return nullptr; +} + +std::pair CodeNoteModel::GetPointerNoteAtAddress(ra::ByteAddress nAddress) const +{ + if (m_pPointerData == nullptr) + return {0, nullptr}; + + const auto nPointerAddress = m_pPointerData->PointerAddress; + const auto nConvertedAddress = (m_pPointerData->OffsetType == PointerData::OffsetType::Overflow) + ? ConvertPointer(nPointerAddress) : nPointerAddress; + + if (nAddress >= nConvertedAddress && nAddress < nConvertedAddress + m_pPointerData->OffsetRange) + { + auto nOffset = ra::to_signed(nAddress - nPointerAddress); + + // check for exact matches first + for (const auto& pOffsetNote : m_pPointerData->OffsetNotes) + { + if (nOffset == pOffsetNote.Offset) + return {pOffsetNote.Offset + nPointerAddress, &pOffsetNote.CodeNote}; + } + + // check for trailing bytes in a multi-byte note + for (const auto& pOffsetNote : m_pPointerData->OffsetNotes) + { + if (nOffset > pOffsetNote.Offset) + { + const auto nBytes = ra::to_signed(pOffsetNote.CodeNote.GetBytes()); + if (nBytes > 1 && nOffset < pOffsetNote.Offset + nBytes) + return {pOffsetNote.Offset + nPointerAddress, &pOffsetNote.CodeNote}; + } + } + } + + return {0, nullptr}; +} + +bool CodeNoteModel::GetPreviousAddress(ra::ByteAddress nBeforeAddress, ra::ByteAddress& nPreviousAddress) const +{ + if (m_pPointerData == nullptr) + return false; + + const auto nPointerAddress = m_pPointerData->PointerAddress; + const auto nConvertedAddress = (m_pPointerData->OffsetType == PointerData::OffsetType::Overflow) + ? ConvertPointer(nPointerAddress) : nPointerAddress; + + if (nConvertedAddress > nBeforeAddress) + return false; + + bool bResult = false; + nPreviousAddress = 0; + for (const auto& pOffset : m_pPointerData->OffsetNotes) + { + const auto nOffsetAddress = nPointerAddress + pOffset.Offset; + if (nOffsetAddress < nBeforeAddress && nOffsetAddress > nPreviousAddress) + { + nPreviousAddress = nOffsetAddress; + bResult = true; + } + } + + return bResult; +} + +bool CodeNoteModel::GetNextAddress(ra::ByteAddress nAfterAddress, ra::ByteAddress& nNextAddress) const +{ + if (m_pPointerData == nullptr) + return false; + + const auto nPointerAddress = m_pPointerData->PointerAddress; + const auto nConvertedAddress = (m_pPointerData->OffsetType == PointerData::OffsetType::Overflow) + ? ConvertPointer(nPointerAddress) : nPointerAddress; + + if (nConvertedAddress + m_pPointerData->OffsetRange < nAfterAddress) + return false; + + bool bResult = false; + nNextAddress = 0xFFFFFFFF; + for (const auto& pOffset : m_pPointerData->OffsetNotes) + { + const auto nOffsetAddress = nPointerAddress + pOffset.Offset; + if (nOffsetAddress > nAfterAddress && nOffsetAddress < nNextAddress) + { + nNextAddress = nOffsetAddress; + bResult = true; + } + } + + return bResult; +} + +void CodeNoteModel::SetNote(const std::wstring& sNote) +{ + if (m_sNote == sNote) + return; + + m_sNote = sNote; + + auto nIndex = sNote.find(L'\n'); + auto sFirstLine = (nIndex == std::string::npos) ? sNote : sNote.substr(0, nIndex); + StringMakeLowercase(sFirstLine); + ExtractSize(sFirstLine); + + if (sFirstLine.find(L"pointer") != std::string::npos) + { + if (m_nMemSize == MemSize::Unknown) + { + m_nBytes = 4; + m_nMemSize = MemSize::ThirtyTwoBit; + } + + // if there are any lines starting with a plus sign, extract the indirect code notes + nIndex = sNote.find(L"\n+"); + if (nIndex != std::string::npos) + ProcessIndirectNotes(sNote, nIndex); + } +} + +void CodeNoteModel::ExtractSize(const std::wstring& sNote) +{ + // provide defaults in case no matches are found + m_nBytes = 1; + m_nMemSize = MemSize::Unknown; + + // "Nbit" smallest possible note - and that's just the size annotation + if (sNote.length() < 4) + return; + + bool bBytesFromBits = false; + bool bFoundSize = false; + bool bLastWordIsSize = false; + bool bLastWordIsNumber = false; + bool bWordIsNumber = false; + + std::wstring sPreviousWord, sWord; + const size_t nLength = sNote.length(); + for (size_t nIndex = 0; nIndex <= nLength; ++nIndex) + { + // support reading null terminator so we process the last word in the string + const wchar_t c = (nIndex == nLength) ? 0 : sNote.at(nIndex); + + // find the next word + if (c > 255) + { + // ignore unicode characters - isalpha with the default locale would return false, + // but also likes to pop up asserts when in a debug build. + } + else if (isalpha(c)) + { + if (sWord.empty()) + { + sWord.push_back(gsl::narrow_cast(tolower(c))); + bWordIsNumber = false; + continue; + } + + if (!bWordIsNumber) + { + sWord.push_back(gsl::narrow_cast(tolower(c))); + continue; + } + } + else if (isdigit(c)) + { + if (sWord.empty()) + { + sWord.push_back(c); + bWordIsNumber = true; + continue; + } + + if (bWordIsNumber) + { + sWord.push_back(c); + continue; + } + } + + if (sWord.empty()) + continue; + + // process the word + bool bWordIsSize = false; + if (bWordIsNumber) + { + if (sPreviousWord == L"mbf") + { + const auto nBits = _wtoi(sWord.c_str()); + if (nBits == 32) + { + m_nBytes = 4; + m_nMemSize = MemSize::MBF32; + bWordIsSize = true; + bFoundSize = true; + } + else if (nBits == 40) + { + m_nBytes = 5; + m_nMemSize = MemSize::MBF32; + bWordIsSize = true; + bFoundSize = true; + } + } + else if (sPreviousWord == L"double" && sWord == L"32") + { + m_nBytes = 4; + m_nMemSize = MemSize::Double32; + bWordIsSize = true; + bFoundSize = true; + } + } + else if (bLastWordIsSize) + { + if (sWord == L"float") + { + if (m_nMemSize == MemSize::ThirtyTwoBit) + { + m_nMemSize = MemSize::Float; + bWordIsSize = true; // allow trailing be/bigendian + } + } + else if (sWord == L"double") + { + if (m_nMemSize == MemSize::ThirtyTwoBit || m_nBytes == 8) + { + m_nMemSize = MemSize::Double32; + bWordIsSize = true; // allow trailing be/bigendian + } + } + else if (sWord == L"be" || sWord == L"bigendian") + { + switch (m_nMemSize) + { + case MemSize::SixteenBit: m_nMemSize = MemSize::SixteenBitBigEndian; break; + case MemSize::TwentyFourBit: m_nMemSize = MemSize::TwentyFourBitBigEndian; break; + case MemSize::ThirtyTwoBit: m_nMemSize = MemSize::ThirtyTwoBitBigEndian; break; + case MemSize::Float: m_nMemSize = MemSize::FloatBigEndian; break; + case MemSize::Double32: m_nMemSize = MemSize::Double32BigEndian; break; + default: break; + } + } + else if (sWord == L"le") + { + if (m_nMemSize == MemSize::MBF32) + m_nMemSize = MemSize::MBF32LE; + } + else if (sWord == L"mbf") + { + if (m_nBytes == 4 || m_nBytes == 5) + m_nMemSize = MemSize::MBF32; + } + } + else if (bLastWordIsNumber) + { + if (sWord == L"bit" || sWord == L"bits") + { + if (!bFoundSize) + { + const auto nBits = _wtoi(sPreviousWord.c_str()); + m_nBytes = (nBits + 7) / 8; + m_nMemSize = MemSize::Unknown; + bBytesFromBits = true; + bWordIsSize = true; + bFoundSize = true; + } + } + else if (sWord == L"byte" || sWord == L"bytes") + { + if (!bFoundSize || bBytesFromBits) + { + m_nBytes = _wtoi(sPreviousWord.c_str()); + m_nMemSize = MemSize::Unknown; + bBytesFromBits = false; + bWordIsSize = true; + bFoundSize = true; + } + } + + if (bWordIsSize) + { + switch (m_nBytes) + { + case 0: m_nBytes = 1; break; // Unexpected size, reset to defaults (1 byte, Unknown) + case 1: m_nMemSize = MemSize::EightBit; break; + case 2: m_nMemSize = MemSize::SixteenBit; break; + case 3: m_nMemSize = MemSize::TwentyFourBit; break; + case 4: m_nMemSize = MemSize::ThirtyTwoBit; break; + default: m_nMemSize = MemSize::Array; break; + } + } + } + else if (sWord == L"float") + { + if (!bFoundSize) + { + m_nBytes = 4; + m_nMemSize = MemSize::Float; + bWordIsSize = true; // allow trailing be/bigendian + + if (sPreviousWord == L"be" || sPreviousWord == L"bigendian") + m_nMemSize = MemSize::FloatBigEndian; + } + } + else if (sWord == L"double") + { + if (!bFoundSize) + { + m_nBytes = 8; + m_nMemSize = MemSize::Double32; + bWordIsSize = true; // allow trailing be/bigendian + + if (sPreviousWord == L"be" || sPreviousWord == L"bigendian") + m_nMemSize = MemSize::Double32BigEndian; + } + } + + // store information about the word for later + bLastWordIsSize = bWordIsSize; + bLastWordIsNumber = bWordIsNumber; + + if (c < 256 && isalnum(c)) + { + std::swap(sPreviousWord, sWord); + sWord.clear(); + + sWord.push_back(gsl::narrow_cast(tolower(c))); + bWordIsNumber = isdigit(c); + } + else + { + // only join words with spaces or hyphens. + if (c == L' ' || c == L'-') + std::swap(sPreviousWord, sWord); + else + sPreviousWord.clear(); + + sWord.clear(); + } + } +} + +void CodeNoteModel::ProcessIndirectNotes(const std::wstring& sNote, size_t nIndex) +{ + nIndex += 2; + + auto pointerData = std::make_unique(); + do + { + PointerData::OffsetCodeNote offsetNote; + const auto nNextIndex = sNote.find(L"\n+", nIndex); + auto sNextNote = sNote.substr(nIndex, nNextIndex - nIndex); + ra::Trim(sNextNote); + + wchar_t* pEnd = nullptr; + + try + { + if (sNextNote.length() > 2 && sNextNote.at(1) == 'x') + offsetNote.Offset = gsl::narrow_cast(std::wcstoll(sNextNote.c_str() + 2, &pEnd, 16)); + else + offsetNote.Offset = gsl::narrow_cast(std::wcstoll(sNextNote.c_str(), &pEnd, 10)); + } catch (const std::exception&) + { + break; + } + + // if there are any error processing offsets, don't treat this as a pointer note + if (!pEnd || isalnum(*pEnd)) + return; + + const wchar_t* pStop = sNextNote.c_str() + sNextNote.length(); + while (pEnd < pStop && isspace(*pEnd)) + pEnd++; + if (pEnd < pStop && !isalnum(*pEnd)) + { + pEnd++; + while (pEnd < pStop && isspace(*pEnd)) + pEnd++; + } + + offsetNote.CodeNote.SetAuthor(m_sAuthor); + offsetNote.CodeNote.SetNote(sNextNote.substr(pEnd - sNextNote.c_str())); + + const auto nRangeOffset = offsetNote.Offset + offsetNote.CodeNote.GetBytes(); + pointerData->OffsetRange = std::max(pointerData->OffsetRange, nRangeOffset); + + pointerData->OffsetNotes.push_back(std::move(offsetNote)); + + if (nNextIndex == std::string::npos) + break; + + nIndex = nNextIndex + 2; + } while (true); + + // 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 (m_nMemSize == MemSize::ThirtyTwoBit || m_nMemSize == MemSize::ThirtyTwoBitBigEndian) + { + const auto& pEmulatorContext = ra::services::ServiceLocator::Get(); + const auto nMaxAddress = pEmulatorContext.TotalMemorySize(); + + pointerData->OffsetType = PointerData::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 = PointerData::OffsetType::Overflow; + break; + } + } + } + + m_pPointerData = std::move(pointerData); +} + +void CodeNoteModel::EnumeratePointerNotes( + std::function callback) const +{ + if (m_pPointerData == nullptr) + return; + + if (m_pPointerData->OffsetType == PointerData::OffsetType::Overflow) + EnumeratePointerNotes(m_pPointerData->RawPointerValue, callback); + else + EnumeratePointerNotes(m_pPointerData->PointerAddress, callback); +} + +void CodeNoteModel::EnumeratePointerNotes(ra::ByteAddress nPointerAddress, + std::function callback) const +{ + if (m_pPointerData == nullptr) + return; + + for (const auto& pNote : m_pPointerData->OffsetNotes) + { + if (!callback(nPointerAddress + pNote.Offset, pNote.CodeNote)) + break; + } +} + +} // namespace models +} // namespace data +} // namespace ra diff --git a/src/data/models/CodeNoteModel.hh b/src/data/models/CodeNoteModel.hh new file mode 100644 index 00000000..3e0e22fe --- /dev/null +++ b/src/data/models/CodeNoteModel.hh @@ -0,0 +1,61 @@ +#ifndef RA_DATA_CODE_NOTE_MODEL_H +#define RA_DATA_CODE_NOTE_MODEL_H +#pragma once + +#include "data/Types.hh" + +namespace ra { +namespace data { +namespace models { + +class CodeNoteModel +{ +public: + CodeNoteModel() noexcept; + ~CodeNoteModel(); + CodeNoteModel(const CodeNoteModel&) noexcept = delete; + CodeNoteModel& operator=(const CodeNoteModel&) noexcept = delete; + CodeNoteModel(CodeNoteModel&&) noexcept; + CodeNoteModel& operator=(CodeNoteModel&&) noexcept; + + const std::string& GetAuthor() const noexcept { return m_sAuthor; } + const std::wstring& GetNote() const noexcept { return m_sNote; } + const unsigned int GetBytes() const noexcept { return m_nBytes; } + const MemSize GetMemSize() const noexcept { return m_nMemSize; } + + void SetAuthor(const std::string& sAuthor) { m_sAuthor = sAuthor; } + void SetNote(const std::wstring& sNote); + + bool IsPointer() const noexcept { return m_pPointerData != nullptr; } + ra::ByteAddress GetPointerAddress() const noexcept; + uint32_t GetRawPointerValue() const noexcept; + bool SetRawPointerValue(uint32_t nValue); + const CodeNoteModel* GetPointerNoteAtOffset(int nOffset) const; + std::pair GetPointerNoteAtAddress(ra::ByteAddress nAddress) const; + + bool GetPreviousAddress(ra::ByteAddress nBeforeAddress, ra::ByteAddress& nPreviousAddress) const; + bool GetNextAddress(ra::ByteAddress nAfterAddress, ra::ByteAddress& nNextAddress) const; + + void EnumeratePointerNotes(ra::ByteAddress nPointerAddress, + std::function callback) const; + void EnumeratePointerNotes(std::function callback) const; + +private: + std::string m_sAuthor; + std::wstring m_sNote; + unsigned int m_nBytes = 1; + MemSize m_nMemSize = MemSize::Unknown; + + struct PointerData; + std::unique_ptr m_pPointerData; + +private: + void ProcessIndirectNotes(const std::wstring& sNote, size_t nIndex); + void ExtractSize(const std::wstring& sNote); +}; + +} // namespace models +} // namespace data +} // namespace ra + +#endif RA_DATA_CODE_NOTE_MODEL_H diff --git a/src/data/models/CodeNotesModel.cpp b/src/data/models/CodeNotesModel.cpp index 3b9ddd53..9b6d1c68 100644 --- a/src/data/models/CodeNotesModel.cpp +++ b/src/data/models/CodeNotesModel.cpp @@ -86,374 +86,38 @@ void CodeNotesModel::Refresh(unsigned int nGameId, CodeNoteChangedFunction fCode }); } -void CodeNotesModel::ExtractSize(CodeNote& pNote) -{ - // provide defaults in case no matches are found - pNote.Bytes = 1; - pNote.MemSize = MemSize::Unknown; - - // "Nbit" smallest possible note - and that's just the size annotation - if (pNote.Note.length() < 4) - return; - - bool bBytesFromBits = false; - bool bFoundSize = false; - bool bLastWordIsSize = false; - bool bLastWordIsNumber = false; - bool bWordIsNumber = false; - - std::wstring sPreviousWord, sWord; - const size_t nLength = pNote.Note.length(); - for (size_t nIndex = 0; nIndex <= nLength; ++nIndex) - { - // support reading null terminator so we process the last word in the string - const wchar_t c = (nIndex == nLength) ? 0 : pNote.Note.at(nIndex); - - // find the next word - if (c > 255) - { - // ignore unicode characters - isalpha with the default locale would return false, - // but also likes to pop up asserts when in a debug build. - } - else if (isalpha(c)) - { - if (sWord.empty()) - { - sWord.push_back(gsl::narrow_cast(tolower(c))); - bWordIsNumber = false; - continue; - } - - if (!bWordIsNumber) - { - sWord.push_back(gsl::narrow_cast(tolower(c))); - continue; - } - } - else if (isdigit(c)) - { - if (sWord.empty()) - { - sWord.push_back(c); - bWordIsNumber = true; - continue; - } - - if (bWordIsNumber) - { - sWord.push_back(c); - continue; - } - } - - if (sWord.empty()) - continue; - - // process the word - bool bWordIsSize = false; - if (bWordIsNumber) - { - if (sPreviousWord == L"mbf") - { - const auto nBits = _wtoi(sWord.c_str()); - if (nBits == 32) - { - pNote.Bytes = 4; - pNote.MemSize = MemSize::MBF32; - bWordIsSize = true; - bFoundSize = true; - } - else if (nBits == 40) - { - pNote.Bytes = 5; - pNote.MemSize = MemSize::MBF32; - bWordIsSize = true; - bFoundSize = true; - } - } - else if (sPreviousWord == L"double" && sWord == L"32") - { - pNote.Bytes = 4; - pNote.MemSize = MemSize::Double32; - bWordIsSize = true; - bFoundSize = true; - } - } - else if (bLastWordIsSize) - { - if (sWord == L"float") - { - if (pNote.MemSize == MemSize::ThirtyTwoBit) - { - pNote.MemSize = MemSize::Float; - bWordIsSize = true; // allow trailing be/bigendian - } - } - else if (sWord == L"double") - { - if (pNote.MemSize == MemSize::ThirtyTwoBit || pNote.Bytes == 8) - { - pNote.MemSize = MemSize::Double32; - bWordIsSize = true; // allow trailing be/bigendian - } - } - else if (sWord == L"be" || sWord == L"bigendian") - { - switch (pNote.MemSize) - { - case MemSize::SixteenBit: pNote.MemSize = MemSize::SixteenBitBigEndian; break; - case MemSize::TwentyFourBit: pNote.MemSize = MemSize::TwentyFourBitBigEndian; break; - case MemSize::ThirtyTwoBit: pNote.MemSize = MemSize::ThirtyTwoBitBigEndian; break; - case MemSize::Float: pNote.MemSize = MemSize::FloatBigEndian; break; - case MemSize::Double32: pNote.MemSize = MemSize::Double32BigEndian; break; - default: break; - } - } - else if (sWord == L"le") - { - if (pNote.MemSize == MemSize::MBF32) - pNote.MemSize = MemSize::MBF32LE; - } - else if (sWord == L"mbf") - { - if (pNote.Bytes == 4 || pNote.Bytes == 5) - pNote.MemSize = MemSize::MBF32; - } - } - else if (bLastWordIsNumber) - { - if (sWord == L"bit" || sWord == L"bits") - { - if (!bFoundSize) - { - const auto nBits = _wtoi(sPreviousWord.c_str()); - pNote.Bytes = (nBits + 7) / 8; - pNote.MemSize = MemSize::Unknown; - bBytesFromBits = true; - bWordIsSize = true; - bFoundSize = true; - } - } - else if (sWord == L"byte" || sWord == L"bytes") - { - if (!bFoundSize || bBytesFromBits) - { - pNote.Bytes = _wtoi(sPreviousWord.c_str()); - pNote.MemSize = MemSize::Unknown; - bBytesFromBits = false; - bWordIsSize = true; - bFoundSize = true; - } - } - - if (bWordIsSize) - { - switch (pNote.Bytes) - { - case 0: pNote.Bytes = 1; break; // Unexpected size, reset to defaults (1 byte, Unknown) - case 1: pNote.MemSize = MemSize::EightBit; break; - case 2: pNote.MemSize = MemSize::SixteenBit; break; - case 3: pNote.MemSize = MemSize::TwentyFourBit; break; - case 4: pNote.MemSize = MemSize::ThirtyTwoBit; break; - default: pNote.MemSize = MemSize::Array; break; - } - } - } - else if (sWord == L"float") - { - if (!bFoundSize) - { - pNote.Bytes = 4; - pNote.MemSize = MemSize::Float; - bWordIsSize = true; // allow trailing be/bigendian - - if (sPreviousWord == L"be" || sPreviousWord == L"bigendian") - pNote.MemSize = MemSize::FloatBigEndian; - } - } - else if (sWord == L"double") - { - if (!bFoundSize) - { - pNote.Bytes = 8; - pNote.MemSize = MemSize::Double32; - bWordIsSize = true; // allow trailing be/bigendian - - if (sPreviousWord == L"be" || sPreviousWord == L"bigendian") - pNote.MemSize = MemSize::Double32BigEndian; - } - } - - // store information about the word for later - bLastWordIsSize = bWordIsSize; - bLastWordIsNumber = bWordIsNumber; - - if (c < 256 && isalnum(c)) - { - std::swap(sPreviousWord, sWord); - sWord.clear(); - - sWord.push_back(gsl::narrow_cast(tolower(c))); - bWordIsNumber = isdigit(c); - } - else - { - // only join words with spaces or hyphens. - if (c == L' ' || c == L'-') - std::swap(sPreviousWord, sWord); - else - sPreviousWord.clear(); - - sWord.clear(); - } - } -} - -static ra::ByteAddress ConvertPointer(ra::ByteAddress nAddress) -{ - const auto& pConsoleContext = ra::services::ServiceLocator::Get(); - const auto nConvertedAddress = pConsoleContext.ByteAddressFromRealAddress(nAddress); - if (nConvertedAddress != 0xFFFFFFFF) - nAddress = nConvertedAddress; - - return nAddress; -} - void CodeNotesModel::AddCodeNote(ra::ByteAddress nAddress, const std::string& sAuthor, const std::wstring& sNote) { - auto nIndex = sNote.find(L'\n'); - auto sFirstLine = (nIndex == std::string::npos) ? sNote : sNote.substr(0, nIndex); - StringMakeLowercase(sFirstLine); + CodeNoteModel note; + note.SetAuthor(sAuthor); + note.SetNote(sNote); - if (sFirstLine.find(L"pointer") != std::string::npos) + const bool bIsPointer = note.IsPointer(); + if (bIsPointer) { - nIndex = sNote.find(L"\n+"); // look for line starting with plus sign - if (nIndex != std::string::npos) - { - nIndex += 2; + m_bHasPointers = true; - auto pointerData = std::make_unique(); - do - { - OffsetCodeNote offsetNote; - const auto nNextIndex = sNote.find(L"\n+", nIndex); - auto sNextNote = sNote.substr(nIndex, nNextIndex - nIndex); - ra::Trim(sNextNote); - - wchar_t* pEnd = nullptr; - - try - { - if (sNextNote.length() > 2 && sNextNote.at(1) == 'x') - offsetNote.Offset = gsl::narrow_cast(std::wcstoll(sNextNote.c_str() + 2, &pEnd, 16)); - else - offsetNote.Offset = gsl::narrow_cast(std::wcstoll(sNextNote.c_str(), &pEnd, 10)); - } - catch (const std::exception&) - { - break; - } - - if (!pEnd || isalnum(*pEnd)) - break; - - const wchar_t* pStop = sNextNote.c_str() + sNextNote.length(); - while (pEnd < pStop && isspace(*pEnd)) - pEnd++; - if (pEnd < pStop && !isalnum(*pEnd)) - { - pEnd++; - while (pEnd < pStop && isspace(*pEnd)) - pEnd++; - } - - offsetNote.Author = sAuthor; - offsetNote.Note = sNextNote.substr(pEnd - sNextNote.c_str()); - ExtractSize(offsetNote); - - const auto nRangeOffset = offsetNote.Offset + offsetNote.Bytes; - pointerData->OffsetRange = std::max(pointerData->OffsetRange, nRangeOffset); - - pointerData->OffsetNotes.push_back(std::move(offsetNote)); - - if (nNextIndex == std::string::npos) - { - CodeNote pointerNote; - pointerNote.Author = sAuthor; - - // extract pointer size from first line (assume 32-bit if not specified) - pointerNote.Note = sFirstLine; - ExtractSize(pointerNote); - if (pointerNote.MemSize == MemSize::Unknown) - { - pointerNote.MemSize = MemSize::ThirtyTwoBit; - pointerNote.Bytes = 4; - } - - const auto& pEmulatorContext = ra::services::ServiceLocator::Get(); - - // 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); - - // assign entire note to pointer note - pointerNote.Note = sNote; - { - std::unique_lock lock(m_oMutex); - m_mCodeNotes.insert_or_assign(nAddress, std::move(pointerNote)); - } - m_bHasPointers = true; - - OnCodeNoteChanged(nAddress, sNote); - - if (m_fCodeNoteChanged) - { - for (const auto& pNote : m_mCodeNotes[nAddress].PointerData->OffsetNotes) - m_fCodeNoteChanged(nPointerValue + pNote.Offset, pNote.Note); - } - - return; - } - - nIndex = nNextIndex + 2; - } while (true); - } + // capture the initial value of the pointer + const auto& pEmulatorContext = ra::services::ServiceLocator::Get(); + note.SetRawPointerValue(pEmulatorContext.ReadMemory(nAddress, note.GetMemSize())); } - CodeNote note; - note.Author = sAuthor; - note.Note = sNote; - ExtractSize(note); { std::unique_lock lock(m_oMutex); m_mCodeNotes.insert_or_assign(nAddress, std::move(note)); } OnCodeNoteChanged(nAddress, sNote); + + // also raise CodeNoteChanged events for each indirect child note + if (bIsPointer && m_fCodeNoteChanged) + { + const auto& pNote = m_mCodeNotes[nAddress]; + pNote.EnumeratePointerNotes([this](ra::ByteAddress nAddress, const CodeNoteModel& pOffsetNote) { + m_fCodeNoteChanged(nAddress, pOffsetNote.GetNote()); + return true; + }); + } } void CodeNotesModel::OnCodeNoteChanged(ra::ByteAddress nAddress, const std::wstring& sNewNote) @@ -484,7 +148,7 @@ ra::ByteAddress CodeNotesModel::FindCodeNoteStart(ra::ByteAddress nAddress) cons { --pIter; - if (pIter->second.Bytes > 1 && pIter->second.Bytes + pIter->first > nAddress) + if (pIter->second.GetBytes() > 1 && pIter->second.GetBytes() + pIter->first > nAddress) return pIter->first; } while (pIter != m_mCodeNotes.begin()); @@ -495,23 +159,9 @@ ra::ByteAddress CodeNotesModel::FindCodeNoteStart(ra::ByteAddress nAddress) cons { for (const auto& pCodeNote : m_mCodeNotes) { - if (pCodeNote.second.PointerData == nullptr) - continue; - - 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) - { - if (pOffsetNote.Offset <= nOffset && pOffsetNote.Offset + ra::to_signed(pOffsetNote.Bytes) > nOffset) - return nPointerValue + pOffsetNote.Offset; - } - } + const auto pair = pCodeNote.second.GetPointerNoteAtAddress(nAddress); + if (pair.second != nullptr) + return pair.first; } } @@ -519,10 +169,10 @@ ra::ByteAddress CodeNotesModel::FindCodeNoteStart(ra::ByteAddress nAddress) cons } std::wstring CodeNotesModel::BuildCodeNoteSized(ra::ByteAddress nAddress, - unsigned nCheckBytes, ra::ByteAddress nNoteAddress, const CodeNote& pNote) + unsigned nCheckBytes, ra::ByteAddress nNoteAddress, const CodeNoteModel& pNote) { // extract the first line - std::wstring sNote = pNote.Note; + std::wstring sNote = pNote.GetNote(); const auto iNewLine = sNote.find('\n'); if (iNewLine != std::string::npos) { @@ -532,7 +182,7 @@ std::wstring CodeNotesModel::BuildCodeNoteSized(ra::ByteAddress nAddress, sNote.pop_back(); } - const unsigned int nNoteSize = pNote.Bytes; + const unsigned int nNoteSize = pNote.GetBytes(); if (nNoteAddress == nAddress && nNoteSize == nCheckBytes) { // exact size match - don't add a suffix @@ -575,7 +225,7 @@ std::wstring CodeNotesModel::FindCodeNote(ra::ByteAddress nAddress, MemSize nSiz if (pIter != m_mCodeNotes.begin()) { --pIter; - if (pIter->first + pIter->second.Bytes - 1 >= nAddress) + if (pIter->first + pIter->second.GetBytes() - 1 >= nAddress) { // previous item overlaps with requested address return BuildCodeNoteSized(nAddress, nCheckBytes, pIter->first, pIter->second); @@ -585,34 +235,19 @@ std::wstring CodeNotesModel::FindCodeNote(ra::ByteAddress nAddress, MemSize nSiz // no code note on the address, check for pointers if (m_bHasPointers) { - const auto nLastAddress = nAddress + nCheckBytes - 1; for (const auto& pIter2 : m_mCodeNotes) { - if (!pIter2.second.PointerData) - continue; - - const auto nPointerValue = pIter2.second.PointerData->PointerValue; - const auto nConvertedPointerValue = (pIter2.second.PointerData->OffsetType == OffsetType::Overflow) - ? ConvertPointer(nPointerValue) : nPointerValue; + const auto pair = pIter2.second.GetPointerNoteAtAddress(nAddress); + if (pair.second != nullptr) + return BuildCodeNoteSized(nAddress, nCheckBytes, pair.first, *pair.second) + L" [indirect]"; + } - if (nLastAddress >= nConvertedPointerValue) - { - 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) - { - if (pNote.Offset == nOffset) - { - // exact match - return BuildCodeNoteSized(nAddress, nCheckBytes, nAddress, pNote) + L" [indirect]"; - } - else if (pNote.Offset + ra::to_signed(pNote.Bytes) - 1 >= nOffset && pNote.Offset <= nLastOffset) - { - // overlap - return BuildCodeNoteSized(nAddress, nCheckBytes, pIter2.second.PointerData->PointerValue + pNote.Offset, pNote) + L" [indirect]"; - } - } - } + const auto nLastAddress = nAddress + nCheckBytes - 1; + for (const auto& pIter2 : m_mCodeNotes) + { + const auto pair = pIter2.second.GetPointerNoteAtAddress(nLastAddress); + if (pair.second != nullptr) + return BuildCodeNoteSized(nAddress, nCheckBytes, pair.first, *pair.second) + L" [indirect]"; } } @@ -624,8 +259,8 @@ const std::wstring* CodeNotesModel::FindCodeNote(ra::ByteAddress nAddress, _Inou const auto pIter = m_mCodeNotes.find(nAddress); if (pIter != m_mCodeNotes.end()) { - sAuthor = pIter->second.Author; - return &pIter->second.Note; + sAuthor = pIter->second.GetAuthor(); + return &pIter->second.GetNote(); } return nullptr; @@ -647,7 +282,7 @@ void CodeNotesModel::SetCodeNote(ra::ByteAddress nAddress, const std::wstring& s const auto pIter = m_mCodeNotes.find(nAddress); if (pIter != m_mCodeNotes.end()) { - if (pIter->second.Note == sNote) + if (pIter->second.GetNote() == sNote) { // the note at this address is unchanged return; @@ -667,7 +302,7 @@ void CodeNotesModel::SetCodeNote(ra::ByteAddress nAddress, const std::wstring& s { // capture the original value m_mOriginalCodeNotes.insert_or_assign(nAddress, - std::make_pair(pIter->second.Author, pIter->second.Note)); + std::make_pair(pIter->second.GetAuthor(), pIter->second.GetNote())); } else { @@ -705,7 +340,7 @@ void CodeNotesModel::SetCodeNote(ra::ByteAddress nAddress, const std::wstring& s } } -const CodeNotesModel::CodeNote* CodeNotesModel::FindCodeNoteInternal(ra::ByteAddress nAddress) const +const CodeNoteModel* CodeNotesModel::FindCodeNoteInternal(ra::ByteAddress nAddress) const { const auto pIter = m_mCodeNotes.find(nAddress); if (pIter != m_mCodeNotes.end()) @@ -717,29 +352,14 @@ const CodeNotesModel::CodeNote* CodeNotesModel::FindCodeNoteInternal(ra::ByteAdd return nullptr; } -std::pair +std::pair CodeNotesModel::FindIndirectCodeNoteInternal(ra::ByteAddress nAddress) const { for (const auto& pCodeNote : m_mCodeNotes) { - if (pCodeNote.second.PointerData == nullptr) - continue; - - // 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) - { - if (pOffsetNote.Offset == nOffset) - return {pCodeNote.first, &pOffsetNote}; - } - } + auto pair = pCodeNote.second.GetPointerNoteAtAddress(nAddress); + if (pair.second != nullptr && pair.first == nAddress) // only match start of note + return {pCodeNote.first, pair.second}; } return {0, nullptr}; @@ -752,30 +372,14 @@ const std::wstring* CodeNotesModel::FindIndirectCodeNote(ra::ByteAddress nAddres for (const auto& pCodeNote : m_mCodeNotes) { - if (pCodeNote.second.PointerData == nullptr) + if (!pCodeNote.second.IsPointer()) continue; 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; - } - } + const auto* pOffsetNote = pCodeNote.second.GetPointerNoteAtOffset(nOffset); + if (pOffsetNote != nullptr) + return &pOffsetNote->GetNote(); break; } @@ -807,30 +411,11 @@ ra::ByteAddress CodeNotesModel::GetNextNoteAddress(ra::ByteAddress nAfterAddress if (m_bHasPointers && bIncludeDerived) { + ra::ByteAddress nNextAddress = 0U; for (const auto& pNote : m_mCodeNotes) { - if (!pNote.second.PointerData) - continue; - - const auto nPointerValue = pNote.second.PointerData->PointerValue; - const auto nConvertedPointerValue = (pNote.second.PointerData->OffsetType == OffsetType::Overflow) - ? ConvertPointer(nPointerValue) : nPointerValue; - - if (nConvertedPointerValue > nBestAddress) - continue; - - if (nConvertedPointerValue + pNote.second.PointerData->OffsetRange < nAfterAddress) - continue; - - for (const auto& pOffset : pNote.second.PointerData->OffsetNotes) - { - const auto pOffsetAddress = nPointerValue + pOffset.Offset; - if (pOffsetAddress > nAfterAddress) - { - nBestAddress = std::min(nBestAddress, pOffsetAddress); - break; - } - } + if (pNote.second.GetNextAddress(nAfterAddress, nNextAddress)) + nBestAddress = std::min(nBestAddress, nNextAddress); } } @@ -858,38 +443,20 @@ ra::ByteAddress CodeNotesModel::GetPreviousNoteAddress(ra::ByteAddress nBeforeAd if (m_bHasPointers && bIncludeDerived) { + ra::ByteAddress nPreviousAddress = 0U; + // scan pointed-at addresses to see if there's anything between the next lower item and nBeforeAddress for (const auto& pNote : m_mCodeNotes) { - if (!pNote.second.PointerData) - continue; - - const auto nPointerValue = pNote.second.PointerData->PointerValue; - const auto nConvertedPointerValue = (pNote.second.PointerData->OffsetType == OffsetType::Overflow) - ? ConvertPointer(nPointerValue) : nPointerValue; - - if (nConvertedPointerValue > nBeforeAddress) - continue; - - if (nConvertedPointerValue + pNote.second.PointerData->OffsetRange < nBestAddress) - continue; - - for (const auto& pOffset : pNote.second.PointerData->OffsetNotes) - { - const auto pOffsetAddress = nPointerValue + pOffset.Offset; - if (pOffsetAddress >= nBeforeAddress) - break; - - if (pOffsetAddress > nBestAddress || nBestAddress == 0xFFFFFFFF) - nBestAddress = pOffsetAddress; - } + if (pNote.second.GetPreviousAddress(nBeforeAddress, nPreviousAddress)) + nBestAddress = std::max(nBestAddress, nPreviousAddress); } } return nBestAddress; } -void CodeNotesModel::EnumerateCodeNotes(std::function callback, bool bIncludeDerived) const +void CodeNotesModel::EnumerateCodeNotes(std::function callback, bool bIncludeDerived) const { if (!bIncludeDerived || !m_bHasPointers) { @@ -904,15 +471,17 @@ void CodeNotesModel::EnumerateCodeNotes(std::function mNotes; + std::map mNotes; for (const auto& pIter : m_mCodeNotes) { - if (!pIter.second.PointerData) + if (!pIter.second.IsPointer()) continue; - const auto nPointerValue = pIter.second.PointerData->PointerValue; - for (const auto& pNote : pIter.second.PointerData->OffsetNotes) - mNotes[nPointerValue + pNote.Offset] = &pNote; + pIter.second.EnumeratePointerNotes( + [&mNotes](ra::ByteAddress nAddress, const CodeNoteModel& pNote) { + mNotes[nAddress] = &pNote; + return true; + }); } // merge in the non-pointer notes @@ -936,30 +505,26 @@ void CodeNotesModel::DoFrame() for (auto& pNote : m_mCodeNotes) { - if (!pNote.second.PointerData) - continue; - - const auto nNewRawAddress = pEmulatorContext.ReadMemory(pNote.first, pNote.second.MemSize); - if (nNewRawAddress == pNote.second.PointerData->RawPointerValue) + if (!pNote.second.IsPointer()) 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) - continue; - - pNote.second.PointerData->PointerValue = nNewAddress; - - if (m_fCodeNoteChanged) + const auto nOldAddress = pNote.second.GetPointerAddress(); + const auto nNewRawAddress = pEmulatorContext.ReadMemory(pNote.first, pNote.second.GetMemSize()); + if (pNote.second.SetRawPointerValue(nNewRawAddress)) { - for (const auto& pOffset : pNote.second.PointerData->OffsetNotes) - m_fCodeNoteChanged(nOldAddress + pOffset.Offset, L""); + pNote.second.EnumeratePointerNotes(nOldAddress, + [this](ra::ByteAddress nAddress, const CodeNoteModel&) + { + m_fCodeNoteChanged(nAddress, L""); + return true; + }); - for (const auto& pOffset : pNote.second.PointerData->OffsetNotes) - m_fCodeNoteChanged(nNewAddress + pOffset.Offset, pOffset.Note); + pNote.second.EnumeratePointerNotes( + [this](ra::ByteAddress nAddress, const CodeNoteModel& pOffsetNote) + { + m_fCodeNoteChanged(nAddress, pOffsetNote.GetNote()); + return true; + }); } } } @@ -975,7 +540,7 @@ void CodeNotesModel::SetServerCodeNote(ra::ByteAddress nAddress, const std::wstr // if we're just committing the current value, we're done const auto pIter2 = m_mCodeNotes.find(nAddress); - if (pIter2 != m_mCodeNotes.end() && pIter2->second.Note == sNote) + if (pIter2 != m_mCodeNotes.end() && pIter2->second.GetNote() == sNote) { if (sNote.empty()) m_mCodeNotes.erase(pIter2); @@ -1031,7 +596,7 @@ void CodeNotesModel::Serialize(ra::services::TextWriter& pWriter) const const auto pNote = m_mCodeNotes.find(pIter.first); if (pNote != m_mCodeNotes.end()) - WriteQuoted(pWriter, pNote->second.Note); + WriteQuoted(pWriter, pNote->second.GetNote()); else WriteQuoted(pWriter, ""); } diff --git a/src/data/models/CodeNotesModel.hh b/src/data/models/CodeNotesModel.hh index 0bb3d861..ade82c97 100644 --- a/src/data/models/CodeNotesModel.hh +++ b/src/data/models/CodeNotesModel.hh @@ -4,6 +4,8 @@ #include "AssetModelBase.hh" +#include "CodeNoteModel.hh" + #include "data/Types.hh" namespace ra { @@ -39,7 +41,7 @@ public: const std::wstring* FindCodeNote(ra::ByteAddress nAddress) const { const auto* pNote = FindCodeNoteInternal(nAddress); - return (pNote != nullptr) ? &pNote->Note : nullptr; + return (pNote != nullptr) ? &pNote->GetNote() : nullptr; } /// @@ -83,7 +85,7 @@ public: unsigned GetCodeNoteBytes(ra::ByteAddress nAddress) const { const auto* pNote = FindCodeNoteInternal(nAddress); - return (pNote == nullptr) ? 0 : pNote->Bytes; + return (pNote == nullptr) ? 0 : pNote->GetBytes(); } /// @@ -95,7 +97,7 @@ public: MemSize GetCodeNoteMemSize(ra::ByteAddress nAddress) const { const auto* pNote = FindCodeNoteInternal(nAddress); - return (pNote == nullptr) ? MemSize::Unknown : pNote->MemSize; + return (pNote == nullptr) ? MemSize::Unknown : pNote->GetMemSize(); } /// @@ -130,9 +132,9 @@ public: /// void EnumerateCodeNotes(std::function callback, bool bIncludeDerived = false) const { - EnumerateCodeNotes([callback](ra::ByteAddress nAddress, const CodeNote& pCodeNote) + EnumerateCodeNotes([callback](ra::ByteAddress nAddress, const CodeNoteModel& pCodeNote) { - return callback(nAddress, pCodeNote.Bytes, pCodeNote.Note); + return callback(nAddress, pCodeNote.GetBytes(), pCodeNote.GetNote()); }, bIncludeDerived); } @@ -193,46 +195,14 @@ protected: void AddCodeNote(ra::ByteAddress nAddress, const std::string& sAuthor, const std::wstring& sNote); void OnCodeNoteChanged(ra::ByteAddress nAddress, const std::wstring& sNewNote); - struct PointerData; - - struct CodeNote - { - std::string Author; - std::wstring Note; - unsigned int Bytes = 1; - MemSize MemSize = MemSize::Unknown; - std::unique_ptr PointerData; - }; - - struct OffsetCodeNote : public CodeNote - { - 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 OffsetNotes; - }; - - std::map m_mCodeNotes; + std::map m_mCodeNotes; std::map> m_mOriginalCodeNotes; std::map m_mPendingCodeNotes; - const CodeNote* FindCodeNoteInternal(ra::ByteAddress nAddress) const; - std::pair FindIndirectCodeNoteInternal(ra::ByteAddress nAddress) const; - void EnumerateCodeNotes(std::function callback, + const CodeNoteModel* FindCodeNoteInternal(ra::ByteAddress nAddress) const; + std::pair FindIndirectCodeNoteInternal(ra::ByteAddress nAddress) const; + void EnumerateCodeNotes(std::function callback, bool bIncludeDerived) const; unsigned int m_nGameId = 0; @@ -242,8 +212,8 @@ protected: CodeNoteChangedFunction m_fCodeNoteChanged; private: - static std::wstring BuildCodeNoteSized(ra::ByteAddress nAddress, unsigned nCheckBytes, ra::ByteAddress nNoteAddress, const CodeNote& pNote); - static void ExtractSize(CodeNote& pNote); + static std::wstring BuildCodeNoteSized(ra::ByteAddress nAddress, unsigned nCheckBytes, ra::ByteAddress nNoteAddress, const CodeNoteModel& pNote); + static void ExtractSize(CodeNoteModel& pNote); mutable std::mutex m_oMutex; }; @@ -252,4 +222,4 @@ private: } // namespace data } // namespace ra -#endif RA_DATA_LOCAL_BADGES_MODEL_H +#endif RA_DATA_CODE_NOTES_MODEL_H diff --git a/tests/RA_Integration.Tests.vcxproj b/tests/RA_Integration.Tests.vcxproj index 807e8f55..aed899ea 100644 --- a/tests/RA_Integration.Tests.vcxproj +++ b/tests/RA_Integration.Tests.vcxproj @@ -293,6 +293,7 @@ + @@ -358,6 +359,7 @@ + @@ -376,6 +378,7 @@ + diff --git a/tests/RA_Integration.Tests.vcxproj.filters b/tests/RA_Integration.Tests.vcxproj.filters index 590114eb..2a89fb56 100644 --- a/tests/RA_Integration.Tests.vcxproj.filters +++ b/tests/RA_Integration.Tests.vcxproj.filters @@ -468,6 +468,12 @@ Tests\Services + + Code + + + Tests\Data\Models + @@ -563,5 +569,8 @@ Code + + Code + \ No newline at end of file diff --git a/tests/data/models/CodeNoteModel_Tests.cpp b/tests/data/models/CodeNoteModel_Tests.cpp new file mode 100644 index 00000000..02e6c393 --- /dev/null +++ b/tests/data/models/CodeNoteModel_Tests.cpp @@ -0,0 +1,237 @@ +#include "CppUnitTest.h" + +#include "data\models\CodeNotesModel.hh" + +#include "services\impl\StringTextWriter.hh" + +#include "tests\RA_UnitTestHelpers.h" +#include "tests\data\DataAsserts.hh" + +#include "tests\mocks\MockConsoleContext.hh" +#include "tests\mocks\MockDesktop.hh" +#include "tests\mocks\MockEmulatorContext.hh" +#include "tests\mocks\MockServer.hh" +#include "tests\mocks\MockThreadPool.hh" +#include "tests\mocks\MockUserContext.hh" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace ra { +namespace data { +namespace models { +namespace tests { + +TEST_CLASS(CodeNoteModel_Tests) +{ +private: + class CodeNoteModelHarness : public CodeNoteModel + { + public: + ra::data::context::mocks::MockConsoleContext mockConsoleContext; + ra::data::context::mocks::MockEmulatorContext mockEmulatorContext; + }; + + void TestCodeNoteSize(const std::wstring& sNote, unsigned int nExpectedBytes, MemSize nExpectedSize) + { + CodeNoteModel note; + note.SetNote(sNote); + + Assert::AreEqual(nExpectedBytes, note.GetBytes(), sNote.c_str()); + Assert::AreEqual(nExpectedSize, note.GetMemSize(), sNote.c_str()); + } + + void AssertIndirectNote(const CodeNoteModel& note, unsigned int nOffset, + const std::wstring& sExpectedNote, MemSize nExpectedSize, unsigned int nExpectedBytes) + { + const auto* offsetNote = note.GetPointerNoteAtOffset(nOffset); + Assert::AreEqual(nExpectedSize, offsetNote->GetMemSize()); + Assert::AreEqual(nExpectedBytes, offsetNote->GetBytes()); + Assert::AreEqual(sExpectedNote, offsetNote->GetNote()); + } + +public: + TEST_METHOD(TestExtractSize) + { + TestCodeNoteSize(L"", 1U, MemSize::Unknown); + TestCodeNoteSize(L"Test", 1U, MemSize::Unknown); + TestCodeNoteSize(L"16-bit Test", 2U, MemSize::SixteenBit); + TestCodeNoteSize(L"Test 16-bit", 2U, MemSize::SixteenBit); + TestCodeNoteSize(L"Test 16-bi", 1U, MemSize::Unknown); + TestCodeNoteSize(L"[16-bit] Test", 2U, MemSize::SixteenBit); + TestCodeNoteSize(L"[16 bit] Test", 2U, MemSize::SixteenBit); + TestCodeNoteSize(L"[16 Bit] Test", 2U, MemSize::SixteenBit); + TestCodeNoteSize(L"[24-bit] Test", 3U, MemSize::TwentyFourBit); + TestCodeNoteSize(L"[32-bit] Test", 4U, MemSize::ThirtyTwoBit); + TestCodeNoteSize(L"[32 bit] Test", 4U, MemSize::ThirtyTwoBit); + TestCodeNoteSize(L"[32bit] Test", 4U, MemSize::ThirtyTwoBit); + TestCodeNoteSize(L"Test [16-bit]", 2U, MemSize::SixteenBit); + TestCodeNoteSize(L"Test (16-bit)", 2U, MemSize::SixteenBit); + TestCodeNoteSize(L"Test (16 bits)", 2U, MemSize::SixteenBit); + TestCodeNoteSize(L"[64-bit] Test", 8U, MemSize::Array); + TestCodeNoteSize(L"[128-bit] Test", 16U, MemSize::Array); + TestCodeNoteSize(L"[17-bit] Test", 3U, MemSize::TwentyFourBit); + TestCodeNoteSize(L"[100-bit] Test", 13U, MemSize::Array); + TestCodeNoteSize(L"[0-bit] Test", 1U, MemSize::Unknown); + TestCodeNoteSize(L"[1-bit] Test", 1U, MemSize::EightBit); + TestCodeNoteSize(L"[4-bit] Test", 1U, MemSize::EightBit); + TestCodeNoteSize(L"[8-bit] Test", 1U, MemSize::EightBit); + TestCodeNoteSize(L"[9-bit] Test", 2U, MemSize::SixteenBit); + TestCodeNoteSize(L"bit", 1U, MemSize::Unknown); + TestCodeNoteSize(L"9bit", 2U, MemSize::SixteenBit); + TestCodeNoteSize(L"-bit", 1U, MemSize::Unknown); + + TestCodeNoteSize(L"[16-bit BE] Test", 2U, MemSize::SixteenBitBigEndian); + TestCodeNoteSize(L"[24-bit BE] Test", 3U, MemSize::TwentyFourBitBigEndian); + TestCodeNoteSize(L"[32-bit BE] Test", 4U, MemSize::ThirtyTwoBitBigEndian); + TestCodeNoteSize(L"Test [32-bit BE]", 4U, MemSize::ThirtyTwoBitBigEndian); + TestCodeNoteSize(L"Test (32-bit BE)", 4U, MemSize::ThirtyTwoBitBigEndian); + TestCodeNoteSize(L"Test 32-bit BE", 4U, MemSize::ThirtyTwoBitBigEndian); + TestCodeNoteSize(L"[16-bit BigEndian] Test", 2U, MemSize::SixteenBitBigEndian); + TestCodeNoteSize(L"[16-bit-BE] Test", 2U, MemSize::SixteenBitBigEndian); + TestCodeNoteSize(L"[4-bit BE] Test", 1U, MemSize::EightBit); + + TestCodeNoteSize(L"8 BYTE Test", 8U, MemSize::Array); + TestCodeNoteSize(L"Test 8 BYTE", 8U, MemSize::Array); + TestCodeNoteSize(L"Test 8 BYT", 1U, MemSize::Unknown); + TestCodeNoteSize(L"[2 Byte] Test", 2U, MemSize::SixteenBit); + TestCodeNoteSize(L"[4 Byte] Test", 4U, MemSize::ThirtyTwoBit); + TestCodeNoteSize(L"[4 Byte - Float] Test", 4U, MemSize::Float); + TestCodeNoteSize(L"[8 Byte] Test", 8U, MemSize::Array); + TestCodeNoteSize(L"[100 Bytes] Test", 100U, MemSize::Array); + TestCodeNoteSize(L"[2 byte] Test", 2U, MemSize::SixteenBit); + TestCodeNoteSize(L"[2-byte] Test", 2U, MemSize::SixteenBit); + TestCodeNoteSize(L"Test (6 bytes)", 6U, MemSize::Array); + TestCodeNoteSize(L"[2byte] Test", 2U, MemSize::SixteenBit); + + TestCodeNoteSize(L"[float] Test", 4U, MemSize::Float); + TestCodeNoteSize(L"[float32] Test", 4U, MemSize::Float); + TestCodeNoteSize(L"Test float", 4U, MemSize::Float); + TestCodeNoteSize(L"Test floa", 1U, MemSize::Unknown); + TestCodeNoteSize(L"is floating", 1U, MemSize::Unknown); + TestCodeNoteSize(L"has floated", 1U, MemSize::Unknown); + TestCodeNoteSize(L"16-afloat", 1U, MemSize::Unknown); + TestCodeNoteSize(L"[float be] Test", 4U, MemSize::FloatBigEndian); + TestCodeNoteSize(L"[float bigendian] Test", 4U, MemSize::FloatBigEndian); + TestCodeNoteSize(L"[be float] Test", 4U, MemSize::FloatBigEndian); + TestCodeNoteSize(L"[bigendian float] Test", 4U, MemSize::FloatBigEndian); + TestCodeNoteSize(L"[32-bit] pointer to float", 4U, MemSize::ThirtyTwoBit); + + TestCodeNoteSize(L"[64-bit double] Test", 8U, MemSize::Double32); + TestCodeNoteSize(L"[64-bit double BE] Test", 8U, MemSize::Double32BigEndian); + TestCodeNoteSize(L"[double] Test", 8U, MemSize::Double32); + TestCodeNoteSize(L"[double BE] Test", 8U, MemSize::Double32BigEndian); + TestCodeNoteSize(L"[double32] Test", 4U, MemSize::Double32); + TestCodeNoteSize(L"[double32 BE] Test", 4U, MemSize::Double32BigEndian); + TestCodeNoteSize(L"[double64] Test", 8U, MemSize::Double32); + + TestCodeNoteSize(L"[MBF32] Test", 4U, MemSize::MBF32); + TestCodeNoteSize(L"[MBF40] Test", 5U, MemSize::MBF32); + TestCodeNoteSize(L"[MBF32 float] Test", 4U, MemSize::MBF32); + TestCodeNoteSize(L"[MBF80] Test", 1U, MemSize::Unknown); + TestCodeNoteSize(L"[MBF320] Test", 1U, MemSize::Unknown); + TestCodeNoteSize(L"[MBF-32] Test", 4U, MemSize::MBF32); + TestCodeNoteSize(L"[32-bit MBF] Test", 4U, MemSize::MBF32); + TestCodeNoteSize(L"[40-bit MBF] Test", 5U, MemSize::MBF32); + TestCodeNoteSize(L"[MBF] Test", 1U, MemSize::Unknown); + TestCodeNoteSize(L"Test MBF32", 4U, MemSize::MBF32); + TestCodeNoteSize(L"[MBF32 LE] Test", 4U, MemSize::MBF32LE); + TestCodeNoteSize(L"[MBF40-LE] Test", 5U, MemSize::MBF32LE); + + TestCodeNoteSize(L"42=bitten", 1U, MemSize::Unknown); + TestCodeNoteSize(L"42-bitten", 1U, MemSize::Unknown); + TestCodeNoteSize(L"bit by bit", 1U, MemSize::Unknown); + TestCodeNoteSize(L"bit1=chest", 1U, MemSize::Unknown); + + TestCodeNoteSize(L"Bite count (16-bit)", 2U, MemSize::SixteenBit); + TestCodeNoteSize(L"Number of bits collected (32 bits)", 4U, MemSize::ThirtyTwoBit); + + TestCodeNoteSize(L"100 32-bit pointers [400 bytes]", 400U, MemSize::Array); + TestCodeNoteSize(L"[400 bytes] 100 32-bit pointers", 400U, MemSize::Array); + } + + TEST_METHOD(TestGetPointerNoteAtOffset) + { + CodeNoteModelHarness note; + const std::wstring sNote = + L"Bomb Timer Pointer (24-bit)\n" + L"+03 - Bombs Defused\n" + L"+04 - Bomb Timer"; + note.SetNote(sNote); + + Assert::AreEqual(MemSize::TwentyFourBit, note.GetMemSize()); + Assert::AreEqual(sNote, note.GetNote()); // full note for pointer address + + // extracted notes for offset fields + AssertIndirectNote(note, 3U, L"Bombs Defused", MemSize::Unknown, 1); + AssertIndirectNote(note, 4U, L"Bomb Timer", MemSize::Unknown, 1); + } + + TEST_METHOD(TestGetPointerNoteAtOffsetMultiline) + { + CodeNoteModelHarness note; + const std::wstring sNote = + L"Pointer [32bit]\n" + L"+0x1BC | Equipment - Head - String[24 Bytes]\n" + L"---DEFAULT_HEAD = Barry's Head\n" + L"---FRAGGER_HEAD = Fragger Helmet"; + note.SetNote(sNote); + + Assert::AreEqual(MemSize::ThirtyTwoBit, note.GetMemSize()); + Assert::AreEqual(sNote, note.GetNote()); // full note for pointer address + + // extracted notes for offset fields + AssertIndirectNote(note, 0x1BCU, + L"Equipment - Head - String[24 Bytes]\n" + L"---DEFAULT_HEAD = Barry's Head\n" + L"---FRAGGER_HEAD = Fragger Helmet", + MemSize::Array, 24); + } + + TEST_METHOD(TestCodeNoteHeadered) + { + CodeNoteModelHarness note; + const std::wstring sNote = + L"Pointer (16bit because negative)\n\n" + L"Circuit:\n" + L"+0x1B56E = Current Position\n" + L"+0x1B57E = Total Racers\n\n" + L"Free Run:\n" + L"+0x1B5BE = Seconds 0x\n" + L"+0x1B5CE = Lap"; + note.SetNote(sNote); + + Assert::AreEqual(MemSize::SixteenBit, note.GetMemSize()); + Assert::AreEqual(sNote, note.GetNote()); // full note for pointer address + + // extracted notes for offset fields (note: pointer base default is $0000) + AssertIndirectNote(note, 0x1B56EU, L"Current Position", MemSize::Unknown, 1); + AssertIndirectNote(note, 0x1B57EU, L"Total Racers\n\nFree Run:", MemSize::Unknown, 1); + AssertIndirectNote(note, 0x1B5BEU, L"Seconds 0x", MemSize::Unknown, 1); + AssertIndirectNote(note, 0x1B5CEU, L"Lap", MemSize::Unknown, 1); + } + + TEST_METHOD(TestCodeNotePointerOverlap) + { + CodeNoteModelHarness note; + const std::wstring sNote = + L"Pointer\r\n" + L"[OFFSETS]\r\n" + L"+2 = EXP (32-bit)\r\n" + L"+5 = Base Level (8-bit)\r\n" // 32-bit value at 2 continues into 5 + L"+6 = Job Level (8-bit)"; + note.SetNote(sNote); + + Assert::AreEqual(MemSize::ThirtyTwoBit, note.GetMemSize()); + Assert::AreEqual(sNote, note.GetNote()); // full note for pointer address + + // extracted notes for offset fields (note: pointer base default is $0000) + AssertIndirectNote(note, 2, L"EXP (32-bit)", MemSize::ThirtyTwoBit, 4); + AssertIndirectNote(note, 5, L"Base Level (8-bit)", MemSize::EightBit, 1); + AssertIndirectNote(note, 6, L"Job Level (8-bit)", MemSize::EightBit, 1); + } +}; + +} // namespace tests +} // namespace models +} // namespace data +} // namespace ra diff --git a/tests/data/models/CodeNotesModel_Tests.cpp b/tests/data/models/CodeNotesModel_Tests.cpp index 27ab6059..1dca269e 100644 --- a/tests/data/models/CodeNotesModel_Tests.cpp +++ b/tests/data/models/CodeNotesModel_Tests.cpp @@ -159,122 +159,6 @@ TEST_CLASS(CodeNotesModel_Tests) Assert::AreEqual({0U}, notes.mNewNotes.size()); } - void TestCodeNoteSize(const std::wstring& sNote, unsigned int nExpectedBytes, MemSize nExpectedSize) - { - CodeNotesModelHarness notes; - notes.mockServer.HandleRequest([&sNote](const ra::api::FetchCodeNotes::Request& request, ra::api::FetchCodeNotes::Response& response) - { - Assert::AreEqual(1U, request.GameId); - - response.Notes.emplace_back(ra::api::FetchCodeNotes::Response::CodeNote{ 1234, sNote, "Author" }); - return true; - }); - - notes.InitializeCodeNotes(1U); - - Assert::AreEqual(nExpectedBytes, notes.GetCodeNoteBytes(1234U), sNote.c_str()); - Assert::AreEqual(nExpectedSize, notes.GetCodeNoteMemSize(1234U), sNote.c_str()); - } - - TEST_METHOD(TestLoadCodeNotesSized) - { - TestCodeNoteSize(L"", 1U, MemSize::Unknown); - TestCodeNoteSize(L"Test", 1U, MemSize::Unknown); - TestCodeNoteSize(L"16-bit Test", 2U, MemSize::SixteenBit); - TestCodeNoteSize(L"Test 16-bit", 2U, MemSize::SixteenBit); - TestCodeNoteSize(L"Test 16-bi", 1U, MemSize::Unknown); - TestCodeNoteSize(L"[16-bit] Test", 2U, MemSize::SixteenBit); - TestCodeNoteSize(L"[16 bit] Test", 2U, MemSize::SixteenBit); - TestCodeNoteSize(L"[16 Bit] Test", 2U, MemSize::SixteenBit); - TestCodeNoteSize(L"[24-bit] Test", 3U, MemSize::TwentyFourBit); - TestCodeNoteSize(L"[32-bit] Test", 4U, MemSize::ThirtyTwoBit); - TestCodeNoteSize(L"[32 bit] Test", 4U, MemSize::ThirtyTwoBit); - TestCodeNoteSize(L"[32bit] Test", 4U, MemSize::ThirtyTwoBit); - TestCodeNoteSize(L"Test [16-bit]", 2U, MemSize::SixteenBit); - TestCodeNoteSize(L"Test (16-bit)", 2U, MemSize::SixteenBit); - TestCodeNoteSize(L"Test (16 bits)", 2U, MemSize::SixteenBit); - TestCodeNoteSize(L"[64-bit] Test", 8U, MemSize::Array); - TestCodeNoteSize(L"[128-bit] Test", 16U, MemSize::Array); - TestCodeNoteSize(L"[17-bit] Test", 3U, MemSize::TwentyFourBit); - TestCodeNoteSize(L"[100-bit] Test", 13U, MemSize::Array); - TestCodeNoteSize(L"[0-bit] Test", 1U, MemSize::Unknown); - TestCodeNoteSize(L"[1-bit] Test", 1U, MemSize::EightBit); - TestCodeNoteSize(L"[4-bit] Test", 1U, MemSize::EightBit); - TestCodeNoteSize(L"[8-bit] Test", 1U, MemSize::EightBit); - TestCodeNoteSize(L"[9-bit] Test", 2U, MemSize::SixteenBit); - TestCodeNoteSize(L"bit", 1U, MemSize::Unknown); - TestCodeNoteSize(L"9bit", 2U, MemSize::SixteenBit); - TestCodeNoteSize(L"-bit", 1U, MemSize::Unknown); - - TestCodeNoteSize(L"[16-bit BE] Test", 2U, MemSize::SixteenBitBigEndian); - TestCodeNoteSize(L"[24-bit BE] Test", 3U, MemSize::TwentyFourBitBigEndian); - TestCodeNoteSize(L"[32-bit BE] Test", 4U, MemSize::ThirtyTwoBitBigEndian); - TestCodeNoteSize(L"Test [32-bit BE]", 4U, MemSize::ThirtyTwoBitBigEndian); - TestCodeNoteSize(L"Test (32-bit BE)", 4U, MemSize::ThirtyTwoBitBigEndian); - TestCodeNoteSize(L"Test 32-bit BE", 4U, MemSize::ThirtyTwoBitBigEndian); - TestCodeNoteSize(L"[16-bit BigEndian] Test", 2U, MemSize::SixteenBitBigEndian); - TestCodeNoteSize(L"[16-bit-BE] Test", 2U, MemSize::SixteenBitBigEndian); - TestCodeNoteSize(L"[4-bit BE] Test", 1U, MemSize::EightBit); - - TestCodeNoteSize(L"8 BYTE Test", 8U, MemSize::Array); - TestCodeNoteSize(L"Test 8 BYTE", 8U, MemSize::Array); - TestCodeNoteSize(L"Test 8 BYT", 1U, MemSize::Unknown); - TestCodeNoteSize(L"[2 Byte] Test", 2U, MemSize::SixteenBit); - TestCodeNoteSize(L"[4 Byte] Test", 4U, MemSize::ThirtyTwoBit); - TestCodeNoteSize(L"[4 Byte - Float] Test", 4U, MemSize::Float); - TestCodeNoteSize(L"[8 Byte] Test", 8U, MemSize::Array); - TestCodeNoteSize(L"[100 Bytes] Test", 100U, MemSize::Array); - TestCodeNoteSize(L"[2 byte] Test", 2U, MemSize::SixteenBit); - TestCodeNoteSize(L"[2-byte] Test", 2U, MemSize::SixteenBit); - TestCodeNoteSize(L"Test (6 bytes)", 6U, MemSize::Array); - TestCodeNoteSize(L"[2byte] Test", 2U, MemSize::SixteenBit); - - TestCodeNoteSize(L"[float] Test", 4U, MemSize::Float); - TestCodeNoteSize(L"[float32] Test", 4U, MemSize::Float); - TestCodeNoteSize(L"Test float", 4U, MemSize::Float); - TestCodeNoteSize(L"Test floa", 1U, MemSize::Unknown); - TestCodeNoteSize(L"is floating", 1U, MemSize::Unknown); - TestCodeNoteSize(L"has floated", 1U, MemSize::Unknown); - TestCodeNoteSize(L"16-afloat", 1U, MemSize::Unknown); - TestCodeNoteSize(L"[float be] Test", 4U, MemSize::FloatBigEndian); - TestCodeNoteSize(L"[float bigendian] Test", 4U, MemSize::FloatBigEndian); - TestCodeNoteSize(L"[be float] Test", 4U, MemSize::FloatBigEndian); - TestCodeNoteSize(L"[bigendian float] Test", 4U, MemSize::FloatBigEndian); - TestCodeNoteSize(L"[32-bit] pointer to float", 4U, MemSize::ThirtyTwoBit); - - TestCodeNoteSize(L"[64-bit double] Test", 8U, MemSize::Double32); - TestCodeNoteSize(L"[64-bit double BE] Test", 8U, MemSize::Double32BigEndian); - TestCodeNoteSize(L"[double] Test", 8U, MemSize::Double32); - TestCodeNoteSize(L"[double BE] Test", 8U, MemSize::Double32BigEndian); - TestCodeNoteSize(L"[double32] Test", 4U, MemSize::Double32); - TestCodeNoteSize(L"[double32 BE] Test", 4U, MemSize::Double32BigEndian); - TestCodeNoteSize(L"[double64] Test", 8U, MemSize::Double32); - - TestCodeNoteSize(L"[MBF32] Test", 4U, MemSize::MBF32); - TestCodeNoteSize(L"[MBF40] Test", 5U, MemSize::MBF32); - TestCodeNoteSize(L"[MBF32 float] Test", 4U, MemSize::MBF32); - TestCodeNoteSize(L"[MBF80] Test", 1U, MemSize::Unknown); - TestCodeNoteSize(L"[MBF320] Test", 1U, MemSize::Unknown); - TestCodeNoteSize(L"[MBF-32] Test", 4U, MemSize::MBF32); - TestCodeNoteSize(L"[32-bit MBF] Test", 4U, MemSize::MBF32); - TestCodeNoteSize(L"[40-bit MBF] Test", 5U, MemSize::MBF32); - TestCodeNoteSize(L"[MBF] Test", 1U, MemSize::Unknown); - TestCodeNoteSize(L"Test MBF32", 4U, MemSize::MBF32); - TestCodeNoteSize(L"[MBF32 LE] Test", 4U, MemSize::MBF32LE); - TestCodeNoteSize(L"[MBF40-LE] Test", 5U, MemSize::MBF32LE); - - TestCodeNoteSize(L"42=bitten", 1U, MemSize::Unknown); - TestCodeNoteSize(L"42-bitten", 1U, MemSize::Unknown); - TestCodeNoteSize(L"bit by bit", 1U, MemSize::Unknown); - TestCodeNoteSize(L"bit1=chest", 1U, MemSize::Unknown); - - TestCodeNoteSize(L"Bite count (16-bit)", 2U, MemSize::SixteenBit); - TestCodeNoteSize(L"Number of bits collected (32 bits)", 4U, MemSize::ThirtyTwoBit); - - TestCodeNoteSize(L"100 32-bit pointers [400 bytes]", 400U, MemSize::Array); - TestCodeNoteSize(L"[400 bytes] 100 32-bit pointers", 400U, MemSize::Array); - } - TEST_METHOD(TestFindCodeNoteSized) { CodeNotesModelHarness notes;