Skip to content

Commit

Permalink
Version 1.4.1
Browse files Browse the repository at this point in the history
  • Loading branch information
SomeCrazyGuy committed Jul 3, 2024
1 parent 4bcd641 commit 195cda8
Show file tree
Hide file tree
Showing 9 changed files with 344 additions and 146 deletions.
2 changes: 2 additions & 0 deletions Starfield Console Replacer.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ copy /Y "$(OutDir)$(TargetName).dll" "$(OutDir)BetterConsole.asi"</Command>
<ClCompile Include="src\console.cpp" />
<ClCompile Include="src\d3d11on12ui.cpp" />
<ClCompile Include="src\fake_vcruntime140_1.cpp" />
<ClCompile Include="src\game_hooks.cpp" />
<ClCompile Include="src\gui.cpp" />
<ClCompile Include="src\hook_api.cpp" />
<ClCompile Include="src\hotkeys.cpp" />
Expand All @@ -137,6 +138,7 @@ copy /Y "$(OutDir)$(TargetName).dll" "$(OutDir)BetterConsole.asi"</Command>
<ClInclude Include="src\callback.h" />
<ClInclude Include="src\console.h" />
<ClInclude Include="src\d3d11on12ui.h" />
<ClInclude Include="src\game_hooks.h" />
<ClInclude Include="src\gui.h" />
<ClInclude Include="src\gui_interface.h" />
<ClInclude Include="src\hook_api.h" />
Expand Down
Binary file modified VersionInfo.rc
Binary file not shown.
217 changes: 81 additions & 136 deletions src/console.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,8 @@
#include <vector>
#include <ctype.h>

// Console print interface:
//48 89 5c 24 ?? 48 89 6c 24 ?? 48 89 74 24 ?? 57 b8 30 10 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 49 - new sig with 1 match
//48 89 5c 24 ?? 48 89 6c 24 ?? 48 89 74 24 ?? 57 b8 30 10 -- the 0x1030 stack size might be too unique
/*
undefined4 uVar1;
int iVar2;
char local_1010 [4096];
undefined8 local_10;
local_10 = 0x142883992;
uVar1 = FUN_140839080();
FUN_140587784(0x40);
//calling vsnprintf() with 4096 buffer and using the return value to append "\n\0"
iVar2 = FUN_1412518e8(local_1010,0x1000,param_2,param_4,param_3);
if (0 < iVar2) {
local_1010[iVar2] = '\n'; //note the behavior of adding "\n\0"
local_1010[iVar2 + 1] = '\0';
FUN_14288379c(param_1,local_1010,param_2);
}
FUN_140587784(uVar1);
return;
*/

// Console run command interface:
//48 8b c4 48 89 50 ?? 4c 89 40 ?? 4c 89 48 ?? 55 53 56 57 41 55 41 56 41 57 48 8d
/*
check for this in ghidra:
_strnicmp((char *)local_838,"ForEachRef[",0xb)
...
memset(local_c38,0,0x400);
pcVar15 = "float fresult\nref refr\nset refr to GetSelectedRef\nset fresult to ";
*/

// Forced offsets 1.12.32
#define OFFSET_CONSOLE_PRINT 0x29cd9b8
#define OFFSET_CONSOLE_RUN 0x29c7f44

#define OUTPUT_FILE_PATH "BetterConsoleOutput.txt"
#define HISTORY_FILE_PATH "BetterConsoleHistory.txt"
#include "game_hooks.h"

#define NUM_HOTKEY_COMMANDS 16


Expand All @@ -55,16 +15,10 @@ enum class InputMode : uint32_t {
SearchHistory,
};

static void console_run(void* consolemgr, char* cmd);
static void console_print(void* consolemgr, const char* fmt, va_list args);

static int CALLBACK_inputtext_cmdline(ImGuiInputTextCallbackData* data);
static int CALLBACK_inputtext_switch_mode(ImGuiInputTextCallbackData* data);
static bool strcasestr(const char* haystack, const char* needle);

static void(*OLD_ConsolePrintV)(void*, const char*, va_list) = nullptr;
static void(*OLD_ConsoleRun)(void*, char*) = nullptr;

static bool UpdateScroll = false;
static bool UpdateFocus = true;
static int HistoryIndex = -1;
Expand All @@ -75,10 +29,9 @@ static const hook_api_t* HookAPI = nullptr;
static const log_buffer_api_t* LogBuffer = nullptr;
static const simple_draw_t* SimpleDraw = nullptr;
static const config_api_t* Config = nullptr;
static const auto GameHook = GameHook_GetData();

static char IOBuffer[256 * 1024];
static LogBufferHandle OutputHandle;
static LogBufferHandle HistoryHandle;
static std::vector<uint32_t> SearchOutputLines{};
static std::vector<uint32_t> SearchHistoryLines{};

Expand All @@ -91,83 +44,112 @@ struct Command {
} HotkeyCommands[NUM_HOTKEY_COMMANDS];


static void forward_to_old_consoleprint(void* consolemgr, const char* fmt, ...) {
if (!consolemgr) return;
va_list args;
va_start(args, fmt);
OLD_ConsolePrintV(consolemgr, fmt, args);
va_end(args);
}
static void draw_console_window(void* imgui_context) {
(void)imgui_context;

static bool ConsoleReadyIgnored = false;
if (!GameHook->ConsoleReadyFlag && !ConsoleReadyIgnored) {
SimpleDraw->Text("Cannot detect if the console is ready. Proceed anyway?");
if (SimpleDraw->Button("Ignore and continue")) {
ConsoleReadyIgnored = true;
}
return;
}

static void console_print(void* consolemgr, const char* fmt, va_list args) {
auto size = vsnprintf(IOBuffer, sizeof(IOBuffer), fmt, args);
if (size <= 0) return;
forward_to_old_consoleprint(consolemgr, "%s", IOBuffer); //send it already converted
LogBuffer->Append(OutputHandle, IOBuffer);
IOBuffer[0] = 0;
UpdateScroll = true;
}
if (!(GameHook->ConsoleRun && GameHook->ConsoleOutputHooked)) {
if (!GameHook->ConsoleOutputHooked) {
SimpleDraw->Text("Cannot hook console print function. cannot start console");
}

if (!GameHook->ConsoleRun) {
SimpleDraw->Text("Cannot hook console run function. cannot start console");
}

static void console_run(void* consolemgr, char* cmd) {
LogBuffer->Append(HistoryHandle, cmd);
if (OLD_ConsoleRun) {
OLD_ConsoleRun(consolemgr, cmd);
SimpleDraw->Text("Incompatible mod loaded or incompatible game version");
SimpleDraw->Text("BetterConsole '%s' is compatible with game version '%s'", BETTERCONSOLE_VERSION, GAME_VERSION);
return;
}
else {
DEBUG("console hook not ready when running command: %s", cmd);

static bool GamePausedIgnored = false;
if (!GameHook->GetGamePausedFlag() && !GamePausedIgnored) {
SimpleDraw->Text("Cannot detect if the game is paused. Proceed anyway?");
if (SimpleDraw->Button("Ignore and continue")) {
GamePausedIgnored = true;
}
return;
}
}

static void draw_console_window(void* imgui_context) {
(void)imgui_context;

static bool GameState = true;
static const char* GameStates[] = { "Paused", "Running" };
if (ImGui::Button(GameStates[GameState])) {
char tgp[] = "ToggleGamePause";
console_run(NULL, tgp);
GameState = !GameState;
if(GameHook->ConsoleReadyFlag && !*GameHook->ConsoleReadyFlag) {
SimpleDraw->Text("Waiting for console to become ready...");
return;
}


if (GamePausedIgnored) {
if (SimpleDraw->Button("Toggle Game Pause")) {
GameHook->ConsoleRun("ToggleGamePause");
}
}
else {
bool* flag = GameHook->GetGamePausedFlag();
const char* const GamePauseText = *flag ? "Resume Game" : "Pause Game";
if (SimpleDraw->Button(GamePauseText)) {
*flag = !*flag;
}
}
ImGui::SameLine();

ImGui::SetNextItemWidth(-(ImGui::GetFontSize() * 20));
ImGui::SetNextItemWidth(-(ImGui::GetFontSize() * 12.0f));

if (UpdateFocus) {
ImGui::SetKeyboardFocusHere();
UpdateFocus = false;
}

if (CommandMode == InputMode::Command) {
if (ImGui::InputText("Command Mode", IOBuffer, sizeof(IOBuffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackCompletion, CALLBACK_inputtext_cmdline)) {
HistoryIndex = LogBuffer->GetLineCount(HistoryHandle);
console_run(NULL, IOBuffer);
if (ImGui::InputText("Command Mode ", IOBuffer, sizeof(IOBuffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackCompletion, CALLBACK_inputtext_cmdline)) {
HistoryIndex = LogBuffer->GetLineCount(GameHook->ConsoleInput);
GameHook->ConsoleRun(IOBuffer);
IOBuffer[0] = 0;
UpdateFocus = true;
}
SimpleDraw->ShowLogBuffer(OutputHandle, UpdateScroll);
SimpleDraw->SameLine();
if (SimpleDraw->Button("Clear")) {
LogBuffer->Clear(GameHook->ConsoleOutput);
}
SimpleDraw->ShowLogBuffer(GameHook->ConsoleOutput, UpdateScroll);

}
else if (CommandMode == InputMode::SearchOutput) {
if (ImGui::InputText("Search Output", IOBuffer, sizeof(IOBuffer), ImGuiInputTextFlags_CallbackCompletion, CALLBACK_inputtext_switch_mode)) {
if (ImGui::InputText("Search Output ", IOBuffer, sizeof(IOBuffer), ImGuiInputTextFlags_CallbackCompletion, CALLBACK_inputtext_switch_mode)) {
SearchOutputLines.clear();
for (uint32_t i = 0; i < LogBuffer->GetLineCount(OutputHandle); ++i) {
if (strcasestr(LogBuffer->GetLine(OutputHandle, i), IOBuffer)) {
for (uint32_t i = 0; i < LogBuffer->GetLineCount(GameHook->ConsoleOutput); ++i) {
if (strcasestr(LogBuffer->GetLine(GameHook->ConsoleOutput, i), IOBuffer)) {
SearchOutputLines.push_back(i);
}
}
}
SimpleDraw->ShowFilteredLogBuffer(OutputHandle, SearchOutputLines.data(), (uint32_t)SearchOutputLines.size(), UpdateScroll);
SimpleDraw->SameLine();
if (SimpleDraw->Button("Clear")) {
LogBuffer->Clear(GameHook->ConsoleOutput);
}
SimpleDraw->ShowFilteredLogBuffer(GameHook->ConsoleOutput, SearchOutputLines.data(), (uint32_t)SearchOutputLines.size(), UpdateScroll);
}
else if (CommandMode == InputMode::SearchHistory) {
if (ImGui::InputText("Search History", IOBuffer, sizeof(IOBuffer), ImGuiInputTextFlags_CallbackCompletion, CALLBACK_inputtext_switch_mode)) {
SearchHistoryLines.clear();
for (uint32_t i = 0; i < LogBuffer->GetLineCount(HistoryHandle); ++i) {
if (strcasestr(LogBuffer->GetLine(HistoryHandle, i), IOBuffer)) {
for (uint32_t i = 0; i < LogBuffer->GetLineCount(GameHook->ConsoleInput); ++i) {
if (strcasestr(LogBuffer->GetLine(GameHook->ConsoleInput, i), IOBuffer)) {
SearchHistoryLines.push_back(i);
}
}
}
SimpleDraw->ShowFilteredLogBuffer(HistoryHandle, SearchHistoryLines.data(), (uint32_t)SearchHistoryLines.size(), UpdateScroll);
SimpleDraw->SameLine();
if (SimpleDraw->Button("Clear")) {
LogBuffer->Clear(GameHook->ConsoleInput);
}
SimpleDraw->ShowFilteredLogBuffer(GameHook->ConsoleInput, SearchHistoryLines.data(), (uint32_t)SearchHistoryLines.size(), UpdateScroll);
}
else {
ASSERT(false && "Invalid command mode");
Expand All @@ -179,7 +161,7 @@ static void draw_console_window(void* imgui_context) {

static int CALLBACK_inputtext_cmdline(ImGuiInputTextCallbackData* data) {
if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory) {
const auto HistoryMax = (int)LogBuffer->GetLineCount(HistoryHandle);
const auto HistoryMax = (int)LogBuffer->GetLineCount(GameHook->ConsoleInput);

if (data->EventKey == ImGuiKey_UpArrow) {
--HistoryIndex;
Expand All @@ -198,7 +180,7 @@ static int CALLBACK_inputtext_cmdline(ImGuiInputTextCallbackData* data) {

if (HistoryMax) {
data->DeleteChars(0, data->BufTextLen);
data->InsertChars(0, LogBuffer->GetLine(HistoryHandle, HistoryIndex));
data->InsertChars(0, LogBuffer->GetLine(GameHook->ConsoleInput, HistoryIndex));
}
}
else if (data->EventFlag == ImGuiInputTextFlags_CallbackCompletion) {
Expand Down Expand Up @@ -265,7 +247,7 @@ static void CALLBACK_console_hotkey(uintptr_t userdata) {
auto index = (uint32_t)userdata;
char* hotkey = HotkeyCommands[index].data;
if (hotkey[0]) {
API->Console->RunCommand(hotkey);
GameHook->ConsoleRun(hotkey);
}
}

Expand All @@ -290,49 +272,12 @@ extern void setup_console(const BetterAPI* api) {
HookAPI = API->Hook;
SimpleDraw = API->SimpleDraw;
Config = API->Config;

OutputHandle = LogBuffer->Create("Console Output", OUTPUT_FILE_PATH);
HistoryHandle = LogBuffer->Restore("Command History", HISTORY_FILE_PATH);

DEBUG("Hooking print function using AOB method");
auto hook_print_aob = HookAPI->AOBScanEXE("48 89 5c 24 ?? 48 89 6c 24 ?? 48 89 74 24 ?? 57 b8 30 10 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 49");

if (!hook_print_aob) {
DEBUG("hook_print_aob AOB method failed, (incompatible mod or game update?)");
hook_print_aob = HookAPI->Relocate(OFFSET_CONSOLE_PRINT);
}

OLD_ConsolePrintV = (decltype(OLD_ConsolePrintV))HookAPI->HookFunction(
(FUNC_PTR)hook_print_aob,
(FUNC_PTR)console_print
);

DEBUG("Hooking run function using AOB method");
auto hook_run_aob = HookAPI->AOBScanEXE("48 8b c4 48 89 50 ?? 4c 89 40 ?? 4c 89 48 ?? 55 53 56 57 41 55 41 56 41 57 48 8d");

if (!hook_run_aob) {
DEBUG("hook_run_aob AOB method failed, (incompatible mod or game update?)");
hook_run_aob = HookAPI->Relocate(OFFSET_CONSOLE_RUN);
}

OLD_ConsoleRun = (decltype(OLD_ConsoleRun))HookAPI->HookFunction(
(FUNC_PTR)hook_run_aob,
(FUNC_PTR)console_run
);

IOBuffer[0] = 0;
}


static void run_comand(char* command) {
console_run(NULL, command);
}


static constexpr struct console_api_t Console {
run_comand
};

extern constexpr const struct console_api_t* GetConsoleAPI() {
return &Console;
//probably move this out in the future now that all hooks are in one place
extern const struct console_api_t* GetConsoleAPI() {
static const console_api_t api = { GameHook->ConsoleRun };
return &api;
}
Loading

0 comments on commit 195cda8

Please sign in to comment.