If you want to quickly
solve a quest, here's how to do it:
1. Make sure the one quest is the only active one.
2. Move over to a place with not that many people around, just to rule out any other "intersections" of code being called-in by other entities (e.g.: fights, purchasing, etc.) - - anything that triggers accessing of an
Inventory container - - head over to Andros Forge, for example; it's quiet there).
3. Break here:
Code: Select all
ACOdyssey.exe+2AD7430 - 40 53 - push rbx <--
ACOdyssey.exe+2AD7432 - 48 83 EC 20 - sub rsp,20 { 32 }
ACOdyssey.exe+2AD7436 - 48 8B D9 - mov rbx,rcx
ACOdyssey.exe+2AD7439 - 48 8B CA - mov rcx,rdx
ACOdyssey.exe+2AD743C - E8 7F63ACFE - call ACOdyssey.exe+159D7C0
ACOdyssey.exe+2AD7441 - 48 8B 88 90000000 - mov rcx,[rax+00000090]
ACOdyssey.exe+2AD7448 - 8B 83 00030000 - mov eax,[rbx+00000300]
ACOdyssey.exe+2AD744E - 48 81 C1 A0010000 - add rcx,000001A0 { 416 }
ACOdyssey.exe+2AD7455 - 85 C0 - test eax,eax
ACOdyssey.exe+2AD7457 - 75 0D - jne ACOdyssey.exe+2AD7466
ACOdyssey.exe+2AD7459 - 48 8B D1 - mov rdx,rcx
ACOdyssey.exe+2AD745C - 48 8B CB - mov rcx,rbx
ACOdyssey.exe+2AD745F - E8 4C87FDFF - call ACOdyssey.exe+2AAFBB0
ACOdyssey.exe+2AD7464 - EB 33 - jmp ACOdyssey.exe+2AD7499
ACOdyssey.exe+2AD7466 - 83 F8 01 - cmp eax,01 { 1 }
ACOdyssey.exe+2AD7469 - 75 2C - jne ACOdyssey.exe+2AD7497
ACOdyssey.exe+2AD746B - E8 90B290FE - call ACOdyssey.exe+13E2700
ACOdyssey.exe+2AD7470 - 48 89 44 24 40 - mov [rsp+40],rax
ACOdyssey.exe+2AD7475 - 48 8D 93 98020000 - lea rdx,[rbx+00000298]
ACOdyssey.exe+2AD747C - 33 C0 - xor eax,eax
ACOdyssey.exe+2AD747E - 4C 8D 44 24 30 - lea r8,[rsp+30]
ACOdyssey.exe+2AD7483 - 48 8D 4C 24 40 - lea rcx,[rsp+40]
ACOdyssey.exe+2AD7488 - 89 44 24 30 - mov [rsp+30],eax
ACOdyssey.exe+2AD748C - E8 DF6076FF - call ACOdyssey.exe+223D570
ACOdyssey.exe+2AD7491 - 8B 44 24 30 - mov eax,[rsp+30]
ACOdyssey.exe+2AD7495 - EB 02 - jmp ACOdyssey.exe+2AD7499
ACOdyssey.exe+2AD7497 - 33 C0 - xor eax,eax
ACOdyssey.exe+2AD7499 - 39 83 D8030000 - cmp [rbx+000003D8],eax
ACOdyssey.exe+2AD749F - 74 13 - je ACOdyssey.exe+2AD74B4
ACOdyssey.exe+2AD74A1 - 48 8B CB - mov rcx,rbx
ACOdyssey.exe+2AD74A4 - 89 83 D8030000 - mov [rbx+000003D8],eax
ACOdyssey.exe+2AD74AA - 48 83 C4 20 - add rsp,20 { 32 }
ACOdyssey.exe+2AD74AE - 5B - pop rbx
ACOdyssey.exe+2AD74AF - E9 3C58F3FE - jmp ACOdyssey.exe+1A0CCF0
ACOdyssey.exe+2AD74B4 - 48 83 C4 20 - add rsp,20 { 32 }
ACOdyssey.exe+2AD74B8 - 5B - pop rbx
ACOdyssey.exe+2AD74B9 - C3 - ret
4. Note the RCX pointer on break. You may query it with this Lua script (Ctrl+L; paste script; adjust GetName( ... )):
Code: Select all
function _readInteger( Input )
-- thanks, Pox!
local Value = readInteger( Input )
if Value < 0x80000000 then return Value
else return Value - 0x100000000 end
end
function GetName( input )
local addr = readQword( input )
addr = addr + 0x38 -- GetName; 0x30 for ACS; 0x38 for ACO
addr = readQword( addr )
if readBytes( addr, 1 ) == 0xE9 then
addr = addr + _readInteger( addr + 0x1 ) + 0x5
end
addr = addr + _readInteger( addr + 0x3 ) + 0x7
addr = readQword( addr )
print( string.format( "IStruct: 0x%X", input ) )
print( string.format( "IName: 0x%X", addr ) )
local str = readString( readQword( addr + 0x18 ) )
print( string.format( "ObjStr: %s", str ) )
addr = readInteger( addr + 0x24 )
print( string.format( "ObjHash: 0x%X", addr ) )
end
GetName( 0xput_pointer_here )
So:
Code: Select all
IStruct: 0x946A86CF0
IName: 0x1451157A0
ObjStr: QuestTransactionObjectiveData
ObjHash: 0x107F0B9D
5. Past the CALL, stop at "ACOdyssey.exe+2AD744E - 48 81 C1 A0010000 - add rcx,000001A0". Query RCX once more:
Code: Select all
IStruct: 0x91CEF7990
IName: 0x144C3FF40
ObjStr: Entity
ObjHash: 0x984415E
So what get here is Entity+0x1A0.
6. Then this happens:
Code: Select all
ACOdyssey.exe+2AD7459 - 48 8B D1 - mov rdx,rcx
ACOdyssey.exe+2AD745C - 48 8B CB - mov rcx,rbx
ACOdyssey.exe+2AD745F - E8 4C87FDFF - call ACOdyssey.exe+2AAFBB0
If you F8 over the CALL, you'll notice RAX now contains the amount of resource the objective asks for
In my case, I am trying this with
Upgrade the Spear quest, while in the Forge on Andros. So my RAX is
8, which is the amount of Animus Fragments I have in my inventory
Ok, ok. All nice and dandy. That is the amount. But how do we get to the actual item pointer? And its amount?
7. Let CE run and it will break again. This time around continue inside the CALL @ ACOdyssey.exe+2AD745F. And we get here:
Code: Select all
ACOdyssey.exe+2AAFC00 - 48 8B 86 88020000 - mov rax,[rsi+00000288]
What this does is to read the pointer to InventoryItemSettings for the quest item. In my case, the pointer being read is 0x71FB2488 (of course yours will be different). If we take a look at its memory we see this:
Well, that is the hash for
Animus Fragments (0x0000015BD3E6413B). Let's continue.
8. In here the engine compares the pointer to InventoryItemSettings till it matches the one the quest item has:
Code: Select all
ACOdyssey.exe+2AAFC40 - 48 8B 08 - mov rcx,[rax]
ACOdyssey.exe+2AAFC43 - 48 39 8E 88020000 - cmp [rsi+00000288],rcx
ACOdyssey.exe+2AAFC4A - 0F84 A2000000 - je ACOdyssey.exe+2AAFCF2
ACOdyssey.exe+2AAFC50 - 48 83 C3 10 - add rbx,10
ACOdyssey.exe+2AAFC54 - 48 3B DF - cmp rbx,rdi
ACOdyssey.exe+2AAFC57 - 75 DF - jne ACOdyssey.exe+2AAFC38
Once found, it exits the loop via the JE. And lands here:
Code: Select all
ACOdyssey.exe+2AAFCF2 - 33 F6 - xor esi,esi
ACOdyssey.exe+2AAFCF4 - 4C 8D 44 24 20 - lea r8,[rsp+20]
ACOdyssey.exe+2AAFCF9 - 48 8B D3 - mov rdx,rbx
ACOdyssey.exe+2AAFCFC - 89 74 24 20 - mov [rsp+20],esi
ACOdyssey.exe+2AAFD00 - 48 8D 4C 24 28 - lea rcx,[rsp+28]
ACOdyssey.exe+2AAFD05 - E8 66D978FF - call ACOdyssey.exe+223D670
At this CALL you have the RCX == CharacterAI (query it with the Lua script). Enter the CALL and execute the function (its gets Inventory) till this location "ACOdyssey.exe+223D6C9 - 48 FF 60 78 - jmp qword ptr [rax+78]". Then follow the JMP.
9. Stop here with the tracing:
Code: Select all
ACOdyssey.exe+22437F0 - 40 53 - push rbx
ACOdyssey.exe+22437F2 - 48 83 EC 20 - sub rsp,20
ACOdyssey.exe+22437F6 - 48 83 C1 B0 - add rcx,-50
ACOdyssey.exe+22437FA - 49 8B D8 - mov rbx,r8 <-- stop here
Then query RCX with the Lua script:
Code: Select all
IStruct: 0x91D5BC448
IName: 0x144FC2830
ObjStr: Inventory
ObjHash: 0x33DCC895
10. Now, the next CALL is a function we've all found and used; the one that is also in the Reader script above this post
What the function does is to read-up the amount of the item being fed to it. So, go inside the CALL and trace code all the way down to here (or just break directly, if you want):
Code: Select all
ACOdyssey.exe+2452D28 - 8B 38 - mov edi,[rax]
ACOdyssey.exe+2452D2A - EB 02 - jmp ACOdyssey.exe+2452D2E
In my case, I got this:
There you have it -> RAX holds the pointer to the item amount. Add that address to your list or edit it in memory and you get this:
Done on UPlay 1.0.3 version of the game. The Steam binary may prove some of the code is shifted at different locations.
Happy playing
BR,
Sun
EDIT: Ah, crap. It so happens I had the required amount for the quest to be completed. That piece of code I posted up top breaks ONLY when you've met the requirements
Wel.. it's good analysis nevertheless. Sorry..