Jump to content

Lineage 2 GF AI Decompiler


Recommended Posts

1 minute ago, verbrannt said:

It's already reverted.

 

Ah, ok :) How about boss_id - that shouldn't be global map ID or should it be?

Edited by eressea
Link to comment
Share on other sites

11 minutes ago, eressea said:

 

Ah, ok :) How about boss_id - that shouldn't be global map ID or should it be?

Idk. I have only one occurrence of this variable in my ai.obj files, so can't tell.

 

Also I think boss_pch.txt should be renamed to gm_pch.txt. "Boss" prefix is misleading here.

Edited by verbrannt
Link to comment
Share on other sites

5 hours ago, verbrannt said:

Idk. I have only one occurrence of this variable in my ai.obj files, so can't tell.

 

Something tells me it should be int - objectID/index of NPC who is boss for the group, but I'm really not sure about that

 

5 hours ago, verbrannt said:

Also I think boss_pch.txt should be renamed to gm_pch.txt. "Boss" prefix is misleading here.

 

+1 maybe globalmap_pch.txt... OR maybe parse all of them from manual_pch.txt - that would be the best way

Link to comment
Share on other sites

Comments inside ai.obj like
 // -- test comment -- 

ect, causes the decompiler to throw errors like "Decompile default_npcPHP Fatal error:  Uncaught TypeError: Argument 1 passed to Parser::fixTypeCase() must be of the type string, null given, "

but if the comments are removed, it decompiles fine. If at all possible can support for these types of comments be added?, l2npc handles them fine

Link to comment
Share on other sites

After adding C1 support, I've compared all ai.obj from original one, and after decompilation/compilation. There is bunch of classes (about 15 of them of 2200) with 2 significant differences.

 

Here is one example of 1 style of difference. Could you check, what's the problem here?

https://drive.google.com/open?id=1WZx4FGF_FrkAOkem3bC8Stdd3Y--ixUj

 

Most probably the same thing is in other chronicles, since looks like it's related with some nested if() conditions

Link to comment
Share on other sites

7 minutes ago, MasterToma said:

After adding C1 support, I've compared all ai.obj from original one, and after decompilation/compilation. There is bunch of classes (about 15 of them of 2200) with 2 significant differences.

 

Here is one example of 1 style of difference. Could you check, what's the problem here?

https://drive.google.com/open?id=1WZx4FGF_FrkAOkem3bC8Stdd3Y--ixUj

 

Most probably the same thing is in other chronicles, since looks like it's related with some nested if() conditions

Looks like nothing serious, only one missing shift stack pointer command :D
I think is always at the end of the handlers?
Can you post second type of difference?

Link to comment
Share on other sites

	push_reg_sp
	fetch_i

is missed in files from the link, which I posted. Also two jumps at the end

    jump L23975
    jump L23982
L23977
L23982
L23975
    shift_sp -1

 

I think, there is no "no op" operands, and those lines are simply missed from the decompiled/compiled code. Doesn't look good for me. Also, shift_sp -1 is called before exit_handler(). So, caller will receive not-shifted stack

 

I will post second difference, when find some smaller handler for it, right now there are about 30kb obj files

Edited by MasterToma
Link to comment
Share on other sites

2 hours ago, MasterToma said:

	push_reg_sp
	fetch_i

is missed in files from the link, which I posted. Also two jumps at the end


    jump L23975
    jump L23982
L23977
L23982
L23975
    shift_sp -1

 

I think, there is no "no op" operands, and those lines are simply missed from the decompiled/compiled code. Doesn't look good for me. Also, shift_sp -1 is called before exit_handler(). So, caller will receive not-shifted stack

 

I will post second difference, when find some smaller handler for it, right now there are about 30kb obj files

 

I think last “if” in this class should be “select” with one case inside. Jumps looks like “break” inside that case.

I can’t test it today tho.

Link to comment
Share on other sites

1 hour ago, jornik said:

@MasterToma can you please post whole hander code for the original and compiled ai.obj? Can you also share decompiled code?

Everything is inside archive under the link, which I posted. Here it is again: 

https://drive.google.com/file/d/1WZx4FGF_FrkAOkem3bC8Stdd3Y--ixUj/view

 

Decompiled code looks is

class guard_babenco : guard_fixed {
handler:
	EventHandler TALK_SELECTED(talker) {
		if (_from_choice == 0) {
			if (HaveMemo(talker, @path_to_elven_scout) == 1 && GetMemoState(talker, @path_to_elven_scout) == 1) {
				_choiceN = _choiceN + 1;
				_code = 0;
				AddChoice(0, "询问有关废墟的事");
			}

			if (_choiceN > 1) {
				ShowChoicePage(talker, 1);
				return;
			}
		}

		if (_from_choice || _choiceN == 1) {
			if (_code == 0) {
				if (_from_choice == 0 || HaveMemo(talker, @path_to_elven_scout) == 1 && GetMemoState(talker, @path_to_elven_scout) == 1) {
					ShowPage(talker, "guard_babenco_q0407_01.htm");
				}
			}

			return;
		}

		super;
	}
}

There is some mess with _from_choice. Tonight I will try to decompile it manually. 

 

Link to comment
Share on other sites

1 hour ago, MasterToma said:

There is some mess with _from_choice. Tonight I will try to decompile it manually.

 

Replace

if (_code == 0) {
    if (_from_choice == 0 || HaveMemo(talker, @path_to_elven_scout) == 1 && GetMemoState(talker, @path_to_elven_scout) == 1) {
        ShowPage(talker, "guard_babenco_q0407_01.htm");
    }
}

with

select (_code) {
    case 0:
        if (_from_choice == 0 || HaveMemo(talker, @path_to_elven_scout) == 1 && GetMemoState(talker, @path_to_elven_scout) == 1) {
            ShowPage(talker, "guard_babenco_q0407_01.htm");
        }
        
        break;
}

& check compiled code

Link to comment
Share on other sites

Ok, now it works. Another bug with operator precedence. For instance, look at blacksmith_duning (code is too large).

Here is how it should look like:

	fetch_i
	push_const 10
	mod
	mul
	greater_equal
			else if(reply == 6)
				{
					if (OwnItemCount(talker, @q_gold_wyvern) >= (4*(talker.param2 % 10)))
					{
						DeleteItem1(talker, @q_gold_wyvern, 4*(talker.param2 % 10));
						talker.param1 = talker.param1+((Rand(3)+1)*16);
					}
					else
						ShowPage(talker, "blacksmith_duning_q0336_10.htm");	
				}

Note about two points

1. (4*(talker.param2 % 10))

2. ((Rand(3)+1)*4)

 

Decompiler generates 

 

} else if (reply == 6) {
				if (OwnItemCount(talker, @q_gold_wyvern) >= 4 * talker.param2 % 10) {
					if (GetCurrentTick() - talker.quest_last_reward_time > 1) {
						DeleteItem1(talker, @q_gold_wyvern, 4 * talker.param2 % 10);
						talker.param1 = talker.param1 + (Rand(3) + 1) * 4;
						talker.param1 = talker.param1 + (Rand(3) + 1) * 16;
					}
				} else {
					ShowPage(talker, "blacksmith_duning_q0336_10.htm");
				}

1. 4 * talker.param2 % 10

2. (Rand(3) + 1) * 4

 

While point #2 in THIS case is not crucial (but in other cases it might be crucial), point #1 is for sure wrongly decompiled - extra () are missed and cause wrong calculation, which will be

 

	fetch_i
	mul
	push_const 10
	mod
	greater_equal

 

Link to comment
Share on other sites

I've noticed the same with nested &&. Original pseudo-code

if (a && (c && d))

after decompilation will be 

if (a && c && d)

Which is OK. But some times not:

if (a && (c || d))

goes into

if (a && c || d)

 

Trying to find some nice example...

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.




×
×
  • Create New...