ThirteenAG / WidescreenFixesPack

Plugins to make or improve widescreen resolutions support in games, add more features and fix bugs.
https://thirteenag.github.io/wfp
MIT License
2.22k stars 211 forks source link

[NFSU2] Black screen hang when SingleCoreAffinity = 1 #1573

Open bscout9956 opened 2 weeks ago

bscout9956 commented 2 weeks ago

Issue: For some reason Need for Speed Underground 2 hangs on a black screen if I set SingleCoreAffinity to 1.

Tested Version: Latest as of the writing of this issue

Steps to reproduce:

  1. Install the mod
  2. Launch the game

The issue does not present itself if I switch it back to 0. Interestingly enough, I can workaround the black screen by going into Task Manager and switching thread affinities. But that defeats the purpose and causes crashes later on.

Specs: AMD Ryzen 5 5600 with an RTX 3060. OS is Windows 11 Pro Build 26100.994 (24H2).

PS: By the way I did try to mess with the source code and I do believe it gets through the checks finding KERNEL32.DLL and CreateThread but then the game just stays there doing nothing...

INI File: `[MAIN] ResX = 2560 ; Use this option to control the horizontal resolution. ResY = 1440 ; Use this option to control the vertical resolution. FixHUD = 1 ; Corrects HUD aspect ratio. FixFOV = 1 ; Corrects FOV aspect ratio. Scaling = 1 ; Adjusts FOV aspect ratio to be mathematically correct. Requires FixFOV to be enabled. HUDWidescreenMode = 1 ; Moves HUD to the edge of the screen. Change offset in "NFSUnderground2.WidescreenFix.dat" file for other aspect ratios. FMVWidescreenMode = 1 ; FMVs will appear in fullscreen for 16:9. (1 = Cropped | 2 = Stretched)

[MISC] SkipIntro = 1 ; Skips FMVs that play when you launch the game. WindowedMode = 0 ; Enables windowed mode. (1 = Borderless | 2 = Border | 3 = Resizable Border | 4 = Borderless Fullscreen | 5 = Borderless Fullscreen Stretched) DisableCutsceneBorders = 1 ; Removes letterboxing that appears during cutscenes. CustomUserFilesDirectoryInGameDir = 1 ; User files will be stored in a specified directory (for example: "save"). Use '0' to disable. WriteSettingsToFile = 1 ; All registry settings will be saved to "settings.ini" in your profile folder. You must input your CD key and langauge in "settings.ini" when this option is enabled. ImproveGamepadSupport = 1 ; Adds text to buttons and assigns front-end actions. Requires an XInput gamepad. (1 = Xbox Text | 2 = PlayStation Text | 3 = PC Text | 4 = None) LeftStickDeadzone = 10.0 ; Controls the deadzone of the left analog stick. FPSLimit = 120 ; Allows users to control the frame rate limit. (0 = Disabled | -1 = Monitor Refresh Rate | -2 = Double Monitor Refresh Rate) HighFPSCutscenes = 1 ; Increases the frame rate limit for cutscenes to the nearest multiple of 30 (maximum is currently 60 due to bugs) SingleCoreAffinity = 0 ; Limits game to one CPU core to prevent crashing. NoOpticalDriveFix = 1 ; Allows the game to run without an optical drive. RainDropletsScale = 0.5 ; Adjusts the size of the on-screen rain droplets.

[NOSTrail] FixNOSTrailLength = 1 ; Fixes the NOS trail length for higher FPS. CustomNOSTrailLength = 1.5 ; Adjusts the total distance of the NOS trail. You may need to adjust this if you're playing at very high FPS (240+). The higher the SimRate & FPS difference, the longer the trail. `

bscout9956 commented 1 week ago

Update (not directly related):

I discovered the game has a function called bVerifyPoolIntegrity which can be enabled by toggling a Global Variable bMemoryAutomaticVerifyPoolIntegrity (located at 0x8284FC for the v1.2 US SPEED2.exe, same ExtraOpts guys use).

By enabling this variable the game seems to perform some memory pool integrity checks (thanks Gamecube version executable) which seem to prevent crashing from my limited testing. The only downside is ever so slightly slower loading times.

I'm not sure if those memory checks are what prevent crashes or if it just makes the game go through another code path in its memory freeing code (func is void bFree() at 0x440540) and thus doesn't crash.

I tried my best to implement it and even maybe submit a Pull Request but Hooking.Patterns being undocumented and throwing asserts made me lose my patience. Plus, I'm not a fan of C++.

Anyway, I tried the following pattern but got nowhere:

uint32_t* dword_8284FC = *hook::pattern("81 FA ?? ?? ?? ?? 7C DF 8B 0D ?? ?? ?? ?? 0F B6 77 F4").count(1).get(0).get<uint32_t*>(0xA);
    injector::WriteMemory<uint8_t>(dword_8284FC, 0x01, true);
}

This is executable memory (.text) so I set it as a pointer. You probably can figure this one out though.

It does seem like a better and more reliable alternative to just making the game only access 1 thread. Could be optional anyway.

ThirteenAG commented 1 week ago

Can you do more testing and report back?

bscout9956 commented 1 week ago

Can you do more testing and report back?

Just did. Here's a video to better show how it behaves with the flag enabled. https://www.youtube.com/watch?v=yHNg1r23sEA

Somehow that Specialties Shop in Beacon Hill is very happy to crash my game