Skip to content

Commit 6f90880

Browse files
authored
Fix some small problems with Device Selection Dialog (#3851)
Makes the device selection dialog more suited for multi-monitor setups
1 parent f095410 commit 6f90880

File tree

4 files changed

+203
-0
lines changed

4 files changed

+203
-0
lines changed

Client/multiplayer_sa/CMultiplayerSA.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,6 +1574,7 @@ void CMultiplayerSA::InitHooks()
15741574
MemSet((void*)0x6C4453, 0x90, 0x68);
15751575

15761576
InitHooks_CrashFixHacks();
1577+
InitHooks_DeviceSelection();
15771578

15781579
// Init our 1.3 hooks.
15791580
Init_13();

Client/multiplayer_sa/CMultiplayerSA.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class CMultiplayerSA : public CMultiplayer
8080
void InitHooks_ProjectileCollisionFix();
8181
void InitHooks_ObjectStreamerOptimization();
8282
void InitHooks_Postprocess();
83+
void InitHooks_DeviceSelection();
8384
CRemoteDataStorage* CreateRemoteDataStorage();
8485
void DestroyRemoteDataStorage(CRemoteDataStorage* pData);
8586
void AddRemoteDataStorage(CPlayerPed* pPed, CRemoteDataStorage* pData);
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto v1.0
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: multiplayer_sa/CMultiplayerSA_DeviceSelection.cpp
6+
*
7+
* Multi Theft Auto is available from http://www.multitheftauto.com/
8+
*
9+
*****************************************************************************/
10+
11+
#include "StdInc.h"
12+
#define FUNC_rwDeviceSystemRequest 0x7F2AB0
13+
#define FUNC_DialogFunc 0x745E50
14+
#define FUNC_RwEngineGetSubSystemInfo 0x7F2C30
15+
#define CLASS_RwGlobals 0xC97B24
16+
#define CLASS_IDirect3D9 0xC97C20
17+
#define NUM_DialogFuncStackPushAddress 0x746239
18+
19+
// This is copied from SilentPatch:
20+
// https://github.com/CookiePLMonster/SilentPatch/blob/dev/SilentPatch/FriendlyMonitorNames.cpp
21+
std::unordered_map<std::string, std::string> GetFriendlyMonitorNamesForDevicePaths()
22+
{
23+
std::unordered_map<std::string, std::string> monitorNames;
24+
25+
HMODULE user32Lib = LoadLibrary(TEXT("user32"));
26+
if (!user32Lib)
27+
return monitorNames;
28+
29+
auto* getDisplayConfigBufferSizes = (decltype(GetDisplayConfigBufferSizes)*)GetProcAddress(user32Lib, "GetDisplayConfigBufferSizes");
30+
auto* queryDisplayConfig = (decltype(QueryDisplayConfig)*)GetProcAddress(user32Lib, "QueryDisplayConfig");
31+
auto* displayConfigGetDeviceInfo = (decltype(DisplayConfigGetDeviceInfo)*)GetProcAddress(user32Lib, "DisplayConfigGetDeviceInfo");
32+
if (!getDisplayConfigBufferSizes || !queryDisplayConfig || !displayConfigGetDeviceInfo)
33+
{
34+
FreeLibrary(user32Lib);
35+
return monitorNames;
36+
}
37+
38+
UINT32 pathCount, modeCount;
39+
std::unique_ptr<DISPLAYCONFIG_PATH_INFO[]> paths;
40+
std::unique_ptr<DISPLAYCONFIG_MODE_INFO[]> modes;
41+
42+
LONG result = ERROR_SUCCESS;
43+
do
44+
{
45+
result = getDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount);
46+
if (result != ERROR_SUCCESS)
47+
{
48+
break;
49+
}
50+
paths = std::make_unique<DISPLAYCONFIG_PATH_INFO[]>(pathCount);
51+
modes = std::make_unique<DISPLAYCONFIG_MODE_INFO[]>(modeCount);
52+
result = queryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths.get(), &modeCount, modes.get(), nullptr);
53+
} while (result == ERROR_INSUFFICIENT_BUFFER);
54+
55+
if (result != ERROR_SUCCESS)
56+
{
57+
FreeLibrary(user32Lib);
58+
return monitorNames;
59+
}
60+
61+
for (size_t i = 0; i < pathCount; i++)
62+
{
63+
DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = {};
64+
targetName.header.adapterId = paths[i].targetInfo.adapterId;
65+
targetName.header.id = paths[i].targetInfo.id;
66+
targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
67+
targetName.header.size = sizeof(targetName);
68+
const LONG targetNameResult = DisplayConfigGetDeviceInfo(&targetName.header);
69+
70+
DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName = {};
71+
sourceName.header.adapterId = paths[i].sourceInfo.adapterId;
72+
sourceName.header.id = paths[i].sourceInfo.id;
73+
sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
74+
sourceName.header.size = sizeof(sourceName);
75+
const LONG sourceNameResult = DisplayConfigGetDeviceInfo(&sourceName.header);
76+
if (targetNameResult == ERROR_SUCCESS && sourceNameResult == ERROR_SUCCESS && targetName.monitorFriendlyDeviceName[0] != '\0')
77+
{
78+
char gdiDeviceName[std::size(sourceName.viewGdiDeviceName)];
79+
char monitorFriendlyDeviceName[std::size(targetName.monitorFriendlyDeviceName)];
80+
WideCharToMultiByte(CP_ACP, 0, sourceName.viewGdiDeviceName, -1, gdiDeviceName, static_cast<int>(std::size(gdiDeviceName)), nullptr, nullptr);
81+
WideCharToMultiByte(CP_ACP, 0, targetName.monitorFriendlyDeviceName, -1, monitorFriendlyDeviceName,
82+
static_cast<int>(std::size(monitorFriendlyDeviceName)), nullptr, nullptr);
83+
84+
monitorNames.try_emplace(gdiDeviceName, monitorFriendlyDeviceName);
85+
}
86+
}
87+
88+
FreeLibrary(user32Lib);
89+
return monitorNames;
90+
}
91+
92+
struct RwSubSystemInfo
93+
{
94+
char name[80];
95+
};
96+
97+
using rwDeviceSystemRequest = RwSubSystemInfo*(__cdecl*)(RwDevice* device, std::int32_t requestId, RwSubSystemInfo* pOut, void* pInOut, std::int32_t numIn);
98+
static RwSubSystemInfo* RwEngineGetSubSystemInfo_Hooked(RwSubSystemInfo* subSystemInfo, std::int32_t subSystemIndex)
99+
{
100+
auto* rwGlobals = *(RwGlobals**)CLASS_RwGlobals;
101+
auto* rwDeviceSystemRequestFunc = (rwDeviceSystemRequest)(FUNC_rwDeviceSystemRequest);
102+
if (!rwDeviceSystemRequestFunc(&rwGlobals->dOpenDevice, 14, subSystemInfo, nullptr, subSystemIndex))
103+
return nullptr;
104+
105+
auto* pDxDevice = *(IDirect3D9**)CLASS_IDirect3D9;
106+
if (!pDxDevice)
107+
return subSystemInfo;
108+
109+
D3DADAPTER_IDENTIFIER9 identifier;
110+
if (FAILED(pDxDevice->GetAdapterIdentifier(subSystemIndex, 0, &identifier)))
111+
return subSystemInfo;
112+
113+
static const auto friendlyNames = GetFriendlyMonitorNamesForDevicePaths();
114+
115+
// If we can't find the friendly name, either because it doesn't exist or we're on an ancient Windows, fall back to the device name
116+
auto it = friendlyNames.find(identifier.DeviceName);
117+
if (it != friendlyNames.end())
118+
{
119+
strncpy_s(subSystemInfo->name, it->second.c_str(), _TRUNCATE);
120+
}
121+
else
122+
{
123+
strncpy_s(subSystemInfo->name, identifier.Description, _TRUNCATE);
124+
}
125+
126+
return subSystemInfo;
127+
}
128+
129+
INT_PTR CALLBACK CustomDlgProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam)
130+
{
131+
auto* orgDialogFunc = (DLGPROC)FUNC_DialogFunc;
132+
if (msg != WM_INITDIALOG)
133+
return orgDialogFunc(window, msg, wParam, lParam);
134+
135+
orgDialogFunc(window, msg, wParam, lParam);
136+
137+
// Set Icon
138+
HMODULE hGameModule = GetModuleHandle(nullptr);
139+
SendMessage(window, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(LoadIcon(hGameModule, MAKEINTRESOURCE(100))));
140+
141+
// Make the dialog visible in the task bar
142+
// https://stackoverflow.com/a/1462811
143+
SetWindowLongPtr(window, GWL_EXSTYLE, WS_EX_APPWINDOW);
144+
ShowWindow(window, SW_HIDE);
145+
ShowWindow(window, SW_SHOW);
146+
return FALSE;
147+
}
148+
149+
void CMultiplayerSA::InitHooks_DeviceSelection()
150+
{
151+
// 0x746239 -> Exact address where the original DialogFunc address is being pushed as an argument to DialogBoxParamA(),
152+
// we're replacing it with out own proxy function
153+
MemPut<DLGPROC>(NUM_DialogFuncStackPushAddress, (DLGPROC)&CustomDlgProc);
154+
HookInstall(FUNC_RwEngineGetSubSystemInfo, (DWORD)RwEngineGetSubSystemInfo_Hooked, 6);
155+
}

Client/sdk/game/RenderWare.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,3 +526,49 @@ struct RwError
526526
{
527527
int err1, err2;
528528
};
529+
530+
/*****************************************************************************/
531+
/** RenderWare Globals **/
532+
/*****************************************************************************/
533+
534+
typedef bool (*RwSystemFunc)(std::int32_t, void*, void*, std::int32_t);
535+
struct RwDevice
536+
{
537+
float gammaCorrection;
538+
RwSystemFunc fpSystem;
539+
float zBufferNear;
540+
float zBufferFar;
541+
// RwRenderStateSetFunction fpRenderStateSet;
542+
// RwRenderStateGetFunction fpRenderStateGet;
543+
// RwIm2DRenderLineFunction fpIm2DRenderLine;
544+
// RwIm2DRenderTriangleFunction fpIm2DRenderTriangle;
545+
// RwIm2DRenderPrimitiveFunction fpIm2DRenderPrimitive;
546+
// RwIm2DRenderIndexedPrimitiveFunction fpIm2DRenderIndexedPrimitive;
547+
// RwIm3DRenderLineFunction fpIm3DRenderLine;
548+
// RwIm3DRenderTriangleFunction fpIm3DRenderTriangle;
549+
// RwIm3DRenderPrimitiveFunction fpIm3DRenderPrimitive;
550+
// RwIm3DRenderIndexedPrimitiveFunction fpIm3DRenderIndexedPrimitive;
551+
};
552+
// static_assert(sizeof(RwDevice) == 0x38, "Incorrect class size: RwDevice");
553+
554+
typedef bool (*RwStandardFunc)(void*, void*, std::int32_t);
555+
struct RwGlobals
556+
{
557+
void* curCamera;
558+
void* curWorld;
559+
std::uint16_t renderFrame;
560+
std::uint16_t lightFrame;
561+
std::uint16_t pad[2];
562+
RwDevice dOpenDevice;
563+
RwStandardFunc stdFunc[29];
564+
// RwLinkList dirtyFrameList;
565+
// RwFileFunctions fileFuncs;
566+
// RwStringFunctions stringFuncs;
567+
// RwMemoryFunctions memoryFuncs;
568+
// RwMemoryAllocFn memoryAlloc;
569+
// RwMemoryFreeFn memoryFree;
570+
// RwMetrics* metrics;
571+
// RwEngineStatus engineStatus;
572+
// RwUInt32 resArenaInitSize;
573+
};
574+
//static_assert(sizeof(RwGlobals) == 0x158, "Incorrect class size: RwGlobals");

0 commit comments

Comments
 (0)