Skip to content

Commit 1bd0c3d

Browse files
authored
extract value assignments from tooltip (#1271)
1 parent 96557f0 commit 1bd0c3d

6 files changed

Lines changed: 441 additions & 0 deletions

File tree

src/data/models/CodeNoteModel.cpp

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,244 @@ void CodeNoteModel::EnumeratePointerNotes(ra::ByteAddress nPointerAddress,
12371237
}
12381238
}
12391239

1240+
static uint32_t Convert(std::wstring_view svValue, bool isHex) noexcept
1241+
{
1242+
uint32_t nValue = 0;
1243+
if (isHex)
1244+
{
1245+
for (const auto c : svValue)
1246+
{
1247+
nValue <<= 4;
1248+
if (c <= '9')
1249+
nValue |= (c - '0');
1250+
else if (c <= 'F')
1251+
nValue |= (c - 'A' + 10);
1252+
else
1253+
nValue |= (c - 'a' + 10);
1254+
}
1255+
}
1256+
else
1257+
{
1258+
for (const auto c : svValue)
1259+
{
1260+
nValue *= 10;
1261+
nValue += (c - '0');
1262+
}
1263+
}
1264+
1265+
return nValue;
1266+
}
1267+
1268+
static bool ParseRange(std::wstring_view svRange, uint32_t& nLow, uint32_t& nHigh, bool isHex)
1269+
{
1270+
if (svRange.size() > 2 && svRange.at(0) == L'0' && svRange.at(1) == L'x')
1271+
svRange = svRange.substr(2);
1272+
else if (svRange.size() > 1 && (svRange.at(0) == L'h' || svRange.at(0) == L'H'))
1273+
svRange = svRange.substr(1);
1274+
1275+
size_t nIndex = 0;
1276+
while (nIndex < svRange.size() && IsHexDigit(svRange.at(nIndex)))
1277+
++nIndex;
1278+
1279+
if (nIndex == 0)
1280+
return false;
1281+
1282+
const auto svLow = svRange.substr(0, nIndex);
1283+
std::wstring_view svHigh;
1284+
1285+
while (nIndex < svRange.size() && isspace(svRange.at(nIndex)))
1286+
++nIndex;
1287+
1288+
if (nIndex < svRange.size())
1289+
{
1290+
if (svRange.at(nIndex++) != '-')
1291+
return false;
1292+
1293+
const auto nStart = nIndex;
1294+
while (nIndex < svRange.size() && IsHexDigit(svRange.at(nIndex)))
1295+
++nIndex;
1296+
1297+
if (nIndex == nStart)
1298+
return false;
1299+
1300+
svHigh = svRange.substr(nStart, nIndex);
1301+
}
1302+
1303+
nLow = Convert(svLow, isHex);
1304+
nHigh = svHigh.empty() ? nLow : Convert(svHigh, isHex);
1305+
1306+
return true;
1307+
}
1308+
1309+
static bool MatchEnumText(const std::wstring_view svValue, uint32_t nValue, bool isHex)
1310+
{
1311+
const auto nSplit = svValue.find_first_of(L"=:");
1312+
if (nSplit == std::wstring::npos)
1313+
return false;
1314+
1315+
const auto svLeft = svValue.substr(0, nSplit);
1316+
1317+
uint32_t nLow, nHigh;
1318+
if (ParseRange(svLeft, nLow, nHigh, isHex))
1319+
{
1320+
if (nValue >= nLow && nValue <= nHigh)
1321+
return true;
1322+
}
1323+
else if (svValue.at(nSplit) == L'=')
1324+
{
1325+
auto svRight = svValue.substr(nSplit + 1);
1326+
while (!svRight.empty() && isspace(svRight.at(0)))
1327+
svRight.remove_prefix(1);
1328+
1329+
if (ParseRange(svRight, nLow, nHigh, isHex))
1330+
{
1331+
if (nValue >= nLow && nValue <= nHigh)
1332+
return true;
1333+
}
1334+
}
1335+
1336+
return false;
1337+
}
1338+
1339+
static std::wstring_view GetValues(const std::wstring_view svLine)
1340+
{
1341+
const auto nSplit = svLine.find_first_of(L"=:");
1342+
if (nSplit == std::wstring::npos)
1343+
return {};
1344+
1345+
size_t nRightBracket = svLine.size() + 1;
1346+
const auto nLeftBracket = svLine.find_last_of(L"([{", nSplit);
1347+
if (nLeftBracket == std::wstring::npos)
1348+
return svLine;
1349+
1350+
switch (svLine.at(nLeftBracket))
1351+
{
1352+
case '(':
1353+
nRightBracket = svLine.find(L')', nSplit);
1354+
break;
1355+
case '[':
1356+
nRightBracket = svLine.find(L']', nSplit);
1357+
break;
1358+
case '{':
1359+
nRightBracket = svLine.find(L'}', nSplit);
1360+
break;
1361+
}
1362+
1363+
if (nRightBracket == std::wstring::npos)
1364+
return svLine;
1365+
1366+
return svLine.substr(nLeftBracket + 1, nRightBracket - nLeftBracket - 1);
1367+
}
1368+
1369+
CodeNoteModel::EnumState CodeNoteModel::DetermineEnumState(const std::wstring_view svNote)
1370+
{
1371+
EnumState nState = EnumState::None;
1372+
size_t nStart = 0;
1373+
while (nStart < svNote.size())
1374+
{
1375+
auto nEnd = svNote.find_first_of(L"\n\r", nStart);
1376+
1377+
if (nStart != nEnd)
1378+
{
1379+
const auto svLine = svNote.substr(nStart, nEnd - nStart);
1380+
const auto svValues = GetValues(svLine);
1381+
if (!svValues.empty())
1382+
{
1383+
size_t nFront = 0;
1384+
do {
1385+
const auto nComma = svValues.find_first_of(L",;", nFront);
1386+
const auto svValue = (nComma == std::wstring::npos) ? svValues.substr(nFront) : svValues.substr(nFront, nComma - nFront);
1387+
1388+
const auto nSplit = svValue.find_first_of(L"=:");
1389+
if (nSplit != std::wstring::npos)
1390+
{
1391+
uint32_t nLow, nHigh;
1392+
const auto svLeft = svValue.substr(0, nSplit);
1393+
if (ParseRange(svLeft, nLow, nHigh, true))
1394+
{
1395+
if (svLeft.find_first_of(L"ABCDEFabcdefHhx") != std::wstring::npos)
1396+
return EnumState::Hex;
1397+
1398+
nState = EnumState::Dec;
1399+
}
1400+
}
1401+
1402+
if (nComma == std::wstring::npos)
1403+
break;
1404+
1405+
nFront = nComma + 1;
1406+
while (nFront < svValues.size() && isspace(svValues.at(nFront)))
1407+
++nFront;
1408+
} while (nFront < svValues.size());
1409+
}
1410+
}
1411+
1412+
while (nEnd < svNote.size() && (svNote.at(nEnd) == L'\n' || svNote.at(nEnd) == L'\r'))
1413+
++nEnd;
1414+
1415+
nStart = nEnd;
1416+
}
1417+
1418+
return nState;
1419+
1420+
}
1421+
1422+
std::wstring_view CodeNoteModel::GetEnumText(uint32_t nValue) const
1423+
{
1424+
if (m_nEnumState == EnumState::None)
1425+
return {};
1426+
1427+
std::wstring_view svNote(m_sNote);
1428+
1429+
if (m_pPointerData != nullptr)
1430+
svNote = svNote.substr(0, m_pPointerData->HeaderLength);
1431+
1432+
if (m_nEnumState == EnumState::Unknown)
1433+
{
1434+
m_nEnumState = DetermineEnumState(svNote);
1435+
if (m_nEnumState == EnumState::None)
1436+
return {};
1437+
}
1438+
1439+
const bool isHex = (m_nEnumState == EnumState::Hex);
1440+
size_t nStart = 0;
1441+
while (nStart < svNote.size())
1442+
{
1443+
auto nEnd = svNote.find_first_of(L"\n\r", nStart);
1444+
1445+
if (nStart != nEnd)
1446+
{
1447+
const auto svLine = svNote.substr(nStart, nEnd - nStart);
1448+
const auto svValues = GetValues(svLine);
1449+
if (!svValues.empty())
1450+
{
1451+
size_t nFront = 0;
1452+
do {
1453+
const auto nComma = svValues.find_first_of(L",;", nFront);
1454+
const auto svValue = (nComma == std::wstring::npos) ? svValues.substr(nFront) : svValues.substr(nFront, nComma - nFront);
1455+
1456+
if (MatchEnumText(svValue, nValue, isHex))
1457+
return svValue;
1458+
1459+
if (nComma == std::wstring::npos)
1460+
break;
1461+
1462+
nFront = nComma + 1;
1463+
while (nFront < svValues.size() && isspace(svValues.at(nFront)))
1464+
++nFront;
1465+
} while (nFront < svValues.size());
1466+
}
1467+
}
1468+
1469+
while (nEnd < svNote.size() && (svNote.at(nEnd) == L'\n' || svNote.at(nEnd) == L'\r'))
1470+
++nEnd;
1471+
1472+
nStart = nEnd;
1473+
}
1474+
1475+
return {};
1476+
}
1477+
12401478
} // namespace models
12411479
} // namespace data
12421480
} // namespace ra

src/data/models/CodeNoteModel.hh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public:
2626
const unsigned int GetBytes() const noexcept { return m_nBytes; }
2727
const MemSize GetMemSize() const noexcept { return m_nMemSize; }
2828
const MemFormat GetDefaultMemFormat() const noexcept { return m_nMemFormat; }
29+
std::wstring_view GetEnumText(uint32_t nValue) const;
2930

3031
void SetAuthor(const std::string& sAuthor) { m_sAuthor = sAuthor; }
3132
void SetAddress(ra::ByteAddress nAddress) noexcept { m_nAddress = nAddress; }
@@ -99,6 +100,15 @@ private:
99100
MemSize m_nMemSize = MemSize::Unknown;
100101
MemFormat m_nMemFormat = MemFormat::Dec;
101102

103+
enum EnumState {
104+
None,
105+
Hex,
106+
Dec,
107+
Unknown,
108+
};
109+
mutable EnumState m_nEnumState = EnumState::Unknown;
110+
static EnumState DetermineEnumState(const std::wstring_view svNote);
111+
102112
struct PointerData;
103113
std::unique_ptr<PointerData> m_pPointerData;
104114

src/ui/viewmodels/TriggerConditionViewModel.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,12 @@ std::wstring TriggerConditionViewModel::GetTooltip(const StringModelProperty& nP
431431
{
432432
const auto nType = GetSourceType();
433433
if (nType == TriggerOperandType::Value)
434+
{
435+
if (IsAddressType(GetTargetType()) && ra::data::MemSizeBits(GetTargetSize()) >= 8)
436+
return GetPotentialEnumValueTooltip(GetSourceAddress(), GetTargetAddress());
437+
434438
return GetValueTooltip(GetSourceAddress());
439+
}
435440

436441
if (nType == TriggerOperandType::Float)
437442
return L"";
@@ -455,7 +460,12 @@ std::wstring TriggerConditionViewModel::GetTooltip(const StringModelProperty& nP
455460
{
456461
const auto nType = GetTargetType();
457462
if (nType == TriggerOperandType::Value)
463+
{
464+
if (IsAddressType(GetSourceType()) && ra::data::MemSizeBits(GetSourceSize()) >= 8)
465+
return GetPotentialEnumValueTooltip(GetTargetAddress(), GetSourceAddress());
466+
458467
return GetValueTooltip(GetTargetAddress());
468+
}
459469

460470
if (nType == TriggerOperandType::Float)
461471
return L"";
@@ -497,6 +507,33 @@ std::wstring TriggerConditionViewModel::GetTooltip(const IntModelProperty& nProp
497507
return L"";
498508
}
499509

510+
std::wstring TriggerConditionViewModel::GetPotentialEnumValueTooltip(unsigned int nValue, ra::ByteAddress nCompareAddress) const
511+
{
512+
const ra::data::models::CodeNoteModel* pNote = nullptr;
513+
514+
if (IsIndirect())
515+
{
516+
std::wstring sPointerChain;
517+
GetIndirectAddress(nCompareAddress, sPointerChain, &pNote);
518+
}
519+
else
520+
{
521+
const auto& pGameContext = ra::services::ServiceLocator::Get<ra::data::context::GameContext>();
522+
const auto* pCodeNotes = pGameContext.Assets().FindCodeNotes();
523+
if (pCodeNotes)
524+
pNote = pCodeNotes->FindCodeNoteModel(nCompareAddress);
525+
}
526+
527+
if (pNote != nullptr)
528+
{
529+
const auto pEnumText = pNote->GetEnumText(nValue);
530+
if (!pEnumText.empty())
531+
return std::wstring(pEnumText);
532+
}
533+
534+
return GetValueTooltip(nValue);
535+
}
536+
500537
std::wstring TriggerConditionViewModel::GetValueTooltip(unsigned int nValue)
501538
{
502539
const auto& pConfiguration = ra::services::ServiceLocator::Get<ra::services::IConfiguration>();

src/ui/viewmodels/TriggerConditionViewModel.hh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ private:
119119

120120
void SerializeAppendOperand(std::string& sBuffer, TriggerOperandType nType, MemSize nSize, const std::wstring& nValue) const;
121121

122+
std::wstring GetPotentialEnumValueTooltip(unsigned int nValue, ra::ByteAddress nCompareAddress) const;
122123
static std::wstring GetValueTooltip(unsigned int nValue);
123124
std::wstring GetAddressTooltip(ra::ByteAddress nAddress, const std::wstring& sPointerChain, const ra::data::models::CodeNoteModel* pNote) const;
124125
std::wstring GetRecallTooltip(bool bOperand2) const;

0 commit comments

Comments
 (0)