Skip to content

Commit

Permalink
Fix some small problems with Device Selection Dialog (#3851)
Browse files Browse the repository at this point in the history
Makes the device selection dialog more suited for multi-monitor setups
  • Loading branch information
forkerer authored Nov 21, 2024
1 parent f095410 commit 6f90880
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 0 deletions.
1 change: 1 addition & 0 deletions Client/multiplayer_sa/CMultiplayerSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1574,6 +1574,7 @@ void CMultiplayerSA::InitHooks()
MemSet((void*)0x6C4453, 0x90, 0x68);

InitHooks_CrashFixHacks();
InitHooks_DeviceSelection();

// Init our 1.3 hooks.
Init_13();
Expand Down
1 change: 1 addition & 0 deletions Client/multiplayer_sa/CMultiplayerSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class CMultiplayerSA : public CMultiplayer
void InitHooks_ProjectileCollisionFix();
void InitHooks_ObjectStreamerOptimization();
void InitHooks_Postprocess();
void InitHooks_DeviceSelection();
CRemoteDataStorage* CreateRemoteDataStorage();
void DestroyRemoteDataStorage(CRemoteDataStorage* pData);
void AddRemoteDataStorage(CPlayerPed* pPed, CRemoteDataStorage* pData);
Expand Down
155 changes: 155 additions & 0 deletions Client/multiplayer_sa/CMultiplayerSA_DeviceSelection.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto v1.0
* LICENSE: See LICENSE in the top level directory
* FILE: multiplayer_sa/CMultiplayerSA_DeviceSelection.cpp
*
* Multi Theft Auto is available from http://www.multitheftauto.com/
*
*****************************************************************************/

#include "StdInc.h"
#define FUNC_rwDeviceSystemRequest 0x7F2AB0
#define FUNC_DialogFunc 0x745E50
#define FUNC_RwEngineGetSubSystemInfo 0x7F2C30
#define CLASS_RwGlobals 0xC97B24
#define CLASS_IDirect3D9 0xC97C20
#define NUM_DialogFuncStackPushAddress 0x746239

// This is copied from SilentPatch:
// https://github.com/CookiePLMonster/SilentPatch/blob/dev/SilentPatch/FriendlyMonitorNames.cpp
std::unordered_map<std::string, std::string> GetFriendlyMonitorNamesForDevicePaths()
{
std::unordered_map<std::string, std::string> monitorNames;

HMODULE user32Lib = LoadLibrary(TEXT("user32"));
if (!user32Lib)
return monitorNames;

auto* getDisplayConfigBufferSizes = (decltype(GetDisplayConfigBufferSizes)*)GetProcAddress(user32Lib, "GetDisplayConfigBufferSizes");
auto* queryDisplayConfig = (decltype(QueryDisplayConfig)*)GetProcAddress(user32Lib, "QueryDisplayConfig");
auto* displayConfigGetDeviceInfo = (decltype(DisplayConfigGetDeviceInfo)*)GetProcAddress(user32Lib, "DisplayConfigGetDeviceInfo");
if (!getDisplayConfigBufferSizes || !queryDisplayConfig || !displayConfigGetDeviceInfo)
{
FreeLibrary(user32Lib);
return monitorNames;
}

UINT32 pathCount, modeCount;
std::unique_ptr<DISPLAYCONFIG_PATH_INFO[]> paths;
std::unique_ptr<DISPLAYCONFIG_MODE_INFO[]> modes;

LONG result = ERROR_SUCCESS;
do
{
result = getDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount);
if (result != ERROR_SUCCESS)
{
break;
}
paths = std::make_unique<DISPLAYCONFIG_PATH_INFO[]>(pathCount);
modes = std::make_unique<DISPLAYCONFIG_MODE_INFO[]>(modeCount);
result = queryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths.get(), &modeCount, modes.get(), nullptr);
} while (result == ERROR_INSUFFICIENT_BUFFER);

if (result != ERROR_SUCCESS)
{
FreeLibrary(user32Lib);
return monitorNames;
}

for (size_t i = 0; i < pathCount; i++)
{
DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = {};
targetName.header.adapterId = paths[i].targetInfo.adapterId;
targetName.header.id = paths[i].targetInfo.id;
targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
targetName.header.size = sizeof(targetName);
const LONG targetNameResult = DisplayConfigGetDeviceInfo(&targetName.header);

DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName = {};
sourceName.header.adapterId = paths[i].sourceInfo.adapterId;
sourceName.header.id = paths[i].sourceInfo.id;
sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
sourceName.header.size = sizeof(sourceName);
const LONG sourceNameResult = DisplayConfigGetDeviceInfo(&sourceName.header);
if (targetNameResult == ERROR_SUCCESS && sourceNameResult == ERROR_SUCCESS && targetName.monitorFriendlyDeviceName[0] != '\0')
{
char gdiDeviceName[std::size(sourceName.viewGdiDeviceName)];
char monitorFriendlyDeviceName[std::size(targetName.monitorFriendlyDeviceName)];
WideCharToMultiByte(CP_ACP, 0, sourceName.viewGdiDeviceName, -1, gdiDeviceName, static_cast<int>(std::size(gdiDeviceName)), nullptr, nullptr);
WideCharToMultiByte(CP_ACP, 0, targetName.monitorFriendlyDeviceName, -1, monitorFriendlyDeviceName,
static_cast<int>(std::size(monitorFriendlyDeviceName)), nullptr, nullptr);

monitorNames.try_emplace(gdiDeviceName, monitorFriendlyDeviceName);
}
}

FreeLibrary(user32Lib);
return monitorNames;
}

struct RwSubSystemInfo
{
char name[80];
};

using rwDeviceSystemRequest = RwSubSystemInfo*(__cdecl*)(RwDevice* device, std::int32_t requestId, RwSubSystemInfo* pOut, void* pInOut, std::int32_t numIn);
static RwSubSystemInfo* RwEngineGetSubSystemInfo_Hooked(RwSubSystemInfo* subSystemInfo, std::int32_t subSystemIndex)
{
auto* rwGlobals = *(RwGlobals**)CLASS_RwGlobals;
auto* rwDeviceSystemRequestFunc = (rwDeviceSystemRequest)(FUNC_rwDeviceSystemRequest);
if (!rwDeviceSystemRequestFunc(&rwGlobals->dOpenDevice, 14, subSystemInfo, nullptr, subSystemIndex))
return nullptr;

auto* pDxDevice = *(IDirect3D9**)CLASS_IDirect3D9;
if (!pDxDevice)
return subSystemInfo;

D3DADAPTER_IDENTIFIER9 identifier;
if (FAILED(pDxDevice->GetAdapterIdentifier(subSystemIndex, 0, &identifier)))
return subSystemInfo;

static const auto friendlyNames = GetFriendlyMonitorNamesForDevicePaths();

// 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
auto it = friendlyNames.find(identifier.DeviceName);
if (it != friendlyNames.end())
{
strncpy_s(subSystemInfo->name, it->second.c_str(), _TRUNCATE);
}
else
{
strncpy_s(subSystemInfo->name, identifier.Description, _TRUNCATE);
}

return subSystemInfo;
}

INT_PTR CALLBACK CustomDlgProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam)
{
auto* orgDialogFunc = (DLGPROC)FUNC_DialogFunc;
if (msg != WM_INITDIALOG)
return orgDialogFunc(window, msg, wParam, lParam);

orgDialogFunc(window, msg, wParam, lParam);

// Set Icon
HMODULE hGameModule = GetModuleHandle(nullptr);
SendMessage(window, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(LoadIcon(hGameModule, MAKEINTRESOURCE(100))));

// Make the dialog visible in the task bar
// https://stackoverflow.com/a/1462811
SetWindowLongPtr(window, GWL_EXSTYLE, WS_EX_APPWINDOW);
ShowWindow(window, SW_HIDE);
ShowWindow(window, SW_SHOW);
return FALSE;
}

void CMultiplayerSA::InitHooks_DeviceSelection()
{
// 0x746239 -> Exact address where the original DialogFunc address is being pushed as an argument to DialogBoxParamA(),
// we're replacing it with out own proxy function
MemPut<DLGPROC>(NUM_DialogFuncStackPushAddress, (DLGPROC)&CustomDlgProc);
HookInstall(FUNC_RwEngineGetSubSystemInfo, (DWORD)RwEngineGetSubSystemInfo_Hooked, 6);
}
46 changes: 46 additions & 0 deletions Client/sdk/game/RenderWare.h
Original file line number Diff line number Diff line change
Expand Up @@ -526,3 +526,49 @@ struct RwError
{
int err1, err2;
};

/*****************************************************************************/
/** RenderWare Globals **/
/*****************************************************************************/

typedef bool (*RwSystemFunc)(std::int32_t, void*, void*, std::int32_t);
struct RwDevice
{
float gammaCorrection;
RwSystemFunc fpSystem;
float zBufferNear;
float zBufferFar;
// RwRenderStateSetFunction fpRenderStateSet;
// RwRenderStateGetFunction fpRenderStateGet;
// RwIm2DRenderLineFunction fpIm2DRenderLine;
// RwIm2DRenderTriangleFunction fpIm2DRenderTriangle;
// RwIm2DRenderPrimitiveFunction fpIm2DRenderPrimitive;
// RwIm2DRenderIndexedPrimitiveFunction fpIm2DRenderIndexedPrimitive;
// RwIm3DRenderLineFunction fpIm3DRenderLine;
// RwIm3DRenderTriangleFunction fpIm3DRenderTriangle;
// RwIm3DRenderPrimitiveFunction fpIm3DRenderPrimitive;
// RwIm3DRenderIndexedPrimitiveFunction fpIm3DRenderIndexedPrimitive;
};
// static_assert(sizeof(RwDevice) == 0x38, "Incorrect class size: RwDevice");

typedef bool (*RwStandardFunc)(void*, void*, std::int32_t);
struct RwGlobals
{
void* curCamera;
void* curWorld;
std::uint16_t renderFrame;
std::uint16_t lightFrame;
std::uint16_t pad[2];
RwDevice dOpenDevice;
RwStandardFunc stdFunc[29];
// RwLinkList dirtyFrameList;
// RwFileFunctions fileFuncs;
// RwStringFunctions stringFuncs;
// RwMemoryFunctions memoryFuncs;
// RwMemoryAllocFn memoryAlloc;
// RwMemoryFreeFn memoryFree;
// RwMetrics* metrics;
// RwEngineStatus engineStatus;
// RwUInt32 resArenaInitSize;
};
//static_assert(sizeof(RwGlobals) == 0x158, "Incorrect class size: RwGlobals");

0 comments on commit 6f90880

Please sign in to comment.