From 2976eb323b6d690ecb6d5c2c44a06cb6f9b2778f Mon Sep 17 00:00:00 2001 From: Miran Date: Sat, 28 Oct 2023 03:53:54 +0200 Subject: [PATCH 01/10] Text argument type names in error messages. --- cleo_sdk/CLEO.h | 27 +++++++++++++++++++++++++++ source/CCustomOpcodeSystem.cpp | 21 +++++++++++---------- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/cleo_sdk/CLEO.h b/cleo_sdk/CLEO.h index bf72d10e..eb52e71f 100644 --- a/cleo_sdk/CLEO.h +++ b/cleo_sdk/CLEO.h @@ -66,6 +66,33 @@ enum eDataType : int DT_VAR_STRING_ARRAY, DT_LVAR_STRING_ARRAY }; +static const char* ToString(eDataType v) +{ + switch (v) + { + case DT_END: return "VArgEnd"; break; + case DT_DWORD: return "Int32"; break; + case DT_VAR: return "GlobVar"; break; + case DT_LVAR: return "LocVar"; break; + case DT_BYTE: return "Int8"; break; + case DT_WORD: return "Int16"; break; + case DT_FLOAT: return "Float32"; break; + case DT_VAR_ARRAY: return "GlobVarArr"; break; + case DT_LVAR_ARRAY: return "LocVarArr"; break; + case DT_TEXTLABEL: return "STxt"; break; + case DT_VAR_TEXTLABEL: return "GlobVarSTxt"; break; + case DT_LVAR_TEXTLABEL: return "LocVarSTxt"; break; + case DT_VAR_TEXTLABEL_ARRAY: return "GlobVarSTxtArr"; break; + case DT_LVAR_TEXTLABEL_ARRAY: return "LocVarSTxtArr"; break; + case DT_VARLEN_STRING: return "Txt"; break; + case DT_STRING: return "Txt2"; break; // ? + case DT_VAR_STRING: return "GlobVarLTxt"; break; + case DT_LVAR_STRING: return "LocVarLTxt"; break; + case DT_VAR_STRING_ARRAY: return "GlobVarLTxtArr"; break; + case DT_LVAR_STRING_ARRAY: return "LocVarLTxtArr"; break; + default: return "LocVarLTxtArr"; + } +} const size_t MAX_STR_LEN = 0xff; // max length of string type parameter diff --git a/source/CCustomOpcodeSystem.cpp b/source/CCustomOpcodeSystem.cpp index 1ecf69d2..8167fb5a 100644 --- a/source/CCustomOpcodeSystem.cpp +++ b/source/CCustomOpcodeSystem.cpp @@ -465,7 +465,7 @@ namespace CLEO { break; default: - LOG_WARNING("Reading integer from invalid argument type (%02X) in script %s", paramType, ((CCustomScript*)&thread)->GetInfoStr().c_str()); + LOG_WARNING("Reading integer from invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); } GetScriptParams(&thread, 1); @@ -489,7 +489,7 @@ namespace CLEO { break; default: - LOG_WARNING("Writing integer into invalid argument type (%02X) in script %s", paramType, ((CCustomScript*)&thread)->GetInfoStr().c_str()); + LOG_WARNING("Writing integer into invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); } opcodeParams[0].dwParam = uval; @@ -513,7 +513,7 @@ namespace CLEO { break; default: - LOG_WARNING("Reading integer from invalid argument type (%02X) in script %s", paramType, ((CCustomScript*)&thread)->GetInfoStr().c_str()); + LOG_WARNING("Reading integer from invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); } GetScriptParams(&thread, 1); @@ -537,7 +537,7 @@ namespace CLEO { break; default: - LOG_WARNING("Writing integer into invalid argument type (%02X) in script %s", paramType, ((CCustomScript*)&thread)->GetInfoStr().c_str()); + LOG_WARNING("Writing integer into invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); } opcodeParams[0].nParam = nval; @@ -558,7 +558,7 @@ namespace CLEO { break; default: - LOG_WARNING("Reading float from invalid argument type (%02X) in script %s", paramType, ((CCustomScript*)&thread)->GetInfoStr().c_str()); + LOG_WARNING("Reading float from invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); } GetScriptParams(&thread, 1); @@ -578,7 +578,7 @@ namespace CLEO { break; default: - LOG_WARNING("Writing float into invalid argument type (%02X) in script %s", paramType, ((CCustomScript*)&thread)->GetInfoStr().c_str()); + LOG_WARNING("Writing float into invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); } opcodeParams[0].fParam = fval; @@ -721,7 +721,7 @@ namespace CLEO { // unsupported param type GetScriptParams(thread, 1); // skip unhandled param - SHOW_ERROR("Reading string from invalid argument type (%02X) in script %s", paramType, ((CCustomScript*)thread)->GetInfoStr().c_str()); + SHOW_ERROR("Reading string from invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)thread)->GetInfoStr().c_str()); return nullptr; } @@ -777,7 +777,7 @@ namespace CLEO { default: { - SHOW_ERROR("Outputing string into invalid argument type (%02X) in script %s", paramType, ((CCustomScript*)thread)->GetInfoStr().c_str()); + SHOW_ERROR("Outputing string into invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)thread)->GetInfoStr().c_str()); CLEO_SkipOpcodeParams(thread, 1); // skip unhandled param return { nullptr, 0 }; } @@ -2018,7 +2018,8 @@ namespace CLEO { int label = 0; char* moduleTxt = nullptr; - switch (*thread->GetBytePointer()) + auto paramType = CLEO_GetOperandType(thread); + switch (paramType) { // label of current script case DT_DWORD: @@ -2046,7 +2047,7 @@ namespace CLEO { break; default: - SHOW_ERROR("Invalid type (%02X) of the first argument in opcode [0AB1] in script %s \nScript suspended.", *thread->GetBytePointer(), ((CCustomScript*)thread)->GetInfoStr().c_str()); + SHOW_ERROR("Invalid type (%s) of the first argument in opcode [0AB1] in script %s \nScript suspended.", ToString(paramType), ((CCustomScript*)thread)->GetInfoStr().c_str()); return CCustomOpcodeSystem::ErrorSuspendScript(thread); } From c42f0cbb696e0a57b5c89ed86a88561b341e6d5c Mon Sep 17 00:00:00 2001 From: Miran Date: Sat, 28 Oct 2023 03:55:11 +0200 Subject: [PATCH 02/10] Added pointer minimal value validation when reading or writing string params. --- source/CCustomOpcodeSystem.cpp | 34 ++++++++++++++++++++++------------ source/CCustomOpcodeSystem.h | 2 ++ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/source/CCustomOpcodeSystem.cpp b/source/CCustomOpcodeSystem.cpp index 8167fb5a..bf36fddb 100644 --- a/source/CCustomOpcodeSystem.cpp +++ b/source/CCustomOpcodeSystem.cpp @@ -652,23 +652,29 @@ namespace CLEO { case DT_LVAR_ARRAY: { GetScriptParams(thread, 1); - char* str = opcodeParams[0].pcParam; - size_t length; - if(str != nullptr) - length = strlen(str); - else + size_t length = bufSize ? bufSize - 1 : 0; // minus terminator char + + if (opcodeParams[0].dwParam == 0) { - length = 0; - LOG_WARNING("Reading string from null pointer in script %s", ((CCustomScript*)thread)->GetInfoStr().c_str()); + const char* eStr = "(null)"; + length = min(length, strlen(eStr)); + if (length) strncpy(buf, eStr, length); + } + else if(opcodeParams[0].dwParam <= CCustomOpcodeSystem::MinValidAddress) + { + LOG_WARNING("Reading string from argument with invalid pointer value (0x%X) in script %s", opcodeParams[0].dwParam, ((CCustomScript*)thread)->GetInfoStr().c_str()); + const char* eStr = "(badPtr)"; + length = min(length, strlen(eStr)); + if (length) strncpy(buf, eStr, length); } - - if(bufSize > 0) - length = min(length, bufSize - 1); // minus terminator char else - length = 0; // no target buffer + { + char* str = opcodeParams[0].pcParam; + length = min(length, strlen(str)); + if (length) strncpy(buf, str, length); + } - if (length) strncpy(buf, str, length); if (bufSize > 0) buf[length] = '\0'; // string terminator return buf; } @@ -759,6 +765,10 @@ namespace CLEO { case DT_VAR_ARRAY: case DT_LVAR_ARRAY: GetScriptParams(thread, 1); + + if (opcodeParams[0].dwParam <= CCustomOpcodeSystem::MinValidAddress) + LOG_WARNING("Writing string into param with invalid target pointer value (0x%X) in script %s", opcodeParams[0].dwParam, ((CCustomScript*)thread)->GetInfoStr().c_str()); + return { opcodeParams[0].pcParam, 0x7FFFFFFF }; // user allocated memory block can be any size // short string variable diff --git a/source/CCustomOpcodeSystem.h b/source/CCustomOpcodeSystem.h index 6a0a023f..299df31c 100644 --- a/source/CCustomOpcodeSystem.h +++ b/source/CCustomOpcodeSystem.h @@ -19,6 +19,8 @@ namespace CLEO class CCustomOpcodeSystem : public VInjectible { public: + static const size_t MinValidAddress = 0x10000; // used for validation of pointers received from scripts. First 64kb are for sure reserved by Windows. + static const size_t LastOriginalOpcode = 0x0A4E; // GTA SA static const size_t LastCustomOpcode = 0x7FFF; From a6a8330c7a29b2a3e2ed1a68c5adf93708e65b55 Mon Sep 17 00:00:00 2001 From: Miran Date: Sat, 28 Oct 2023 05:20:18 +0200 Subject: [PATCH 03/10] Extra log traces. --- source/CCustomOpcodeSystem.cpp | 4 ++++ source/CCustomOpcodeSystem.h | 5 ++++- source/CScriptEngine.cpp | 4 +++- source/CleoBase.cpp | 1 + 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/source/CCustomOpcodeSystem.cpp b/source/CCustomOpcodeSystem.cpp index bf36fddb..51b45ab6 100644 --- a/source/CCustomOpcodeSystem.cpp +++ b/source/CCustomOpcodeSystem.cpp @@ -161,10 +161,14 @@ namespace CLEO { WORD CCustomOpcodeSystem::lastOpcode = 0; WORD* CCustomOpcodeSystem::lastOpcodePtr = nullptr; WORD CCustomOpcodeSystem::lastCustomOpcode = 0; + WORD CCustomOpcodeSystem::prevOpcode = 0; + // opcode handler for custom opcodes OpcodeResult __fastcall CCustomOpcodeSystem::customOpcodeHandler(CRunningScript *thread, int dummy, WORD opcode) { + prevOpcode = lastOpcode; + lastScript = thread; lastOpcode = opcode; lastOpcodePtr = (WORD*)thread->GetBytePointer() - 1; // rewind to the opcode start diff --git a/source/CCustomOpcodeSystem.h b/source/CCustomOpcodeSystem.h index 299df31c..f862947b 100644 --- a/source/CCustomOpcodeSystem.h +++ b/source/CCustomOpcodeSystem.h @@ -29,6 +29,8 @@ namespace CLEO static WORD lastOpcode; static WORD* lastOpcodePtr; static WORD lastCustomOpcode; + + static WORD prevOpcode; // previous void FinalizeScriptObjects(); @@ -36,7 +38,8 @@ namespace CLEO virtual void Inject(CCodeInjector& inj); ~CCustomOpcodeSystem() { - TRACE("Last opcode executed %04X", lastOpcode); + TRACE("Last opcode executed: %04X", lastOpcode); + TRACE("Previous opcode executed: %04X", prevOpcode); } static bool RegisterOpcode(WORD opcode, CustomOpcodeHandler callback); diff --git a/source/CScriptEngine.cpp b/source/CScriptEngine.cpp index 8b10064a..985e1bff 100644 --- a/source/CScriptEngine.cpp +++ b/source/CScriptEngine.cpp @@ -1000,7 +1000,7 @@ namespace CLEO scriptsDir += "cleo";*/ std::string scriptsDir = "cleo"; // TODO: restore to absolute path when ModLoader is updated to support CLEO5 - TRACE("Searching for cleo scripts"); + TRACE("Searching for CLEO scripts"); CCustomScript* cs = nullptr; FilesWalk(scriptsDir.c_str(), cs_ext, [&](const char* fullPath, const char* filename) { @@ -1027,6 +1027,8 @@ namespace CLEO typedef void WINAPI callback(void); ((callback*)func)(); } + + TRACE("Scripts search done."); } CCustomScript * CScriptEngine::LoadScript(const char * szFilePath) diff --git a/source/CleoBase.cpp b/source/CleoBase.cpp index fe8f03ad..244f9107 100644 --- a/source/CleoBase.cpp +++ b/source/CleoBase.cpp @@ -50,6 +50,7 @@ namespace CLEO CodeInjector.ReplaceFunction(OnDrawingFinished, 0x00734640); // nullsub_63 - originally something like renderDebugStuff? m_bStarted = true; + TRACE("CLEO instance started successfully!"); } void CCleoInstance::Stop() From 696d315a5c2a522a7c4988c95a8f6ac57547f5d2 Mon Sep 17 00:00:00 2001 From: Miran Date: Sat, 28 Oct 2023 05:33:00 +0200 Subject: [PATCH 04/10] fixup! Extra log traces. --- cleo_sdk/CLEO.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cleo_sdk/CLEO.h b/cleo_sdk/CLEO.h index eb52e71f..6946f38f 100644 --- a/cleo_sdk/CLEO.h +++ b/cleo_sdk/CLEO.h @@ -85,7 +85,7 @@ static const char* ToString(eDataType v) case DT_VAR_TEXTLABEL_ARRAY: return "GlobVarSTxtArr"; break; case DT_LVAR_TEXTLABEL_ARRAY: return "LocVarSTxtArr"; break; case DT_VARLEN_STRING: return "Txt"; break; - case DT_STRING: return "Txt2"; break; // ? + case DT_STRING: return "LTxt"; break; case DT_VAR_STRING: return "GlobVarLTxt"; break; case DT_LVAR_STRING: return "LocVarLTxt"; break; case DT_VAR_STRING_ARRAY: return "GlobVarLTxtArr"; break; From 45c54e5de05afbfdde622b604acd0dea4c625579 Mon Sep 17 00:00:00 2001 From: Miran Date: Sat, 28 Oct 2023 07:22:23 +0200 Subject: [PATCH 05/10] Suspending script on any string param read\write error. --- source/CCustomOpcodeSystem.cpp | 137 +++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 56 deletions(-) diff --git a/source/CCustomOpcodeSystem.cpp b/source/CCustomOpcodeSystem.cpp index 51b45ab6..8a2f6b2c 100644 --- a/source/CCustomOpcodeSystem.cpp +++ b/source/CCustomOpcodeSystem.cpp @@ -9,6 +9,9 @@ #include #include +#define OPCODE_VALIDATE_STR_ARG_READ(x) if((void*)x == nullptr) { SHOW_ERROR("ReadStringParam error in script %s \nScript suspended.", ((CCustomScript*)thread)->GetInfoStr().c_str()); return CCustomOpcodeSystem::ErrorSuspendScript(thread); } +#define OPCODE_VALIDATE_STR_ARG_WRITE(x) if((void*)x == nullptr) { SHOW_ERROR("WriteStringParam error in script %s \nScript suspended.", ((CCustomScript*)thread)->GetInfoStr().c_str()); return CCustomOpcodeSystem::ErrorSuspendScript(thread); } + namespace CLEO { DWORD FUNC_fopen; DWORD FUNC_fclose; @@ -671,6 +674,7 @@ namespace CLEO { const char* eStr = "(badPtr)"; length = min(length, strlen(eStr)); if (length) strncpy(buf, eStr, length); + return nullptr; // error } else { @@ -770,9 +774,11 @@ namespace CLEO { case DT_LVAR_ARRAY: GetScriptParams(thread, 1); - if (opcodeParams[0].dwParam <= CCustomOpcodeSystem::MinValidAddress) + if (opcodeParams[0].dwParam <= CCustomOpcodeSystem::MinValidAddress) + { LOG_WARNING("Writing string into param with invalid target pointer value (0x%X) in script %s", opcodeParams[0].dwParam, ((CCustomScript*)thread)->GetInfoStr().c_str()); - + return { 0, 0 }; // error + } return { opcodeParams[0].pcParam, 0x7FFFFFFF }; // user allocated memory block can be any size // short string variable @@ -1397,7 +1403,9 @@ namespace CLEO { //0A92=-1,create_custom_thread %1d% OpcodeResult __stdcall opcode_0A92(CRunningScript *thread) { - auto filename = reinterpret_cast(thread)->ResolvePath(ReadStringParam(thread), DIR_CLEO); // legacy: default search location is game\cleo directory + auto path = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(path) + + auto filename = reinterpret_cast(thread)->ResolvePath(path, DIR_CLEO); // legacy: default search location is game\cleo directory TRACE("[0A92] Starting new custom script %s from thread named %s", filename.c_str(), thread->GetName()); auto cs = new CCustomScript(filename.c_str()); @@ -1435,7 +1443,9 @@ namespace CLEO { //0A94=-1,create_custom_mission %1d% OpcodeResult __stdcall opcode_0A94(CRunningScript *thread) { - auto filename = reinterpret_cast(thread)->ResolvePath(ReadStringParam(thread), DIR_CLEO); // legacy: default search location is game\cleo directory + auto path = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(path) + + auto filename = reinterpret_cast(thread)->ResolvePath(path, DIR_CLEO); // legacy: default search location is game\cleo directory filename += ".cm"; // add custom mission extension TRACE("[0A94] Starting new custom mission %s from thread named %s", filename.c_str(), thread->GetName()); @@ -1525,7 +1535,8 @@ namespace CLEO { } else { - reinterpret_cast(thread)->SetWorkDir(ReadStringParam(thread)); + auto path = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(path) + reinterpret_cast(thread)->SetWorkDir(path); } return OR_CONTINUE; } @@ -1533,7 +1544,9 @@ namespace CLEO { //0A9A=3,%3d% = openfile %1d% mode %2d% // IF and SET OpcodeResult __stdcall opcode_0A9A(CRunningScript *thread) { - auto filename = reinterpret_cast(thread)->ResolvePath(ReadStringParam(thread)); + auto path = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(path) + + auto filename = reinterpret_cast(thread)->ResolvePath(path); auto paramType = *thread->GetBytePointer(); char mode[0x10]; @@ -1555,8 +1568,8 @@ namespace CLEO { } else { - // string param - ReadStringParam(thread, mode, sizeof(mode)); + auto modeOk = ReadStringParam(thread, mode, sizeof(mode)); + OPCODE_VALIDATE_STR_ARG_READ(modeOk) } if (auto hfile = open_file(filename.c_str(), mode, bLegacyMode)) @@ -1656,7 +1669,9 @@ namespace CLEO { //0AA2=2,%2h% = load_library %1d% // IF and SET OpcodeResult __stdcall opcode_0AA2(CRunningScript *thread) { - auto filename = reinterpret_cast(thread)->ResolvePath(ReadStringParam(thread)); + auto path = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(path) + + auto filename = reinterpret_cast(thread)->ResolvePath(path); auto libHandle = LoadLibrary(filename.c_str()); *thread << libHandle; @@ -1679,7 +1694,8 @@ namespace CLEO { //0AA4=3,%3d% = get_proc_address %1d% library %2d% // IF and SET OpcodeResult __stdcall opcode_0AA4(CRunningScript *thread) { - char *funcName = ReadStringParam(thread); + auto funcName = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(funcName) + HMODULE libHandle; *thread >> libHandle; void *funcAddr = (void *)GetProcAddress(libHandle, funcName); @@ -1957,7 +1973,7 @@ namespace CLEO { //0AAA=2, %2d% = thread %1d% pointer // IF and SET OpcodeResult __stdcall opcode_0AAA(CRunningScript *thread) { - char *threadName = ReadStringParam(thread); + auto threadName = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(threadName) threadName[7] = '\0'; CRunningScript *cs = GetInstance().ScriptEngine.FindCustomScriptNamed(threadName); if (!cs) cs = GetInstance().ScriptEngine.FindScriptNamed(threadName); @@ -1969,7 +1985,8 @@ namespace CLEO { //0AAC=2, %2d% = load_audiostream %1d% // IF and SET OpcodeResult __stdcall opcode_0AAC(CRunningScript *thread) { - auto filename = reinterpret_cast(thread)->ResolvePath(ReadStringParam(thread)); + auto path = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(path) + auto filename = reinterpret_cast(thread)->ResolvePath(path); auto stream = GetInstance().SoundSystem.LoadStream(filename.c_str()); *thread << stream; @@ -2310,7 +2327,8 @@ namespace CLEO { //0ABA=1,end_custom_thread_named %1d% OpcodeResult __stdcall opcode_0ABA(CRunningScript *thread) { - char *threadName = ReadStringParam(thread); + auto threadName = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(threadName) + auto deleted_thread = GetInstance().ScriptEngine.FindCustomScriptNamed(threadName); if (deleted_thread) { @@ -2380,7 +2398,9 @@ namespace CLEO { //0AC1=2,%2d% = load_audiostream_with_3d_support %1d% //IF and SET OpcodeResult __stdcall opcode_0AC1(CRunningScript *thread) { - auto stream = GetInstance().SoundSystem.LoadStream(ReadStringParam(thread), true); + auto path = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(path) + + auto stream = GetInstance().SoundSystem.LoadStream(path, true); *thread << stream; SetScriptCondResult(thread, stream != nullptr); return OR_CONTINUE; @@ -2471,14 +2491,15 @@ namespace CLEO { //0ACA=1,show_text_box %1d% OpcodeResult __stdcall opcode_0ACA(CRunningScript *thread) { - PrintHelp(ReadStringParam(thread)); + auto text = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(text) + PrintHelp(text); return OR_CONTINUE; } //0ACB=3,show_styled_text %1d% time %2d% style %3d% OpcodeResult __stdcall opcode_0ACB(CRunningScript *thread) { - auto text = ReadStringParam(thread); + auto text = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(text) DWORD time; *thread >> time; DWORD style; *thread >> style; @@ -2489,7 +2510,7 @@ namespace CLEO { //0ACC=2,show_text_lowpriority %1d% time %2d% OpcodeResult __stdcall opcode_0ACC(CRunningScript *thread) { - auto text = ReadStringParam(thread); + auto text = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(text) DWORD time; *thread >> time; Print(text, time); @@ -2499,7 +2520,7 @@ namespace CLEO { //0ACD=2,show_text_highpriority %1d% time %2d% OpcodeResult __stdcall opcode_0ACD(CRunningScript *thread) { - auto text = ReadStringParam(thread); + auto text = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(text) DWORD time; *thread >> time; PrintNow(text, time); @@ -2509,7 +2530,7 @@ namespace CLEO { //0ACE=-1,show_formatted_text_box %1d% OpcodeResult __stdcall opcode_0ACE(CRunningScript *thread) { - auto format = ReadStringParam(thread); + auto format = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(format) char text[MAX_STR_LEN]; ReadFormattedString(thread, text, sizeof(text), format); PrintHelp(text); @@ -2519,7 +2540,7 @@ namespace CLEO { //0ACF=-1,show_formatted_styled_text %1d% time %2d% style %3d% OpcodeResult __stdcall opcode_0ACF(CRunningScript *thread) { - auto format = ReadStringParam(thread); + auto format = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(format) DWORD time; *thread >> time; DWORD style; *thread >> style; char text[MAX_STR_LEN]; ReadFormattedString(thread, text, sizeof(text), format); @@ -2531,7 +2552,7 @@ namespace CLEO { //0AD0=-1,show_formatted_text_lowpriority %1d% time %2d% OpcodeResult __stdcall opcode_0AD0(CRunningScript *thread) { - auto format = ReadStringParam(thread); + auto format = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(format) DWORD time; *thread >> time; char text[MAX_STR_LEN]; ReadFormattedString(thread, text, sizeof(text), format); @@ -2542,7 +2563,7 @@ namespace CLEO { //0AD1=-1,show_formatted_text_highpriority %1d% time %2d% OpcodeResult __stdcall opcode_0AD1(CRunningScript *thread) { - auto format = ReadStringParam(thread); + auto format = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(format) DWORD time; *thread >> time; char text[MAX_STR_LEN]; ReadFormattedString(thread, text, sizeof(text), format); @@ -2574,9 +2595,8 @@ namespace CLEO { //0AD3=-1,string %1d% format %2d% ... OpcodeResult __stdcall opcode_0AD3(CRunningScript *thread) { - auto resultArg = GetStringParamWriteBuffer(thread); - - auto format = ReadStringParam(thread); + auto resultArg = GetStringParamWriteBuffer(thread); OPCODE_VALIDATE_STR_ARG_WRITE(resultArg.first) + auto format = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(format) char text[MAX_STR_LEN]; ReadFormattedString(thread, text, sizeof(text), format); if (resultArg.first != nullptr && resultArg.second > 0) @@ -2594,9 +2614,9 @@ namespace CLEO { //0AD4=-1,%3d% = scan_string %1d% format %2d% //IF and SET OpcodeResult __stdcall opcode_0AD4(CRunningScript *thread) { - char fmt[MAX_STR_LEN], *format, *src; - src = ReadStringParam(thread); - format = ReadStringParam(thread, fmt, sizeof(fmt)); + auto src = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(src) + char fmt[MAX_STR_LEN]; + auto format = ReadStringParam(thread, fmt, sizeof(fmt)); OPCODE_VALIDATE_STR_ARG_READ(format) size_t cExParams = 0; int *result = (int *)GetScriptParamPointer(thread); @@ -2667,14 +2687,16 @@ namespace CLEO { //0AD8=2,write_string_to_file %1d% from %2d% //IF and SET OpcodeResult __stdcall opcode_0AD8(CRunningScript *thread) { - DWORD hFile; - *thread >> hFile; + DWORD hFile; *thread >> hFile; + auto text = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(text) + if (FILE * file = convert_handle_to_file(hFile)) { - SetScriptCondResult(thread, fputs(ReadStringParam(thread), file) > 0); + SetScriptCondResult(thread, fputs(text, file) > 0); fflush(file); } - else { + else + { SetScriptCondResult(thread, false); } return OR_CONTINUE; @@ -2684,7 +2706,7 @@ namespace CLEO { OpcodeResult __stdcall opcode_0AD9(CRunningScript *thread) { DWORD hFile; *thread >> hFile; - auto format = ReadStringParam(thread); + auto format = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(format) char text[MAX_STR_LEN]; ReadFormattedString(thread, text, sizeof(text), format); if (FILE * file = convert_handle_to_file(hFile)) @@ -2699,10 +2721,9 @@ namespace CLEO { OpcodeResult __stdcall opcode_0ADA(CRunningScript *thread) { DWORD hFile; *thread >> hFile; - auto format = ReadStringParam(thread); + auto format = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(format) int *result = (int *)GetScriptParamPointer(thread); - size_t cExParams = 0; SCRIPT_VAR *ExParams[35]; // read extra params @@ -2748,7 +2769,8 @@ namespace CLEO { //0ADC=1, test_cheat %1d% OpcodeResult __stdcall opcode_0ADC(CRunningScript *thread) { - SetScriptCondResult(thread, TestCheat(ReadStringParam(thread))); + auto text = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(text) + SetScriptCondResult(thread, TestCheat(text)); return OR_CONTINUE; } @@ -2773,11 +2795,15 @@ namespace CLEO { //0ADE=2,%2d% = text_by_GXT_entry %1d% OpcodeResult __stdcall opcode_0ADE(CRunningScript *thread) { - const char *gxt = ReadStringParam(thread); + auto gxt = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(gxt) + if (*thread->GetBytePointer() >= 1 && *thread->GetBytePointer() <= 8) *thread << GetInstance().TextManager.Get(gxt); else - strcpy((char *)GetScriptParamPointer(thread), GetInstance().TextManager.Get(gxt)); + { + auto ok = WriteStringParam(thread, GetInstance().TextManager.Get(gxt)); OPCODE_VALIDATE_STR_ARG_WRITE(ok) + } + return OR_CONTINUE; } @@ -2785,9 +2811,8 @@ namespace CLEO { OpcodeResult __stdcall opcode_0ADF(CRunningScript *thread) { char gxtLabel[8]; // 7 + terminator character - ReadStringParam(thread, gxtLabel, sizeof(gxtLabel)); - - char *text = ReadStringParam(thread); + auto gxtOk = ReadStringParam(thread, gxtLabel, sizeof(gxtLabel)); OPCODE_VALIDATE_STR_ARG_READ(gxtOk) + auto text = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(text) GetInstance().TextManager.AddFxt(gxtLabel, text); return OR_CONTINUE; @@ -2796,7 +2821,9 @@ namespace CLEO { //0AE0=1,remove_dynamic_GXT_entry %1d% OpcodeResult __stdcall opcode_0AE0(CRunningScript *thread) { - GetInstance().TextManager.RemoveFxt(ReadStringParam(thread)); + auto gxt = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(gxt) + + GetInstance().TextManager.RemoveFxt(gxt); return OR_CONTINUE; } @@ -2964,15 +2991,11 @@ namespace CLEO { OpcodeResult __stdcall opcode_0AED(CRunningScript *thread) { // this opcode is useless now - float val; - char *format, *result; - *thread >> val; - format = ReadStringParam(thread); - if (*thread->GetBytePointer() >= 1 && *thread->GetBytePointer() <= 8) - *thread >> result; - else - result = &GetScriptParamPointer(thread)->cParam; - sprintf(result, format, val); + float val; *thread >> val; + auto format = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(format) + auto resultArg = GetStringParamWriteBuffer(thread); OPCODE_VALIDATE_STR_ARG_WRITE(resultArg.first) + + sprintf(resultArg.first, format, val); return OR_CONTINUE; } @@ -3004,9 +3027,9 @@ namespace CLEO { //2000=2,%2s% = resolve_filepath %1s% OpcodeResult __stdcall opcode_2000(CRunningScript* thread) { - auto path = CLEO_ReadStringOpcodeParam(thread); - CLEO_ResolvePath(thread, path, MAX_STR_LEN); - CLEO_WriteStringOpcodeParam(thread, path); + auto path = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(path) + auto resolved = reinterpret_cast(thread)->ResolvePath(path); + auto ok = WriteStringParam(thread, resolved.c_str()); OPCODE_VALIDATE_STR_ARG_WRITE(ok) return OR_CONTINUE; } @@ -3046,10 +3069,12 @@ namespace CLEO { path.append(script->GetScriptFileName()); path = script->ResolvePath(path.c_str()); // real absolute path - CLEO_WriteStringOpcodeParam(thread, path.c_str()); + auto ok = WriteStringParam(thread, path.c_str()); OPCODE_VALIDATE_STR_ARG_WRITE(ok) } else - CLEO_WriteStringOpcodeParam(thread, script->GetScriptFileName()); + { + auto ok = WriteStringParam(thread, script->GetScriptFileName()); OPCODE_VALIDATE_STR_ARG_WRITE(ok) + } SetScriptCondResult(thread, true); return OR_CONTINUE; From aea3102a497c570d0b9918cd24266740dfc109ae Mon Sep 17 00:00:00 2001 From: Miran Date: Sat, 28 Oct 2023 07:59:33 +0200 Subject: [PATCH 06/10] Condensed argument type read/write error messages. --- cleo_sdk/CLEO.h | 84 ++++++++++++++++++++++++---------- source/CCustomOpcodeSystem.cpp | 23 +++++----- 2 files changed, 73 insertions(+), 34 deletions(-) diff --git a/cleo_sdk/CLEO.h b/cleo_sdk/CLEO.h index 6946f38f..eef1eb44 100644 --- a/cleo_sdk/CLEO.h +++ b/cleo_sdk/CLEO.h @@ -66,31 +66,69 @@ enum eDataType : int DT_VAR_STRING_ARRAY, DT_LVAR_STRING_ARRAY }; -static const char* ToString(eDataType v) +static const char* ToStr(eDataType type) { - switch (v) + switch (type) { - case DT_END: return "VArgEnd"; break; - case DT_DWORD: return "Int32"; break; - case DT_VAR: return "GlobVar"; break; - case DT_LVAR: return "LocVar"; break; - case DT_BYTE: return "Int8"; break; - case DT_WORD: return "Int16"; break; - case DT_FLOAT: return "Float32"; break; - case DT_VAR_ARRAY: return "GlobVarArr"; break; - case DT_LVAR_ARRAY: return "LocVarArr"; break; - case DT_TEXTLABEL: return "STxt"; break; - case DT_VAR_TEXTLABEL: return "GlobVarSTxt"; break; - case DT_LVAR_TEXTLABEL: return "LocVarSTxt"; break; - case DT_VAR_TEXTLABEL_ARRAY: return "GlobVarSTxtArr"; break; - case DT_LVAR_TEXTLABEL_ARRAY: return "LocVarSTxtArr"; break; - case DT_VARLEN_STRING: return "Txt"; break; - case DT_STRING: return "LTxt"; break; - case DT_VAR_STRING: return "GlobVarLTxt"; break; - case DT_LVAR_STRING: return "LocVarLTxt"; break; - case DT_VAR_STRING_ARRAY: return "GlobVarLTxtArr"; break; - case DT_LVAR_STRING_ARRAY: return "LocVarLTxtArr"; break; - default: return "LocVarLTxtArr"; + case DT_END: return "VArgEnd"; break; + case DT_DWORD: return "Int32"; break; + case DT_VAR: return "GlobVar"; break; + case DT_LVAR: return "LocVar"; break; + case DT_BYTE: return "Int8"; break; + case DT_WORD: return "Int16"; break; + case DT_FLOAT: return "Float32"; break; + case DT_VAR_ARRAY: return "GlobVarArr"; break; + case DT_LVAR_ARRAY: return "LocVarArr"; break; + case DT_TEXTLABEL: return "STxt"; break; + case DT_VAR_TEXTLABEL: return "GlobVarSTxt"; break; + case DT_LVAR_TEXTLABEL: return "LocVarSTxt"; break; + case DT_VAR_TEXTLABEL_ARRAY: return "GlobVarSTxtArr"; break; + case DT_LVAR_TEXTLABEL_ARRAY: return "LocVarSTxtArr"; break; + case DT_VARLEN_STRING: return "Txt"; break; + case DT_STRING: return "LTxt"; break; + case DT_VAR_STRING: return "GlobVarLTxt"; break; + case DT_LVAR_STRING: return "LocVarLTxt"; break; + case DT_VAR_STRING_ARRAY: return "GlobVarLTxtArr"; break; + case DT_LVAR_STRING_ARRAY: return "LocVarLTxtArr"; break; + default: return "corrupted"; + } +} +static const char* ToKindStr(eDataType type) +{ + switch (type) + { + case DT_BYTE: + case DT_WORD: + case DT_DWORD: + return "int"; break; + + case DT_FLOAT: + return "float"; break; + + case DT_STRING: + case DT_TEXTLABEL: + case DT_LVAR_TEXTLABEL: + case DT_LVAR_TEXTLABEL_ARRAY: + case DT_LVAR_STRING: + case DT_LVAR_STRING_ARRAY: + case DT_VAR_TEXTLABEL: + case DT_VAR_TEXTLABEL_ARRAY: + case DT_VAR_STRING: + case DT_VAR_STRING_ARRAY: + case DT_VARLEN_STRING: + return "string"; break; + + case DT_VAR: + case DT_VAR_ARRAY: + case DT_LVAR: + case DT_LVAR_ARRAY: + return "variable"; break; + + case DT_END: + return "varArgEnd"; break; + + default: + return "corrupted"; break; } } diff --git a/source/CCustomOpcodeSystem.cpp b/source/CCustomOpcodeSystem.cpp index 8a2f6b2c..5944044e 100644 --- a/source/CCustomOpcodeSystem.cpp +++ b/source/CCustomOpcodeSystem.cpp @@ -12,7 +12,8 @@ #define OPCODE_VALIDATE_STR_ARG_READ(x) if((void*)x == nullptr) { SHOW_ERROR("ReadStringParam error in script %s \nScript suspended.", ((CCustomScript*)thread)->GetInfoStr().c_str()); return CCustomOpcodeSystem::ErrorSuspendScript(thread); } #define OPCODE_VALIDATE_STR_ARG_WRITE(x) if((void*)x == nullptr) { SHOW_ERROR("WriteStringParam error in script %s \nScript suspended.", ((CCustomScript*)thread)->GetInfoStr().c_str()); return CCustomOpcodeSystem::ErrorSuspendScript(thread); } -namespace CLEO { +namespace CLEO +{ DWORD FUNC_fopen; DWORD FUNC_fclose; DWORD FUNC_fwrite; @@ -472,7 +473,7 @@ namespace CLEO { break; default: - LOG_WARNING("Reading integer from invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); + LOG_WARNING("Reading integer argument, got %s in script %s", ToKindStr(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); } GetScriptParams(&thread, 1); @@ -496,7 +497,7 @@ namespace CLEO { break; default: - LOG_WARNING("Writing integer into invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); + LOG_WARNING("Writing integer, got argument type %s in script %s", ToKindStr(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); } opcodeParams[0].dwParam = uval; @@ -520,7 +521,7 @@ namespace CLEO { break; default: - LOG_WARNING("Reading integer from invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); + LOG_WARNING("Reading integer argument, got %s in script %s", ToKindStr(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); } GetScriptParams(&thread, 1); @@ -544,7 +545,7 @@ namespace CLEO { break; default: - LOG_WARNING("Writing integer into invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); + LOG_WARNING("Writing integer, got argument type %s in script %s", ToKindStr(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); } opcodeParams[0].nParam = nval; @@ -565,7 +566,7 @@ namespace CLEO { break; default: - LOG_WARNING("Reading float from invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); + LOG_WARNING("Reading float argument, got %s in script %s", ToKindStr(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); } GetScriptParams(&thread, 1); @@ -585,7 +586,7 @@ namespace CLEO { break; default: - LOG_WARNING("Writing float into invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); + LOG_WARNING("Writing float, got argument type %s in script %s", ToKindStr(paramType), ((CCustomScript*)&thread)->GetInfoStr().c_str()); } opcodeParams[0].fParam = fval; @@ -735,7 +736,7 @@ namespace CLEO { // unsupported param type GetScriptParams(thread, 1); // skip unhandled param - SHOW_ERROR("Reading string from invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)thread)->GetInfoStr().c_str()); + SHOW_ERROR("Reading string argument, got %s in script %s", ToKindStr(paramType), ((CCustomScript*)thread)->GetInfoStr().c_str()); return nullptr; } @@ -776,7 +777,7 @@ namespace CLEO { if (opcodeParams[0].dwParam <= CCustomOpcodeSystem::MinValidAddress) { - LOG_WARNING("Writing string into param with invalid target pointer value (0x%X) in script %s", opcodeParams[0].dwParam, ((CCustomScript*)thread)->GetInfoStr().c_str()); + LOG_WARNING("Writing string, got target pointer with value (0x%X) in script %s", opcodeParams[0].dwParam, ((CCustomScript*)thread)->GetInfoStr().c_str()); return { 0, 0 }; // error } return { opcodeParams[0].pcParam, 0x7FFFFFFF }; // user allocated memory block can be any size @@ -797,7 +798,7 @@ namespace CLEO { default: { - SHOW_ERROR("Outputing string into invalid argument type (%s) in script %s", ToString(paramType), ((CCustomScript*)thread)->GetInfoStr().c_str()); + SHOW_ERROR("Writing string, got argument type %s in script %s", ToKindStr(paramType), ((CCustomScript*)thread)->GetInfoStr().c_str()); CLEO_SkipOpcodeParams(thread, 1); // skip unhandled param return { nullptr, 0 }; } @@ -2078,7 +2079,7 @@ namespace CLEO { break; default: - SHOW_ERROR("Invalid type (%s) of the first argument in opcode [0AB1] in script %s \nScript suspended.", ToString(paramType), ((CCustomScript*)thread)->GetInfoStr().c_str()); + SHOW_ERROR("Invalid type (%s) of the first argument in opcode [0AB1] in script %s \nScript suspended.", ToKindStr(paramType), ((CCustomScript*)thread)->GetInfoStr().c_str()); return CCustomOpcodeSystem::ErrorSuspendScript(thread); } From 8d53b0fdb2bb1441c7ded3bbf43b718ab63949e1 Mon Sep 17 00:00:00 2001 From: Miran Date: Sat, 28 Oct 2023 08:00:30 +0200 Subject: [PATCH 07/10] Minimize game on messagebox only if running fullscreen. --- source/CDebug.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/source/CDebug.cpp b/source/CDebug.cpp index 00c73f36..1ead5fd8 100644 --- a/source/CDebug.cpp +++ b/source/CDebug.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "CDebug.h" #include "CleoBase.h" +#include CDebug Debug; using namespace CLEO; @@ -59,12 +60,25 @@ void CDebug::Error(const char* format, ...) auto msg = TraceVArg(eLogLevel::Error, format, args); va_end(args); + QUERY_USER_NOTIFICATION_STATE pquns; + SHQueryUserNotificationState(&pquns); + bool fullscreen = (pquns == QUNS_BUSY) || (pquns == QUNS_RUNNING_D3D_FULL_SCREEN) || (pquns == QUNS_PRESENTATION_MODE); + auto mainWnd = GetInstance().MainWnd; - PostMessage(mainWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); - ShowWindow(mainWnd, SW_MINIMIZE); + + if(fullscreen) + { + PostMessage(mainWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); + ShowWindow(mainWnd, SW_MINIMIZE); + } + MessageBox(mainWnd, msg, "CLEO error", MB_SYSTEMMODAL | MB_TOPMOST | MB_ICONERROR | MB_OK); - PostMessage(mainWnd, WM_SYSCOMMAND, SC_RESTORE, 0); - ShowWindow(mainWnd, SW_RESTORE); + + if (fullscreen) + { + PostMessage(mainWnd, WM_SYSCOMMAND, SC_RESTORE, 0); + ShowWindow(mainWnd, SW_RESTORE); + } } extern "C" From d1792ed8ece11e9e1d73546ee18fc38f8187dbba Mon Sep 17 00:00:00 2001 From: Miran Date: Sun, 29 Oct 2023 05:30:53 +0100 Subject: [PATCH 08/10] Updates in ReadStringParam error handling. General error reporting related updates. --- source/CCustomOpcodeSystem.cpp | 136 ++++++++++++++++++++------------- source/CCustomOpcodeSystem.h | 2 +- source/CDebug.cpp | 17 +++++ source/CDebug.h | 2 + 4 files changed, 103 insertions(+), 54 deletions(-) diff --git a/source/CCustomOpcodeSystem.cpp b/source/CCustomOpcodeSystem.cpp index 5944044e..1cfcdd52 100644 --- a/source/CCustomOpcodeSystem.cpp +++ b/source/CCustomOpcodeSystem.cpp @@ -9,8 +9,9 @@ #include #include -#define OPCODE_VALIDATE_STR_ARG_READ(x) if((void*)x == nullptr) { SHOW_ERROR("ReadStringParam error in script %s \nScript suspended.", ((CCustomScript*)thread)->GetInfoStr().c_str()); return CCustomOpcodeSystem::ErrorSuspendScript(thread); } -#define OPCODE_VALIDATE_STR_ARG_WRITE(x) if((void*)x == nullptr) { SHOW_ERROR("WriteStringParam error in script %s \nScript suspended.", ((CCustomScript*)thread)->GetInfoStr().c_str()); return CCustomOpcodeSystem::ErrorSuspendScript(thread); } +#define OPCODE_VALIDATE_STR_ARG_READ(x) if((void*)x == nullptr) { SHOW_ERROR("%s in script %s \nScript suspended.", lastErrorMsg.c_str(), ((CCustomScript*)thread)->GetInfoStr().c_str()); return CCustomOpcodeSystem::ErrorSuspendScript(thread); } +#define OPCODE_VALIDATE_STR_ARG_WRITE(x) if((void*)x == nullptr) { SHOW_ERROR("%s in script %s \nScript suspended.", lastErrorMsg.c_str(), ((CCustomScript*)thread)->GetInfoStr().c_str()); return CCustomOpcodeSystem::ErrorSuspendScript(thread); } +#define OPCODE_READ_FORMATTED_STRING(thread, buf, bufSize, format) if(ReadFormattedString(thread, buf, bufSize, format) == -1) { SHOW_ERROR("%s in script %s \nScript suspended.", lastErrorMsg.c_str(), ((CCustomScript*)thread)->GetInfoStr().c_str()); return CCustomOpcodeSystem::ErrorSuspendScript(thread); } namespace CLEO { @@ -165,6 +166,7 @@ namespace CLEO WORD CCustomOpcodeSystem::lastOpcode = 0; WORD* CCustomOpcodeSystem::lastOpcodePtr = nullptr; WORD CCustomOpcodeSystem::lastCustomOpcode = 0; + std::string lastErrorMsg = {}; WORD CCustomOpcodeSystem::prevOpcode = 0; @@ -648,6 +650,9 @@ namespace CLEO { static char internal_buf[MAX_STR_LEN]; if (!buf) { buf = internal_buf; bufSize = MAX_STR_LEN; } + auto length = bufSize ? bufSize - 1 : 0; // max text length (minus terminator char) + + lastErrorMsg.clear(); auto paramType = CLEO_GetOperandType(thread); switch(paramType) @@ -660,30 +665,27 @@ namespace CLEO case DT_LVAR_ARRAY: { GetScriptParams(thread, 1); - - size_t length = bufSize ? bufSize - 1 : 0; // minus terminator char - if (opcodeParams[0].dwParam == 0) - { - const char* eStr = "(null)"; - length = min(length, strlen(eStr)); - if (length) strncpy(buf, eStr, length); - } - else if(opcodeParams[0].dwParam <= CCustomOpcodeSystem::MinValidAddress) + if(opcodeParams[0].dwParam <= CCustomOpcodeSystem::MinValidAddress) { - LOG_WARNING("Reading string from argument with invalid pointer value (0x%X) in script %s", opcodeParams[0].dwParam, ((CCustomScript*)thread)->GetInfoStr().c_str()); - const char* eStr = "(badPtr)"; - length = min(length, strlen(eStr)); - if (length) strncpy(buf, eStr, length); - return nullptr; // error + lastErrorMsg = (opcodeParams[0].dwParam == 0) ? + "Reading string from 'null' pointer argument" : + stringPrintf("Reading string from invalid '0x%X' pointer argument", opcodeParams[0].dwParam); + + return nullptr; // error, target buffer untouched } - else + + char* str = opcodeParams[0].pcParam; + auto readLength = strlen(str); + + if(readLength > length) { - char* str = opcodeParams[0].pcParam; - length = min(length, strlen(str)); - if (length) strncpy(buf, str, length); + lastErrorMsg = stringPrintf("Target buffer too small (%d) to read whole string (%d) from argument", length, readLength); + readLength = length; // clamp to target buffer size } + if (length) strncpy(buf, str, length); + if (bufSize > 0) buf[length] = '\0'; // string terminator return buf; } @@ -710,16 +712,17 @@ namespace CLEO // prococess here as GetScriptStringParam can not obtain strings with lenght greater than 128 thread->IncPtr(1); // already processed paramType - DWORD length = (BYTE)*thread->GetBytePointer(); // as unsigned byte! + DWORD readLength = (BYTE)*thread->GetBytePointer(); // as unsigned byte! thread->IncPtr(1); // length info char* str = (char*)thread->GetBytePointer(); - thread->IncPtr(length); // text data + thread->IncPtr(readLength); // text data - if (bufSize > 0) - length = min(length, bufSize - 1); // minus terminator char - else - length = 0; // no target buffer + if (readLength > length) + { + lastErrorMsg = stringPrintf("Target buffer too small (%d) to read whole string (%d) from argument", length, readLength); + readLength = length; // clamp to target buffer size + } if (length) strncpy(buf, str, length); if (bufSize > 0) buf[length] = '\0'; // string terminator @@ -736,8 +739,8 @@ namespace CLEO // unsupported param type GetScriptParams(thread, 1); // skip unhandled param - SHOW_ERROR("Reading string argument, got %s in script %s", ToKindStr(paramType), ((CCustomScript*)thread)->GetInfoStr().c_str()); - return nullptr; + lastErrorMsg = stringPrintf("Reading string argument, got %s", ToKindStr(paramType)); + return nullptr; // error, target buffer untouched } // write output\result string parameter @@ -764,6 +767,8 @@ namespace CLEO char* targetBuff; DWORD targetSize; + lastErrorMsg.clear(); + auto paramType = CLEO_GetOperandType(thread); switch(paramType) { @@ -777,8 +782,8 @@ namespace CLEO if (opcodeParams[0].dwParam <= CCustomOpcodeSystem::MinValidAddress) { - LOG_WARNING("Writing string, got target pointer with value (0x%X) in script %s", opcodeParams[0].dwParam, ((CCustomScript*)thread)->GetInfoStr().c_str()); - return { 0, 0 }; // error + lastErrorMsg = stringPrintf("Writing string into invalid '0x%X' pointer argument", opcodeParams[0].dwParam); + return { nullptr, 0 }; // error } return { opcodeParams[0].pcParam, 0x7FFFFFFF }; // user allocated memory block can be any size @@ -798,9 +803,9 @@ namespace CLEO default: { - SHOW_ERROR("Writing string, got argument type %s in script %s", ToKindStr(paramType), ((CCustomScript*)thread)->GetInfoStr().c_str()); + lastErrorMsg = stringPrintf("Writing string, got argument %s", ToKindStr(paramType)); CLEO_SkipOpcodeParams(thread, 1); // skip unhandled param - return { nullptr, 0 }; + return { nullptr, 0 }; // error } } } @@ -813,11 +818,14 @@ namespace CLEO char* outIter = outputStr; char bufa[256], fmtbufa[64], *fmta; + lastErrorMsg.clear(); + // invalid input arguments - if(outputStr == nullptr || len == 0) + if(outputStr == nullptr || len == 0) { + lastErrorMsg = "Need target buffer to read formatted string"; SkipUnusedVarArgs(thread); - return -1; + return -1; // error } if(len > 1 && format != nullptr) @@ -895,14 +903,29 @@ namespace CLEO { case 's': { - static const char none[] = "(null)"; if (CLEO_GetOperandType(thread) == DT_END) goto _ReadFormattedString_ArgMissing; - const char *astr = ReadStringParam(thread, bufa, sizeof(bufa)); - const char *striter = astr ? astr : none; - while (*striter) + + const char* str = ReadStringParam(thread, bufa, sizeof(bufa)); + if(str == nullptr) // read error + { + if(lastErrorMsg.find("'null' pointer") != std::string::npos) + { + static const char none[] = "(null)"; + str = none; + } + else + { + // lastErrorMsg already set by ReadStringParam + SkipUnusedVarArgs(thread); + outputStr[written] = '\0'; + return -1; // error + } + } + + while (*str) { if (written++ >= len) goto _ReadFormattedString_OutOfMemory; - *outIter++ = *striter++; + *outIter++ = *str++; } iter++; break; @@ -962,28 +985,30 @@ namespace CLEO if (written >= len) { - _ReadFormattedString_OutOfMemory: // jump here on error - LOG_WARNING("Read formatted string error: Insufficient output buffer size in script %s", ((CCustomScript*)thread)->GetInfoStr().c_str()); + _ReadFormattedString_OutOfMemory: // jump here on error + + lastErrorMsg = stringPrintf("Target buffer too small (%d) to read whole formatted string", len); SkipUnusedVarArgs(thread); outputStr[len - 1] = '\0'; - return -1; + return -1; // error } // still more var-args available if (CLEO_GetOperandType(thread) != DT_END) { - LOG_WARNING("Read formatted string: Found more params than format slots in script %s", ((CCustomScript*)thread)->GetInfoStr().c_str()); + lastErrorMsg = "More params than slots in formatted string"; + LOG_WARNING("%s in script %s", lastErrorMsg.c_str(), ((CCustomScript*)thread)->GetInfoStr().c_str()); } SkipUnusedVarArgs(thread); // skip terminator too outputStr[written] = '\0'; return (int)written; - _ReadFormattedString_ArgMissing: // jump here on error - LOG_WARNING("Read formatted string: Not enough arguments to fulfill specified format in script %s", ((CCustomScript*)thread)->GetInfoStr().c_str()); + _ReadFormattedString_ArgMissing: // jump here on error + lastErrorMsg = "Less params than slots in formatted string"; thread->IncPtr(); // skip vararg terminator outputStr[written] = '\0'; - return (int)written; + return -1; // error } // Legacy modes for CLEO 3 @@ -2532,7 +2557,7 @@ namespace CLEO OpcodeResult __stdcall opcode_0ACE(CRunningScript *thread) { auto format = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(format) - char text[MAX_STR_LEN]; ReadFormattedString(thread, text, sizeof(text), format); + char text[MAX_STR_LEN]; OPCODE_READ_FORMATTED_STRING(thread, text, sizeof(text), format) PrintHelp(text); return OR_CONTINUE; @@ -2544,7 +2569,7 @@ namespace CLEO auto format = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(format) DWORD time; *thread >> time; DWORD style; *thread >> style; - char text[MAX_STR_LEN]; ReadFormattedString(thread, text, sizeof(text), format); + char text[MAX_STR_LEN]; OPCODE_READ_FORMATTED_STRING(thread, text, sizeof(text), format) PrintBig(text, time, style); return OR_CONTINUE; @@ -2555,7 +2580,7 @@ namespace CLEO { auto format = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(format) DWORD time; *thread >> time; - char text[MAX_STR_LEN]; ReadFormattedString(thread, text, sizeof(text), format); + char text[MAX_STR_LEN]; OPCODE_READ_FORMATTED_STRING(thread, text, sizeof(text), format) Print(text, time); return OR_CONTINUE; @@ -2566,7 +2591,7 @@ namespace CLEO { auto format = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(format) DWORD time; *thread >> time; - char text[MAX_STR_LEN]; ReadFormattedString(thread, text, sizeof(text), format); + char text[MAX_STR_LEN]; OPCODE_READ_FORMATTED_STRING(thread, text, sizeof(text), format) PrintNow(text, time); return OR_CONTINUE; @@ -2598,7 +2623,7 @@ namespace CLEO { auto resultArg = GetStringParamWriteBuffer(thread); OPCODE_VALIDATE_STR_ARG_WRITE(resultArg.first) auto format = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(format) - char text[MAX_STR_LEN]; ReadFormattedString(thread, text, sizeof(text), format); + char text[MAX_STR_LEN]; OPCODE_READ_FORMATTED_STRING(thread, text, sizeof(text), format) if (resultArg.first != nullptr && resultArg.second > 0) { @@ -2708,7 +2733,7 @@ namespace CLEO { DWORD hFile; *thread >> hFile; auto format = ReadStringParam(thread); OPCODE_VALIDATE_STR_ARG_READ(format) - char text[MAX_STR_LEN]; ReadFormattedString(thread, text, sizeof(text), format); + char text[MAX_STR_LEN]; OPCODE_READ_FORMATTED_STRING(thread, text, sizeof(text), format) if (FILE * file = convert_handle_to_file(hFile)) { @@ -3150,7 +3175,12 @@ extern "C" if (!bufSize) bufSize = MAX_STR_LEN; if(format != nullptr && strlen(format) > 0) - ReadFormattedString(thread, buf, bufSize, format); + { + if(ReadFormattedString(thread, buf, bufSize, format) == -1) // error? + { + LOG_WARNING("%s in script %s", lastErrorMsg.c_str(), ((CCustomScript*)thread)->GetInfoStr().c_str()); + } + } else { SkipUnusedVarArgs(thread); diff --git a/source/CCustomOpcodeSystem.h b/source/CCustomOpcodeSystem.h index f862947b..4835b993 100644 --- a/source/CCustomOpcodeSystem.h +++ b/source/CCustomOpcodeSystem.h @@ -29,7 +29,7 @@ namespace CLEO static WORD lastOpcode; static WORD* lastOpcodePtr; static WORD lastCustomOpcode; - + static std::string lastErrorMsg; static WORD prevOpcode; // previous void FinalizeScriptObjects(); diff --git a/source/CDebug.cpp b/source/CDebug.cpp index 1ead5fd8..3dc8d445 100644 --- a/source/CDebug.cpp +++ b/source/CDebug.cpp @@ -6,6 +6,23 @@ CDebug Debug; using namespace CLEO; +std::string stringPrintf(const char* format, ...) +{ + va_list args; + + va_start(args, format); + auto len = std::vsnprintf(nullptr, 0, format, args) + 1; + va_end(args); + + std::string result(len, '\0'); + + va_start(args, format); + std::vsnprintf(result.data(), result.length(), format, args); + va_end(args); + + return result; +} + void CDebug::Trace(eLogLevel level, const char* format, ...) { va_list args; diff --git a/source/CDebug.h b/source/CDebug.h index e2129132..afa9b045 100644 --- a/source/CDebug.h +++ b/source/CDebug.h @@ -7,6 +7,8 @@ const char szLogFileName[] = "cleo.log"; +std::string stringPrintf(const char* format, ...); + class CDebug { public: From dad53b75f9b3953a3f2609387974158810cf4a6c Mon Sep 17 00:00:00 2001 From: Miran Date: Sun, 29 Oct 2023 05:50:25 +0100 Subject: [PATCH 09/10] fixup! Updates in ReadStringParam error handling. --- source/CCustomOpcodeSystem.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/CCustomOpcodeSystem.cpp b/source/CCustomOpcodeSystem.cpp index 1cfcdd52..1159bf5e 100644 --- a/source/CCustomOpcodeSystem.cpp +++ b/source/CCustomOpcodeSystem.cpp @@ -650,7 +650,7 @@ namespace CLEO { static char internal_buf[MAX_STR_LEN]; if (!buf) { buf = internal_buf; bufSize = MAX_STR_LEN; } - auto length = bufSize ? bufSize - 1 : 0; // max text length (minus terminator char) + const auto bufLength = bufSize ? bufSize - 1 : 0; // max text length (minus terminator char) lastErrorMsg.clear(); @@ -676,12 +676,12 @@ namespace CLEO } char* str = opcodeParams[0].pcParam; - auto readLength = strlen(str); + auto length = strlen(str); - if(readLength > length) + if(length > bufLength) { - lastErrorMsg = stringPrintf("Target buffer too small (%d) to read whole string (%d) from argument", length, readLength); - readLength = length; // clamp to target buffer size + lastErrorMsg = stringPrintf("Target buffer too small (%d) to read whole string (%d) from argument", bufLength, length); + length = bufLength; // clamp to target buffer size } if (length) strncpy(buf, str, length); @@ -712,16 +712,16 @@ namespace CLEO // prococess here as GetScriptStringParam can not obtain strings with lenght greater than 128 thread->IncPtr(1); // already processed paramType - DWORD readLength = (BYTE)*thread->GetBytePointer(); // as unsigned byte! + DWORD length = (BYTE)*thread->GetBytePointer(); // as unsigned byte! thread->IncPtr(1); // length info char* str = (char*)thread->GetBytePointer(); - thread->IncPtr(readLength); // text data + thread->IncPtr(length); // text data - if (readLength > length) + if (length > bufLength) { - lastErrorMsg = stringPrintf("Target buffer too small (%d) to read whole string (%d) from argument", length, readLength); - readLength = length; // clamp to target buffer size + lastErrorMsg = stringPrintf("Target buffer too small (%d) to read whole string (%d) from argument", bufLength, length); + length = bufLength; // clamp to target buffer size } if (length) strncpy(buf, str, length); From e384a72f0b765904c6a73ae12e2a330b2d776ceb Mon Sep 17 00:00:00 2001 From: Miran Date: Sun, 29 Oct 2023 12:06:40 +0100 Subject: [PATCH 10/10] Read/Write string param showing warnings also when ran with SDK methods. --- source/CCustomOpcodeSystem.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/source/CCustomOpcodeSystem.cpp b/source/CCustomOpcodeSystem.cpp index 1159bf5e..2672c1cb 100644 --- a/source/CCustomOpcodeSystem.cpp +++ b/source/CCustomOpcodeSystem.cpp @@ -3160,12 +3160,18 @@ extern "C" LPSTR WINAPI CLEO_ReadStringPointerOpcodeParam(CLEO::CRunningScript* thread, char *buf, int size) { - return ReadStringParam(thread, buf, size); + auto result = ReadStringParam(thread, buf, size); + + if (result == nullptr) + LOG_WARNING("%s in script %s", lastErrorMsg.c_str(), ((CCustomScript*)thread)->GetInfoStr().c_str()); + + return result; } void WINAPI CLEO_WriteStringOpcodeParam(CLEO::CRunningScript* thread, const char* str) { - WriteStringParam(thread, str); + if(!WriteStringParam(thread, str)) + LOG_WARNING("%s in script %s", lastErrorMsg.c_str(), ((CCustomScript*)thread)->GetInfoStr().c_str()); } char* WINAPI CLEO_ReadParamsFormatted(CLEO::CRunningScript* thread, const char* format, char* buf, int bufSize) @@ -3174,17 +3180,10 @@ extern "C" if (!buf) { buf = internal_buf; bufSize = sizeof(internal_buf); } if (!bufSize) bufSize = MAX_STR_LEN; - if(format != nullptr && strlen(format) > 0) - { - if(ReadFormattedString(thread, buf, bufSize, format) == -1) // error? - { - LOG_WARNING("%s in script %s", lastErrorMsg.c_str(), ((CCustomScript*)thread)->GetInfoStr().c_str()); - } - } - else + if(ReadFormattedString(thread, buf, bufSize, format) == -1) // error? { - SkipUnusedVarArgs(thread); - if(bufSize > 0) buf[0] = '\0'; + LOG_WARNING("%s in script %s", lastErrorMsg.c_str(), ((CCustomScript*)thread)->GetInfoStr().c_str()); + return nullptr; // error } return buf;