Jump to content

Programing for Sacred (or any other game)


Recommended Posts

A programing subforum and only one topic?

And that topic isnt about Sacred? (even if it's very interessting)


If there is any interest I could post some stuff about programing for Sacred, stuff that also could be applied to other games.


What do I mean by that and who am I?

I call myself Thorium on the internet and I am a hobby coder. Well I was, actualy I am a professional for 1,5 years now. However I dont work in the gaming industry.


I was one of the cofounders of a coding crew called SacredVault. As the name sugests we developed stuff for Sacred, Sacred Underworld to be precise.


So I am not a develeoper of the Sacred team and I dont want to write about how to develop games. I want to write about twiddleing with games as a coder. If you think about that the first thing that comes to mind are trainers and multiplayer cheat and cracks. Well SacredVault was not about that. It was about improving the game without making the original developers angry. ^^


So for the start I just want to post a source code which is open for quite some time now. Still you might not read it or even heard about it: The unofficial 2.29 patch for Sacred Underworld.

So I just post the source and write some background infos about it later. If there is any interest. No point in writing if nobody cares to read. ^^


Also I have more stuff to share. Do you know there is a interface in Sacred Underworld to read out character infos? The Sacred developers build it in for us.

I even have some cool stuff for Sacred 2.


The patch is a ASM hack, so the source is pretty much a list of changes done to the disassembled code of Sacred.exe.

I just translated the comments from german to english and found there is some code missing. And it's actualy a older source version. I cant find the "newest" build version, which would be 14. So there are actualy 2 bugs existing in build 12.

Who ever finds them gets a cookie. ^^


| Inofficiel Patch 2.29 Build 12 |
Stand: 29.01.2007 by Thorium (SacredVault)

* Change 1: *

* removal of loading of the binary ressource *
* and add loading of Global.res from HDD *

Starting at address 0080DC09 replace asm-code:


;preserve registers
push ecx
push ebx
push esi
push edi

mov edi,eax ;preserve pointer to Global.res path for later use
mov ebx,ecx ;preserve pointer to pointer to Global.res for later use

;install SEH
push -1
push 00889257
mov eax,dword ptr fs:[0]
push eax
mov dword ptr fs:[0],esp

;call CreateFile to open Global.res
push 0 ;hTemplateFile (nothing)
push 0 ;dwFlagsAndAttributes (nothing)
push 3 ;dwCreationDisposition (OPEN_EXISTING)
push 0 ;lpSecurityAttributes (nothing)
push 0 ;dwShareMode (no sharing of the file)
push 80000000 ;dwDesiredAccess (GENERIC_READ)
push edi ;lpFileName (pointer to Global.res path)
call [0088E244] ;Kernel32.CreateFile
mov ebp,eax ;preserve file handle

;check if file is open
cmp eax,-1
je 0080DC73 ;jump to error handling

;get size of Global.res
push 0 ;lpFileSizeHigh (nothing)
push ebp ;hFile (file handle)
call [0088E1D8] ;Kernel32.GetFileSize
mov esi,eax ;preserve file size

;check if file size could be read
cmp eax,-1
je 0080DC73 ;jump to error handling

;check if file is big enough (it's damaged if smaller)
cmp eax,4
jb 0080DC73 ;jump to error handling

;call Sacred-procedure to allocate a buffer for Global.res
push esi ;nSize (size of buffer)
call 008485E2 ;allocate buffer
mov [ebx],eax ;save buffer address to variable

;load Global.res into the buffer
push 0 ;lpOverlapped (nothing)
push edi ;lpNumberOfBytesRead (pointer to variable)
push esi ;nNumberOfBytesToRead (size of Global.res)
mov eax,[ebx] ;get memory address for Global.res into eax
push eax ;lpBuffer (address of the Global.res buffer)
push ebp ;hFile (file handle)
call [0088E1D4] ;Kernel32.ReadFile

;close Glboal.res file
push ebp ;hObject (file handle)
call [0088E24C] ;Kernel32.CloseHandle

;cleanup and return
mov al,1
mov ecx,[esp+4]
mov DWORD PTR FS:[0],ecx
pop ebp
pop edi
pop esi
pop ebx
add esp,10
ret 4

;error handling on address 0080DC73

;display MessageBox
push 009E1128 ;Title (Sacred) text is allready there
push 0095CFBC ;Text (Error: Can't load Global.res!) Text must be replaced on that address
push 0 ;hOwner
call [0088E34C] ;User32.MessageBoxA

;terminate Sacred
push 0 ;ExitCode
call [0088E0EC] ;Kernel32.ExitProcess

Now fill code with NOP's up to address 0080DCCB.
And we now can remove binary ressource 107 to save 2MB .exe size.

* Change 2: *
* Remove cheat codes secure and clean *
* to get space for new code we want to write *

Starting at address 0061561B replace asm-code:

Assures the cheat code will never be jumped to.


Fill address space starting at 006156B8 up to 00615FA7 with NOP's.
This address space can now be used for our own code.

* Change 3: *
* Multi-Instancing *
Sorry, somehow this code got lost. Dont ask me how.

It wasnt very interesting anyway. Just changed a conditional jump on the check of a global mutex.

* Change 4: *
* Chat-Crash fix *

Starting at address 0084AE81 replace asm-code:

jmp 006156DE ;jump to error checking

Starting at address 006156DE replace asm-code:

;Instructions must be executed here, because we overwrote them in the original procedure.
je 0084AEAB
mov edx,dword ptr ss:[esp+10] ;get pointer to the data into edx

;preserve registers
push ecx
push edx
push ebx
push ebp
push esi
push edi

;call IsBadReadPtr to check if memory region can be read
push ecx ;DataSize
push edx ;DataAddress
call [0088E240] ;Kernel32.IsBadReadPtr

;restore registers
pop edi
pop esi
pop ebp
pop ebx
pop edx
pop ecx

;check if read is possible
cmp eax,0
je 0084AE87 ;yes, return to original procedure

;no, output debug message to Sacreds log file
push 0095CFF4 ;Msg (Sacred.exe Crash fixed!)
call 0066F1C0 ;Sacred-Procedure: Debugoutput
add esp,4 ;cleanup stack

;return to end of original procedure
jmp 0084AEAB

Starting at address 0095CFF4 replace text:

Sacred.exe Crash fixed!

And replace hex numbers:

0A 00 00 00

* Change 5: *
* change displayed version number *

First of all change version number in ressources.
Now the version system is: Major.Minor.Build
That means for this version:

Now change version number on main menu screen:
starting at address 00758ED0 replace asm-code:

push 20
push 0095D014

Now write new version number as zero terminated string to address 0095D014.
With that change Sacred will display a different version number than the internal version number. This makes it possible to still connect to the internet lobby and display a new version number.

* Change 6: *
* Debuggerfreez-Fix *

Fix for freez if Sacred is started by a debugger.

Starting at address 00810B0F replace asm-code:
;remove forcing of window focus

* change 7: *
* load Balance.bin by using hardcodes path *
* implemented for VeteranMod, but never used *

Starting at address 00856A8A replace asm-code:

jmp 00615717

Starting at address 00615717 replace asm-code:

;preserve registers
push ecx
push esi
push edi

;check if balance.bin is about to get loaded
mov esi,dword ptr ss:[esp+10] ;get address of file path from stack
mov edi,0094C4D4 ;address of compare string
mov ecx,4
repe cmpsd
jne 00615736

;force use of hardcoded file path
mov ecx,009E1AFC
mov dword ptr ss:[esp+10],ecx

;restore registers
pop edi
pop esi
pop ecx

;instructions must be executed here, because we overwrote them in the original procedure
push ebp
mov ebp,esp
sub esp,1C

;return to original procedure
jmp 00856A90

Starting at address 0094C4D4 replace text:


Edited by Thorium
  • Like! 1
Link to comment

By any chance, do you know of a Sound.pak Unpacker/Repacker?

I can unpack the data, but have no way to put it back into Sound.pak


The only person I know of who could have a packer would be SonicMouse. Maybe you can find him. After Sacred I think he wend on hacking MapleStory. I know he did wrote unpacker for most of the .pak's. Also he wrote tools he didnt released.


If you can code here are some infos: http://wiki.xentax.com/index.php?title=Sacred#Format_Specifications_2

If the files are compressed, it should be ZLib.

Link to comment

Let's talk about how to make a unofficial patch.


First of all there are 3 different ways to alter the game code:

1.) You edit game scripts. I dont talk about that here.

2.) You hook procedures of API's to manipulate parameters and return values. This is actualy very powerfull and often used by unofficial patches.

3.) You can change the actual executable code of the game to do whatever you want. I want to focus on that in this post. I will not go terribly deep into it, just give you some directions so you get some ideas.


First of all, if you dont know assembler language: learn it!

You might have heard that assembler is very complicated and hard to learn. This is totaly false!

In fact assembler is very simple and very easy to learn. We dont use high level languages because assembler is so complicated. We use them because coding in assembler is a lot more work and your code is platform dependent.

There are many tutorials about assembler. Go and learn it!


In case you dont know what assembler language is:

It's a way of programming on which you directly program the CPU. Every command you write is one command the CPU understands and executes without the need for a compiler, which breaks commands down into smaler commands the CPU understands. That CPU commands are called: Instructions. So you work at the lowest possible level. There is only one level lower: machine code. But assembler code is just a human readable representation of machine code, so I dont realy count machine code as lower.


Every program is made of machine code (with some exceptions like Java). Typicly programs are written in high level languages and are translated into machine code by a program called: compiler.

Basicly the compiler takes a high level command (such as a command to output text to a window) and breaks it down into machine code instructions, so the CPU can actualy execute the program.


This is important to understand because you can actualy look at the machine code of a compiled program, such as Sacred.exe. And even better you can translate it into a human readable form (assembler language) with a so called disassembler.


There is also decompiling, which is the process of translating machine code back to high level language code. But better forget about it for now. There is no automatic working decompiler. The only decompilers that are working are interactive and using them requires quite some knowlege. Which you dont have right now, or you woudnt read this. ^^


However it's actualy pretty easy to translate machine code into assembler code with a disassembler.


There is just one tiny little problem: Disassembled assembler code of a game such as Sacred is huge. And I mean realy huge, literally millions of instructions. And no meaningfull names for variables and procedures, just memory addresses.


So your efforts to make a unofficial patch do not start at disassembling the .exe of the game.


First you need to know about the bugs you want to fix or about the features you want to add. Take some time and think about if and how this could work. What do you need to change to get the result you want. This is the most important step. Because it will make the next step a lot easier and the next step is the hardest.


Now, you know what you want to change. You have some ideas how this could work. Now you need to find the code you need to change. And thats the challenge. Once you found the code that relates to a bug it's easy to change the code or add some new code.


The process of finding the code you want to change is called reverse engineering.

Reverse engineering is the art of taking something apart to learn how it works. And thats what we do with the game we want to patch. However we dont start at the beginning of the disassembly and read all the millions instructions and try to understand the whole program. That would be way to much work. Instead we try to get as near to the code we search as possible befor starting to read the actual assembler code.


Again that starts with thinking about the code you want to find. Think about how the code could work. What API procedures it could use. Does it access a file? Does it display something on the screen?

Lets say it accesses a file. So you fire up your assembler level debugger. What? You dont have a assembler level debugger? Get one! I use OllyDbg.

A debugger is basicly a tool that helps you find bugs in your software. A assembler level debugger helps you find bugs in any software but only operates on the assembler level.


So you fire up your debugger and take a look at the imported WinAPI functions. You will find a function called CreateFile, thats a function for accessing files, not just for creating them. So you tell your debugger to stopp the program every time this procedure is been called. Thats called a break point. Start the program and as soon as the program accesses a file the debugger will show you the code that does access the file. Just skip until your desired file is accessed and you should be pretty near to the code you search.


So thats just to give you some ideas and some keywords to google about.


Changing the code is pretty easy in most cases. Finding it can be very challenging and there are a number of tools you can use for this, like Debuggers and memory scanners. But it still is the hardest part and it requires the most time.


You might end up searching 10 hours for a one byte change. So making a unofficial patch is more about reverse engineering than it is about writing code.


I hope that makes any sense to you, I will write more later.

Edited by Thorium
Link to comment

That was a great read! Didn't understand it but still. I would like to say that if you make your own tools or whatever you should share them, people go poof and that kills off projects because they are the only owners of the tool that does whatever it does.

Link to comment

I would like to say that if you make your own tools or whatever you should share them, people go poof and that kills off projects because they are the only owners of the tool that does whatever it does.

Yes, we should do this.

I do it. As SacredVault stopped, we released a lot of stuff that was not public befor. Mostly because it was unfinished other stuff because it could be used for cheating. For example our member Ufo wrote a excelent character editor for our testing. Which he did release after SacredVault went inactive. He is also the one that keeps the SacredVault website online, so there still is a source for our stuff.


I did release unfinished modding tools we used to create the VeteranMod. There is a creature.pak editor, a balance.bin editor and a weapon.pak editor in addition text files with format specification and constant definitions. The creature.pak editor is the most complete and allows you to edit 70% of the values of every creature in Sacred Underworld. However this is all german only.


I also released the source code for the inofficial patch 2.29 and the source code of the unfinished 2.30 patch.

Another interessting source code is the Cheaters's Nightmare source code. A program that protects your Sacred Underworld Server by scanning for cheated items and allowing you to keep a blacklist of users with autokick function.


This is all released for educational purposes and to inspire other people for new projects.

As a coder you do take a lot of informations and even code from other people, so yes, you should give back.

Edited by Thorium
Link to comment
  • 2 weeks later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Create New...
Please Sign In or Sign Up