1
0
Fork 0

DBG: New expression function plugin system, string support

This commit is contained in:
xenocidewiki 2021-03-19 17:33:08 +00:00
parent 19069dcff8
commit 3ab89502cd
10 changed files with 336 additions and 67 deletions

View File

@ -158,6 +158,11 @@ bool _plugin_registerexprfunction(int pluginHandle, const char* name, int argc,
return pluginexprfuncregister(pluginHandle, name, argc, cbFunction, userdata);
}
bool _plugin_registerexprfunctionex(int pluginHandle, const char* name, const ValueType & returnType, const ValueType* argTypes, size_t argCount, CBPLUGINEXPRFUNCTIONEX cbFunction, void* userdata)
{
return pluginexprfuncregisterex(pluginHandle, name, returnType, argTypes, argCount, cbFunction, userdata);
}
bool _plugin_unregisterexprfunction(int pluginHandle, const char* name)
{
return pluginexprfuncunregister(pluginHandle, name);

View File

@ -231,6 +231,25 @@ typedef struct
GUIMENUTYPE hMenu;
} PLUG_CB_MENUPREPARE;
typedef enum
{
ValueTypeNumber,
ValueTypeString,
} ValueType;
typedef struct
{
const char* ptr;
bool isOwner;
} StringValue;
typedef struct
{
ValueType type;
duint number;
StringValue string;
} ExpressionValue;
//enums
typedef enum
{
@ -280,7 +299,8 @@ typedef enum
typedef void (*CBPLUGIN)(CBTYPE cbType, void* callbackInfo);
typedef bool (*CBPLUGINCOMMAND)(int argc, char** argv);
typedef void (*CBPLUGINSCRIPT)();
typedef duint(*CBPLUGINEXPRFUNCTION)(int argc, duint* argv, void* userdata);
typedef duint(*CBPLUGINEXPRFUNCTION)(int argc, const duint* argv, void* userdata);
typedef bool(*CBPLUGINEXPRFUNCTIONEX)(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata);
typedef FORMATRESULT(*CBPLUGINFORMATFUNCTION)(char* dest, size_t destCount, int argc, char* argv[], duint value, void* userdata);
typedef bool (*CBPLUGINPREDICATE)(void* userdata);
@ -316,6 +336,7 @@ PLUG_IMPEXP bool _plugin_menuentryremove(int pluginHandle, int hEntry);
PLUG_IMPEXP void _plugin_startscript(CBPLUGINSCRIPT cbScript);
PLUG_IMPEXP bool _plugin_waituntilpaused();
PLUG_IMPEXP bool _plugin_registerexprfunction(int pluginHandle, const char* name, int argc, CBPLUGINEXPRFUNCTION cbFunction, void* userdata);
PLUG_IMPEXP bool _plugin_registerexprfunctionex(int pluginHandle, const char* name, const ValueType & returnType, const ValueType* argTypes, size_t argCount, CBPLUGINEXPRFUNCTIONEX cbFunction, void* userdata);
PLUG_IMPEXP bool _plugin_unregisterexprfunction(int pluginHandle, const char* name);
PLUG_IMPEXP bool _plugin_unload(const char* pluginName);
PLUG_IMPEXP bool _plugin_load(const char* pluginName);

View File

@ -21,20 +21,28 @@ struct gens<0, S...>
};
template<typename T, int ...S, typename... Ts>
static T callFunc(const T* argv, T(*cbFunction)(Ts...), seq<S...>)
static duint callFunc(const T* argv, duint(*cbFunction)(Ts...), seq<S...>)
{
return cbFunction(argv[S]...);
return cbFunction(argv[S].number...);
}
template<typename... Ts>
static bool RegisterEasy(const String & name, duint(*cbFunction)(Ts...))
{
auto aliases = StringUtils::Split(name, ',');
auto tempFunc = [cbFunction](int argc, duint * argv, void* userdata)
auto tempFunc = [cbFunction](ExpressionValue * result, int argc, const ExpressionValue * argv, void* userdata) -> bool
{
return callFunc(argv, cbFunction, typename gens<sizeof...(Ts)>::type());
result->type = ValueTypeNumber;
result->number = callFunc(argv, cbFunction, typename gens<sizeof...(Ts)>::type());
return true;
};
if(!ExpressionFunctions::Register(aliases[0], sizeof...(Ts), tempFunc))
std::vector<ValueType> args(sizeof...(Ts));
for(auto & arg : args)
arg = ValueTypeNumber;
if(!ExpressionFunctions::Register(aliases[0], ValueTypeNumber, args, tempFunc))
return false;
for(size_t i = 1; i < aliases.size(); i++)
ExpressionFunctions::RegisterAlias(aliases[0], aliases[i]);
@ -143,9 +151,15 @@ void ExpressionFunctions::Init()
//Undocumented
RegisterEasy("bpgoto", bpgoto);
ExpressionFunctions::Register("strcmp", ValueTypeNumber, { ValueTypeString, ValueTypeString }, Exprfunc::strcmp, nullptr);
ExpressionFunctions::Register("strstr", ValueTypeNumber, { ValueTypeString, ValueTypeString }, Exprfunc::strstr, nullptr);
ExpressionFunctions::Register("utf16", ValueTypeString, { ValueTypeNumber }, Exprfunc::utf16, nullptr);
ExpressionFunctions::Register("utf8", ValueTypeString, { ValueTypeNumber }, Exprfunc::utf8, nullptr);
ExpressionFunctions::Register("mod.fromname", ValueTypeNumber, { ValueTypeString }, Exprfunc::modbasefromname, nullptr);
}
bool ExpressionFunctions::Register(const String & name, int argc, const CBEXPRESSIONFUNCTION & cbFunction, void* userdata)
bool ExpressionFunctions::Register(const String & name, const ValueType & returnType, const std::vector<ValueType> & argTypes, const CBEXPRESSIONFUNCTION & cbFunction, void* userdata)
{
if(!isValidName(name))
return false;
@ -154,7 +168,8 @@ bool ExpressionFunctions::Register(const String & name, int argc, const CBEXPRES
return false;
Function f;
f.name = name;
f.argc = argc;
f.argTypes = argTypes;
f.returnType = returnType;
f.cbFunction = cbFunction;
f.userdata = userdata;
mFunctions[name] = f;
@ -167,8 +182,10 @@ bool ExpressionFunctions::RegisterAlias(const String & name, const String & alia
auto found = mFunctions.find(name);
if(found == mFunctions.end())
return false;
if(!Register(alias, found->second.argc, found->second.cbFunction, found->second.userdata))
if(!Register(alias, found->second.returnType, found->second.argTypes, found->second.cbFunction, found->second.userdata))
return false;
found->second.aliases.push_back(alias);
return true;
}
@ -186,29 +203,35 @@ bool ExpressionFunctions::Unregister(const String & name)
return true;
}
bool ExpressionFunctions::Call(const String & name, std::vector<duint> & argv, duint & result)
bool ExpressionFunctions::Call(const String & name, ExpressionValue & result, std::vector<ExpressionValue> & argv)
{
SHARED_ACQUIRE(LockExpressionFunctions);
auto found = mFunctions.find(name);
if(found == mFunctions.end())
return false;
const auto & f = found->second;
if(f.argc != int(argv.size()))
if(f.argTypes.size() != int(argv.size()))
return false;
result = f.cbFunction(f.argc, argv.data(), f.userdata);
return true;
for(size_t i = 0; i < argv.size(); i++)
{
if(argv[i].type != f.argTypes[i])
return false;
}
return f.cbFunction(&result, argv.size(), argv.data(), f.userdata);
}
bool ExpressionFunctions::GetArgc(const String & name, int & argc)
bool ExpressionFunctions::GetType(const String & name, ValueType & returnType, std::vector<ValueType> & argTypes)
{
SHARED_ACQUIRE(LockExpressionFunctions);
auto found = mFunctions.find(name);
if(found == mFunctions.end())
return false;
argc = found->second.argc;
returnType = found->second.returnType;
argTypes = found->second.argTypes;
return true;
}
bool ExpressionFunctions::isValidName(const String & name)
{
if(!name.length())

View File

@ -1,25 +1,29 @@
#pragma once
#include "_global.h"
#include "_plugins.h"
#include <functional>
class ExpressionFunctions
{
public:
using CBEXPRESSIONFUNCTION = std::function<duint(int argc, duint* argv, void* userdata)>;
// TODO: also register the argument types
using CBEXPRESSIONFUNCTION = std::function<bool(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)>;
static void Init();
static bool Register(const String & name, int argc, const CBEXPRESSIONFUNCTION & cbFunction, void* userdata = nullptr);
static bool Register(const String & name, const ValueType & returnType, const std::vector<ValueType> & argTypes, const CBEXPRESSIONFUNCTION & cbFunction, void* userdata = nullptr);
static bool RegisterAlias(const String & name, const String & alias);
static bool Unregister(const String & name);
static bool Call(const String & name, std::vector<duint> & argv, duint & result);
static bool GetArgc(const String & name, int & argc);
static bool Call(const String & name, ExpressionValue & result, std::vector<ExpressionValue> & argv);
static bool GetType(const String & name, ValueType & returnType, std::vector<ValueType> & argTypes);
private:
struct Function
{
String name;
int argc = 0;
ValueType returnType;
std::vector<ValueType> argTypes;
CBEXPRESSIONFUNCTION cbFunction;
void* userdata = nullptr;
std::vector<String> aliases;

View File

@ -873,25 +873,76 @@ bool ExpressionParser::Calculate(duint & value, bool signedcalc, bool allowassig
else if(token.type() == Token::Type::Function)
{
const auto & name = token.data();
int argc;
if(!ExpressionFunctions::GetArgc(name, argc))
ValueType returnType;
std::vector<ValueType> argTypes;
if(!ExpressionFunctions::GetType(name, returnType, argTypes))
return false;
if(int(stack.size()) < argc)
if(int(stack.size()) < argTypes.size())
return false;
std::vector<duint> argv;
argv.resize(argc);
for(auto i = 0; i < argc; i++)
std::vector<ExpressionValue> argv;
argv.resize(argTypes.size());
for(auto i = 0; i < argTypes.size(); i++)
{
duint arg;
/*duint arg;
if(!stack[stack.size() - 1].DoEvaluate(arg, silent, baseonly))
return false;
stack.pop_back();
argv[argc - i - 1] = arg;
return false;*/
// TODO: put in EvalValue -> ExpressionValue
auto & top = stack[stack.size() - i - 1];
ExpressionValue arg;
if(top.evaluated)
arg = { ValueTypeNumber, top.value };
else
{
arg = { ValueTypeString, 0, StringValue{top.data.c_str(), false} };
}
if(arg.type != argTypes[i])
{
if(arg.type == ValueTypeString)
{
duint result;
if(!top.DoEvaluate(result, silent, baseonly, value_size, isvar, hexonly))
return false;
arg.number = result;
if(arg.string.isOwner)
BridgeFree((void*)arg.string.ptr);
arg.string.ptr = nullptr;
}
else
{
char str[32] = "";
sprintf_s(str, "0x%p", arg.number);
void* ptr = BridgeAlloc(sizeof(str));
strcpy_s((char*)ptr, _countof(str), str);
arg.string.ptr = (const char*)ptr;
arg.string.isOwner = true;
}
arg.type = argTypes[i];
}
argv[argTypes.size() - i - 1] = arg;
}
duint result;
if(!ExpressionFunctions::Call(name, argv, result))
ExpressionValue result = { ValueTypeNumber, 0 };
if(!ExpressionFunctions::Call(name, result, argv))
return false;
stack.push_back(EvalValue(result));
for(size_t i = 0; i < argv.size(); i++)
{
stack.pop_back();
}
if(result.type == ValueTypeString)
{
stack.push_back(EvalValue(result.string.ptr));
if(result.string.isOwner)
BridgeFree((void*)result.string.ptr);
}
else
stack.push_back(EvalValue(result.number));
}
else
stack.push_back(EvalValue(token.data()));

View File

@ -11,6 +11,7 @@
#include "value.h"
#include "TraceRecord.h"
#include "exhandlerinfo.h"
#include <vector>
namespace Exprfunc
{
@ -181,7 +182,7 @@ namespace Exprfunc
BASIC_INSTRUCTION_INFO info;
if(!disasmfast(addr, &info, true))
return 0;
return info.branch && !info.call && !strstr(info.instruction, "jmp");
return info.branch && !info.call && !::strstr(info.instruction, "jmp");
}
duint disisbranch(duint addr)
@ -197,7 +198,7 @@ namespace Exprfunc
BASIC_INSTRUCTION_INFO info;
if(!disasmfast(addr, &info, true))
return 0;
return strstr(info.instruction, "ret") != nullptr;
return ::strstr(info.instruction, "ret") != nullptr;
}
duint disiscall(duint addr)
@ -271,7 +272,7 @@ namespace Exprfunc
BASIC_INSTRUCTION_INFO info;
if(!disasmfast(addr, &info, true))
return 0;
return info.branch && !strstr(info.instruction, "jmp") ? addr + info.size : 0;
return info.branch && !::strstr(info.instruction, "jmp") ? addr + info.size : 0;
}
duint disnext(duint addr)
@ -471,4 +472,102 @@ namespace Exprfunc
return 0;
return getLastExceptionInfo().ExceptionRecord.ExceptionInformation[index];
}
bool utf16(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
if(argc > 1 || !argc)
return false;
assert(argv[0].type == ValueTypeNumber);
duint addr = argv[0].number;
std::vector<wchar_t> tempStr(MAX_STRING_SIZE + 1);
if(!DbgMemRead(addr, tempStr.data(), sizeof(wchar_t) * (tempStr.size() - 1)))
{
// TODO: fail or just return ""?
}
auto utf8Str = StringUtils::Utf16ToUtf8(tempStr.data());
if(utf8Str.empty() && wcslen(tempStr.data()) > 0)
{
return false;
}
auto strBuf = BridgeAlloc(utf8Str.size() + 1);
memcpy(strBuf, utf8Str.c_str(), utf8Str.size());
result->type = ValueTypeString;
result->string.ptr = (const char*)strBuf;
result->string.isOwner = true;
return true;
}
bool utf8(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
if(argc > 1 || !argc)
return false;
assert(argv[0].type == ValueTypeNumber);
duint addr = argv[0].number;
std::vector<char> tempStr(MAX_STRING_SIZE + 1);
if(!DbgMemRead(addr, tempStr.data(), tempStr.size() - 1))
{
//Todo
}
auto strlen = ::strlen(tempStr.data());
auto strBuf = BridgeAlloc(strlen + 1);
memcpy(strBuf, tempStr.data(), strlen + 1);
result->type = ValueTypeString;
result->string.ptr = (const char*)strBuf;
result->string.isOwner = true;
return true;
}
bool modbasefromname(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
result->type = ValueTypeNumber;
result->number = ModBaseFromName(argv[0].string.ptr);
if(!result->number)
return false;
return true;
}
bool strcmp(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
assert(argv[0].type == ValueTypeString);
result->type = ValueTypeNumber;
result->number = 0;
if(argc > 2 || argc <= 1)
return false;
result->number = !::strcmp(argv[0].string.ptr, argv[1].string.ptr);
return true;
}
bool strstr(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
assert(argv[0].type == ValueTypeString);
result->type = ValueTypeNumber;
result->number = 0;
//Todo: find a way to cancel this action if the user does not use the utf functions
//I.e. argv[0].string.ptr will point to the address from the debugger 0xABAB as a string: strstr(0xABAB, "A")
if(argc > 2 || argc <= 1)
return false;
result->number = ::strstr(argv[0].string.ptr, argv[1].string.ptr) != nullptr;
return true;
}
}

View File

@ -1,6 +1,7 @@
#pragma once
#include "_global.h"
#include "expressionfunctions.h"
namespace Exprfunc
{
@ -79,4 +80,10 @@ namespace Exprfunc
duint exflags();
duint exinfocount();
duint exinfo(duint index);
bool strcmp(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata);
bool strstr(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata);
bool utf16(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata);
bool utf8(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata);
bool modbasefromname(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata);
}

View File

@ -983,6 +983,26 @@ bool pluginmenuentryremove(int pluginHandle, int hEntry)
return false;
}
struct ExprFuncWrapper
{
void* user;
int argc;
CBPLUGINEXPRFUNCTION cbFunc;
std::vector<duint> cbArgv;
static bool callback(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
auto cbUser = reinterpret_cast<ExprFuncWrapper*>(userdata);
for(auto i = 0; i < argc; i++)
cbUser->cbArgv.push_back(argv[i].number);
result->type = ValueTypeNumber;
result->number = cbUser->cbFunc(argc, cbUser->cbArgv.data(), cbUser->user);
return true;
}
};
bool pluginexprfuncregister(int pluginHandle, const char* name, int argc, CBPLUGINEXPRFUNCTION cbFunction, void* userdata)
{
String plugName;
@ -991,7 +1011,18 @@ bool pluginexprfuncregister(int pluginHandle, const char* name, int argc, CBPLUG
PLUG_EXPRFUNCTION plugExprfunction;
plugExprfunction.pluginHandle = pluginHandle;
strcpy_s(plugExprfunction.name, name);
if(!ExpressionFunctions::Register(name, argc, cbFunction, userdata))
ExprFuncWrapper* wrapper = new ExprFuncWrapper;
wrapper->argc = argc;
wrapper->cbFunc = cbFunction;
wrapper->user = userdata;
std::vector<ValueType> args(argc);
for(auto & arg : args)
arg = ValueTypeNumber;
if(!ExpressionFunctions::Register(name, ValueTypeNumber, args, wrapper->callback, wrapper))
{
dprintf(QT_TRANSLATE_NOOP("DBG", "[PLUGIN, %s] Expression function \"%s\" failed to register...\n"), plugName.c_str(), name);
return false;
@ -1003,6 +1034,33 @@ bool pluginexprfuncregister(int pluginHandle, const char* name, int argc, CBPLUG
return true;
}
bool pluginexprfuncregisterex(int pluginHandle, const char* name, const ValueType & returnType, const ValueType* argTypes, size_t argCount, CBPLUGINEXPRFUNCTIONEX cbFunction, void* userdata)
{
String plugName;
if(!findPluginName(pluginHandle, plugName))
return false;
PLUG_EXPRFUNCTION plugExprfunction;
plugExprfunction.pluginHandle = pluginHandle;
strcpy_s(plugExprfunction.name, name);
std::vector<ValueType> argTypesVec(argCount);
for(auto i = 0; i < argCount; i++)
argTypesVec[i] = argTypes[i];
if(!ExpressionFunctions::Register(name, returnType, argTypesVec, cbFunction, userdata))
{
dprintf(QT_TRANSLATE_NOOP("DBG", "[PLUGIN, %s] Expression function \"%s\" failed to register...\n"), plugName.c_str(), name);
return false;
}
EXCLUSIVE_ACQUIRE(LockPluginExprfunctionList);
pluginExprfunctionList.push_back(plugExprfunction);
EXCLUSIVE_RELEASE();
dprintf(QT_TRANSLATE_NOOP("DBG", "[PLUGIN, %s] Expression function \"%s\" registered!\n"), plugName.c_str(), name);
return true;
}
bool pluginexprfuncunregister(int pluginHandle, const char* name)
{
String plugName;

View File

@ -98,6 +98,7 @@ void pluginmenuentrysethotkey(int pluginHandle, int hEntry, const char* hotkey);
bool pluginmenuremove(int hMenu);
bool pluginmenuentryremove(int pluginHandle, int hEntry);
bool pluginexprfuncregister(int pluginHandle, const char* name, int argc, CBPLUGINEXPRFUNCTION cbFunction, void* userdata);
bool pluginexprfuncregisterex(int pluginHandle, const char* name, const ValueType & returnType, const ValueType* argTypes, size_t argCount, CBPLUGINEXPRFUNCTIONEX cbFunction, void* userdata);
bool pluginexprfuncunregister(int pluginHandle, const char* name);
bool pluginformatfuncregister(int pluginHandle, const char* type, CBPLUGINFORMATFUNCTION cbFunction, void* userdata);
bool pluginformatfuncunregister(int pluginHandle, const char* type);

View File

@ -6,7 +6,7 @@
#include "disasm_helper.h"
#include "formatfunctions.h"
enum class ValueType
enum class StringValueType
{
Unknown,
SignedDecimal,
@ -19,7 +19,7 @@ enum class ValueType
Instruction
};
static String printValue(FormatValueType value, ValueType type)
static String printValue(FormatValueType value, StringValueType type)
{
duint valuint = 0;
char string[MAX_STRING_SIZE] = "";
@ -28,37 +28,37 @@ static String printValue(FormatValueType value, ValueType type)
{
switch(type)
{
case ValueType::Unknown:
case StringValueType::Unknown:
break;
#ifdef _WIN64
case ValueType::SignedDecimal:
case StringValueType::SignedDecimal:
result = StringUtils::sprintf("%lld", valuint);
break;
case ValueType::UnsignedDecimal:
case StringValueType::UnsignedDecimal:
result = StringUtils::sprintf("%llu", valuint);
break;
case ValueType::Hex:
case StringValueType::Hex:
result = StringUtils::sprintf("%llX", valuint);
break;
#else //x86
case ValueType::SignedDecimal:
case StringValueType::SignedDecimal:
result = StringUtils::sprintf("%d", valuint);
break;
case ValueType::UnsignedDecimal:
case StringValueType::UnsignedDecimal:
result = StringUtils::sprintf("%u", valuint);
break;
case ValueType::Hex:
case StringValueType::Hex:
result = StringUtils::sprintf("%X", valuint);
break;
#endif //_WIN64
case ValueType::Pointer:
case StringValueType::Pointer:
result = StringUtils::sprintf("%p", valuint);
break;
case ValueType::String:
case StringValueType::String:
if(disasmgetstringatwrapper(valuint, string, false))
result = string;
break;
case ValueType::AddrInfo:
case StringValueType::AddrInfo:
{
auto symbolic = SymGetSymbolicName(valuint);
if(disasmgetstringatwrapper(valuint, string, false))
@ -69,14 +69,14 @@ static String printValue(FormatValueType value, ValueType type)
result.clear();
}
break;
case ValueType::Module:
case StringValueType::Module:
{
char mod[MAX_MODULE_SIZE] = "";
ModNameFromAddr(valuint, mod, true);
result = mod;
}
break;
case ValueType::Instruction:
case StringValueType::Instruction:
{
BASIC_INSTRUCTION_INFO info;
if(!disasmfast(valuint, &info, true))
@ -92,33 +92,33 @@ static String printValue(FormatValueType value, ValueType type)
return result;
}
static bool typeFromCh(char ch, ValueType & type)
static bool typeFromCh(char ch, StringValueType & type)
{
switch(ch)
{
case 'd':
type = ValueType::SignedDecimal;
type = StringValueType::SignedDecimal;
break;
case 'u':
type = ValueType::UnsignedDecimal;
type = StringValueType::UnsignedDecimal;
break;
case 'p':
type = ValueType::Pointer;
type = StringValueType::Pointer;
break;
case 's':
type = ValueType::String;
type = StringValueType::String;
break;
case 'x':
type = ValueType::Hex;
type = StringValueType::Hex;
break;
case 'a':
type = ValueType::AddrInfo;
type = StringValueType::AddrInfo;
break;
case 'm':
type = ValueType::Module;
type = StringValueType::Module;
break;
case 'i':
type = ValueType::Instruction;
type = StringValueType::Instruction;
break;
default: //invalid format
return false;
@ -126,10 +126,10 @@ static bool typeFromCh(char ch, ValueType & type)
return true;
}
static const char* getArgExpressionType(const String & formatString, ValueType & type, String & complexArgs)
static const char* getArgExpressionType(const String & formatString, StringValueType & type, String & complexArgs)
{
size_t toSkip = 0;
type = ValueType::Hex;
type = StringValueType::Hex;
complexArgs.clear();
if(formatString.size() > 2 && !isdigit(formatString[0]) && formatString[1] == ':') //simple type
{
@ -152,21 +152,21 @@ static const char* getArgExpressionType(const String & formatString, ValueType &
return formatString.c_str() + toSkip;
}
static unsigned int getArgNumType(const String & formatString, ValueType & type)
static unsigned int getArgNumType(const String & formatString, StringValueType & type)
{
String complexArgs;
auto expression = getArgExpressionType(formatString, type, complexArgs);
unsigned int argnum = 0;
if(!expression || sscanf_s(expression, "%u", &argnum) != 1)
type = ValueType::Unknown;
type = StringValueType::Unknown;
return argnum;
}
static String handleFormatString(const String & formatString, const FormatValueVector & values)
{
auto type = ValueType::Unknown;
auto type = StringValueType::Unknown;
auto argnum = getArgNumType(formatString, type);
if(type != ValueType::Unknown && argnum < values.size())
if(type != StringValueType::Unknown && argnum < values.size())
return printValue(values.at(argnum), type);
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "[Formatting Error]"));
}
@ -234,7 +234,7 @@ static String printComplexValue(FormatValueType value, const String & complexArg
static String handleFormatStringInline(const String & formatString)
{
auto type = ValueType::Unknown;
auto type = StringValueType::Unknown;
String complexArgs;
auto value = getArgExpressionType(formatString, type, complexArgs);
if(!complexArgs.empty())