- 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:
You'll also need these function types defined somewhere:
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.
The second set of addresses are the large screen addresses in the same function as mentioned above.
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).
And this dummy struct for CWvsContext needs to be put somewhere as well.
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).
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:
And finally, you'll need to apply the WindowProc hook and set the res change pointer. If you're using my
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
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
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
You must be registered to see links
.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:
You must be registered to see links
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,
You must be registered to see links
The required struct offset is the following guy in the CWvsContext::SetScreenResolution function.
The second set of addresses are the large screen addresses in the same function as mentioned above.
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
}
};
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).
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
You must be registered to see links
, 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
You must be registered to see links
.Because I prefaced this with requiring a rudimentary understanding of IDA and C++ and given the fact that there is a
You must be registered to see links
, 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: