This is my own little (shitty) resolution hack, similar to all the previous resolution hacks, but by no means completed. There is still much work to be done, and yet, so little time...
REV 04/08/12 @ 3:10 PM EST
This is assuming you are developing for Closed Battle.net. A lot of things can be simplified if you only plan to use this for Single Player/Open Battle.net.The first order of business is something you will need to do on your own: develop a system to auto-inject your DLL before SetDisplayMode is called. After you solve this little dilemma, it's all smooth sailing. You might also consider developing a system for auto-injecting when the Diablo II window gains the focus, in case you Alt+Tab.
Below is a simple DLL injector, but does not include either of the systems I mentioned above. I don't feel like I need to explain this, because I am lazy, and there are tons of explanations out there already. Plus, I'm just no good at commenting my code.
D2LoadDll.cpp#pragma warning(disable:4996)
#include <Windows.h>
Before the WinMain function, there is the user-defined function, SetDebugPrivilege, which enables/disables the Debug privilege. This will allow D2LoadDll to open the Diablo II (Game.exe) process handle. For a more detailed explanation, take a look at
this article, on MSDN. This step is skipped a lot because most processes don't require the Debug privilege to open the process handle. You can avoid this by creating your own launcher.
void SetDebugPrivilege(HANDLE TokenHandle, BOOL bPrivilegeEnabled)
{
LUID Luid;
TOKEN_PRIVILEGES NewState;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Luid);
NewState.PrivilegeCount = 1;
NewState.Privileges[0].Luid = Luid;
if(bPrivilegeEnabled)
{
NewState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
}
else
{
NewState.Privileges[0].Attributes = 0;
}
AdjustTokenPrivileges(TokenHandle, FALSE, &NewState, 0, NULL, NULL);
}
Again, for a more detailed explanation, take a look at
this thread, by Darawk, or type "Dll Injection" into
Google. You need only change the second strcat parameter to that of your DLL name for this to function. Unfortunately, without the systems I mentioned earlier, you will be required to inject after every game, which is a very tedious task.
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HANDLE TokenHandle;
HWND hWnd;
DWORD dwProcessId;
HANDLE hProcess;
char lpBuffer[MAX_PATH];
size_t nSize;
LPVOID lpFileName;
HANDLE hThread;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &TokenHandle);
SetDebugPrivilege(TokenHandle, TRUE);
hWnd = FindWindow("Diablo II", "Diablo II");
GetWindowThreadProcessId(hWnd, &dwProcessId);
hProcess = OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_VM_WRITE, FALSE, dwProcessId);
SetDebugPrivilege(TokenHandle, FALSE);
CloseHandle(TokenHandle);
GetCurrentDirectory(sizeof(lpBuffer), lpBuffer);
strcat(lpBuffer, "\\D2res.dll");
nSize = strlen(lpBuffer) + 1;
lpFileName = VirtualAllocEx(hProcess, NULL, nSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
WriteProcessMemory(hProcess, lpFileName, lpBuffer, nSize, NULL);
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, lpFileName, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
VirtualFreeEx(hProcess, lpFileName, nSize, MEM_RELEASE);
CloseHandle(hProcess);
CloseHandle(hThread);
return 0;
}
I'd like to mention some (less than) valuable tips for making your own resolution hack before throwing unexplained code at you. If you want to make a fully functional resolution hack (DD/D3D/Glide/Win), you will need to find the functions and pointers on your own. To do this is relatively simple. First, set the mode you want by running the
Video Test or setting
-w as a command line parameter. Next, go into a game, search for your current resolution width, change it, refine your search using the new resolution width, and repeat; Game Hacking 101. Finally, break on Write and viola. Some of what you will find might be in D2DDraw.dll/D2Direct3D.dll/D2Glide.dll/D2Gdi.dll, but the main components are located in D2gfx.dll/D2Win.dll/D2Client.dll. For example, display mode for D3D is in D2Direct3D.dll, but the resolution is in D2Client.dll.
D2Direct3D.dll6F88BE5E |. 8935 6C318B6F mov dword ptr ds:[6F8B316C], esi
6F88BE64 |. 75 16 jnz short 6F88BE7C
6F88BE66 |. C705 6C298B6F 80020000 mov dword ptr ds:[6F8B296C], 280
6F88BE70 |. C705 FC2D8B6F E0010000 mov dword ptr ds:[6F8B2DFC], 1E0
6F88BE7A |. EB 14 jmp short 6F88BE90
6F88BE7C |> C705 6C298B6F 20030000 mov dword ptr ds:[6F8B296C], 320
6F88BE86 |. C705 FC2D8B6F 58020000 mov dword ptr ds:[6F8B2DFC], 258
...
6F88BEDE |> \8B0D FC2D8B6F mov ecx, dword ptr ds:[6F8B2DFC]
6F88BEE4 |. A1 44318B6F mov eax, dword ptr ds:[6F8B3144]
6F88BEE9 |. 8B10 mov edx, dword ptr ds:[eax]
6F88BEEB |. 53 push ebx
6F88BEEC |. 53 push ebx
6F88BEED |. 6A 10 push 10
6F88BEEF |. 51 push ecx
6F88BEF0 |. 8B0D 6C298B6F mov ecx, dword ptr ds:[6F8B296C]
6F88BEF6 |. 51 push ecx
6F88BEF7 |. 50 push eax
6F88BEF8 |. FF52 54 call dword ptr ds:[edx+54]
D2Client.dll6FADC220 $ E8 BB0DFEFF call <jmp.&D2gfx.#10012>
6FADC225 . 3BF0 cmp esi, eax
6FADC227 . 0F84 89000000 je 6FADC2B6
6FADC22D . 8BC6 mov eax, esi
6FADC22F . 83E8 00 sub eax, 0 ; Switch (cases 0..2)
6FADC232 . 74 25 je short 6FADC259
6FADC234 . 83E8 02 sub eax, 2
6FADC237 . 75 40 jnz short 6FADC279
6FADC239 . B8 20030000 mov eax, 320 ; Case 2 of switch 6FADC22F
6FADC23E . A3 3470BA6F mov dword ptr ds:[6FBA7034], eax
6FADC243 . C705 3870BA6F 58020000 mov dword ptr ds:[6FBA7038], 258
6FADC24D . C705 B4D2BC6F 01000000 mov dword ptr ds:[6FBCD2B4], 1
6FADC257 . EB 25 jmp short 6FADC27E
6FADC259 > B8 80020000 mov eax, 280 ; Case 0 of switch 6FADC22F
6FADC25E . A3 3470BA6F mov dword ptr ds:[6FBA7034], eax
6FADC263 . C705 3870BA6F E0010000 mov dword ptr ds:[6FBA7038], 1E0
6FADC26D . C705 B4D2BC6F 00000000 mov dword ptr ds:[6FBCD2B4], 0
6FADC277 . EB 05 jmp short 6FADC27E
6FADC279 > A1 3470BA6F mov eax, dword ptr ds:[6FBA7034] ; Default case of switch 6FADC22F
6FADC27E > 8B0D 3870BA6F mov ecx, dword ptr ds:[6FBA7038]
6FADC284 . 83C1 D8 add ecx, -28
6FADC287 . 56 push esi
6FADC288 . A3 643DBD6F mov dword ptr ds:[6FBD3D64], eax
6FADC28D . 890D 603DBD6F mov dword ptr ds:[6FBD3D60], ecx
6FADC293 . A3 3C70BA6F mov dword ptr ds:[6FBA703C], eax
6FADC298 . E8 A910FEFF call <jmp.&D2Win.#10037>
6FADC29D . A1 70D0BC6F mov eax, dword ptr ds:[6FBCD070]
6FADC2A2 . E8 49020300 call 6FB0C4F0
6FADC2A7 . B9 01000000 mov ecx, 1
6FADC2AC . E8 0F5D0400 call 6FB21FC0
6FADC2B1 .^ E9 DA07FFFF jmp 6FACCA90
6FADC2B6 > C3 retn
A little helper function I developed to get the correct base for the specified module: GetModuleBase will attempt to get the base using GetModuleHandle; however, if the module isn't loaded (D2Client.dll, for example), it's loaded into the process with LoadLibrary. This is assuming the specified module is in the Diablo II directory. The great thing about this is that you don't have to worry about when you can access certain functions or pointers.
D2res.cppDWORD GetModuleBase(LPCSTR lpModuleName)
{
DWORD dwModuleBase;
dwModuleBase = (DWORD)GetModuleHandle(lpModuleName);
if(dwModuleBase == NULL)
{
dwModuleBase = (DWORD)LoadLibrary(lpModuleName);
}
return dwModuleBase;
}
Changing the display mode without using "detours" (Microsoft or otherwise) requires a vtable hook; do the hooking inside the DLL_PROCESS_ATTACH routine (or a thread therein).
You must remember to unhook the vtable after SetDisplayMode is called, and before you return from the hook. Also, clean up any other changes to the client in the DLL_PROCESS_DETACH routine. The code in its current state will not work. You must develop a system to unload the DLL after the hook is executed. An incomplete exampled is embedded in the code.
D2res.cppvoid OnDllProcessAttach(HMODULE hModule)
{
/*
6F88BEE4 |. A1 44318B6F mov eax, dword ptr ds:[6F8B3144]
6F88BEE9 |. 8B10 mov edx, dword ptr ds:[eax]
...
6F88BEF8 |. FF52 54 call dword ptr ds:[edx+54]
*/
LPDIRECTDRAW4 lpDD;
// IDirectDraw4 *
lpDD = (LPDIRECTDRAW4)*(LPDWORD)(D2Direct3D_dll + 0x33144); // [6F8B3144]
g_lpDDVTable = (LPDWORD)*(LPDWORD)lpDD;
// SetDisplayMode
*(LPDWORD)&SetDisplayMode = g_lpDDVTable[21];
*(LPDWORD)&g_lpDDVTable[21] = (DWORD)SetDisplayModeHook;
// while(!bExitThread)
// {
// Sleep(1);
// }
FreeLibraryAndExitThread(hModule, EXIT_SUCCESS);
}
The SetDisplayMode hook is where the display mode and resolution are changed. Don't forget to declare the D2Client_dll global somewhere (D2Client.h, perhaps). You will probably want to find a different time to change the resolution, and is probably the source of a small bug I'll mention later.
D2Direct3D.h#include <ddraw.h>
const DWORD D2Direct3D_dll = GetModuleBase("D2Direct3D.dll");
LPDWORD g_lpDDVTable;
DWORD g_dwWidth = GetSystemMetrics(SM_CXSCREEN);
DWORD g_dwHeight = GetSystemMetrics(SM_CYSCREEN);
typedef HRESULT (WINAPI *SETDISPLAYMODE)(LPDIRECTDRAW4 lpDD, DWORD dwWidth, DWORD dwHeight, DWORD dwBPP, DWORD dwRefreshRate, DWORD dwFlags);
SETDISPLAYMODE SetDisplayMode;
HRESULT WINAPI SetDisplayModeHook(LPDIRECTDRAW4 lpDD, DWORD dwWidth, DWORD dwHeight, DWORD dwBPP, DWORD dwRefreshRate, DWORD dwFlags)
{
HRESULT hResult;
*(LPDWORD)(D2Direct3D_dll + 0x3296C) = g_dwWidth; // dwWidth [6F8B296C]
*(LPDWORD)(D2Direct3D_dll + 0x32DFC) = g_dwHeight; // dwHeight [6F8B2DFC]
dwWidth = g_dwWidth;
dwHeight = g_dwHeight;
hResult = SetDisplayMode(lpDD, dwWidth, dwHeight, dwBPP, dwRefreshRate, dwFlags);
*(LPDWORD)(D2Client_dll + 0xF7034) = g_dwWidth; // [6FBA7034]
*(LPDWORD)(D2Client_dll + 0xF7038) = g_dwHeight; // [6FBA7038]
*(LPDWORD)(D2Client_dll + 0x11D2B4) = 0x1; // [6FBCD2B4]
*(LPDWORD)(D2Client_dll + 0x123D64) = g_dwWidth; // [6FBD3D64]
*(LPDWORD)(D2Client_dll + 0x123D60) = (g_dwHeight - 0x28); // [6FBD3D60] (y-0x28)
*(LPDWORD)(D2Client_dll + 0xF703C) = g_dwWidth; // [6FBA703C]
// SetDisplayMode
*(LPDWORD)&g_lpDDVTable[21] = (DWORD)SetDisplayMode; // Retour
return hResult;
}
Unfortunately, the screen is glitched until you bring up an inventory (inventory/stash/cube/etc.; press I, twice, and problem solved). If you can fix this, good for you. Everything should be self-explanatory at this point. I probably forgot to mention some things, so when/if I remember, I'll append them to this post.
Source:
http://www.fileswap.com/dl/ZdvDml00fL/D2res.zip.htmlExample:
viewtopic.php?f=166&t=488658