From f9dc7a3e25f7109bcc4f9b33cf62605ed486c043 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Thu, 15 Jun 2023 22:33:36 +0800 Subject: [PATCH 1/5] Added code page setting for the console window --- artifacts/ddraw.ini | 3 +++ sfall/Logging.cpp | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 71e5028c..429bd899 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -906,3 +906,6 @@ ConsoleWindow=0 ;Console window position and size data. Do not modify ConsoleWindowData= + +;Set the code page for the console window (Default is your system code page) +ConsoleCodePage=0 diff --git a/sfall/Logging.cpp b/sfall/Logging.cpp index 8ee69fd2..af029916 100644 --- a/sfall/Logging.cpp +++ b/sfall/Logging.cpp @@ -46,6 +46,7 @@ public: static constexpr char* IniSection = "Debugging"; static constexpr char* IniModeKey = "ConsoleWindow"; static constexpr char* IniPositionKey = "ConsoleWindowData"; + static constexpr char* IniCodePageKey = "ConsoleCodePage"; static ConsoleWindow& instance() { return _instance; } @@ -137,6 +138,9 @@ void ConsoleWindow::init() { dlog_f("Failed to allocate console: 0x%x\n", DL_MAIN, GetLastError()); return; } + int cp = IniReader::GetIntDefaultConfig(IniSection, IniCodePageKey, 0); + if (cp > 0) SetConsoleOutputCP(cp); + freopen("CONOUT$", "w", stdout); // this allows to print to console via std::cout if (_mode & ConsoleSource::GAME) { From 347b4a3ecb4269619d096f2678ab927c4bf2e06d Mon Sep 17 00:00:00 2001 From: phobos2077 Date: Thu, 15 Jun 2023 23:53:39 +0200 Subject: [PATCH 2/5] Fix buffer overflow when DebugMode is enabled and printing messages longer than 259 via script - Refactoring: moved ConsoleWindow into separate file --- sfall/ConsoleWindow.cpp | 136 ++++++++++++++++++++++++++++++ sfall/ConsoleWindow.h | 57 +++++++++++++ sfall/Logging.cpp | 152 +--------------------------------- sfall/ModuleManager.cpp | 2 +- sfall/ModuleManager.h | 2 +- sfall/Modules/DebugEditor.cpp | 25 ++++++ sfall/Modules/MiscPatches.cpp | 2 +- sfall/SafeWrite.h | 4 + sfall/ddraw.vcxproj | 2 + sfall/ddraw.vcxproj.filters | 2 + sfall/main.cpp | 4 +- sfall/main.h | 4 - 12 files changed, 235 insertions(+), 157 deletions(-) create mode 100644 sfall/ConsoleWindow.cpp create mode 100644 sfall/ConsoleWindow.h diff --git a/sfall/ConsoleWindow.cpp b/sfall/ConsoleWindow.cpp new file mode 100644 index 00000000..b337cb58 --- /dev/null +++ b/sfall/ConsoleWindow.cpp @@ -0,0 +1,136 @@ +/* + * sfall + * Copyright (C) 2008-2023 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ConsoleWindow.h" + +#include "FalloutEngine\Fallout2.h" +#include "IniReader.h" +#include "Logging.h" +#include "SafeWrite.h" +#include "Utils.h" + +#include +#include + +namespace sfall +{ + +ConsoleWindow ConsoleWindow::_instance; + +bool ConsoleWindow::tryGetWindow(HWND* wnd) { + *wnd = GetConsoleWindow(); + if (!*wnd) { + dlogr("Error getting console window.", DL_MAIN); + return false; + } + return true; +} + +void ConsoleWindow::loadPosition() { + auto windowDataStr = IniReader::GetStringDefaultConfig(IniSection, IniPositionKey, ""); + auto windowDataSplit = split(windowDataStr, ','); + if (windowDataSplit.size() < 4) return; + + HWND wnd; + if (!tryGetWindow(&wnd)) return; + + int windowData[4]; + for (size_t i = 0; i < 4; i++) { + windowData[i] = atoi(windowDataSplit.at(i).c_str()); + } + if (!SetWindowPos(wnd, HWND_TOP, windowData[0], windowData[1], windowData[2], windowData[3], 0)) { + dlog_f("Error repositioning console window: 0x%x\n", DL_MAIN, GetLastError()); + } +} + +void ConsoleWindow::savePosition() { + HWND wnd; + if (!tryGetWindow(&wnd)) return; + + RECT wndRect; + if (!GetWindowRect(wnd, &wndRect)) { + dlog_f("Error getting console window position: 0x%x\n", DL_MAIN, GetLastError()); + } + int width = wndRect.right - wndRect.left; + int height = wndRect.bottom - wndRect.top; + std::ostringstream ss; + ss << wndRect.left << "," << wndRect.top << "," << width << "," << height; + auto wndDataStr = ss.str(); + dlog_f("Saving console window position & size: %s\n", DL_MAIN, wndDataStr.c_str()); + + IniReader::SetDefaultConfigString(IniSection, IniPositionKey, wndDataStr.c_str()); +} + +static void __fastcall PrintDebugLog(const char* a) { + ConsoleWindow::instance().falloutLog(a); +} + +static void __declspec(naked) debug_printf_hook() { + __asm { + call fo::funcoffs::vsprintf_; + pushadc; + lea ecx, [esp + 16]; + call PrintDebugLog; + popadc; + retn; + } +} + +void ConsoleWindow::init() { + _mode = IniReader::GetIntDefaultConfig(IniSection, IniModeKey, 0); + if (_mode == 0) return; + if (!AllocConsole()) { + dlog_f("Failed to allocate console: 0x%x\n", DL_MAIN, GetLastError()); + return; + } + freopen("CONOUT$", "w", stdout); // this allows to print to console via std::cout + + if (_mode & ConsoleSource::GAME) { + std::cout << "Displaying debug_printf output.\n"; + HookCall(0x4C6F77, debug_printf_hook); + } + if (_mode & ConsoleSource::SFALL) { + std::cout << "Displaying sfall debug output.\n"; + } + std::cout << std::endl; + + loadPosition(); +} + +ConsoleWindow::~ConsoleWindow() { + if (_mode == 0) return; + + savePosition(); +} + +void ConsoleWindow::falloutLog(const char* a) { + std::cout << a; + _lastSource = ConsoleSource::GAME; +} + +void ConsoleWindow::sfallLog(const std::string& a, int type) { + if (!(_mode & ConsoleSource::SFALL)) return; + + if (_lastSource == ConsoleSource::GAME) { + std::cout << "\n"; // To make logs prettier, because debug_msg places newline before the message. + } + std::cout << a; + _lastSource = ConsoleSource::SFALL; +} + +} diff --git a/sfall/ConsoleWindow.h b/sfall/ConsoleWindow.h new file mode 100644 index 00000000..127b3e04 --- /dev/null +++ b/sfall/ConsoleWindow.h @@ -0,0 +1,57 @@ +/* + * sfall + * Copyright (C) 2008-2023 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +namespace sfall +{ + +enum ConsoleSource : int { + GAME = 1, + SFALL = 2 +}; + +class ConsoleWindow { +public: + static constexpr char* IniSection = "Debugging"; + static constexpr char* IniModeKey = "ConsoleWindow"; + static constexpr char* IniPositionKey = "ConsoleWindowData"; + + static ConsoleWindow& instance() { return _instance; } + + ConsoleWindow() {} + ~ConsoleWindow(); + + void init(); + + void loadPosition(); + void savePosition(); + + void sfallLog(const std::string& a, int type); + void falloutLog(const char* a); + +private: + static ConsoleWindow _instance; + + int _mode = 0; + ConsoleSource _lastSource; + + bool tryGetWindow(HWND* wnd); +}; + +} diff --git a/sfall/Logging.cpp b/sfall/Logging.cpp index af029916..27fae13d 100644 --- a/sfall/Logging.cpp +++ b/sfall/Logging.cpp @@ -16,167 +16,24 @@ * along with this program. If not, see . */ -#include "main.h" #include "Logging.h" + +#include "main.h" #include "FalloutEngine\Fallout2.h" +#include "ConsoleWindow.h" #include "Utils.h" -#ifndef NO_SFALL_DEBUG - #include -#include -#include namespace sfall { -enum ConsoleSource : int { - GAME = 1, - SFALL = 2 -}; - static int DebugTypes = 0; static std::ofstream Log; static int LastType = -1; static int LastNewLine; -class ConsoleWindow { -public: - static constexpr char* IniSection = "Debugging"; - static constexpr char* IniModeKey = "ConsoleWindow"; - static constexpr char* IniPositionKey = "ConsoleWindowData"; - static constexpr char* IniCodePageKey = "ConsoleCodePage"; - - static ConsoleWindow& instance() { return _instance; } - - ConsoleWindow() {} - ~ConsoleWindow(); - - void init(); - - void loadPosition(); - void savePosition(); - - void sfallLog(const std::string& a, int type); - void falloutLog(const char* a); - -private: - static ConsoleWindow _instance; - - int _mode = 0; - ConsoleSource _lastSource; - - bool tryGetWindow(HWND* wnd); -}; - -ConsoleWindow ConsoleWindow::_instance; - -bool ConsoleWindow::tryGetWindow(HWND* wnd) { - *wnd = GetConsoleWindow(); - if (!*wnd) { - dlogr("Error getting console window.", DL_MAIN); - return false; - } - return true; -} - -void ConsoleWindow::loadPosition() { - auto windowDataStr = IniReader::GetStringDefaultConfig(IniSection, IniPositionKey, ""); - auto windowDataSplit = split(windowDataStr, ','); - if (windowDataSplit.size() < 4) return; - - HWND wnd; - if (!tryGetWindow(&wnd)) return; - - int windowData[4]; - for (size_t i = 0; i < 4; i++) { - windowData[i] = atoi(windowDataSplit.at(i).c_str()); - } - if (!SetWindowPos(wnd, HWND_TOP, windowData[0], windowData[1], windowData[2], windowData[3], 0)) { - dlog_f("Error repositioning console window: 0x%x\n", DL_MAIN, GetLastError()); - } -} - -void ConsoleWindow::savePosition() { - HWND wnd; - if (!tryGetWindow(&wnd)) return; - - RECT wndRect; - if (!GetWindowRect(wnd, &wndRect)) { - dlog_f("Error getting console window position: 0x%x\n", DL_MAIN, GetLastError()); - } - int width = wndRect.right - wndRect.left; - int height = wndRect.bottom - wndRect.top; - std::ostringstream ss; - ss << wndRect.left << "," << wndRect.top << "," << width << "," << height; - auto wndDataStr = ss.str(); - dlog_f("Saving console window position & size: %s\n", DL_MAIN, wndDataStr.c_str()); - - IniReader::SetDefaultConfigString(IniSection, IniPositionKey, wndDataStr.c_str()); -} - -static void __fastcall PrintToConsole(const char* a) { - ConsoleWindow::instance().falloutLog(a); -} - -static void __declspec(naked) debug_printf_hook() { - __asm { - call fo::funcoffs::vsprintf_; - pushadc; - lea ecx, [esp + 16]; - call PrintToConsole; - popadc; - retn; - } -} - -void ConsoleWindow::init() { - _mode = IniReader::GetIntDefaultConfig(IniSection, IniModeKey, 0); - if (_mode == 0) return; - if (!AllocConsole()) { - dlog_f("Failed to allocate console: 0x%x\n", DL_MAIN, GetLastError()); - return; - } - int cp = IniReader::GetIntDefaultConfig(IniSection, IniCodePageKey, 0); - if (cp > 0) SetConsoleOutputCP(cp); - - freopen("CONOUT$", "w", stdout); // this allows to print to console via std::cout - - if (_mode & ConsoleSource::GAME) { - std::cout << "Displaying debug_printf output.\n"; - HookCall(0x4C6F77, debug_printf_hook); - } - if (_mode & ConsoleSource::SFALL) { - std::cout << "Displaying sfall debug output.\n"; - } - std::cout << std::endl; - - loadPosition(); -} - -ConsoleWindow::~ConsoleWindow() { - if (_mode == 0) return; - - savePosition(); -} - -void ConsoleWindow::falloutLog(const char* a) { - std::cout << a; - _lastSource = ConsoleSource::GAME; -} - -void ConsoleWindow::sfallLog(const std::string& a, int type) { - if (!(_mode & ConsoleSource::SFALL)) return; - - if (_lastSource == ConsoleSource::GAME) { - std::cout << "\n"; // To make logs prettier, because debug_msg places newline before the message. - } - std::cout << a; - _lastSource = ConsoleSource::SFALL; -} - - template static void OutLog(T a, int type, bool newLine = false) { std::ostringstream ss; @@ -280,10 +137,7 @@ void LoggingInit() { if (IniReader::GetIntDefaultConfig("Debugging", "Fixes", 0)) { DebugTypes |= DL_FIX; } - - ConsoleWindow::instance().init(); } } -#endif diff --git a/sfall/ModuleManager.cpp b/sfall/ModuleManager.cpp index 13ebfbde..06db0ffb 100644 --- a/sfall/ModuleManager.cpp +++ b/sfall/ModuleManager.cpp @@ -23,7 +23,7 @@ void ModuleManager::initAll() { } } -ModuleManager& ModuleManager::getInstance() { +ModuleManager& ModuleManager::instance() { return _instance; } diff --git a/sfall/ModuleManager.h b/sfall/ModuleManager.h index c1a5eb07..8f0c6ba1 100644 --- a/sfall/ModuleManager.h +++ b/sfall/ModuleManager.h @@ -23,7 +23,7 @@ public: _modules.emplace_back(new T()); } - static ModuleManager& getInstance(); + static ModuleManager& instance(); private: // disallow copy constructor and copy assignment because we're dealing with unique_ptr's here diff --git a/sfall/Modules/DebugEditor.cpp b/sfall/Modules/DebugEditor.cpp index 7104d575..ee372c0c 100644 --- a/sfall/Modules/DebugEditor.cpp +++ b/sfall/Modules/DebugEditor.cpp @@ -19,6 +19,7 @@ #include #include "..\main.h" +#include "..\ConsoleWindow.h" #include "..\FalloutEngine\Fallout2.h" #include "..\InputFuncs.h" //#include "Graphics.h" @@ -394,6 +395,28 @@ static void __declspec(naked) combat_load_hack() { } } +static void __fastcall PrintLogToConsole(const char* a) { + auto& console = ConsoleWindow::instance(); + console.falloutLog("\n"); + console.falloutLog(a); +} + +static void __declspec(naked) op_display_debug_msg_hack() { + __asm { + mov eax, 0x505224; // "\n" + call ds:[FO_VAR_debug_func]; + mov eax, esi; // actual message + call ds:[FO_VAR_debug_func]; + pushadc; + mov ecx, esi; + call PrintLogToConsole; // duplicate messages to console window + popadc; + pop eax; + add eax, 17; // skip to the end of functions + jmp eax; + } +} + // Shifts the string one character to the right and inserts a newline control character at the beginning static void MoveDebugString(char* messageAddr) { int i = 0; @@ -445,6 +468,8 @@ static void DebugModePatch() { MakeCall(0x4C703F, debug_log_hack); BlockCall(0x4C7044); // just nop code } + // replace calling debug_printf_ with _debug_func, to avoid buffer overflow with messages longer than 260-bytes. + MakeCalls(op_display_debug_msg_hack, {0x45540F, 0x45CB4E}); // op_display_msg and op_debug_msg // set the position of the debug window SafeWrite8(0x4DC34D, 15); diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index e0059bba..1035b57b 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -254,7 +254,7 @@ static void __fastcall SwapHandSlots(fo::GameObject* item, fo::GameObject* &toSl } std::memcpy(dstSlot, &item, 0x14); } else { // swap slots - auto hands = fo::var::itemButtonItems; + auto& hands = fo::var::itemButtonItems; hands[fo::HandSlot::Left].primaryAttack = fo::AttackType::ATKTYPE_RWEAPON_PRIMARY; hands[fo::HandSlot::Left].secondaryAttack = fo::AttackType::ATKTYPE_RWEAPON_SECONDARY; hands[fo::HandSlot::Right].primaryAttack = fo::AttackType::ATKTYPE_LWEAPON_PRIMARY; diff --git a/sfall/SafeWrite.h b/sfall/SafeWrite.h index ea834eda..fb34f8e1 100644 --- a/sfall/SafeWrite.h +++ b/sfall/SafeWrite.h @@ -17,6 +17,10 @@ enum CodeType : BYTE { JumpZ = 0x74, // 0x74 [jz short ...] }; +// Macros for quick replacement of assembler opcodes pushad/popad +#define pushadc __asm push eax __asm push edx __asm push ecx +#define popadc __asm pop ecx __asm pop edx __asm pop eax + template void __stdcall SafeWrite(DWORD addr, T data) { DWORD oldProtect; diff --git a/sfall/ddraw.vcxproj b/sfall/ddraw.vcxproj index bba9c713..b4816deb 100644 --- a/sfall/ddraw.vcxproj +++ b/sfall/ddraw.vcxproj @@ -345,6 +345,7 @@ + @@ -496,6 +497,7 @@ + diff --git a/sfall/ddraw.vcxproj.filters b/sfall/ddraw.vcxproj.filters index ae1ca255..cf178f63 100644 --- a/sfall/ddraw.vcxproj.filters +++ b/sfall/ddraw.vcxproj.filters @@ -452,6 +452,7 @@ HLSL + @@ -824,6 +825,7 @@ Modules\SubModules + diff --git a/sfall/main.cpp b/sfall/main.cpp index d5184420..a07c348c 100644 --- a/sfall/main.cpp +++ b/sfall/main.cpp @@ -75,6 +75,7 @@ #include "Modules\Unarmed.h" #include "Modules\Worldmap.h" +#include "ConsoleWindow.h" #include "CRC.h" #include "InputFuncs.h" #include "Logging.h" @@ -102,7 +103,7 @@ char falloutConfigName[65]; static void InitModules() { dlogr("In InitModules", DL_INIT); - auto& manager = ModuleManager::getInstance(); + auto& manager = ModuleManager::instance(); // initialize all modules manager.add(); // fixes should be applied at the beginning @@ -214,6 +215,7 @@ static HMODULE SfallInit() { if (!CRC(filepath)) return 0; LoggingInit(); + ConsoleWindow::instance().init(); // enabling debugging features isDebug = (IniReader::GetIntDefaultConfig("Debugging", "Enable", 0) != 0); diff --git a/sfall/main.h b/sfall/main.h index 8ce73d39..8cf1e690 100644 --- a/sfall/main.h +++ b/sfall/main.h @@ -81,10 +81,6 @@ namespace sfall // Trap for Debugger #define BREAKPOINT __asm int 3 -// Macros for quick replacement of assembler opcodes pushad/popad -#define pushadc __asm push eax __asm push edx __asm push ecx -#define popadc __asm pop ecx __asm pop edx __asm pop eax - extern bool versionCHI; extern char falloutConfigName[65]; From 4de5de6a0ce97b54fa033d3a1a6a6dac2356edd5 Mon Sep 17 00:00:00 2001 From: phobos2077 Date: Fri, 16 Jun 2023 00:02:55 +0200 Subject: [PATCH 3/5] Restore code page option from NovaRain --- sfall/ConsoleWindow.cpp | 8 ++++++++ sfall/ConsoleWindow.h | 4 ---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/sfall/ConsoleWindow.cpp b/sfall/ConsoleWindow.cpp index b337cb58..2ecdbe3d 100644 --- a/sfall/ConsoleWindow.cpp +++ b/sfall/ConsoleWindow.cpp @@ -30,6 +30,11 @@ namespace sfall { +static constexpr char* IniSection = "Debugging"; +static constexpr char* IniModeKey = "ConsoleWindow"; +static constexpr char* IniPositionKey = "ConsoleWindowData"; +static constexpr char* IniCodePageKey = "ConsoleCodePage"; + ConsoleWindow ConsoleWindow::_instance; bool ConsoleWindow::tryGetWindow(HWND* wnd) { @@ -98,6 +103,9 @@ void ConsoleWindow::init() { dlog_f("Failed to allocate console: 0x%x\n", DL_MAIN, GetLastError()); return; } + int cp = IniReader::GetIntDefaultConfig(IniSection, IniCodePageKey, 0); + if (cp > 0) SetConsoleOutputCP(cp); + freopen("CONOUT$", "w", stdout); // this allows to print to console via std::cout if (_mode & ConsoleSource::GAME) { diff --git a/sfall/ConsoleWindow.h b/sfall/ConsoleWindow.h index 127b3e04..fb22e117 100644 --- a/sfall/ConsoleWindow.h +++ b/sfall/ConsoleWindow.h @@ -28,10 +28,6 @@ enum ConsoleSource : int { class ConsoleWindow { public: - static constexpr char* IniSection = "Debugging"; - static constexpr char* IniModeKey = "ConsoleWindow"; - static constexpr char* IniPositionKey = "ConsoleWindowData"; - static ConsoleWindow& instance() { return _instance; } ConsoleWindow() {} From ad6491d61ee67b83845cb009d49a2dabfd84b20e Mon Sep 17 00:00:00 2001 From: phobos2077 Date: Fri, 16 Jun 2023 00:16:36 +0200 Subject: [PATCH 4/5] Increase array string limit to 1024 bytes - Because it is now safe to log longer stuff --- sfall/Modules/Scripting/Arrays.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sfall/Modules/Scripting/Arrays.h b/sfall/Modules/Scripting/Arrays.h index c5bf29a6..4a72464e 100644 --- a/sfall/Modules/Scripting/Arrays.h +++ b/sfall/Modules/Scripting/Arrays.h @@ -29,7 +29,7 @@ namespace sfall namespace script { -#define ARRAY_MAX_STRING (255) // maximum length of string to be stored as array key or value +#define ARRAY_MAX_STRING (1024) // maximum length of string to be stored as array key or value (including null-terminator) #define ARRAY_MAX_SIZE (100000) // maximum number of array elements, // so total maximum memory/disk footprint of one array is: 16 + (ARRAY_MAX_STRING + 8) * ARRAY_MAX_SIZE From 2cef5310e29162abad6558bbe2923e8db89c45f4 Mon Sep 17 00:00:00 2001 From: phobos2077 Date: Fri, 16 Jun 2023 14:57:41 +0200 Subject: [PATCH 5/5] Rewrite console writing - Separate debug_msg and display_msg as separate Source - Merge writing to one function with proper filtering --- sfall/ConsoleWindow.cpp | 31 ++++++++++++++++--------------- sfall/ConsoleWindow.h | 18 ++++++++++-------- sfall/Logging.cpp | 2 +- sfall/Modules/DebugEditor.cpp | 28 +++++++++++++++++++++++----- 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/sfall/ConsoleWindow.cpp b/sfall/ConsoleWindow.cpp index 2ecdbe3d..31febeee 100644 --- a/sfall/ConsoleWindow.cpp +++ b/sfall/ConsoleWindow.cpp @@ -81,8 +81,8 @@ void ConsoleWindow::savePosition() { IniReader::SetDefaultConfigString(IniSection, IniPositionKey, wndDataStr.c_str()); } -static void __fastcall PrintDebugLog(const char* a) { - ConsoleWindow::instance().falloutLog(a); +static void __fastcall WriteGameLog(const char* a) { + ConsoleWindow::instance().write(a, ConsoleWindow::Source::GAME); } static void __declspec(naked) debug_printf_hook() { @@ -90,7 +90,7 @@ static void __declspec(naked) debug_printf_hook() { call fo::funcoffs::vsprintf_; pushadc; lea ecx, [esp + 16]; - call PrintDebugLog; + call WriteGameLog; popadc; retn; } @@ -108,13 +108,19 @@ void ConsoleWindow::init() { freopen("CONOUT$", "w", stdout); // this allows to print to console via std::cout - if (_mode & ConsoleSource::GAME) { + if (_mode & Source::GAME) { std::cout << "Displaying debug_printf output.\n"; HookCall(0x4C6F77, debug_printf_hook); } - if (_mode & ConsoleSource::SFALL) { + if (_mode & Source::SFALL) { std::cout << "Displaying sfall debug output.\n"; } + if (_mode & Source::DEBUG_MSG) { + std::cout << "Displaying debug_msg output.\n"; + } + if (_mode & Source::DISPLAY_MSG) { + std::cout << "Displaying display_msg output.\n"; + } std::cout << std::endl; loadPosition(); @@ -126,19 +132,14 @@ ConsoleWindow::~ConsoleWindow() { savePosition(); } -void ConsoleWindow::falloutLog(const char* a) { - std::cout << a; - _lastSource = ConsoleSource::GAME; -} +void ConsoleWindow::write(const char* message, ConsoleWindow::Source source) { + if (!(_mode & source)) return; -void ConsoleWindow::sfallLog(const std::string& a, int type) { - if (!(_mode & ConsoleSource::SFALL)) return; - - if (_lastSource == ConsoleSource::GAME) { + if (source == Source::SFALL && _lastSource != Source::SFALL) { std::cout << "\n"; // To make logs prettier, because debug_msg places newline before the message. } - std::cout << a; - _lastSource = ConsoleSource::SFALL; + std::cout << message; + _lastSource = source; } } diff --git a/sfall/ConsoleWindow.h b/sfall/ConsoleWindow.h index fb22e117..ef2d6e25 100644 --- a/sfall/ConsoleWindow.h +++ b/sfall/ConsoleWindow.h @@ -21,13 +21,16 @@ namespace sfall { -enum ConsoleSource : int { - GAME = 1, - SFALL = 2 -}; - class ConsoleWindow { public: + + enum Source : int { + GAME = 1, + SFALL = 2, + DEBUG_MSG = 4, + DISPLAY_MSG = 8, + }; + static ConsoleWindow& instance() { return _instance; } ConsoleWindow() {} @@ -38,14 +41,13 @@ public: void loadPosition(); void savePosition(); - void sfallLog(const std::string& a, int type); - void falloutLog(const char* a); + void write(const char* message, Source source); private: static ConsoleWindow _instance; int _mode = 0; - ConsoleSource _lastSource; + Source _lastSource; bool tryGetWindow(HWND* wnd); }; diff --git a/sfall/Logging.cpp b/sfall/Logging.cpp index 27fae13d..b195fc9a 100644 --- a/sfall/Logging.cpp +++ b/sfall/Logging.cpp @@ -44,7 +44,7 @@ static void OutLog(T a, int type, bool newLine = false) { if (newLine) ss << "\n"; std::string str = ss.str(); - ConsoleWindow::instance().sfallLog(str, type); + ConsoleWindow::instance().write(str.c_str(), ConsoleWindow::Source::SFALL); Log << str; Log.flush(); diff --git a/sfall/Modules/DebugEditor.cpp b/sfall/Modules/DebugEditor.cpp index ee372c0c..5efd010c 100644 --- a/sfall/Modules/DebugEditor.cpp +++ b/sfall/Modules/DebugEditor.cpp @@ -395,10 +395,11 @@ static void __declspec(naked) combat_load_hack() { } } -static void __fastcall PrintLogToConsole(const char* a) { +static void __fastcall DuplicateLogToConsole(const char* a, unsigned long displayMsg) { auto& console = ConsoleWindow::instance(); - console.falloutLog("\n"); - console.falloutLog(a); + auto source = displayMsg ? ConsoleWindow::Source::DISPLAY_MSG : ConsoleWindow::Source::DEBUG_MSG; + console.write("\n", source); + console.write(a, source); } static void __declspec(naked) op_display_debug_msg_hack() { @@ -409,14 +410,30 @@ static void __declspec(naked) op_display_debug_msg_hack() { call ds:[FO_VAR_debug_func]; pushadc; mov ecx, esi; - call PrintLogToConsole; // duplicate messages to console window + mov edx, [esp + 12]; + call DuplicateLogToConsole; // duplicate messages to console window popadc; + add esp, 4; // eat displayMsg flag pop eax; add eax, 17; // skip to the end of functions jmp eax; } } +static void __declspec(naked) op_display_msg_hack() { + __asm { + push 1; // displayMsg = true + jmp op_display_debug_msg_hack; + } +} + +static void __declspec(naked) op_debug_msg_hack() { + __asm { + push 0; // displayMsg = false + jmp op_display_debug_msg_hack; + } +} + // Shifts the string one character to the right and inserts a newline control character at the beginning static void MoveDebugString(char* messageAddr) { int i = 0; @@ -469,7 +486,8 @@ static void DebugModePatch() { BlockCall(0x4C7044); // just nop code } // replace calling debug_printf_ with _debug_func, to avoid buffer overflow with messages longer than 260-bytes. - MakeCalls(op_display_debug_msg_hack, {0x45540F, 0x45CB4E}); // op_display_msg and op_debug_msg + MakeCall(0x45540F, op_display_msg_hack); + MakeCall(0x45CB4E, op_debug_msg_hack); // set the position of the debug window SafeWrite8(0x4DC34D, 15);