Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

[Client Edit] [v93+] Drag-to-Resize Resolution Change

Skilled Illusionist
Joined
Feb 18, 2010
Messages
320
Reaction score
112
Hello all,

Trigger-warning: I shamelessly plug my YouTube channel and GitHub profile in this post.

Context:
Someone asked me the other day why drag-resizing wasn't possible in MapleStory, which I thought was an excellent question, so I did some digging and figured out why. In order to follow along with this tutorial, you have to be familiar with detouring, have some knowledge of C/C++, and be familiar with IDA. I will provide pretty much all of the code in this post (except for memory addresses), and any code that is not in this post will exist in my .
I have added a handful of preset resolution values, but feel free to add more or edit the existing ones.

Here is a short recording of the final product:
The snap-to-size is a little janky, but it works. Should be relatively simple to smooth over.
This works for GMS v93+ (or whatever version Nexon released res change in).

Let's dive into it!

First off, you'll need these macros defined somewhere:
Code:
#define WINDOW_THIN_BORDER_SIZE 8
#define WINDOW_TOP_BORDER_SIZE 30

#define WINDOW_LR_BORDER_PADDING (WINDOW_THIN_BORDER_SIZE * 2)
#define WINDOW_TB_BORDER_PADDING (WINDOW_TOP_BORDER_SIZE + WINDOW_THIN_BORDER_SIZE + 1)

// https://upload.wikimedia.org/wikipedia/commons/6/63/Vector_Video_Standards.svg
#define LOWRES_W	800
#define LOWRES_H	600
#define MIDRES_W	1024
#define MIDRES_H	768
#define MIDRES2_W	1366
#define MIDRES2_H	768
#define HIRES_W		1440
#define HIRES_H		900
#define HIRES2_W	1600
#define HIRES2_H	900
#define ULTRARES_W	1680
#define ULTRARES_H	1050

You'll also need these function types defined somewhere:
Code:
typedef LRESULT(CALLBACK* _CWvsAppt__WindowProc_t)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
_CWvsAppt__WindowProc_t _CWvsAppt__WindowProc;

typedef VOID(__fastcall* _CWvsContext__SetScreenResolution_t)(void* pThis, void* edx, bool bLargeScreen, bool bSave);
_CWvsContext__SetScreenResolution_t _CWvsContext__SetScreenResolution;

The resolution change function will require some sleuthing on your part. If you are not familiar with IDA,
The required struct offset is the following guy in the CWvsContext::SetScreenResolution function.
OS3hEcO - [Client Edit] [v93+] Drag-to-Resize Resolution Change - RaGEZONE Forums

The second set of addresses are the large screen addresses in the same function as mentioned above.
3mtXrE - [Client Edit] [v93+] Drag-to-Resize Resolution Change - RaGEZONE Forums


CWvsContext::SetScreenResolution can be found (in some versions) by using the aob: BD 00 04 00 00 BB 00 03 00 00

Here is the resolution change function that you'll need to edit for your version:
Again, please note this will NOT work for versions below GMS v93 (or whenever Nexon released res change).
Code:
namespace MapleAPI
{
	void ChangeRes(int x, int y) // TODO rewrite this so its not editing memory :(
	{
		*(BOOL*)(CWvsContext::GetInstance() + 0x416C) = FALSE; // bLargeScreen

		// change res values
		WriteValue(0x00ADF998 + 1, x); // swap with your versions's addresses
		WriteValue(0x00ADF99D + 1, y);
		_CWvsContext__SetScreenResolution(CWvsContext::GetInstance(), NULL, TRUE, TRUE);
		// reset res values -> required if you run your own crc 
		WriteValue(0x00ADF998 + 1, 0x400); // same addresses as above
		WriteValue(0x00ADF99D + 1, 0x300);
	}
}

And this dummy struct for CWvsContext needs to be put somewhere as well.
Code:
class CWvsContext
{
public:

	static CWvsContext* GetInstance()
	{
		return *reinterpret_cast<CWvsContext**>(0x00FC176C); // swap with your version's address
	}
};
The address it points to should be the address of the class pointer, not the GetInstance() function.
You can find the pointer in the GetInstance() function, just make sure to use the address of the pointer returned by the function rather the address of the function itself (double click the ::ms_pInstance on the highlighted line to find the pointer address).
oiyBVpv - [Client Edit] [v93+] Drag-to-Resize Resolution Change - RaGEZONE Forums


Now, to the interesting stuff. I won't go into a tremendous amount of detail because I linked some good resources in the code, but basically what we need to do is hook the WindowProc function (CWvsApp::WindowProc). Every Windows window has one of these, and it is the function that the Windows operating system passes events to (mouse movements, keyboard presses, etc). We are going to detour this and add our own code to support window resizing, and then call our resolution change function from inside it as well.

Can be found with this aob in some versions: 3D 30 F0 00 00 75

Here is the WindowProc hook:
Code:
LRESULT CALLBACK CWvsApp__WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static INT nCurrentWidth = 800;
	static INT nCurrentHeight = 600;
	
	// https://stackoverflow.com/questions/2448738/how-to-force-a-window-to-maintain-a-certain-width-height-ratio-when-resized
	// https://stackoverflow.com/questions/7773771/how-do-i-implement-dragging-a-window-using-its-client-area

	switch (uMsg)
	{
	case WM_NCHITTEST: // https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest
	{
		// this enables the window drag
		LRESULT hit = DefWindowProc(hWnd, uMsg, wParam, lParam);

		if (hit == HTBOTTOMRIGHT) return hit;
		
		break;
	}
	case WM_SIZING:
	{
		LPRECT lpRect = reinterpret_cast<LPRECT>(lParam);

		INT nNewWidth = lpRect->right - lpRect->left - WINDOW_LR_BORDER_PADDING;
		INT nNewHeight = lpRect->bottom - lpRect->top - WINDOW_TB_BORDER_PADDING;

		if (nNewWidth < MIDRES_W - 25 || nNewHeight < MIDRES_H - 75)
		{
			lpRect->right = lpRect->left + LOWRES_W + WINDOW_LR_BORDER_PADDING;
			lpRect->bottom = lpRect->top + LOWRES_H + WINDOW_TB_BORDER_PADDING;
		}
		else if (nNewWidth < MIDRES2_W - 25 || nNewHeight < MIDRES2_H - 75)
		{
			lpRect->right = lpRect->left + MIDRES_W + WINDOW_LR_BORDER_PADDING;
			lpRect->bottom = lpRect->top + MIDRES_H + WINDOW_TB_BORDER_PADDING;
		}
		else if (nNewWidth < HIRES_W - 25 || nNewHeight < HIRES_H - 75)
		{
			lpRect->right = lpRect->left + MIDRES2_W + WINDOW_LR_BORDER_PADDING;
			lpRect->bottom = lpRect->top + MIDRES2_H + WINDOW_TB_BORDER_PADDING;
		}
		else if (nNewWidth < HIRES2_W - 25 || nNewHeight < HIRES2_H - 75)
		{
			lpRect->right = lpRect->left + HIRES_W + WINDOW_LR_BORDER_PADDING;
			lpRect->bottom = lpRect->top + HIRES_H + WINDOW_TB_BORDER_PADDING;
		}
		else if (nNewWidth < ULTRARES_W - 25 || nNewHeight < ULTRARES_H - 75)
		{
			lpRect->right = lpRect->left + HIRES2_W + WINDOW_LR_BORDER_PADDING;
			lpRect->bottom = lpRect->top + HIRES2_H + WINDOW_TB_BORDER_PADDING;
		}
		else
		{
			lpRect->right = lpRect->left + ULTRARES_W + WINDOW_LR_BORDER_PADDING;
			lpRect->bottom = lpRect->top + ULTRARES_H + WINDOW_TB_BORDER_PADDING;
		}

		nNewWidth = lpRect->right - lpRect->left - WINDOW_LR_BORDER_PADDING;
		nNewHeight = lpRect->bottom - lpRect->top - WINDOW_TB_BORDER_PADDING;
		
		/* only resize if size is different than the current size */
		if (nCurrentWidth != nNewWidth || nCurrentHeight != nNewHeight)
		{
			nCurrentWidth = nNewWidth;
			nCurrentHeight = nNewHeight;

			MapleAPI::ChangeRes(nCurrentWidth, nCurrentHeight);
		}

		Log("Calculated dimensions: %d %d", nNewWidth, nNewHeight);

		break;
	}
	}

	return _CWvsAppt__WindowProc(hWnd, uMsg, wParam, lParam);
}

And finally, you'll need to apply the WindowProc hook and set the res change pointer. If you're using my , then you can just put the following code into MainFunc, otherwise you're on your own.

Code:
_CWvsContext__SetScreenResolution = reinterpret_cast<_CWvsContext__SetScreenResolution_t>(0x00ADF920); // change to correct address for your version
	
INITMAPLEHOOK(
		_CWvsAppt__WindowProc,
		_CWvsAppt__WindowProc_t,
		CWvsApp__WindowProc,
		0x00ADC260 // change to correct address for your version
	);

I'm pretty sure that's the extent of it. If you have any comments or feedback, feel free to hit me up on Discord or .
Because I prefaced this with requiring a rudimentary understanding of IDA and C++ and given the fact that there is a , I probably won't be walking people through this any further. However, if I made any mistakes or forgot anything in the instructions, please let me know and I'll correct it.

As they say in France, bone apple tea and a river dirt cheap.



Uhhhhh I forgot to change the title lmao. Maybe a mod can change it to: [Client Edit] [v93+] Drag-to-Resize Resolution Change



NEVERMIND I figured out how to edit the title
 

Attachments

You must be registered for see attachments list
Last edited:
Initiate Mage
Joined
Jul 31, 2018
Messages
1
Reaction score
0
Super useful and helpful as usual! Thanks for this gem :)
 
Back
Top