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;