Files

368 lines
11 KiB
C++

#include "Std.h"
#include <QLibraryInfo>
#include <QFileSystemWatcher>
#include <pluginsdk/bridgemain.h>
chaiscript::ChaiScript chai(chaiscript::Std_Lib::library());
#define plugin_name "CHAISCRIPTPLUGIN"
#define plugin_version 1
int pluginHandle;
HWND hwndDlg;
int hMenu;
int hMenuDisasm;
int hMenuDump;
int hMenuStack;
QFileSystemWatcher* fileWatcher = 0;
std::string to_hex(int d, size_t digits = 2) {
std::stringstream ss;
ss << std::hex <<
std::setfill('0') <<
std::uppercase <<
std::setw(digits) << d;
return ss.str();
}
static std::set<std::string> commands;
static std::set<std::string> workspace_set;
static std::vector<std::string> workspace;
std::thread chaiAsyncThread;
duint chaiEvalDirect(const char* cmd) {
try {
auto bv = chai.eval(cmd);
if(bv.get_type_info().is_arithmetic()) {
return chai.boxed_cast<duint>(bv);
}
if(bv.get_type_info().name() == "bool") {
return chai.boxed_cast<bool>(bv);
}
if(bv.get_type_info().name() == "void") {
return 0;
}
_plugin_logprintf(" >>> bv type is: %s \n" , bv.get_type_info().name().c_str() );
return 0;
}
catch ( const std::exception &e ) {
_plugin_logprintf(" >>> Exception thrown: %s \n" , e.what( ) );
}
return 0;
}
duint chaiEvalDirect(const char* cmd, bool async) {
if(async) {
if(chaiAsyncThread.joinable())
chaiAsyncThread.join();
chaiAsyncThread = std::thread([] (std::string cmd) {
chaiEvalDirect(cmd.c_str());
}, std::string(cmd));
return 0;
} else {
return chaiEvalDirect(cmd);
}
}
bool chaiEvalCommand(int argc, char* argv[]) {
if(argc == 0)
return false;
std::string cmdName = argv[0];
auto pos = cmdName.find_first_of(" \t");
if(pos != std::string::npos)
cmdName = cmdName.substr(0, pos);
std::stringstream cmd;
cmd << cmdName << "(";
for(int i = 1;i < argc;i++) {
if(i > 1)
cmd << ", ";
cmd << argv[i];
}
cmd << ")";
return chaiEvalDirect(cmd.str().c_str());
}
bool chaiEval(int argc, char* argv[]) {
if(argc < 2)
return false;
return chaiEvalDirect(argv[1]);
}
bool chaiEvalAsync(int argc, char* argv[]) {
if(argc < 2)
return false;
return chaiEvalDirect(argv[1], true);
}
bool chaiShowEnv(int argc, char* argv[]) {
_plugin_logprintf("Chai locals: \n");
std::regex reg(argc > 1 ? argv[1] : ".*");
for(const auto &sym : chai.get_locals()) {
if(std::regex_search(sym.first, reg)) {
const chaiscript::Boxed_Value& bv = sym.second;
_plugin_logprintf("\t%s: %s\n", sym.first.c_str(), bv.get_type_info().name().c_str() );
}
}
_plugin_logprintf("\nChai functions: \n\t");
const auto funcs = chai.get_state().engine_state.m_boxed_functions;
for(const auto &func : funcs) {
if(std::regex_search(func.first, reg)) {
const chaiscript::Boxed_Value& bv = func.second;
_plugin_logprintf("%s, ", func.first.c_str() );
}
}
_plugin_logprintf("\nWorkspace: \n");
for(const auto &script : workspace) {
if(std::regex_search(script, reg)) {
_plugin_logprintf("\t%s\n", script.c_str() );
}
}
_plugin_logprintf("\nCommands: \n");
for(const auto &command : commands) {
if(std::regex_search(command, reg)) {
_plugin_logprintf("\t%s\n", command.c_str() );
}
}
_plugin_logprintf("\n\n");
return true;
}
static void _chaiLoad(const std::string& fileName, bool reload = false) {
try {
chai.eval_file(fileName);
if(workspace_set.find(fileName) == workspace_set.end()) {
workspace.push_back(fileName);
workspace_set.insert(fileName);
if(fileWatcher) {
QString qtFileName(fileName.c_str());
fileWatcher->addPath(qtFileName);
}
}
if(reload) {
_plugin_logprintf(" >>> [" plugin_name "] Reloaded %s on modifcation\n" , fileName.c_str() );
} else {
_plugin_logprintf(" >>> [" plugin_name "] Loaded %s \n" , fileName.c_str() );
}
}
catch ( const std::exception &e ) {
_plugin_logprintf(" >>> Exception thrown: %s \n" , e.what( ) );
}
}
static void _chaiLoad() {
QString qfileName = QFileDialog::getOpenFileName(nullptr,
"Open chai File", "", "CHAI Files (*.chai)");
if(qfileName.size()) {
std::string fileName = qfileName.toStdString();
_chaiLoad(fileName);
}
}
bool chaiClearWorkspace(int argc, char* argv[]) {
for(auto script : workspace) {
QString path(script.c_str());
if(fileWatcher)
fileWatcher->removePath(path);
}
commands.clear();
workspace.clear();
workspace_set.clear();
return true;
}
duint chaiExpr(int argc, const duint* argv, void * usrdata) {
std::string& cmdName = *(std::string*)usrdata;
std::stringstream cmd;
cmd << cmdName << "(";
for(int i = 0;i < argc;i++) {
if(i != 0)
cmd << ", ";
cmd << argv[i];
}
cmd << ")";
return chaiEvalDirect(cmd.str().c_str());
}
bool chaiRegisterCommand(const char* cmd) {
commands.insert(cmd);
const auto funcs = chai.get_state().engine_state.m_boxed_functions;
try {
const chaiscript::Boxed_Value& c = chai.eval(std::string(cmd) + ".get_arity()");
int arity = chaiscript::boxed_cast<int>(c);
_plugin_registerexprfunction(pluginHandle, cmd, arity, chaiExpr, new std::string(cmd));
}
catch ( const std::exception &e ) {
_plugin_logprintf(" >>> Exception thrown: %s \n" , e.what( ) );
}
return _plugin_registercommand(pluginHandle, cmd, chaiEvalCommand, false);
}
bool chaiRegisterCommand(int argc, char* argv[]) {
if(argc < 2) {
return false;
}
return chaiRegisterCommand(argv[1]);
}
bool chaiLoad(int argc, char* argv[]) {
std::string fileName = "";
if(argc < 2) {
GuiExecuteOnGuiThread(_chaiLoad);
} else {
_chaiLoad(argv[1]);
}
return true;
}
static void _puts(const std::string& v) {
_plugin_logprintf("%s", v.c_str());
}
static void print(const std::string& v) {
std::string msg = v + "\n";
_plugin_logputs(msg.c_str());
}
std::vector<unsigned char> _DbgMemRead(duint va, duint size) {
std::vector<unsigned char> rtn;
rtn.resize(size);
if(DbgMemRead(va, &rtn[0], size) == false)
rtn.clear();
return rtn;
}
static std::string Sanitize(const std::string& name) {
return std::regex_replace(name, std::regex("\\::"), "_");
}
static void registerChaiFunctions() {
chai.add(chaiscript::bootstrap::standard_library::vector_type<std::vector<uint8_t> >("UCharVector"));
chai.add(chaiscript::fun(&print), "print");
chai.add(chaiscript::fun(&_puts), "puts");
chai.add(chaiscript::fun(&to_hex), "to_hex");
chai.add(chaiscript::fun(&_DbgMemRead), "DbgMemRead");
#define DBG_FUNCTION(x) chai.add(chaiscript::fun(FunctionWrapper(&x, 0)), Sanitize(#x));
#include "dbgops.h"
chai.add(chaiscript::type_conversion<int, duint>());
chai.add(chaiscript::type_conversion<unsigned int, duint>());
}
static void saveCache(CBTYPE cbType, void* callbackInfo) {
PLUG_CB_LOADSAVEDB* loadSaveInfo = (PLUG_CB_LOADSAVEDB*)callbackInfo;
json_t* chaiScriptRoot = json_object();
if(!workspace.empty()) {
json_t* loadedScripts = json_array();
for(auto script : workspace) {
json_array_append_new(loadedScripts, json_string(script.c_str()));
}
json_object_set(chaiScriptRoot, "workspace", loadedScripts);
}
if(!commands.empty()) {
json_t* definedCommands = json_array();
for(auto command : commands) {
json_array_append_new(definedCommands, json_string(command.c_str()));
}
json_object_set(chaiScriptRoot, "commands", definedCommands);
}
json_object_set(loadSaveInfo->root, "chaiScript", chaiScriptRoot);
}
static void loadCache(CBTYPE cbType, void* callbackInfo) {
PLUG_CB_LOADSAVEDB* loadSaveInfo = (PLUG_CB_LOADSAVEDB*)callbackInfo;
json_t* chaiScriptRoot = json_object_get(loadSaveInfo->root, "chaiScript");
if(chaiScriptRoot == 0)
return;
json_t* loadedScripts = json_object_get(chaiScriptRoot, "workspace");
for(size_t i = 0;i < json_array_size(loadedScripts);i++) {
_chaiLoad( json_string_value( json_array_get(loadedScripts, i) ) );
}
json_t* definedCommands = json_object_get(chaiScriptRoot, "commands");
for(size_t i = 0;i < json_array_size(definedCommands);i++) {
chaiRegisterCommand(json_string_value(json_array_get(definedCommands, i)));
}
}
extern "C" DLL_EXPORT bool pluginit(PLUG_INITSTRUCT* initStruct) {
initStruct->pluginVersion = plugin_version;
initStruct->sdkVersion = PLUG_SDKVERSION;
strncpy_s(initStruct->pluginName, sizeof(initStruct->pluginName), plugin_name, sizeof(initStruct->pluginName));
pluginHandle = initStruct->pluginHandle;
_plugin_logprintf("[ChaiScript] Qt version %s\n", QLibraryInfo::build());
if(!_plugin_registercommand(pluginHandle, "chaiEval", chaiEval, false))
_plugin_logputs("error registering the \"chaiEval\" command!");
if(!_plugin_registercommand(pluginHandle, "chaiEvalAsync", chaiEvalAsync, false))
_plugin_logputs("error registering the \"chaiEvalAsync\" command!");
if(!_plugin_registercommand(pluginHandle, "chaiLoad", chaiLoad, false))
_plugin_logputs("error registering the \"chaiLoad\" command!");
if(!_plugin_registercommand(pluginHandle, "chaiShowEnv", chaiShowEnv, false))
_plugin_logputs("error registering the \"chaiShowEnv\" command!");
if(!_plugin_registercommand(pluginHandle, "chaiClearWorkspace", chaiClearWorkspace, false))
_plugin_logputs("error registering the \"chaiClearWorkspace\" command!");
if(!_plugin_registercommand(pluginHandle, "chaiRegisterCommand", chaiRegisterCommand, false))
_plugin_logputs("error registering the \"chaiRegisterCommand\" command!");
fileWatcher = new QFileSystemWatcher();
QObject::connect(fileWatcher, &QFileSystemWatcher::fileChanged, [] (const QString& path) {
_chaiLoad(path.toStdString(), true);
});
_plugin_registercallback(pluginHandle, CBTYPE::CB_SAVEDB, saveCache);
_plugin_registercallback(pluginHandle, CBTYPE::CB_LOADDB, loadCache);
return true;
}
extern "C" DLL_EXPORT void plugsetup(PLUG_SETUPSTRUCT* setupStruct) {
hwndDlg = setupStruct->hwndDlg;
hMenu = setupStruct->hMenu;
hMenuDisasm = setupStruct->hMenuDisasm;
hMenuDump = setupStruct->hMenuDump;
hMenuStack = setupStruct->hMenuStack;
registerChaiFunctions();
}
extern "C" DLL_EXPORT bool plugstop() {
delete fileWatcher;
fileWatcher = 0;
if(chaiAsyncThread.joinable())
chaiAsyncThread.join();
return true;
}
extern "C" DLL_EXPORT BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
return TRUE;
}