Little Fighter Empire - Forums
OpenLF2 - Printable Version

+- Little Fighter Empire - Forums (https://lf-empire.de/forum)
+-- Forum: Little Fighter 2 Zone (https://lf-empire.de/forum/forumdisplay.php?fid=7)
+--- Forum: [2.0] Exe Editing (https://lf-empire.de/forum/forumdisplay.php?fid=43)
+--- Thread: OpenLF2 (/showthread.php?tid=9369)



OpenLF2 - o_g349 - 08-25-2014

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.


RE: OpenLF2 - A-Man - 08-25-2014

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.


RE: OpenLF2 - o_g349 - 08-25-2014

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


RE: OpenLF2 - A-Man - 08-25-2014

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!


RE: OpenLF2 - Boop - 08-25-2014

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;
}



RE: OpenLF2 - o_g349 - 08-26-2014

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;
}