- Joined
- May 26, 2007
- Messages
- 5,545
- Reaction score
- 1,315
[SIZE=+2]What is /reload= for?[/SIZE]
(good example of code bumming)
[size=+1]Contents[/size](good example of code bumming)
Introduction
Code Analysis
Code Bumming
[size=+1]Introduction[/size]Code Analysis
Code Bumming
I've checked the parameter passing game.exe /reload= and can pretty much say that the only valid parameter is "0".
(or "00", or any 16 characters or less at all that don't evaluate to a number higher or lower than 0, and has no "&" or "" characters)
If game.exe /reload=0 is run, .smds will not be loaded, thereby forcing the game to re-load every mesh from the original .ase files, and re-create new SMDs for everything.
Of course, for this to work, you need to have the original .ase files for every single asset the game might request.
Just thought this information might be useful for those who have been looking into why the client bothers to read parameters passed to it.
[size=+1]Code Analysis[/size]
Here is the code which is passed the command line arguments (called near sub-main, after the HanDes routine has had a go at them)
Code:
subCmdLineProcess:
mov eax,[arg1] ; ASCII "/reload=WhatEver"
push offset KPT1977NoXTrap2b.005C60C0 ; /Arg2 = ASCII "/reload="
push eax ; |Arg1 => [Arg1]
call subCmpStrStart ; \KPT1977NoXTrap2b.00413A40 add esp,8
test eax,eax
je .NotFound
xor ecx,ecx
lea esp,[arg.retaddr]
.Loop:
mov dl,[eax+ecx]
cmp dl,20
je .Str2Int
cmp dl,26
je .Str2Int
test dl,dl
je .Str2Int
.GetChar2:
mov dl,[eax+ecx+1]
cmp dl,' '
je .Str2Int
cmp dl,'&'
je short .Str2Int
test dl,dl
je short .Str2Int
.GetChar3:
mov dl,[eax+ecx+2]
cmp dl,' '
je short .Str2Int
cmp dl,'&'
je short .Str2Int
test dl,dl
je short .Str2Int
.GetChar4:
mov dl,[eax+ecx+3]
cmp dl,' '
je short .Str2Int
cmp dl,'&'
je short .Str2Int
test dl,dl
je short .Str2Int
.GetChar5:
mov dl,[eax+ecx+4]
cmp dl,' '
je short .Str2Int
cmp dl,'&'
je short .Str2Int
test dl,dl
je short .Str2Int
.GetChar6
mov dl,[eax+ecx+5]
cmp dl,' '
je short .Str2Int
cmp dl,'&'
je short .Str2Int
test dl,dl
je short .Str2Int
.GetChar7
mov dl,[eax+ecx+6]
cmp dl,' '
je short .Str2Int
cmp dl,'&'
je short .Str2Int
test dl,dl
je short .Str2Int
.GetChar8
mov dl,[eax+ecx+7]
cmp dl,' '
je short .Str2Int
cmp dl,'&'
je short .Str2Int
test dl,dl
je short .Str2Int
add ecx,8
cmp ecx,10
.While
jl .Loop
.Str2Int:
push eax ; /Arg1
call jmp_subAsc2Val ; \KPT1977NoXTrap2b.subAsc2Val
add esp,4
test eax,eax
jne short .NotFound
.ReloadEq0:
push 1
push eax
call subReloadEq0
add esp,8
.NotFound:
mov eax,1
ret
Two routines I've not shown off yet. subCmpStrStart (you'll find the offset in your client) this simply compares the all characters in one string with the first characters in the second.
So subCmpStrStart("Mine", "Mineute") == 1 but subCmpStrStar("Bow", "LongBow") == 0... get it?
Second one, I'll go into now. subReloadEq0 looks like this.
Code:
subReloadEq0
mov eax,[arg.1]
mov ecx,[arg.2]
mov [dwReloadNE0],eax
mov [dwReloadEq0],ecx
ret
Quite why it stores one word in memory which starts out 0, and becomes 1 if "/reload=0" is passed, and another that starts out 1 and becomes 0 if "/reload=0" is passed I couldn't say either.
If you compare them, if one is 0 the other should be 1... what ever else happens. If you search for references to either of these values, they lead to all places where .SMD files are loaded. Then, either via JE, or via JNE, or CMP against a register known to be 0 or known to be 1 they bypass the entire loading of the .SMD (which is usually preferred over .ASE) so that .ASE is forced to be loaded instead.
When this happens, loading of a .ASE (ASCII Shape Export) file always creates a new Binary cache .SMD. (Staged Model Data)
So assuming you had freshly updated a load of your .ASE files, and wanted to ensure that all the .SMDs where up-to-date with your changes, you could pass this parameter, and run around checking all the maps, monsters or armour which you just created, to ensure that the .SMD caches where updated... but if you have just one .ASE missing, you are heading for a crash. XD
[size=+1]Code Bumming[/size]
The first code routine is ridiculously complex for a simple job. This smaller code is functionally identical.
Code:
subCmdLineProcess:
mov eax,[arg1] ; ASCII "/reload=WhatEver"
push offset KPT1977NoXTrap2b.005C60C0 ; /Arg2 = ASCII "/reload="
push eax ; |Arg1 => [Arg1]
call subCmpStrStart ; \KPT1977NoXTrap2b.00413A40
add esp,8
test eax,eax
je .NotFound
xor ecx,ecx
lea esp,[arg.retaddr]
.Loop:
mov dl,[eax+ecx]
cmp dl,' '
je .Str2Int
cmp dl,'&'
je .Str2Int
test dl,dl
je .Str2Int
; Snip
add ecx,1
cmp ecx,10
.WhileECX<10
jl .Loop
.Str2Int:
push eax ; /Arg1
call jmp_subAsc2Val ; \KPT1977NoXTrap2b.subAsc2Val
add esp,4
test eax,eax
jne short .NotFound
.ReloadEq0:
push 1
push eax
call subReloadEq0
add esp,8
.NotFound:
mov eax,1
ret
Key points:-
- It is only called from .ReloadEq0: shown in the listings above. Nothing else in the game will ever use this code.
- Because we have already made absolutely certain that /reload=0 (or zero equivalent) we also know that EAX will be 0 when it is PUSHed in the calling routine above the last one shown.
- Because of point 2, we know that this routine doesn't need to take 2 arguments, and could simply substitute EAX for 0 and ECX for 1. (alone the 2 DWords instead of registers make that code bigger, but slightly faster.)
- It is cdecl, not stdcall, so the main calling routine needs to clear the two arguments PUSHed when it returns. If you don't pass those arguments, you don't need to SUB ESP,8 either.
- You could also not push EAX and 1, but simply MOV ECX,1 and CALL... then it wouldn't have to MOV Arg1, and Arg2 back into EAX and ECX making it a fastcall. (Again, no need to SUB ESP,8 on return) This would be very fast, not only because of the reduction in instructions, but because you have reduced 4 DWord calls to the address bus (slow, compared to register use) to 1.
- You could POP EAX and ECX to make it stdcall, and then dump the SUB ESP,8 too.
Last edited: