TheStarport/FLHook

Waypoint distance check does not take system into account

Opened this issue · 3 comments

FL checks all waypoints in your current list and sets them as cleared if you get within 150.0f (300.0f if in a TL), regardless of the system they are in

relevant offsets:

impulse/cruise clear waypoint range: 150.0f, freelancer.exe+1d97b4
check against impulse/cruise clear range: freelancer.exe+f4058

tradelane clear waypoint range: 300.0f, freelancer.exe+1d7e80
check against tradelane clear range: freelancer.exe+f417f, (there are refs in freelancer.exe+d592b and freelancer.exe+d593e as well)

both checks goto freelancer.exe+f41c0 when they're true (i.e., you're in range to clear the waypoint)

Fixed via detouring the fetching of a waypoint.
Implemented method is identical to the original, except it returns a nullptr if the system of the waypoint and system of the player do not match.

To be implemented into the upcoming universal clienthook

PatchCallAddr((char*)hFreelancer, 0xF4141, (char*)WaypointCheckDetour);
void* WaypointCheckDetour(int index)
{
	const static int* maxWaypoint = (int*)((char*)hFreelancer + 0x273374);
	const static uint* playerSystem = (uint*)((char*)hFreelancer + 0x273354);
	if (index < 0 || index >= *maxWaypoint)
	{
		return nullptr;
	}
	const uint* waypointSystem = (uint*)((24 * index) + 0x672984);
	if (*waypointSystem != *playerSystem)
	{
		return nullptr;
	}
	return (void*)((24 * index) + 0x672978);
}
BC46 commented

I'm personally not a big fan of reimplementing whole functions unless it's absolutely necessary. This way you're effectively rewriting code that already exists. Why not just call the original function in the detour and extend the check?

Initially I wrote the following inline assembly function which seems to work ok:

const DWORD WAYPOINT_CHECK_ADDR = 0x4C46A0;
#define PLAYER_SYSTEM_ADDR 0x673354

__declspec(naked) PDWORD WaypointCheckDetour(UINT index)
{
    __asm {
        mov     esi, [esp+4]
        push    esi
        call    [WAYPOINT_CHECK_ADDR]
        add     esp, 4
        test    eax, eax
        jz      done
        mov     esi, PLAYER_SYSTEM_ADDR
        mov     esi, [esi]
        cmp     esi, [eax+12]
        jz      done
        xor     eax, eax
    done:
        ret
    }
}

Alternatively, if you don't like inline assembly, here's the equivalent function in pure C++:

const DWORD WAYPOINT_CHECK_ADDR = 0x4C46A0;
#define PLAYER_SYSTEM_ADDR 0x673354

typedef PDWORD (WaypointCheck)(UINT index);

PDWORD WaypointCheckDetour(UINT index)
{
    PDWORD origResult = ((WaypointCheck*) WAYPOINT_CHECK_ADDR)(index);

    if (!origResult)
        return NULL;

    PUINT playerSystem = (PUINT) PLAYER_SYSTEM_ADDR;
    PUINT waypointSystem = (PUINT) ((PBYTE) origResult + 12);

    return *playerSystem == *waypointSystem ? origResult : NULL;
}

I mean, both approaches work, and the original method is very, VERY short, so I didn't really think much of it.