416 lines
13 KiB
C++
416 lines
13 KiB
C++
#include "watch.h"
|
|
#include "console.h"
|
|
#include "threading.h"
|
|
#include "debugger.h"
|
|
#include "taskthread.h"
|
|
#include <Windows.h>
|
|
|
|
std::map<unsigned int, WatchExpr*> watchexpr;
|
|
unsigned int idCounter = 1;
|
|
|
|
WatchExpr::WatchExpr(const char* name, const char* expression, WATCHVARTYPE type) :
|
|
expr(expression),
|
|
haveCurrValue(false),
|
|
varType(type), currValue(0),
|
|
watchdogTriggered(false),
|
|
watchWindow(0),
|
|
watchdogMode(MODE_DISABLED)
|
|
{
|
|
if(!expr.IsValidExpression())
|
|
varType = WATCHVARTYPE::TYPE_INVALID;
|
|
strcpy_s(this->WatchName, name);
|
|
}
|
|
|
|
duint WatchExpr::getIntValue()
|
|
{
|
|
duint origVal = currValue;
|
|
if(varType == WATCHVARTYPE::TYPE_UINT || varType == WATCHVARTYPE::TYPE_INT || varType == WATCHVARTYPE::TYPE_ASCII || varType == WATCHVARTYPE::TYPE_UNICODE)
|
|
{
|
|
duint val;
|
|
bool ok = expr.Calculate(val, varType == WATCHVARTYPE::TYPE_INT, false);
|
|
if(ok)
|
|
{
|
|
currValue = val;
|
|
haveCurrValue = true;
|
|
if(getType() != WATCHVARTYPE::TYPE_INVALID)
|
|
{
|
|
switch(getWatchdogMode())
|
|
{
|
|
default:
|
|
case WATCHDOGMODE::MODE_DISABLED:
|
|
break;
|
|
case WATCHDOGMODE::MODE_ISTRUE:
|
|
if(currValue != 0)
|
|
{
|
|
duint cip = GetContextDataEx(hActiveThread, UE_CIP);
|
|
dprintf(QT_TRANSLATE_NOOP("DBG", "Watchdog %s (expression \"%s\") is triggered at %p ! Original value: %p, New value: %p\n"), WatchName, getExpr().c_str(), cip, origVal, currValue);
|
|
watchdogTriggered = 1;
|
|
}
|
|
break;
|
|
case WATCHDOGMODE::MODE_ISFALSE:
|
|
if(currValue == 0)
|
|
{
|
|
duint cip = GetContextDataEx(hActiveThread, UE_CIP);
|
|
dprintf(QT_TRANSLATE_NOOP("DBG", "Watchdog %s (expression \"%s\") is triggered at %p ! Original value: %p, New value: %p\n"), WatchName, getExpr().c_str(), cip, origVal, currValue);
|
|
watchdogTriggered = 1;
|
|
}
|
|
break;
|
|
case WATCHDOGMODE::MODE_CHANGED:
|
|
if(currValue != origVal || !haveCurrValue)
|
|
{
|
|
duint cip = GetContextDataEx(hActiveThread, UE_CIP);
|
|
dprintf(QT_TRANSLATE_NOOP("DBG", "Watchdog %s (expression \"%s\") is triggered at %p ! Original value: %p, New value: %p\n"), WatchName, getExpr().c_str(), cip, origVal, currValue);
|
|
watchdogTriggered = 1;
|
|
}
|
|
break;
|
|
case WATCHDOGMODE::MODE_UNCHANGED:
|
|
if(currValue == origVal || !haveCurrValue)
|
|
{
|
|
duint cip = GetContextDataEx(hActiveThread, UE_CIP);
|
|
dprintf(QT_TRANSLATE_NOOP("DBG", "Watchdog %s (expression \"%s\") is triggered at %p ! Original value: %p, New value: %p\n"), WatchName, getExpr().c_str(), cip, origVal, currValue);
|
|
watchdogTriggered = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return val;
|
|
}
|
|
}
|
|
currValue = 0;
|
|
haveCurrValue = false;
|
|
return 0;
|
|
}
|
|
|
|
bool WatchExpr::modifyExpr(const char* expression, WATCHVARTYPE type)
|
|
{
|
|
ExpressionParser b(expression);
|
|
if(b.IsValidExpression())
|
|
{
|
|
expr = b;
|
|
varType = type;
|
|
currValue = 0;
|
|
haveCurrValue = false;
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void WatchExpr::modifyName(const char* newName)
|
|
{
|
|
strcpy_s(WatchName, newName);
|
|
}
|
|
|
|
// Update GUI
|
|
|
|
void GuiUpdateWatchViewAsync()
|
|
{
|
|
static TaskThread_<decltype(&GuiUpdateWatchView)> task(&GuiUpdateWatchView);
|
|
task.WakeUp();
|
|
}
|
|
|
|
// Global functions
|
|
// Clear all watch
|
|
void WatchClear()
|
|
{
|
|
if(!watchexpr.empty())
|
|
{
|
|
for(auto i : watchexpr)
|
|
delete i.second;
|
|
watchexpr.clear();
|
|
}
|
|
}
|
|
|
|
unsigned int WatchAddExprUnlocked(const char* expr, WATCHVARTYPE type)
|
|
{
|
|
unsigned int newid = InterlockedExchangeAdd((volatile long*)&idCounter, 1);
|
|
char DefaultName[MAX_WATCH_NAME_SIZE];
|
|
sprintf_s(DefaultName, GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "Watch %u")), newid);
|
|
WatchExpr* newWatch = new WatchExpr(DefaultName, expr, type);
|
|
auto temp = watchexpr.emplace(std::make_pair(newid, newWatch));
|
|
if(temp.second)
|
|
{
|
|
newWatch->getIntValue();
|
|
return newid;
|
|
}
|
|
else
|
|
{
|
|
delete newWatch;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
unsigned int WatchAddExpr(const char* expr, WATCHVARTYPE type)
|
|
{
|
|
EXCLUSIVE_ACQUIRE(LockWatch);
|
|
unsigned int id = WatchAddExprUnlocked(expr, type);
|
|
EXCLUSIVE_RELEASE();
|
|
GuiUpdateWatchView();
|
|
return id;
|
|
}
|
|
|
|
bool WatchModifyExpr(unsigned int id, const char* expr, WATCHVARTYPE type)
|
|
{
|
|
EXCLUSIVE_ACQUIRE(LockWatch);
|
|
auto obj = watchexpr.find(id);
|
|
if(obj != watchexpr.end())
|
|
{
|
|
bool success = obj->second->modifyExpr(expr, type);
|
|
EXCLUSIVE_RELEASE();
|
|
GuiUpdateWatchView();
|
|
return success;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void WatchModifyNameUnlocked(unsigned int id, const char* newName)
|
|
{
|
|
auto obj = watchexpr.find(id);
|
|
if(obj != watchexpr.end())
|
|
{
|
|
obj->second->modifyName(newName);
|
|
}
|
|
}
|
|
|
|
void WatchModifyName(unsigned int id, const char* newName)
|
|
{
|
|
EXCLUSIVE_ACQUIRE(LockWatch);
|
|
WatchModifyNameUnlocked(id, newName);
|
|
EXCLUSIVE_RELEASE();
|
|
GuiUpdateWatchViewAsync();
|
|
}
|
|
|
|
void WatchDelete(unsigned int id)
|
|
{
|
|
EXCLUSIVE_ACQUIRE(LockWatch);
|
|
auto x = watchexpr.find(id);
|
|
if(x != watchexpr.end())
|
|
{
|
|
delete x->second;
|
|
watchexpr.erase(x);
|
|
EXCLUSIVE_RELEASE();
|
|
GuiUpdateWatchViewAsync();
|
|
}
|
|
}
|
|
|
|
void WatchSetWatchdogModeUnlocked(unsigned int id, WATCHDOGMODE mode)
|
|
{
|
|
auto obj = watchexpr.find(id);
|
|
if(obj != watchexpr.end())
|
|
obj->second->setWatchdogMode(mode);
|
|
}
|
|
|
|
void WatchSetWatchdogMode(unsigned int id, WATCHDOGMODE mode)
|
|
{
|
|
EXCLUSIVE_ACQUIRE(LockWatch);
|
|
WatchSetWatchdogModeUnlocked(id, mode);
|
|
EXCLUSIVE_RELEASE();
|
|
GuiUpdateWatchViewAsync();
|
|
}
|
|
|
|
WATCHDOGMODE WatchGetWatchdogEnabled(unsigned int id)
|
|
{
|
|
SHARED_ACQUIRE(LockWatch);
|
|
auto obj = watchexpr.find(id);
|
|
if(obj != watchexpr.end())
|
|
return obj->second->getWatchdogMode();
|
|
else
|
|
return WATCHDOGMODE::MODE_DISABLED;
|
|
}
|
|
|
|
duint WatchGetUnsignedValue(unsigned int id)
|
|
{
|
|
EXCLUSIVE_ACQUIRE(LockWatch);
|
|
auto obj = watchexpr.find(id);
|
|
if(obj != watchexpr.end())
|
|
return obj->second->getIntValue();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
WATCHVARTYPE WatchGetType(unsigned int id)
|
|
{
|
|
SHARED_ACQUIRE(LockWatch);
|
|
auto obj = watchexpr.find(id);
|
|
if(obj != watchexpr.end())
|
|
return obj->second->getType();
|
|
else
|
|
return WATCHVARTYPE::TYPE_INVALID;
|
|
}
|
|
|
|
unsigned int WatchGetWindow(unsigned int id)
|
|
{
|
|
SHARED_ACQUIRE(LockWatch);
|
|
auto obj = watchexpr.find(id);
|
|
if(obj != watchexpr.end())
|
|
return obj->second->watchWindow;
|
|
else
|
|
return WATCHVARTYPE::TYPE_INVALID;
|
|
}
|
|
|
|
bool WatchIsWatchdogTriggered(unsigned int id)
|
|
{
|
|
SHARED_ACQUIRE(LockWatch);
|
|
auto obj = watchexpr.find(id);
|
|
if(obj != watchexpr.end())
|
|
return obj->second->watchdogTriggered;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void WatchSetWindow(unsigned int id, unsigned int window)
|
|
{
|
|
EXCLUSIVE_ACQUIRE(LockWatch);
|
|
auto obj = watchexpr.find(id);
|
|
if(obj != watchexpr.end())
|
|
obj->second->watchWindow = window;
|
|
EXCLUSIVE_RELEASE();
|
|
GuiUpdateWatchViewAsync();
|
|
}
|
|
|
|
std::vector<WATCHINFO> WatchGetList()
|
|
{
|
|
EXCLUSIVE_ACQUIRE(LockWatch);
|
|
std::vector<WATCHINFO> watchList;
|
|
for(auto & i : watchexpr)
|
|
{
|
|
WATCHINFO info;
|
|
info.value = i.second->getCurrIntValue();
|
|
strcpy_s(info.WatchName, i.second->getName());
|
|
strcpy_s(info.Expression, i.second->getExpr().c_str());
|
|
info.varType = i.second->getType();
|
|
info.id = i.first;
|
|
info.watchdogMode = i.second->getWatchdogMode();
|
|
info.watchdogTriggered = i.second->watchdogTriggered;
|
|
info.window = i.second->watchWindow;
|
|
watchList.push_back(info);
|
|
}
|
|
return watchList;
|
|
}
|
|
|
|
// Initialize id counter so that it begin with a unused value
|
|
void WatchInitIdCounter()
|
|
{
|
|
idCounter = 1;
|
|
for(auto i : watchexpr)
|
|
if(i.first > idCounter)
|
|
idCounter = i.first + 1;
|
|
}
|
|
|
|
void WatchCacheSave(JSON root)
|
|
{
|
|
EXCLUSIVE_ACQUIRE(LockWatch);
|
|
JSON watchroot = json_array();
|
|
for(auto i : watchexpr)
|
|
{
|
|
if(i.second->getType() == WATCHVARTYPE::TYPE_INVALID)
|
|
continue;
|
|
JSON watchitem = json_object();
|
|
json_object_set_new(watchitem, "Expression", json_string(i.second->getExpr().c_str()));
|
|
json_object_set_new(watchitem, "Name", json_string(i.second->getName()));
|
|
switch(i.second->getType())
|
|
{
|
|
case WATCHVARTYPE::TYPE_INT:
|
|
json_object_set_new(watchitem, "DataType", json_string("int"));
|
|
break;
|
|
case WATCHVARTYPE::TYPE_UINT:
|
|
json_object_set_new(watchitem, "DataType", json_string("uint"));
|
|
break;
|
|
case WATCHVARTYPE::TYPE_FLOAT:
|
|
json_object_set_new(watchitem, "DataType", json_string("float"));
|
|
break;
|
|
case WATCHVARTYPE::TYPE_ASCII:
|
|
json_object_set_new(watchitem, "DataType", json_string("ascii"));
|
|
break;
|
|
case WATCHVARTYPE::TYPE_UNICODE:
|
|
json_object_set_new(watchitem, "DataType", json_string("unicode"));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
switch(i.second->getWatchdogMode())
|
|
{
|
|
case WATCHDOGMODE::MODE_DISABLED:
|
|
json_object_set_new(watchitem, "WatchdogMode", json_string("Disabled"));
|
|
break;
|
|
case WATCHDOGMODE::MODE_CHANGED:
|
|
json_object_set_new(watchitem, "WatchdogMode", json_string("Changed"));
|
|
break;
|
|
case WATCHDOGMODE::MODE_UNCHANGED:
|
|
json_object_set_new(watchitem, "WatchdogMode", json_string("Unchanged"));
|
|
break;
|
|
case WATCHDOGMODE::MODE_ISTRUE:
|
|
json_object_set_new(watchitem, "WatchdogMode", json_string("IsTrue"));
|
|
break;
|
|
case WATCHDOGMODE::MODE_ISFALSE:
|
|
json_object_set_new(watchitem, "WatchdogMode", json_string("IsFalse"));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
json_array_append_new(watchroot, watchitem);
|
|
}
|
|
if(json_array_size(watchroot))
|
|
json_object_set(root, "watch", watchroot);
|
|
|
|
json_decref(watchroot);
|
|
}
|
|
|
|
void WatchCacheLoad(JSON root)
|
|
{
|
|
EXCLUSIVE_ACQUIRE(LockWatch);
|
|
JSON watchroot = json_object_get(root, "watch");
|
|
if(!watchroot)
|
|
watchroot = json_object_get(root, "Watch");
|
|
unsigned int i;
|
|
JSON val;
|
|
if(!watchroot)
|
|
return;
|
|
|
|
json_array_foreach(watchroot, i, val)
|
|
{
|
|
const char* expr = json_string_value(json_object_get(val, "Expression"));
|
|
if(!expr)
|
|
continue;
|
|
const char* datatype = json_string_value(json_object_get(val, "DataType"));
|
|
if(!datatype)
|
|
datatype = "uint";
|
|
const char* WatchdogMode = json_string_value(json_object_get(val, "WatchdogMode"));
|
|
if(!WatchdogMode)
|
|
WatchdogMode = "Disabled";
|
|
const char* watchname = json_string_value(json_object_get(val, "Name"));
|
|
if(!watchname)
|
|
watchname = "Watch";
|
|
WATCHVARTYPE varType;
|
|
WATCHDOGMODE watchdog_mode;
|
|
if(strcmp(datatype, "int") == 0)
|
|
varType = WATCHVARTYPE::TYPE_INT;
|
|
else if(strcmp(datatype, "uint") == 0)
|
|
varType = WATCHVARTYPE::TYPE_UINT;
|
|
else if(strcmp(datatype, "float") == 0)
|
|
varType = WATCHVARTYPE::TYPE_FLOAT;
|
|
else if(strcmp(datatype, "ascii") == 0)
|
|
varType = WATCHVARTYPE::TYPE_ASCII;
|
|
else if(strcmp(datatype, "unicode") == 0)
|
|
varType = WATCHVARTYPE::TYPE_UNICODE;
|
|
else
|
|
continue;
|
|
if(strcmp(WatchdogMode, "Disabled") == 0)
|
|
watchdog_mode = WATCHDOGMODE::MODE_DISABLED;
|
|
else if(strcmp(WatchdogMode, "Changed") == 0)
|
|
watchdog_mode = WATCHDOGMODE::MODE_CHANGED;
|
|
else if(strcmp(WatchdogMode, "Unchanged") == 0)
|
|
watchdog_mode = WATCHDOGMODE::MODE_UNCHANGED;
|
|
else if(strcmp(WatchdogMode, "IsTrue") == 0)
|
|
watchdog_mode = WATCHDOGMODE::MODE_ISTRUE;
|
|
else if(strcmp(WatchdogMode, "IsFalse") == 0)
|
|
watchdog_mode = WATCHDOGMODE::MODE_ISFALSE;
|
|
else
|
|
continue;
|
|
unsigned int id = WatchAddExprUnlocked(expr, varType);
|
|
WatchModifyNameUnlocked(id, watchname);
|
|
WatchSetWatchdogModeUnlocked(id, watchdog_mode);
|
|
}
|
|
WatchInitIdCounter(); // Initialize id counter so that it begin with a unused value
|
|
} |