/*
  LaneSpeed.cpp - Configurable trade lane speed (via solararch.ini).

  Jason Hood, 26 June, 2011.

  Adds a lane_speed value to [Solar] sections (it must come after the nickname).
  The default value is whatever's hardcoded into common.dll.  I've kept it
  simple, so it will only apply to the player - NPCs will use whatever the
  player previously used.

  Install:
	Copy LaneSpeed.dll to your EXE directory and add it to the [Libraries]
	section of dacom.ini.  Requires & assumes the official patch.
*/

#include "Common.h"
#include <map>

#define NAKED	__declspec( naked )
#define STDCALL __stdcall


#define ADDR_SPEED   ((PDWORD)(0x62b19b8+1))	// within CShip::go_tradelane
#define ADDR_READ    ((PBYTE)0x6399858) 	// Archetype::Solar::read

#define pLaneSpeed   ((float*)0x639f3cc)


DWORD dummy;
#define ProtectX( addr, size ) \
  VirtualProtect( addr, size, PAGE_EXECUTE_READWRITE, &dummy )
#define ProtectW( addr, size ) \
  VirtualProtect( addr, size, PAGE_READWRITE, &dummy )

#define RELOFS( from, to ) \
  *(PDWORD)(from) = (DWORD)(to) - (DWORD)(from) - 4

#define NEWOFS( from, to ) \
  to##_Old = (DWORD)(from) + *(PDWORD)(from) + 4; \
  RELOFS( from, to##_Hook )

#define NEWABS( from, to ) \
  to##_Old = *(PDWORD)(from); \
  *(PDWORD)(from) = (DWORD)to##_Hook


typedef std::map<UINT, float> SpeedMap;
typedef SpeedMap::const_iterator SpeedMapCIter;

SpeedMap speeds;
float default_speed;


bool STDCALL ReadSpeed( INI_Reader& ini, PDWORD solar )
{
  if (ini.is_value( "lane_speed" ))
  {
    speeds[solar[2]] = ini.get_value_float( 0 );
    return true;
  }
  return false;
}


void STDCALL SetSpeed( IObjInspectImpl* ship, IObjInspectImpl* ring )
{
  if (ship->is_player())
  {
    SpeedMapCIter iter = speeds.find( ring->solar->archetype->id );
    *pLaneSpeed = (iter == speeds.end()) ? default_speed : iter->second;
  }
}


DWORD Read_Old, Read_New;
DWORD Speed_Old;

NAKED
void Speed_Hook()
{
  __asm {
	push	ecx
	push	dword ptr [esp+4+4]
	push	dword ptr [esp+12+8]
	call	SetSpeed
	pop	ecx
	jmp	Speed_Old
  }
}


NAKED
void Read_Hook()
{
  __asm {
	push	ecx
	push	ecx
	push	dword ptr [esp+4+8]
	call	ReadSpeed
	pop	ecx
	test	al, al
	jnz	done
	jmp	Read_Old
  done:
	ret	4
  }
}


void Patch()
{
  ProtectX( ADDR_SPEED, 4 );
  ProtectW( ADDR_READ,	4 );
  ProtectW( pLaneSpeed, 4 );

  NEWOFS( ADDR_SPEED, Speed );
  NEWABS( ADDR_READ,  Read );

  default_speed = *pLaneSpeed;
}


BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
{
  if (fdwReason == DLL_PROCESS_ATTACH)
    Patch();

  return TRUE;
}
