Skip to content

Commit

Permalink
SkipIntroLogos & DisableCountdownTimer: add settings & cmdline args
Browse files Browse the repository at this point in the history
SkipIntroLogos sadly only makes a very small difference to startup times, 3 or 4 seconds at most
game seems stuck waiting for audio data to load into XACT/DirectSound, not sure if much can help with it
  • Loading branch information
emoose committed Jun 9, 2024
1 parent 9497866 commit 8c9d32d
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 70 deletions.
10 changes: 10 additions & 0 deletions Outrun2006Tweaks.ini
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,13 @@ DisableStageCulling = true

# FixZBufferPrecision: Fixes Z-Buffer precision issues, greatly reducing z-fighting and distant object drawing issues (eg. signs/buildings will have much less pop-in)
FixZBufferPrecision = true

[Misc]
# SkipIntroLogos: skips the beginning intro logos, saving a couple seconds from startup time.
# (game will still show a white screen for 5-10 seconds while data loads in)
# Can also be enabled via -SkipIntros launch parameter.
SkipIntroLogos = false

# DisableCountdownTimer: disables the countdown timer, may be useful for modders, or those who just want to take a leisurely drive.
# Can also be enabled via -OuttaTime launch parameter.
DisableCountdownTimer = false
32 changes: 22 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Outrun2006Tweaks
A wrapper DLL that can patch in some minor fixes & tweaks into Outrun 2006: Coast 2 Coast.
# OutRun2006Tweaks
A wrapper DLL that can patch in some minor fixes & tweaks into OutRun 2006: Coast 2 Coast.

### Features
- Game can now run in borderless windowed mode; mouse cursor will now be hidden while game is active
- Adds a built-in framelimiter to prevent game from speeding up (along with an optional fix that can allow higher FPS without speedup)
- Fixes objects being culled out before reaching edge of screen
- Fixes C2C scoreboard not saving scores on the Steam release due to broken anti-piracy checks
- Fixes Z-buffer precision issues, greatly reducing z-fighting & distant object pop-in
- Allows disabling vehicle LODs to reduce the ugly pop-in of them
- Disables culling of certain stage objects
- Fixes Z-buffer precision issues, greatly reducing z-fighting & distant object pop-in
- Can force anisotropic filtering level & enable transparency supersampling, greatly reducing aliasing around the edges of the track. (set `DX/ANTIALIASING = 2` in outrun2006.ini for best results)
- Automatically disables DPI scaling on the game window, fixing scaling issues with certain setups
- Fixes broken lens flare effect by making game load it from correct path
Expand All @@ -16,19 +17,31 @@ A wrapper DLL that can patch in some minor fixes & tweaks into Outrun 2006: Coas
All the above can be toggled/customized via the Outrun2006Tweaks.ini file.

There's also a semi-experimental fix to allow running above 60FPS without speedup, by locking the games tickrate to 60FPS while draw-rate is unlimited.
Since the game will still internally update at 60FPS this won't give as much benefit as a true framerate-unlock though.
(some things like animated textures & UI text also unfortunately have speed issues with it...)
You can use this by increasing the `FramerateLimit` setting (or disabling it), with `FramerateUnlockExperimental` enabled.
Since the game will still internally update at 60FPS this probably won't give as much benefit as a true framerate-unlock though.
(some things like animated textures & menu text also unfortunately have speed issues with it...)

### Setup
Since Steam/DVD releases are packed with ancient DRM that doesn't play well with DLL wrappers, this pack includes a replacement game EXE to run the game with.

This EXE should be compatible with both the Steam release & the original DVD version, along with most OR2006 mods.

To set it up just extract the files from the release ZIP into your `Outrun2006 Coast 2 Coast` folder, where `OR2006C2C.EXE` is located, replacing the original EXE.
To set it up:

- Extract the files from the release ZIP into your `Outrun2006 Coast 2 Coast` folder, where `OR2006C2C.EXE` is located, replacing the original EXE.
- Edit the `Outrun2006Tweaks.ini` to customize the tweaks to your liking (by default all tweaks are enabled)
- Run the game, your desktop resolution will be used by default if `outrun2006.ini` file isn't present.

**If you have framerate issues where game doesn't run at your full FramerateLimit setting**, try adding `DX/WINDOWED = 1` to your `outrun2006.ini` file and check if it helps.

Steam Deck/Linux users may need to run the game with `WINEDLLOVERRIDES="dinput8=n,b" %command%` launch parameters for the mod to load in.

### Building
Building requires Visual Studio 2022, CMake & git to be installed, with those setup just clone this repo and then run `build_2022.bat`.

After that you can edit the `Outrun2006Tweaks.ini` to customize the tweaks to your liking.
If the batch script succeeds you should see a `build\outrun2006tweaks-proj.sln` solution file, just open that in VS and build it.

Recommend running the games Config.exe to setup video settings first before starting the game (if you want to use borderless windowed mode, edit `outrun2006.ini` and add `DX/WINDOWED = 1`)
(if you have issues building with this setup please let me know)

### TODO
- even with LODs & culling disabled some distant cars still pop into view, can distance of them loading in be increased?
Expand All @@ -39,7 +52,6 @@ Recommend running the games Config.exe to setup video settings first before star
- fix broken car horn (haven't seen any code for it yet though...)
- game retiming? probably a pipe dream - seems game is meant for 60.2Hz tickrate, lots of things coded for that & using 0.0166112... frametimes
- a way to change music mid-race would be sweet
- intro/splash skip

### Thanks
Thanks to [debugging.games](http://debugging.games) for hosting debug symbols for Outrun 2 SP (Lindburgh), very useful for looking into Outrun2006.
Thanks to [debugging.games](http://debugging.games) for hosting debug symbols for OutRun 2 SP (Lindburgh), very useful for looking into Outrun2006.
10 changes: 10 additions & 0 deletions src/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,26 @@ namespace Settings
{
spdlog::info("Settings values:");
spdlog::info(" - FramerateLimit: {}", FramerateLimit);
spdlog::info(" - FramerateLimitMode: {}", FramerateLimitMode);
spdlog::info(" - FramerateFastLoad: {}", FramerateFastLoad);
spdlog::info(" - FramerateUnlockExperimental: {}", FramerateUnlockExperimental);
spdlog::info(" - VSync: {}", VSync);

spdlog::info(" - WindowedBorderless: {}", WindowedBorderless);
spdlog::info(" - WindowPosition: {}x{}", WindowPositionX, WindowPositionY);
spdlog::info(" - WindowedHideMouseCursor: {}", WindowedHideMouseCursor);
spdlog::info(" - DisableDPIScaling: {}", DisableDPIScaling);
spdlog::info(" - AutoDetectResolution: {}", AutoDetectResolution);

spdlog::info(" - AnisotropicFiltering: {}", AnisotropicFiltering);
spdlog::info(" - TransparencySupersampling: {}", TransparencySupersampling);
spdlog::info(" - ScreenEdgeCullFix: {}", ScreenEdgeCullFix);
spdlog::info(" - DisableVehicleLODs: {}", DisableVehicleLODs);
spdlog::info(" - DisableStageCulling: {}", DisableStageCulling);
spdlog::info(" - FixZBufferPrecision: {}", FixZBufferPrecision);

spdlog::info(" - SkipIntroLogos: {}", SkipIntroLogos);
spdlog::info(" - CountdownTimerDisable: {}", CountdownTimerDisable);
}

bool read(std::filesystem::path& iniPath)
Expand Down Expand Up @@ -104,6 +111,9 @@ namespace Settings
DisableStageCulling = ini.Get("Graphics", "DisableStageCulling", std::move(DisableStageCulling));
FixZBufferPrecision = ini.Get("Graphics", "FixZBufferPrecision", std::move(FixZBufferPrecision));

SkipIntroLogos = ini.Get("Misc", "SkipIntroLogos", std::move(SkipIntroLogos));
CountdownTimerDisable = ini.Get("Misc", "CountdownTimerDisable", std::move(CountdownTimerDisable));

return true;
}
};
Expand Down
26 changes: 16 additions & 10 deletions src/game_addrs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ namespace Game
inline int* sumo_load_sprani_67F614 = nullptr;
inline int* adv_loading_logo = nullptr;

inline uint8_t* Sumo_CountdownTimerEnable = nullptr;
inline uint8_t* Sumo_IntroLogosEnable = nullptr;

inline D3DPRESENT_PARAMETERS* D3DPresentParams = nullptr;
inline IDirect3DDevice9** D3DDevice_ptr = nullptr;

inline IDirect3DDevice9* D3DDevice() {
return *D3DDevice_ptr;
}

// ini cfg
inline float* screen_width = nullptr;
inline float* screen_height = nullptr;
Expand All @@ -18,13 +28,7 @@ namespace Game
inline uint8_t* D3DWindowed = nullptr;
inline int* CfgLanguage = nullptr;

inline D3DPRESENT_PARAMETERS* D3DPresentParams = nullptr;
inline IDirect3DDevice9** D3DDevice_ptr = nullptr;

inline IDirect3DDevice9* D3DDevice() {
return *D3DDevice_ptr;
}

// game functions
inline fn_0args SetFrameStartCpuTime = nullptr;
inline fn_1arg_int CalcNumUpdatesToRun = nullptr;

Expand All @@ -48,6 +52,11 @@ namespace Game
file_load_progress_code = Module::exe_ptr<int>(0x436718);
sumo_load_sprani_67F614 = Module::exe_ptr<int>(0x27F614);
adv_loading_logo = Module::exe_ptr<int>(0x287778);
Sumo_CountdownTimerEnable = Module::exe_ptr<uint8_t>(0x237911);
Sumo_IntroLogosEnable = Module::exe_ptr<uint8_t>(0x2319A1);

D3DPresentParams = Module::exe_ptr<D3DPRESENT_PARAMETERS>(0x49BD64);
D3DDevice_ptr = Module::exe_ptr<IDirect3DDevice9*>(0x49BD60);

screen_width = Module::exe_ptr<float>(0x340C8C);
screen_height = Module::exe_ptr<float>(0x340C90);
Expand All @@ -58,9 +67,6 @@ namespace Game
D3DWindowed = Module::exe_ptr<uint8_t>(0x55AF08);
CfgLanguage = Module::exe_ptr<int>(0x340CA0);

D3DPresentParams = Module::exe_ptr<D3DPRESENT_PARAMETERS>(0x49BD64);
D3DDevice_ptr = Module::exe_ptr<IDirect3DDevice9*>(0x49BD60);

SetFrameStartCpuTime = Module::fn_ptr<fn_0args>(0x49430);
CalcNumUpdatesToRun = Module::fn_ptr<fn_1arg_int>(0x17890);

Expand Down
135 changes: 85 additions & 50 deletions src/hooks_misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,6 @@
#include "plugin.hpp"
#include "game_addrs.hpp"

class AutoDetectResolution : public Hook
{
public:
std::string_view description() override
{
return "AutoDetectResolution";
}

bool validate() override
{
return Settings::AutoDetectResolution;
}

bool apply() override
{
// Override the default settings used when game INI doesn't exist/is empty
// (these can still be overridden via INI if needed)

// Use the width/height of the primary screen
int width = GetSystemMetrics(SM_CXSCREEN);
int height = GetSystemMetrics(SM_CYSCREEN);
if (width < 640 || height < 480)
return false; // bail out if resolution is less than the default

*Game::screen_width = width;
*Game::screen_height = height;

*Game::D3DFogEnabled = true;
*Game::D3DWindowed = true;
*Game::D3DAntialiasing = 2;
*Game::CfgLanguage = 0;

spdlog::info("AutoDetectResolution: default resolution set to {}x{}, windowed enabled, fog enabled, antialiasing 2", width, height);

return true;
}

static AutoDetectResolution instance;
};
AutoDetectResolution AutoDetectResolution::instance;

class CommandLineArguments : public Hook
{
const static int Win32_CfgRead_Addr = 0xE4D0;
Expand All @@ -55,29 +14,30 @@ class CommandLineArguments : public Hook
{
hook_orig.call(cfgFileName);

// Split the command line into individual arguments
// game settings that we want to override after game has loaded in config file

int argc;
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
if (argv == nullptr)
if (!argv)
return;

for (int i = 1; i < argc; ++i)
{
try
{
if (!wcscmp(argv[i], L"-width") && i + 1 < argc)
if (!wcsicmp(argv[i], L"-width") && i + 1 < argc)
*Game::screen_width = std::stol(argv[++i], 0, 0);
else if (!wcscmp(argv[i], L"-height") && i + 1 < argc)
else if (!wcsicmp(argv[i], L"-height") && i + 1 < argc)
*Game::screen_height = std::stol(argv[++i], 0, 0);
else if ((!wcscmp(argv[i], L"-antialiasing") || !wcscmp(argv[i], L"-aa")) && i + 1 < argc)
else if ((!wcsicmp(argv[i], L"-antialiasing") || !wcsicmp(argv[i], L"-aa")) && i + 1 < argc)
*Game::D3DAntialiasing = std::stol(argv[++i], 0, 0); // TODO: clamp to valid AA options
else if (!wcscmp(argv[i], L"-window") || !wcscmp(argv[i], L"-windowed") || !wcscmp(argv[i], L"-w"))
else if (!wcsicmp(argv[i], L"-window") || !wcsicmp(argv[i], L"-windowed") || !wcsicmp(argv[i], L"-w"))
*Game::D3DWindowed = 1;
else if (!wcscmp(argv[i], L"-fullscreen") || !wcscmp(argv[i], L"-fs"))
else if (!wcsicmp(argv[i], L"-fullscreen") || !wcsicmp(argv[i], L"-fs"))
*Game::D3DWindowed = 0;
else if (!wcscmp(argv[i], L"-fog"))
else if (!wcsicmp(argv[i], L"-fog"))
*Game::D3DFogEnabled = 1;
else if (!wcscmp(argv[i], L"-nofog"))
else if (!wcsicmp(argv[i], L"-nofog"))
*Game::D3DFogEnabled = 0;
}
catch (...) {}
Expand All @@ -99,10 +59,85 @@ class CommandLineArguments : public Hook

bool apply() override
{
// cmd-line args we want to apply before game runs
// TODO: should probably merge this into Settings::read somehow
int argc;
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
if (!argv)
return true; // no cmd-line so hook is pointless..

for (int i = 1; i < argc; ++i)
{
try
{
if (!wcsicmp(argv[i], L"-SkipIntros"))
{
spdlog::info("CommandLineArguments: SkipIntroLogos = true");
Settings::SkipIntroLogos = true;
}
else if (!wcsicmp(argv[i], L"-OuttaTime")) // same arg as FXT wrapper
{
spdlog::info("CommandLineArguments: CountdownTimerDisable = true");
Settings::CountdownTimerDisable = true;
}
}
catch (...) {}
}
LocalFree(argv);

hook_orig = safetyhook::create_inline(Module::exe_ptr(Win32_CfgRead_Addr), destination);
return !!hook_orig;
}

static CommandLineArguments instance;
};
CommandLineArguments CommandLineArguments::instance;

class GameDefaultConfigOverride : public Hook
{
public:
std::string_view description() override
{
return "GameDefaultConfigOverride";
}

bool validate() override
{
return true;
}

bool apply() override
{
// Override the default settings used when game INI doesn't exist/is empty
// (these can still be overridden via INI if needed)

if (Settings::SkipIntroLogos)
*Game::Sumo_IntroLogosEnable = false;
if (Settings::CountdownTimerDisable)
*Game::Sumo_CountdownTimerEnable = false;

if (Settings::AutoDetectResolution)
{
// Use the width/height of the primary screen
int width = GetSystemMetrics(SM_CXSCREEN);
int height = GetSystemMetrics(SM_CYSCREEN);
if (width < 640 || height < 480)
return false; // bail out if resolution is less than the default

*Game::screen_width = width;
*Game::screen_height = height;

*Game::D3DFogEnabled = true;
*Game::D3DWindowed = true;
*Game::D3DAntialiasing = 2;
*Game::CfgLanguage = 0;

spdlog::info("AutoDetectResolution: default resolution set to {}x{}, windowed enabled, fog enabled, antialiasing 2", width, height);
}

return true;
}

static GameDefaultConfigOverride instance;
};
GameDefaultConfigOverride GameDefaultConfigOverride::instance;
3 changes: 3 additions & 0 deletions src/plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ namespace Settings
inline bool DisableVehicleLODs = true;
inline bool DisableStageCulling = true;
inline bool FixZBufferPrecision = true;

inline bool SkipIntroLogos = false;
inline bool CountdownTimerDisable = false;
}

namespace Util
Expand Down

0 comments on commit 8c9d32d

Please sign in to comment.