Thread Rating:
  • 1 Vote(s) - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
OpenLF2
#1
OpenLF2

An open source clone of Little Fighter 2 built by decompiling the original game one bit at a time.

1. 1 Introduction

LF2 was written by Visual C++, and compiled to assembly code.
We want to decompile assembly into C which allowed for the addition of thousands of features to the game.

1.2 Decompiling the game

We use patched lf2.exe to load openlf2.dll. When openlf2.dll is loaded, the patched lf2.exe will call certain procedure in openlf2.dll. This procedure changes origin function call in lf2.exe.
When patched lf2.exe runs, it will jump new procedure in openlf2.dll instead of original one in patched lf2.exe.

1.3 Progress

All decompiled functions are here: [documented] Functions decompiled.
Once any function are decompiled and documented in that project, we will implement it in this project.
Eg. func_403270_teleport

2.1 Getted starting

Project home page

Linux:
Code:
$ sudo apt-get install cmake gcc-mingw-w64-i686 binutils-mingw-w64-i686
$ cmake .
$ make

Please copy openlf2.dll and openlf2.exe to directory contains normal lf2.exe, and run openlf2.exe.
Decompiled functions: [documented] Functions decompiled
Decompile lf2.exe project for documentation: https://github.com/xsoameix/lf2
Decompile lf2.exe project for implementation: https://github.com/xsoameix/openlf2
Once any function fully engineer reversed in documentation project, then we implement it in implementation project.

lf2 data structure: Updated spreadsheet, many changes made by o_g349/xsoameix, I have two different nick names.

A old project: lf2-MS
Reply
Thanks given by: A-Man , mfc , MangaD
#2
I want to help. I never really reverse engineered anything like that, and that is why I so wanna try it. Can you assign to me one function I can start with? Something simple for a start please :P.
[Image: signature.png]
A-Engine: A new beat em up game engine inspired by LF2. Coming soon

A-Engine Dev Blog - Update #8: Timeout

Reply
Thanks given by:
#3
Try this one: func_417170/random, this function is decompiled and documented here: [documented] Functions decompiled
You can take this function I has already implemented as a example: func_403270_teleport

If you want to decompile undecompiled function, please fork this project and create a file named func_xxxxxx_something.rb and put your decompiled code with corresponding assembly code in it.
Like this example: func_417170_random.rb
Decompiled functions: [documented] Functions decompiled
Decompile lf2.exe project for documentation: https://github.com/xsoameix/lf2
Decompile lf2.exe project for implementation: https://github.com/xsoameix/openlf2
Once any function fully engineer reversed in documentation project, then we implement it in implementation project.

lf2 data structure: Updated spreadsheet, many changes made by o_g349/xsoameix, I have two different nick names.

A old project: lf2-MS
Reply
Thanks given by: A-Man
#4
Alright. I didn't look at your random function decompilation yet, but here is my attempt:
http://pastebin.com/d1wmRKai

I am pretty sure I screwed up a lot there :P Anyway, there were few stuff I didn't understand:
1-There were local variables, but there was no "sub esp" that allocates memory for them in the stack? Why?

2-This one:
    ASM-Code:
MOVZX EAX, byte ptr ds:[EDX+0x44FF90]

Just what is happening there? Thought since the address was added, we're dealing with an array. But the outputs from that result address is really random 0_o. What's going on? And what is the difference between MOVZX and MOV?

3-
    ASM-Code:
TEST ESI, ESI
JG XXXX

I read that test does an "AND" logical operation? Of 2 integers? Won't that always return true if none of the operands is a 0? And then the code is checking if the left operand was greater?? And it actually jumps most of the time when both are ESIs !!?!?

Sorry for all these question >.<. Would really appreciate if you would point out any stuff I did wrong in my code.

Thanks in advance!
[Image: signature.png]
A-Engine: A new beat em up game engine inspired by LF2. Coming soon

A-Engine Dev Blog - Update #8: Timeout

Reply
Thanks given by:
#5
1) Everything that gets pushed onto the stack gets popped off, so the stack is unchanged at the end of the function, so everything is fine. Explicitly manipulating esp isn't really done now days by modern compilers unless compiling in debug mode.

2)MOVZX is like a normal MOV except it zero extends so that the value is the correct size.

I mean EAX is a DWORD, not a byte. So if you read a byte, what should you do with the 3 other bytes? Well ZX = zero extend, which means fill them with 0's.

3) That checks if ESI is greater than 0. TEST sets a status flag, JG reads it.

Also this:

Code:
uint32_t _450C34=0;
    uint32_t _450BBC=0;

Is wrong. Those are memory locations. You need to read them.

It's pretty easy to test. If you add your code to the openlf2 project, compile it, it wont work. Although I guess you already knew it was broken, but for future reference :3.

Proper (undocumented)implementation here:

Code:
int random(int unused, int max){//random function that works with replays
    int result; // eax@2
    signed int v3; // eax@3

    DWORD &dword_450C34 = *((DWORD *)0x450C34);
    DWORD &dword_450BCC = *((DWORD *)0x450BCC);

    if ( max > 0 ){
    dword_450C34 = (dword_450C34 + 1) % 1234;
    v3 = dword_450C34 + (unsigned __int8)*(((BYTE *)0x44FF90) + (dword_450BCC + 1) % 3000);
    dword_450BCC = (dword_450BCC + 1) % 3000;
    result = v3 % max;
    }else{
        result = 0;
    }

    return result;
}
[Image: doty7Xn.gif]

10 ʏᴇᴀʀs sɪɴᴄᴇ ɪʀᴄ ɢᴏᴏᴅ.ɪ ᴡᴀʟᴋ ᴛʜʀᴏᴜɢʜ ᴛʜᴇ ᴇᴍᴘᴛʏ sᴛʀᴇᴇᴛs ᴛʀʏɪɴɢ ᴛᴏ ᴛʜɪɴᴋ ᴏғ sᴏᴍᴇᴛʜɪɴɢ ᴇʟsᴇ ʙᴜᴛ ᴍʏ ᴘᴀᴛʜ ᴀʟᴡᴀʏs ʟᴇᴀᴅs ᴛᴏ ᴛʜᴇ ɪʀᴄ. ɪ sᴛᴀʀᴇ ᴀᴛ ᴛʜᴇ sᴄʀᴇᴇɴ ғᴏʀ ʜᴏᴜʀs ᴀɴᴅ ᴛʀʏ ᴛᴏ sᴜᴍᴍᴏɴ ᴛʜᴇ ɢᴏᴏᴅ ɪʀᴄ. ɪ ᴡᴀᴛᴄʜ ᴏᴛʜᴇʀ ɪʀᴄ ᴄʜᴀɴɴᴇʟs ʙᴜᴛ ɪᴛ ɪs ɴᴏ ɢᴏᴏᴅ. ɪ ᴘᴇsᴛᴇʀ ᴢᴏʀᴛ ᴀɴᴅ ᴛʀʏ ᴛᴏ ʀᴇsɪsᴛ ʜɪs sᴇxɪɴᴇss ʙᴜᴛ ɪᴛ ɪs ᴀʟʟ ᴍᴇᴀɴɪɴɢʟᴇss. ᴛʜᴇ ᴇɴᴅ ɪs ɴᴇᴀʀ.ɪ ᴛʜᴇɴ ᴜsᴜᴀʟʟʏ ʀᴇᴀᴅ sᴏᴍᴇ ᴏʟᴅ ɪʀᴄ ʟᴏɢs ᴀɴᴅ ᴄʀʏ ᴍʏsᴇʟғ ᴛᴏ sʟᴇᴇᴘ.


Reply
Thanks given by: mfc , A-Man
#6
1) Thanks for sliva reply :)

2) byte ptr ds: means _44FF90 is byte array, we get a byte from this array.
Then we need to zero extend 1 byte to 4 bytes and we can store these 4 bytes to eax.
    C-Code:
uint8_t * _44FF90 = (void *) 0x0044FF90;
_44FF90[next_j]


3) You can look this reference.
jg holds when zero flag = 0 and sign flag = overflow flag.
When esi > 0, ZF = 0, SF = 0, OF = 0, so ZF = 0 and SF = OF is true.
When esi = 0, ZF = 1, SF = 0, OF = 0, so ZF = 0 and SF = OF is false.
When esi < 0, ZF = 0, SF = 1, OF = 0, so ZF = 0 and SF = OF is false.
So the code is:
    C-Code:
if (range <= 0) {
    return 0;
}


The random function is documented:
    C-Code:
# 0x00417170
def func_417170_random(unused, range)
  if range <= 0
    return 0
  end
  # 0041717D
  next_i = (* _450C34 + 1) % 1234
  next_j = (* _450BCC + 1) % 3000
  * _450C34 = next_i
  * _450BCC = next_j
  (next_i + _44FF90[next_j]) % range
end


And this one is implementation, I haved compiled and ran this code, no bug :)
    C-Code:
uint32_t
func_417170_random(uint32_t unused, uint32_t range) {
    if (range <= 0) {
        return 0;
    }
    uint32_t * _450C34 = (void *) 0x00450C34;
    uint32_t * _450BCC = (void *) 0x00450BCC;
    uint32_t next_i = (* _450C34 + 1) % 1234;
    uint32_t next_j = (* _450BCC + 1) % 3000;
    * _450C34 = next_i;
    * _450BCC = next_j;
    uint8_t * _44FF90 = (void *) 0x0044FF90;
    return (next_i + _44FF90[next_j]) % range;
}
Decompiled functions: [documented] Functions decompiled
Decompile lf2.exe project for documentation: https://github.com/xsoameix/lf2
Decompile lf2.exe project for implementation: https://github.com/xsoameix/openlf2
Once any function fully engineer reversed in documentation project, then we implement it in implementation project.

lf2 data structure: Updated spreadsheet, many changes made by o_g349/xsoameix, I have two different nick names.

A old project: lf2-MS
Reply
Thanks given by: A-Man




Users browsing this thread: 1 Guest(s)