Hello beloved, greedy, dead community,
it's time for another [Only registered and activated users can see links. Click Here To Register...] ( @ronz007 ). I've been asked quite often: "Can you change the 'Create Button' to lead directly to character creation?". I've had no viable solution for this, yet. But let's change that now.
Switching between states
Silkroad's internals is split into so different states. Joymax calls these states processes. Each process is represented by a single class. CPSCharacterSelect is the process showing all the characters. CPSCharacterCreateEurope is the process for creating an european character and CPSCharacterCreateChina is the equivalent for chinese characters.
The game will only run one process which is comprehensibly since the game can only have one state at a time. There are two different ways to change the state:
* Create a new process and replace the current one
* Create a child process and pause the current one
The character selection is made up of different states, too. We distinquish between transitional states and static states. Transitional states are run once, before entering or after leaving a static state. The transitional-state for leaving the character selection and entering the character creation looks like this:
The most interesting parts are these lines
The two creation-processes are run as child processes. My best guess is, that completely switching over to either CPSCharacterCreateEurope or CPSCharacterCreateChina will litereally kill CPSCharacterSelect and therefore lose the network session and require a re-login. After creating a character, we will return to CPSCharacterSelect, so we need that process again, anyways.
So thats how we change to the creation of a character: Use a child process!
Finding the button handler
Lets investigate a little on where to make a change. Silkroad's UI event handling is quite weird. The control-id plays a big role in this.
Since Joymax were just a bunch of thieves .. EH!? ... I mean they got inspired from using MFC!, we just can look up how message handlers work in the MFC ... [Only registered and activated users can see links. Click Here To Register...] (oh, you can't imagine how long I reversed the MFC without noticing it's the MFC, again!!!).
With a bit of magic and a snap with your finger, you'll end up with the OnCreateButton_Click-handler being at 0x0085DE50. Swing your wand and smack your head on the keyboard to extract the pseudocode for this function:
Believe it or not, that's part of my workflow. If you don't have your wand with you, you can also search for the text-string shown in the pseudocode.
Do it!
So we're done now. We know where things happen. We know how things happen. So lets actually do it.
Lets wrap things up. We need to change the button handler to:
* Set the global g_selected_idol to CHINA (1)
* Set the state of CPSCharacterSelect to *leave for character creation* (9)
* Hope we did everything right.
Put all the ingredients in your bubbling cauldron, add a spoon of oldschool-player-tears and two cups of pulverized silkroad money. Give it a pretty good stirr. Okay. Done.
Since most of you got confused by using C++ code, I will switch back to good old assembly for this one, eventho C++ would have been way more fun.
Now where to put it?
[Only registered and activated users can see links. Click Here To Register...]
We just have one problem: The instructions are too large :(. Both instructions are 10 Byte-instructions :/. We need to pad the epilogue.
Before:
[Only registered and activated users can see links. Click Here To Register...]
After:
[Only registered and activated users can see links. Click Here To Register...]
x64dbg Patch File, also appended as file.
Note: All servers that appear in examples or images are for the sole purpose of testing. I'm not affiliated with any of these servers in any way.
it's time for another [Only registered and activated users can see links. Click Here To Register...] ( @ronz007 ). I've been asked quite often: "Can you change the 'Create Button' to lead directly to character creation?". I've had no viable solution for this, yet. But let's change that now.
|
|
Switching between states
Silkroad's internals is split into so different states. Joymax calls these states processes. Each process is represented by a single class. CPSCharacterSelect is the process showing all the characters. CPSCharacterCreateEurope is the process for creating an european character and CPSCharacterCreateChina is the equivalent for chinese characters.
The game will only run one process which is comprehensibly since the game can only have one state at a time. There are two different ways to change the state:
* Create a new process and replace the current one
* Create a child process and pause the current one
The character selection is made up of different states, too. We distinquish between transitional states and static states. Transitional states are run once, before entering or after leaving a static state. The transitional-state for leaving the character selection and entering the character creation looks like this:
Code:
if (m_cameraworking->float_F0 != m_cameraworking->float_E8 || m_pProcessChild)
return;
if (g_selected_idol)
{
if (g_selected_idol == 1)
{
current_state = 12;
CIFWnd* loading_china = m_ifbuilder.GetGUIObjectByID(PSCHARSEL_GDR_LOADING_CHINA, 1);
loading_china->SetVisibility(true);
CIFWnd* frame = m_ifbuilder.GetGUIObjectByID(PSCHARSEL_GDR_LOADINGFRAME, 1);
frame->SetVisibility(true);
CIFWnd* loading_sta = m_ifbuilder.GetGUIObjectByID(PSCHARSEL_GDR_LOADING_STA, 1);
loading_sta->SetVisibility(true);
CIFGauge* gauge = m_ifbuilder.GetGUIObjectByID<CIFGauge>(PSCHARSEL_GDR_LOADINGG, 1);
gauge->SetVisibility(true);
gauge->progress_1 = 0;
gauge->progress_2 = 0;
loading_china->ApplyGlobalScale(1);
frame->ApplyGlobalScale(0);
loading_sta->ApplyGlobalScale(0); // guessed, one unknown call remained ...
gauge->ApplyGlobalScale(0);
//CGWnd::ApplyGlobalScale(a1, 0);
theApp.sub_BA3FC0(0, 0);
CreateAsChildProcess(GFX_RUNTIME_CLASS(CPSCharacterCreateChina), 1);
theApp.RunProcessFromInstance(m_pProcessChild);
}
else
{
current_state = 12;
CIFWnd* loading_china = m_ifbuilder.GetGUIObjectByID(PSCHARSEL_GDR_LOADING_CHINA, 1);
loading_china->SetVisibility(true);
CIFWnd* frame = m_ifbuilder.GetGUIObjectByID(PSCHARSEL_GDR_LOADINGFRAME, 1);
frame->SetVisibility(true);
CIFWnd* loading_sta = m_ifbuilder.GetGUIObjectByID(PSCHARSEL_GDR_LOADING_STA, 1);
loading_sta->SetVisibility(true);
CIFGauge* gauge = m_ifbuilder.GetGUIObjectByID<CIFGauge>(PSCHARSEL_GDR_LOADINGG, 1);
gauge->SetVisibility(true);
gauge->progress_1 = 0;
gauge->progress_2 = 0;
loading_china->ApplyGlobalScale(1);
frame->ApplyGlobalScale(0);
loading_sta->ApplyGlobalScale(0); // guessed, one unknown call remained ...
gauge->ApplyGlobalScale(0);
theApp.sub_BA3FC0(0, 0);
CreateAsChildProcess(GFX_RUNTIME_CLASS(CPSCharacterCreateEurope), 1);
theApp.RunProcessFromInstance(m_pProcessChild);
}
}
Code:
current_state = 12; // ... CreateAsChildProcess(GFX_RUNTIME_CLASS(CPSCharacterCreateChina), 1); theApp.RunProcessFromInstance(m_pProcessChild);
So thats how we change to the creation of a character: Use a child process!
Finding the button handler
Lets investigate a little on where to make a change. Silkroad's UI event handling is quite weird. The control-id plays a big role in this.
Since Joymax were just a bunch of thieves .. EH!? ... I mean they got inspired from using MFC!, we just can look up how message handlers work in the MFC ... [Only registered and activated users can see links. Click Here To Register...] (oh, you can't imagine how long I reversed the MFC without noticing it's the MFC, again!!!).
With a bit of magic and a snap with your finger, you'll end up with the OnCreateButton_Click-handler being at 0x0085DE50. Swing your wand and smack your head on the keyboard to extract the pseudocode for this function:
Code:
void CPSCharacterSelect::OnCreateButton_Click()
{
if ( this->current_charcount < CHAR_MAX_COUNT )
{
this->is_char_view = 3;
this->TriggerCameraPathToIdolBox();
}
else
{
ShowError("UIO_MSG_ERROR_CHARACTER_OVER_3");
}
}
Do it!
So we're done now. We know where things happen. We know how things happen. So lets actually do it.
Lets wrap things up. We need to change the button handler to:
* Set the global g_selected_idol to CHINA (1)
* Set the state of CPSCharacterSelect to *leave for character creation* (9)
* Hope we did everything right.
Put all the ingredients in your bubbling cauldron, add a spoon of oldschool-player-tears and two cups of pulverized silkroad money. Give it a pretty good stirr. Okay. Done.
Code:
void CPSCharacterSelect::OnCreateButton_Click()
{
if ( this->current_charcount < CHAR_MAX_COUNT )
{
g_selected_idol = 1;
this->current_state = 9;
}
else
{
ShowError("UIO_MSG_ERROR_CHARACTER_OVER_3");
}
}
Code:
// g_selected idol is at 00EC2D64 mov dword ptr [00EC2D64], 1 // current_state is a member of the current object at offset 0xE4 // usually, this is referenced as ECX, but if the function gets bigger and has nested calls, ECX is transfered to ESI for simplicity reasons. mov dword ptr [esi+E4], 9
[Only registered and activated users can see links. Click Here To Register...]
We just have one problem: The instructions are too large :(. Both instructions are 10 Byte-instructions :/. We need to pad the epilogue.
Before:
[Only registered and activated users can see links. Click Here To Register...]
After:
[Only registered and activated users can see links. Click Here To Register...]
x64dbg Patch File, also appended as file.
Code:
>sro_client.exe 0045DEC4:C6->C7 0045DEC5:86->05 0045DEC6:2F->64 0045DEC7:01->2D 0045DEC8:00->EC 0045DECA:03->01 0045DECB:E8->00 0045DECC:D0->00 0045DECD:E5->00 0045DECE:FF->C7 0045DECF:FF->86 0045DED0:8B->E4 0045DED1:8C->00 0045DED2:24->00 0045DED3:04->00 0045DED4:01->09 0045DED7:5E->00 0045DED8:33->8B 0045DED9:CC->8C 0045DEDA:E8->24 0045DEDB:8E->04 0045DEDC:4D->01 0045DEDD:2E->00 0045DEDF:81->5E 0045DEE0:C4->31 0045DEE1:04->E1 0045DEE2:01->E8 0045DEE3:00->86 0045DEE4:00->4D 0045DEE5:C3->2E 0045DEE6:CC->00 0045DEE7:CC->81 0045DEE8:CC->C4 0045DEE9:CC->04 0045DEEA:CC->01 0045DEEB:CC->00 0045DEEC:CC->00 0045DEED:CC->C3
Note: All servers that appear in examples or images are for the sole purpose of testing. I'm not affiliated with any of these servers in any way.