Skip to content

Commit

Permalink
HudToggleKey: allow assigning a key to toggle the game HUD
Browse files Browse the repository at this point in the history
also fixed issue with CDTracks getting cleared when using user.ini
  • Loading branch information
emoose committed Aug 21, 2024
1 parent 3e218f1 commit e54c0d1
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 46 deletions.
5 changes: 5 additions & 0 deletions OutRun2006Tweaks.ini
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ DisableCountdownTimer = false
# Clarissa in O2SP arcade mode uses a different model in non-JP versions, this allows restoring the original JP model
RestoreJPClarissa = false

# Allows assigning a keyboard key to toggle the game HUD
# Depends on your keyboard layout which keys can be assigned here, some might work as-is (eg. P to bind it to P, or HOME to bind to Home)
# Binding to a function key such as F10 should work fine on all keyboard layouts
HudToggleKey =

# Changes the "Not Signed In" text to also display number of OutRun miles
ShowOutRunMilesOnMenu = true

Expand Down
2 changes: 2 additions & 0 deletions src/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ namespace Settings

spdlog::info(" - SkipIntroLogos: {}", SkipIntroLogos);
spdlog::info(" - DisableCountdownTimer: {}", DisableCountdownTimer);
spdlog::info(" - HudToggleKey: {}", HudToggleKey);
spdlog::info(" - RestoreJPClarissa: {}", RestoreJPClarissa);
spdlog::info(" - ShowOutRunMilesOnMenu: {}", ShowOutRunMilesOnMenu);
spdlog::info(" - RandomHighwayAnimSets: {}", RandomHighwayAnimSets);
Expand Down Expand Up @@ -225,6 +226,7 @@ namespace Settings

SkipIntroLogos = ini.Get("Misc", "SkipIntroLogos", std::move(SkipIntroLogos));
DisableCountdownTimer = ini.Get("Misc", "DisableCountdownTimer", std::move(DisableCountdownTimer));
HudToggleKey = ini.Get("Misc", "HudToggleKey", std::move(HudToggleKey));
RestoreJPClarissa = ini.Get("Misc", "RestoreJPClarissa", std::move(RestoreJPClarissa));
ShowOutRunMilesOnMenu = ini.Get("Misc", "ShowOutRunMilesOnMenu", std::move(ShowOutRunMilesOnMenu));
RandomHighwayAnimSets = ini.Get("Misc", "RandomHighwayAnimSets", std::move(RandomHighwayAnimSets));
Expand Down
44 changes: 4 additions & 40 deletions src/game.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,50 +68,14 @@ namespace Input
inline XINPUT_STATE PadStateCur{ 0 };
inline uint32_t PadDigitalCur{ 0 };

inline void PadUpdate(int controllerIndex)
{
PadStatePrev = PadStateCur;
PadDigitalPrev = PadDigitalCur;

if (XInputGetState(controllerIndex, &PadStateCur) != ERROR_SUCCESS)
PadStateCur = { 0 };

PadDigitalCur = PadStateCur.Gamepad.wButtons;

// Convert analog inputs to digital bitfield
{
if (PadStateCur.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
PadDigitalCur |= XINPUT_DIGITAL_LEFT_TRIGGER;
if (PadStateCur.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
PadDigitalCur |= XINPUT_DIGITAL_RIGHT_TRIGGER;

// Check left stick
if (PadStateCur.Gamepad.sThumbLY > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_LS_UP;
if (PadStateCur.Gamepad.sThumbLY < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_LS_DOWN;
if (PadStateCur.Gamepad.sThumbLX < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_LS_LEFT;
if (PadStateCur.Gamepad.sThumbLX > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_LS_RIGHT;

// Check right stick
if (PadStateCur.Gamepad.sThumbRY > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_RS_UP;
if (PadStateCur.Gamepad.sThumbRY < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_RS_DOWN;
if (PadStateCur.Gamepad.sThumbRX < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_RS_LEFT;
if (PadStateCur.Gamepad.sThumbRX > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_RS_RIGHT;
}
}

inline bool PadReleased(uint32_t buttons)
{
return (PadDigitalPrev & buttons) == buttons &&
(PadDigitalCur & buttons) != buttons;
(PadDigitalCur & buttons) != buttons;
}

// hooks_input.cpp
void Update();
};

enum GameState
Expand Down
4 changes: 4 additions & 0 deletions src/game_addrs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ namespace Game
return *DirectInput8_ptr;
}

inline uint32_t* navipub_disp_flg = nullptr;

inline int* sel_bgm_kind_buf = nullptr;

inline int* app_time = nullptr; // used by SetTweeningTable etc
Expand Down Expand Up @@ -122,6 +124,8 @@ namespace Game
D3DDevice_ptr = Module::exe_ptr<IDirect3DDevice9*>(0x49BD60);
DirectInput8_ptr = Module::exe_ptr<IDirectInput8A*>(0x4606E8);

navipub_disp_flg = Module::exe_ptr<uint32_t>(0x4447F8);

sel_bgm_kind_buf = Module::exe_ptr<int>(0x430364);

app_time = Module::exe_ptr<int>(0x49EDB8);
Expand Down
13 changes: 9 additions & 4 deletions src/hooks_audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ class CDSwitcher : public Hook
PadStatePrev = false;
}

bool KeyStateNext = (GetAsyncKeyState('X') || PadStateNext);
bool KeyStatePrev = (GetAsyncKeyState('Z') || PadStatePrev);
bool KeyStateNext = ((GetAsyncKeyState('X') & 1) || PadStateNext);
bool KeyStatePrev = ((GetAsyncKeyState('Z') & 1) || PadStatePrev);

bool BGMChanged = false;

Expand Down Expand Up @@ -189,8 +189,15 @@ class CDSwitcher : public Hook

static void __cdecl PettyAutosceneCmdTblAnalysis_adxPlay_dest(int a1, uint32_t bgmIdx, int a3)
{
if (!Settings::CDTracks.size())
{
Game::adxPlay(a1, bgmIdx, a3);
return;
}

if (bgmIdx >= Settings::CDTracks.size())
bgmIdx = 0;

BGMOverridePath = Settings::CDTracks[bgmIdx].first;
Game::adxPlay(0, 0, 0);
}
Expand Down Expand Up @@ -283,8 +290,6 @@ void AudioHooks_Update(int numUpdates)

void CDSwitcher_ReadIni(const std::filesystem::path& iniPath)
{
Settings::CDTracks.clear();

if (!std::filesystem::exists(iniPath))
{
// TODO: fill in defaults if no INI found? for now we'll just disable switcher
Expand Down
2 changes: 1 addition & 1 deletion src/hooks_framerate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ class ReplaceGameUpdateLoop : public Hook
{
// Fetch latest input state
// (do this inside our update-loop so that any hooked game funcs have accurate state...)
Input::PadUpdate(Settings::VibrationControllerId);
Input::Update();

Game::ReadIO();
Game::SoundControl_mb();
Expand Down
119 changes: 119 additions & 0 deletions src/hooks_input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,125 @@
#include "plugin.hpp"
#include "game_addrs.hpp"

namespace Input
{
void PadUpdate(int controllerIndex)
{
PadStatePrev = PadStateCur;
PadDigitalPrev = PadDigitalCur;

if (XInputGetState(controllerIndex, &PadStateCur) != ERROR_SUCCESS)
PadStateCur = { 0 };

PadDigitalCur = PadStateCur.Gamepad.wButtons;

// Convert analog inputs to digital bitfield
{
if (PadStateCur.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
PadDigitalCur |= XINPUT_DIGITAL_LEFT_TRIGGER;
if (PadStateCur.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
PadDigitalCur |= XINPUT_DIGITAL_RIGHT_TRIGGER;

// Check left stick
if (PadStateCur.Gamepad.sThumbLY > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_LS_UP;
if (PadStateCur.Gamepad.sThumbLY < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_LS_DOWN;
if (PadStateCur.Gamepad.sThumbLX < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_LS_LEFT;
if (PadStateCur.Gamepad.sThumbLX > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_LS_RIGHT;

// Check right stick
if (PadStateCur.Gamepad.sThumbRY > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_RS_UP;
if (PadStateCur.Gamepad.sThumbRY < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_RS_DOWN;
if (PadStateCur.Gamepad.sThumbRX < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_RS_LEFT;
if (PadStateCur.Gamepad.sThumbRX > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
PadDigitalCur |= XINPUT_DIGITAL_RS_RIGHT;
}
}

static int HudToggleVKey = 0;

void HudToggleUpdate()
{
static bool HudTogglePrevState = false;
bool hudToggleKeyState = (GetAsyncKeyState(HudToggleVKey) & 0x8000);
if (HudTogglePrevState != hudToggleKeyState)
{
HudTogglePrevState = hudToggleKeyState;
if (!hudToggleKeyState) // if key is being released, toggle the hud flag
*Game::navipub_disp_flg = (*Game::navipub_disp_flg == 0 ? 1 : 0);
}
}

int StringToVK(std::string_view key)
{
// Convert key to uppercase
std::string keyUpper(key);
std::transform(keyUpper.begin(), keyUpper.end(), keyUpper.begin(),
[](unsigned char c) { return std::toupper(c); });

static const std::unordered_map<std::string_view, int> vkMap = {
{"LBUTTON", VK_LBUTTON}, {"RBUTTON", VK_RBUTTON}, {"CANCEL", VK_CANCEL}, {"MBUTTON", VK_MBUTTON},
{"XBUTTON1", VK_XBUTTON1}, {"XBUTTON2", VK_XBUTTON2}, {"BACK", VK_BACK}, {"TAB", VK_TAB},
{"CLEAR", VK_CLEAR}, {"RETURN", VK_RETURN}, {"SHIFT", VK_SHIFT}, {"CONTROL", VK_CONTROL},
{"MENU", VK_MENU}, {"PAUSE", VK_PAUSE}, {"CAPITAL", VK_CAPITAL}, {"ESCAPE", VK_ESCAPE},
{"SPACE", VK_SPACE}, {"PRIOR", VK_PRIOR}, {"NEXT", VK_NEXT}, {"END", VK_END},
{"HOME", VK_HOME}, {"LEFT", VK_LEFT}, {"UP", VK_UP}, {"RIGHT", VK_RIGHT},
{"DOWN", VK_DOWN}, {"SELECT", VK_SELECT}, {"PRINT", VK_PRINT}, {"EXECUTE", VK_EXECUTE},
{"SNAPSHOT", VK_SNAPSHOT}, {"INSERT", VK_INSERT}, {"DELETE", VK_DELETE}, {"HELP", VK_HELP},
{"LWIN", VK_LWIN}, {"RWIN", VK_RWIN}, {"APPS", VK_APPS}, {"SLEEP", VK_SLEEP},
{"NUMPAD0", VK_NUMPAD0}, {"NUMPAD1", VK_NUMPAD1}, {"NUMPAD2", VK_NUMPAD2}, {"NUMPAD3", VK_NUMPAD3},
{"NUMPAD4", VK_NUMPAD4}, {"NUMPAD5", VK_NUMPAD5}, {"NUMPAD6", VK_NUMPAD6}, {"NUMPAD7", VK_NUMPAD7},
{"NUMPAD8", VK_NUMPAD8}, {"NUMPAD9", VK_NUMPAD9}, {"MULTIPLY", VK_MULTIPLY}, {"ADD", VK_ADD},
{"SEPARATOR", VK_SEPARATOR}, {"SUBTRACT", VK_SUBTRACT}, {"DECIMAL", VK_DECIMAL}, {"DIVIDE", VK_DIVIDE},
{"F1", VK_F1}, {"F2", VK_F2}, {"F3", VK_F3}, {"F4", VK_F4}, {"F5", VK_F5},
{"F6", VK_F6}, {"F7", VK_F7}, {"F8", VK_F8}, {"F9", VK_F9}, {"F10", VK_F10},
{"F11", VK_F11}, {"F12", VK_F12}, {"F13", VK_F13}, {"F14", VK_F14}, {"F15", VK_F15},
{"F16", VK_F16}, {"F17", VK_F17}, {"F18", VK_F18}, {"F19", VK_F19}, {"F20", VK_F20},
{"F21", VK_F21}, {"F22", VK_F22}, {"F23", VK_F23}, {"F24", VK_F24}, {"NUMLOCK", VK_NUMLOCK},
{"SCROLL", VK_SCROLL}
// TODO: are there any others worth adding here?
};

// Check if key is a single character
if (keyUpper.size() == 1)
{
char ch = keyUpper[0];
if (std::isalnum(ch))
return std::toupper(ch);
}

// Search in the vkMap
auto it = vkMap.find(keyUpper);
if (it != vkMap.end())
return it->second;

// If not found, return -1 or some invalid value
return 0;
}

void Update()
{
static bool inited = false;
if (!inited)
{
HudToggleVKey = StringToVK(Settings::HudToggleKey);
inited = true;
}

// Update gamepad for main controller id
PadUpdate(Settings::VibrationControllerId);

if (HudToggleVKey)
HudToggleUpdate();
}
};

// Hooks into XINPUT1_4's DeviceIoControl via undocumented DriverHook(0xBAAD0001) call
// Allows us to detect SET_GAMEPAD_STATE ioctl and send the GIP HID command for trigger impulses
// Hopefully will work for most series controller connection types...
Expand Down
1 change: 1 addition & 0 deletions src/plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ namespace Settings

inline bool SkipIntroLogos = false;
inline bool DisableCountdownTimer = false;
inline std::string HudToggleKey = "";
inline bool RestoreJPClarissa = false;
inline bool ShowOutRunMilesOnMenu = true;
inline bool RandomHighwayAnimSets = false;
Expand Down
2 changes: 1 addition & 1 deletion src/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

#define MODULE_VERSION_MAJOR 0
#define MODULE_VERSION_MINOR 5
#define MODULE_VERSION_BUILD 0
#define MODULE_VERSION_BUILD 1
#define MODULE_VERSION_REVISION 0

#define STR(value) #value
Expand Down

0 comments on commit e54c0d1

Please sign in to comment.