#include "main.h"
#include <psapi.h>

const struct weapon_entry weapon_list[] =
{
   { 0,  0,  -1,  "Fist" },
   { 1,  0,  331, "Brass Knuckles" },

   { 2,  1,  333, "Golf Club" },
   { 3,  1,  334, "Nitestick" },
   { 4,  1,  335, "Knife" },
   { 5,  1,  336, "Baseball Bat" },
   { 6,  1,  337, "Shovel" },
   { 7,  1,  338, "Pool Cue" },
   { 8,  1,  339, "Katana" },
   { 9,  1,  341, "Chainsaw" },

   { 22, 2,  346, "Pistol" },
   { 23, 2,  347, "Silenced Pistol" },
   { 24, 2,  348, "Desert Eagle" },

   { 25, 3,  349, "Shotgun" },
   { 26, 3,  350, "Sawn-Off Shotgun" },
   { 27, 3,  351, "SPAZ12" },

   { 28, 4,  352, "Micro UZI" },
   { 29, 4,  353, "MP5" },
   { 32, 4,  372, "Tech9" },

   { 30, 5,  355, "AK47" },
   { 31, 5,  356, "M4" },

   { 33, 6,  357, "Country Rifle" },
   { 34, 6,  358, "Sniper Rifle" },

   { 35, 7,  359, "Rocket Launcher" },
   { 36, 7,  360, "Heat Seeking RPG" },
   { 37, 7,  361, "Flame Thrower" },
   { 38, 7,  362, "Minigun" },

   { 16, 8,  342, "Grenade" },
   { 17, 8,  343, "Teargas" },
   { 18, 8,  344, "Molotov Cocktail" },
   { 39, 8,  363, "Remote Explosives" },

   { 41, 9,  365, "Spray Can" },
   { 42, 9,  366, "Fire Extinguisher" },
   { 43, 9,  367, "Camera" },

   { 10, 10, 321, "Dildo 1" },
   { 11, 10, 322, "Dildo 2" },
   { 12, 10, 323, "Vibe 1" },
   { 13, 10, 324, "Vibe 2" },
   { 14, 10, 325, "Flowers" },
   { 15, 10, 326, "Cane" },

   { 44, 11, 368, "NV Goggles" },
   { 45, 11, 369, "IR Goggles" },
   { 46, 11, 371, "Parachute" },

   { 40, 12, 364, "Detonator" },

   { -1, -1, -1,  NULL }
};

/* IDs must be sequential */
const struct vehicle_entry vehicle_list[VEHICLE_LIST_SIZE] =
{
   { 400, VEHICLE_CLASS_CAR,      "Landstalker" },
   { 401, VEHICLE_CLASS_CAR,      "Bravura" },
   { 402, VEHICLE_CLASS_CAR_FAST, "Buffalo" },
   { 403, VEHICLE_CLASS_HEAVY,    "Linerunner" },
   { 404, VEHICLE_CLASS_CAR,      "Perennial" },
   { 405, VEHICLE_CLASS_CAR,      "Sentinel" },
   { 406, VEHICLE_CLASS_HEAVY,    "Dumper" },
   { 407, VEHICLE_CLASS_HEAVY,    "Fire Truck" },
   { 408, VEHICLE_CLASS_HEAVY,    "Trashmaster" },
   { 409, VEHICLE_CLASS_HEAVY,    "Stretch" },
   { 410, VEHICLE_CLASS_CAR,      "Manana" },
   { 411, VEHICLE_CLASS_CAR_FAST, "Infernus" },
   { 412, VEHICLE_CLASS_CAR,      "Voodoo" },
   { 413, VEHICLE_CLASS_CAR,      "Pony" },
   { 414, VEHICLE_CLASS_CAR,      "Mule" },
   { 415, VEHICLE_CLASS_CAR_FAST, "Cheetah" },
   { 416, VEHICLE_CLASS_HEAVY,    "Ambulance" },
   { 417, VEHICLE_CLASS_HELI,     "Leviathan" },
   { 418, VEHICLE_CLASS_CAR,      "Moonbeam" },
   { 419, VEHICLE_CLASS_CAR,      "Esperanto" },
   { 420, VEHICLE_CLASS_CAR,      "Taxi" },
   { 421, VEHICLE_CLASS_CAR,      "Washington" },
   { 422, VEHICLE_CLASS_CAR,      "Bobcat" },
   { 423, VEHICLE_CLASS_HEAVY,    "Mr. Whoopee" },
   { 424, VEHICLE_CLASS_CAR,      "BF Injection" },
   { 425, VEHICLE_CLASS_HELI,     "Hunter" },
   { 426, VEHICLE_CLASS_CAR,      "Premier" },
   { 427, VEHICLE_CLASS_HEAVY,    "Enforcer" },
   { 428, VEHICLE_CLASS_HEAVY,    "Securicar" },
   { 429, VEHICLE_CLASS_CAR_FAST, "Banshee" },
   { 430, VEHICLE_CLASS_BOAT,     "Predator" },
   { 431, VEHICLE_CLASS_HEAVY,    "Bus" },
   { 432, VEHICLE_CLASS_HEAVY,    "Rhino" },
   { 433, VEHICLE_CLASS_HEAVY,    "Barracks" },
   { 434, VEHICLE_CLASS_CAR_FAST, "Hotknife" },
   { 435, VEHICLE_CLASS_TRAILER,  "Artict Trailer" },
   { 436, VEHICLE_CLASS_CAR,      "Previon" },
   { 437, VEHICLE_CLASS_HEAVY,    "Coach" },
   { 438, VEHICLE_CLASS_CAR,      "Cabbie" },
   { 439, VEHICLE_CLASS_CAR_FAST, "Stallion" },
   { 440, VEHICLE_CLASS_CAR,      "Rumpo" },
   { 441, VEHICLE_CLASS_MINI,     "RC Bandit" },
   { 442, VEHICLE_CLASS_CAR,      "Romero" },
   { 443, VEHICLE_CLASS_HEAVY,    "Packer" },
   { 444, VEHICLE_CLASS_HEAVY,    "Monster" },
   { 445, VEHICLE_CLASS_CAR,      "Admiral" },
   { 446, VEHICLE_CLASS_BOAT,     "Squalo" },
   { 447, VEHICLE_CLASS_HELI,     "Seasparrow" },
   { 448, VEHICLE_CLASS_BIKE,     "Pizza Boy" },
   { 449, VEHICLE_CLASS_HEAVY,    "Trolly" },         /* train... */
   { 450, VEHICLE_CLASS_TRAILER,  "Artict Trailer 2" },
   { 451, VEHICLE_CLASS_CAR_FAST, "Turismo" },
   { 452, VEHICLE_CLASS_BOAT,     "Speeder" },
   { 453, VEHICLE_CLASS_BOAT,     "Reefer" },
   { 454, VEHICLE_CLASS_BOAT,     "Tropic" },
   { 455, VEHICLE_CLASS_HEAVY,    "Flatbed" },
   { 456, VEHICLE_CLASS_HEAVY,    "Yankee" },
   { 457, VEHICLE_CLASS_MINI,     "Caddy" },
   { 458, VEHICLE_CLASS_CAR,      "Solair" },
   { 459, VEHICLE_CLASS_HEAVY,    "Berkley's RC Van" },
   { 460, VEHICLE_CLASS_AIRPLANE, "Skimmer" },
   { 461, VEHICLE_CLASS_BIKE,     "PCJ-600" },
   { 462, VEHICLE_CLASS_BIKE,     "Faggio" },
   { 463, VEHICLE_CLASS_BIKE,     "Freeway" },
   { 464, VEHICLE_CLASS_MINI,     "RC Baron" },
   { 465, VEHICLE_CLASS_MINI,     "RC Raider" },
   { 466, VEHICLE_CLASS_CAR,      "Glendale" },
   { 467, VEHICLE_CLASS_CAR,      "Oceanic" },
   { 468, VEHICLE_CLASS_BIKE,     "Sanchez" },
   { 469, VEHICLE_CLASS_HELI,     "Sparrow" },
   { 470, VEHICLE_CLASS_CAR,      "Patriot" },
   { 471, VEHICLE_CLASS_BIKE,     "Quadbike" },    /* sort of.. */
   { 472, VEHICLE_CLASS_BOAT,     "Coastguard" },
   { 473, VEHICLE_CLASS_BOAT,     "Dinghy" },
   { 474, VEHICLE_CLASS_CAR,      "Hermes" },
   { 475, VEHICLE_CLASS_CAR,      "Sabre" },
   { 476, VEHICLE_CLASS_AIRPLANE, "Rustler" },
   { 477, VEHICLE_CLASS_CAR_FAST, "ZR-350" },
   { 478, VEHICLE_CLASS_CAR,      "Walton" },
   { 479, VEHICLE_CLASS_CAR,      "Regina" },
   { 480, VEHICLE_CLASS_CAR_FAST, "Comet" },
   { 481, VEHICLE_CLASS_BIKE,     "BMX" },
   { 482, VEHICLE_CLASS_HEAVY,    "Burrito" },
   { 483, VEHICLE_CLASS_HEAVY,    "Camper" },
   { 484, VEHICLE_CLASS_BOAT,     "Marquis" },
   { 485, VEHICLE_CLASS_MINI,     "Baggage" },
   { 486, VEHICLE_CLASS_HEAVY,    "Dozer" },
   { 487, VEHICLE_CLASS_HELI,     "Maverick" },
   { 488, VEHICLE_CLASS_HELI,     "News Chopper" },
   { 489, VEHICLE_CLASS_CAR,      "Rancher" },
   { 490, VEHICLE_CLASS_CAR,      "FBI Rancher" },
   { 491, VEHICLE_CLASS_CAR,      "Virgo" },
   { 492, VEHICLE_CLASS_CAR,      "Greenwood" },
   { 493, VEHICLE_CLASS_BOAT,     "Jetmax" },
   { 494, VEHICLE_CLASS_CAR_FAST, "Hotring Racer" },
   { 495, VEHICLE_CLASS_CAR,      "Sandking" },
   { 496, VEHICLE_CLASS_CAR,      "Blista Compact" },
   { 497, VEHICLE_CLASS_HELI,     "Police Maverick" },
   { 498, VEHICLE_CLASS_HEAVY,    "Boxville" },
   { 499, VEHICLE_CLASS_HEAVY,    "Benson" },
   { 500, VEHICLE_CLASS_CAR,      "Mesa" },
   { 501, VEHICLE_CLASS_MINI,     "RC Goblin" },
   { 502, VEHICLE_CLASS_CAR_FAST, "Hotring Racer 2" },
   { 503, VEHICLE_CLASS_CAR_FAST, "Hotring Racer 3" },
   { 504, VEHICLE_CLASS_CAR_FAST, "Bloodring Banger" },
   { 505, VEHICLE_CLASS_CAR,      "Rancher" },
   { 506, VEHICLE_CLASS_CAR_FAST, "Super GT" },
   { 507, VEHICLE_CLASS_CAR,      "Elegant" },
   { 508, VEHICLE_CLASS_HEAVY,    "Journey" },
   { 509, VEHICLE_CLASS_BIKE,     "Bike" },
   { 510, VEHICLE_CLASS_BIKE,     "Mountain Bike" },
   { 511, VEHICLE_CLASS_AIRPLANE, "Beagle" },
   { 512, VEHICLE_CLASS_AIRPLANE, "Cropduster" },
   { 513, VEHICLE_CLASS_AIRPLANE, "Stuntplane" },
   { 514, VEHICLE_CLASS_HEAVY,    "Linerunner" },
   { 515, VEHICLE_CLASS_HEAVY,    "Roadtrain" },
   { 516, VEHICLE_CLASS_CAR,      "Nebula" },
   { 517, VEHICLE_CLASS_CAR,      "Majestic" },
   { 518, VEHICLE_CLASS_CAR,      "Buccaneer" },
   { 519, VEHICLE_CLASS_AIRPLANE, "Shamal" },
   { 520, VEHICLE_CLASS_AIRPLANE, "Hydra" },
   { 521, VEHICLE_CLASS_BIKE,     "FCR-900" },
   { 522, VEHICLE_CLASS_BIKE,     "NRG-500" },
   { 523, VEHICLE_CLASS_BIKE,     "HPV1000" },
   { 524, VEHICLE_CLASS_HEAVY,    "Cement Truck" },
   { 525, VEHICLE_CLASS_HEAVY,    "Towtruck" },
   { 526, VEHICLE_CLASS_CAR,      "Fortune" },
   { 527, VEHICLE_CLASS_CAR,      "Cadrona" },
   { 528, VEHICLE_CLASS_HEAVY,    "FBI Truck" },
   { 529, VEHICLE_CLASS_CAR,      "Willard" },
   { 530, VEHICLE_CLASS_MINI,     "Forklift" },
   { 531, VEHICLE_CLASS_CAR,      "Tractor" },
   { 532, VEHICLE_CLASS_HEAVY,    "Combine Harvester" },
   { 533, VEHICLE_CLASS_CAR,      "Feltzer" },
   { 534, VEHICLE_CLASS_CAR,      "Remington" },
   { 535, VEHICLE_CLASS_CAR_FAST, "Slamvan" },
   { 536, VEHICLE_CLASS_CAR_FAST, "Blade" },
   { 537, VEHICLE_CLASS_HEAVY,    "Freight" },                /* train... */
   { 538, VEHICLE_CLASS_HEAVY,    "Brown Streak Engine" },    /* train... */
   { 539, VEHICLE_CLASS_BOAT,     "Vortex" },
   { 540, VEHICLE_CLASS_CAR,      "Vincent" },
   { 541, VEHICLE_CLASS_CAR_FAST, "Bullet" },
   { 542, VEHICLE_CLASS_CAR,      "Clover" },
   { 543, VEHICLE_CLASS_CAR,      "Sadler" },
   { 544, VEHICLE_CLASS_HEAVY,    "Fire Truck with ladder" },
   { 545, VEHICLE_CLASS_CAR,      "Hustler" },
   { 546, VEHICLE_CLASS_CAR,      "Intruder" },
   { 547, VEHICLE_CLASS_CAR,      "Primo" },
   { 548, VEHICLE_CLASS_HELI,     "Cargobob" },
   { 549, VEHICLE_CLASS_CAR,      "Tampa" },
   { 550, VEHICLE_CLASS_CAR,      "Sunrise" },
   { 551, VEHICLE_CLASS_CAR,      "Merit" },
   { 552, VEHICLE_CLASS_HEAVY,    "Utility Van" },
   { 553, VEHICLE_CLASS_AIRPLANE, "Nevada" },
   { 554, VEHICLE_CLASS_CAR,      "Yosemite" },
   { 555, VEHICLE_CLASS_CAR,      "Windsor" },
   { 556, VEHICLE_CLASS_HEAVY,    "Monster 2" },
   { 557, VEHICLE_CLASS_HEAVY,    "Monster 3" },
   { 558, VEHICLE_CLASS_CAR_FAST, "Uranus" },
   { 559, VEHICLE_CLASS_CAR_FAST, "Jester" },
   { 560, VEHICLE_CLASS_CAR_FAST, "Sultan" },
   { 561, VEHICLE_CLASS_CAR,      "Stratum" },
   { 562, VEHICLE_CLASS_CAR_FAST, "Elegy" },
   { 563, VEHICLE_CLASS_HELI,     "Raindance" },
   { 564, VEHICLE_CLASS_MINI,     "RC Tiger" },
   { 565, VEHICLE_CLASS_CAR,      "Flash" },
   { 566, VEHICLE_CLASS_CAR,      "Tahoma" },
   { 567, VEHICLE_CLASS_CAR,      "Savanna" },
   { 568, VEHICLE_CLASS_CAR_FAST, "Bandito" },
   { 569, VEHICLE_CLASS_HEAVY,    "Freight Train Flatbed" }, /* train... */
   { 570, VEHICLE_CLASS_HEAVY,    "Brown Streak" },          /* XXX dupe, streakc */
   { 571, VEHICLE_CLASS_MINI,     "Kart" },
   { 572, VEHICLE_CLASS_MINI,     "Mower" },
   { 573, VEHICLE_CLASS_HEAVY,    "Dune" },
   { 574, VEHICLE_CLASS_MINI,     "Sweeper" },
   { 575, VEHICLE_CLASS_CAR,      "Broadway" },
   { 576, VEHICLE_CLASS_CAR,      "Tornado" },
   { 577, VEHICLE_CLASS_AIRPLANE, "AT-400" },
   { 578, VEHICLE_CLASS_HEAVY,    "DFT-30" },               /* ? */
   { 579, VEHICLE_CLASS_CAR,      "Huntley" },
   { 580, VEHICLE_CLASS_CAR,      "Stafford" },
   { 581, VEHICLE_CLASS_BIKE,     "BF-400" },
   { 582, VEHICLE_CLASS_HEAVY,    "News Van" },
   { 583, VEHICLE_CLASS_MINI,     "Tug" },
   { 584, VEHICLE_CLASS_TRAILER,  "Petrol Trailer" },
   { 585, VEHICLE_CLASS_CAR,      "Emperor" },
   { 586, VEHICLE_CLASS_BIKE,     "Wayfarer" },
   { 587, VEHICLE_CLASS_CAR_FAST, "Euros" },
   { 588, VEHICLE_CLASS_HEAVY,    "Hotdog" },
   { 589, VEHICLE_CLASS_CAR,      "Club" },
   { 590, VEHICLE_CLASS_HEAVY,    "Freight Train Boxcar" }, /* ? */
   { 591, VEHICLE_CLASS_TRAILER,  "Artict Trailer 3" },
   { 592, VEHICLE_CLASS_AIRPLANE, "Andromada" },
   { 593, VEHICLE_CLASS_AIRPLANE, "Dodo" },
   { 594, VEHICLE_CLASS_MINI,     "RC Cam" },
   { 595, VEHICLE_CLASS_BOAT,     "Launch" },
   { 596, VEHICLE_CLASS_CAR,      "Police Car (LS)" },
   { 597, VEHICLE_CLASS_CAR,      "Police Car (SF)" },
   { 598, VEHICLE_CLASS_CAR,      "Police Car (LV)" },
   { 599, VEHICLE_CLASS_CAR,      "Police Ranger" },
   { 600, VEHICLE_CLASS_CAR,      "Picador" },
   { 601, VEHICLE_CLASS_HEAVY,    "S.W.A.T." },
   { 602, VEHICLE_CLASS_CAR_FAST, "Alpha" },
   { 603, VEHICLE_CLASS_CAR_FAST, "Phoenix" },
   { 604, VEHICLE_CLASS_CAR,      "Damaged Glendale" },
   { 605, VEHICLE_CLASS_CAR,      "Damaged Sadler" },
   { 606, VEHICLE_CLASS_TRAILER,  "Baggage Trailer" },
   { 607, VEHICLE_CLASS_TRAILER,  "Baggage Trailer 2" },
   { 608, VEHICLE_CLASS_TRAILER,  "Tug Staircase" },
   { 609, VEHICLE_CLASS_HEAVY,    "Black Boxville" },
   { 610, VEHICLE_CLASS_TRAILER,  "Farm Trailer" },
   { 611, VEHICLE_CLASS_TRAILER,  "Street Sweeper Trailer" }
};


void gta_weather_state_set(int state)
{
   *(uint16_t *)0x00C81318 = (uint16_t)state;
   *(uint16_t *)0x00C8131C = (uint16_t)state;
   *(uint16_t *)0x00C81320 = (uint16_t)state;
}


void gta_time_hour_set(int hour)
{
   *(uint8_t *)0x00B70153 = (uint8_t)hour;
}


void gta_money_set(uint32_t value)
{
   *(uint32_t *)0x00B7CE50 = value;
   *(uint32_t *)0x00B7CE54 = value;
}


uint32_t gta_money_get(void)
{
   return *(uint32_t *)0x00B7CE50;
}


void gta_gravity_set(float value)
{
   memcpy_safe((void *)0x00863984, &value, sizeof(float));
}


float gta_gravity_get(void)
{
   float value;

   memcpy_safe(&value, (void *)0x00863984, sizeof(float));

   return value;
}


void gta_game_speed_set(float value)
{
   *(float *)0x00B7CB64 = value;
}


float gta_game_speed_get(void)
{
   return *(float *)0x00B7CB64;
}


struct checkpoint *gta_checkpoint_info_get(int n)
{
   struct checkpoint *cp = (struct checkpoint *)0x00C7F158;

   if(vect3_near_zero(cp->position))
      return NULL;

   return &cp[n];
}


int gta_menu_active(void)
{
   return (int)(*(uint8_t *)0x00BA67A4);
}


void gta_menu_active_set(int enabled)
{
   /* untested */
   *(uint8_t *)0x00BA67A4 = (uint8_t)enabled;
}


const struct weapon_entry *gta_weapon_get_by_name(const char *name)
{
   const struct weapon_entry *entry = weapon_list;

   while(entry->name != NULL)
   {
      if(_stricmp(entry->name, name) == 0)
         return entry;
      entry++;
   }

   return NULL;
}


void gta_weapon_set(struct actor_info *info, int slot, int id, int ammo, int ammo_clip)
{
   /*memset(&info->weapon[slot], 0, sizeof(struct weapon));*/
   if(slot < 0 || slot > 12)
   {
      log_debug("invalid weapon slot %d!", slot);
      return;
   }

   if(id >= 0)
      info->weapon[slot].id = id;
   if(ammo >= 0)
      info->weapon[slot].ammo = ammo;
   if(ammo_clip >= 0)
      info->weapon[slot].ammo_clip = ammo_clip;
}


int gta_weapon_ammo_set(struct actor_info *info, int slot, int ammo)
{
   uint32_t ammo_old = info->weapon[slot].ammo;

   if(ammo >= 0)
      info->weapon[slot].ammo = ammo;

   return ammo_old;
}


int gta_weapon_ammo_clip_set(struct actor_info *info, int slot, int ammo_clip)
{
   uint32_t ammo_clip_old = info->weapon[slot].ammo_clip;

   if(ammo_clip >= 0)
      info->weapon[slot].ammo_clip = ammo_clip;

   return ammo_clip_old;
}


const char *gta_vehicle_class_name(int id)
{
   switch(id)
   {
   case VEHICLE_CLASS_CAR:      return "Regular cars";
   case VEHICLE_CLASS_CAR_FAST: return "Fast cars";
   case VEHICLE_CLASS_HEAVY:    return "Heavy vehicles";
   case VEHICLE_CLASS_HELI:     return "Helicopters";
   case VEHICLE_CLASS_AIRPLANE: return "Airplanes";
   case VEHICLE_CLASS_BIKE:     return "Bikes";
   case VEHICLE_CLASS_BOAT:     return "Boats";
   case VEHICLE_CLASS_MINI:     return "Small vehicles";
   case VEHICLE_CLASS_TRAILER:  return "Trailers";
   }
   return "Unknown class (bug)";
}


const struct vehicle_entry *gta_vehicle_get_by_id(int id)
{
   id -= VEHICLE_LIST_ID_START;

   if(id < 0 || id >= VEHICLE_LIST_SIZE)
      return NULL;

   return &vehicle_list[id];
}


typedef void *(__cdecl *model_request_func)(int, int);

void gta_model_request(int model_id)
{
   model_request_func model_request = (model_request_func)0x004087E0;

   if(model_id < 0)
      return;

   model_request(model_id, 2);
}


typedef void *(__cdecl *vehicle_spawn_func)(int);

void gta_vehicle_spawn(int vehicle_id)
{
   vehicle_spawn_func vehicle_spawn = (vehicle_spawn_func)0x0043A0B0;

   log_debug("Calling vehicle_spawn(%d)...", vehicle_id);
   vehicle_spawn(vehicle_id);
   log_debug("Returned.");
}


typedef void *(__cdecl *jetpack_give_func)(void);

void gta_jetpack_give(void)
{
   jetpack_give_func jetpack_give = (jetpack_give_func)0x00439600;

   log_debug("Calling jetpack_give()...");
   jetpack_give();
   log_debug("Returned.");
}


int gta_interior_id_get(void)
{
   return (int)*(uint32_t *)0x00B72914;
}


void gta_interior_id_set(int id)
{
   struct actor_info *info = actor_info_get(ACTOR_SELF, 0);

   *(uint32_t *)0x00B72914 = (uint32_t)id;

   if(info != NULL)
      info->base.interior_id = (uint8_t)id;
}






/* returns the time since start in 100 usec precision
   will wrap after 119.3 hours (should be ok though,
   since SA-MP usually crashes at least once every hour,
   and very few people play for 119 hours straight... */
uint32_t __time_get(void)
{
   static int init = 0;
   static ULARGE_INTEGER init_time;
   FILETIME ft;
   ULARGE_INTEGER time_now;

   GetSystemTimeAsFileTime(&ft);
   time_now.LowPart = ft.dwLowDateTime;
   time_now.HighPart = ft.dwHighDateTime;

   time_now.QuadPart /= 1000; /* convert to 100 usec */

   if(!init)
   {
      init_time.QuadPart = time_now.QuadPart - MSEC_TO_TIME(1000);
      init = 1;
   }

   return (uint32_t)(time_now.QuadPart - init_time.QuadPart);
}


void cheat_state_text(const char *fmt, ...)
{
   va_list ap;


   if(fmt == NULL)
   {
      cheat_state->text_time = 0;
      return;
   }

   va_start(ap, fmt);
   vsnprintf(cheat_state->text, sizeof(cheat_state->text), fmt, ap);
   va_end(ap);

   cheat_state->text_time = time_get();
}


/* returns actor <id>'s struct
   if id is ACTOR_SELF (-1), the pointer to our own actor is returned */
struct actor_info *actor_info_get(int id, int flags)
{
   struct actor_info *info;


   if(pool_actor == NULL)
      return NULL;

   if(id != ACTOR_SELF && (id < 0 && id >= pool_actor->size))
      return NULL;

   if(id == ACTOR_SELF)
   {
      info = (struct actor_info *) (UINT_PTR)*(uint32_t *)ACTOR_POINTER_SELF;
      if(info == NULL)
         return NULL;
   }
   else
   {
      info = &((struct actor_info *)pool_actor->start)[id];
   }

   if(info->base.matrix == NULL)
      return NULL;

   /* XXX this should not be nessecary... but it is. fix it. */
   if(vect3_near_zero(&info->base.matrix[4*3]))
      return NULL;

   if((flags & ACTOR_ALIVE) && ACTOR_IS_DEAD(info))
      return NULL;

   /* exclude actors in the same car as ACTOR_SELF */
   if(flags & ACTOR_NOT_SAME_VEHICLE)
   {
      struct actor_info *self = actor_info_get(ACTOR_SELF, 0);

      if(self          != NULL                &&
         info->state   == ACTOR_STATE_DRIVING &&
         self->state   == ACTOR_STATE_DRIVING &&
         info->vehicle == self->vehicle)
         return NULL;

   }

   return info;
}


void cheat_teleport_history_save(void)
{
   struct cheat_state_teleport *teleport;
   struct object_base *base;

   if(cheat_state->state == CHEAT_STATE_VEHICLE)
      base = &vehicle_info_get(VEHICLE_SELF, 0)->base;
   else if(cheat_state->state == CHEAT_STATE_ACTOR)
      base = &actor_info_get(ACTOR_SELF, 0)->base;
   else
      return;

   if(++cheat_state->teleport_hist_count > TELEPORT_HIST_MAX)
   {
      cheat_state->teleport_hist_count = TELEPORT_HIST_MAX;
      memmove(
         &cheat_state->teleport_hist[0],
         &cheat_state->teleport_hist[1],
         sizeof(*teleport) * (TELEPORT_HIST_MAX - 1));
   }

   teleport = &cheat_state->teleport_hist[ cheat_state->teleport_hist_count - 1 ];
   teleport->set = 1;
   matrix_copy(base->matrix, teleport->matrix);
   teleport->interior_id = gta_interior_id_get();
}


void vehicle_detachables_teleport(struct vehicle_info *info, const float from[3], const float to[3])
{
   int i, n;


   /*log_debug("detachables for type=%d id=%d:", info->vehicle_type, info->vehicle_id);
   dump_vect("bike1[0]", info->detachable_bike1[0].position);
   for(i=0; i<4; i++)
      dump_vect("bike2[]", info->detachable_bike2[i].position);
   for(i=0; i<4; i++)
      dump_vect("car[]", info->detachable_car[i].position);
   log_debug("");*/

   switch(info->vehicle_type)
   {
   case VEHICLE_TYPE_CAR:  /* + helicopters and planes */
      for(n=0; n<4; n++)
      {
         for(i=0; i<3; i++)
            info->detachable_car[n].position[i] += to[i] - from[i];
      }
      break;

   case VEHICLE_TYPE_BIKE:
      for(i=0; i<3; i++)
         info->detachable_bike1[0].position[i] += to[i] - from[i];
      for(n=0; n<4; n++)
      {
         for(i=0; i<3; i++)
            info->detachable_bike2[n].position[i] += to[i] - from[i];
      }
      break;

   case VEHICLE_TYPE_TRAIN:
      /* XXX: fixme */
      break;

   case VEHICLE_TYPE_BOAT:
      for(n=0; n<2; n++)
      {
         for(i=0; i<3; i++)
            info->detachable_bike2[n].position[i] += to[i] - from[i];
      }
      break;
   }

   /*log_debug("detachables after teleport for type=%d id=%d:", info->vehicle_type, info->vehicle_id);
   dump_vect("bike1[0]", info->detachable_bike1[0].position);
   for(i=0; i<4; i++)
      dump_vect("bike2[]", info->detachable_bike2[i].position);
   for(i=0; i<4; i++)
      dump_vect("car[]", info->detachable_car[i].position);
   log_debug("");
   log_debug("");
   log_debug("");*/
}


/* returns vehicle <id>'s struct
   if id is VEHICLE_SELF (-1), the pointer to our own vehicle is returned */
struct vehicle_info *vehicle_info_get(int id, int flags)
{
   struct vehicle_info *info;


   if(pool_vehicle == NULL)
      return NULL;

   if(id != VEHICLE_SELF && (id < 0 && id >= pool_vehicle->size))
      return NULL;

   if(id == VEHICLE_SELF)
   {
      info = (struct vehicle_info *) (UINT_PTR)*(uint32_t *)VEHICLE_POINTER_SELF;
      struct actor_info *actor = actor_info_get(ACTOR_SELF, ACTOR_ALIVE);

      if(info == NULL)
         return NULL;

      if(!vehicle_contains_actor(info, actor))
         return NULL;
   }
   else
   {
      info = &((struct vehicle_info *)pool_vehicle->start)[id];
   }

   if(info->base.matrix == NULL)
      return NULL;

   if((flags & VEHICLE_EMPTY) && info->passengers[0] != NULL)
      return NULL;

   if((flags & VEHICLE_ALIVE) && (info->hitpoints < 250.0f && info->burn_timer == 0.0f))
      return NULL;

   return info;
}


void cheat_vehicle_tires_set(struct vehicle_info *info, uint8_t tire_status)
{
   struct vehicle_info *temp;

   uint8_t tires[4] = { tire_status, tire_status, tire_status, tire_status };

   for(temp = info; temp != NULL; temp = temp->trailer)
   {
      uint8_t type = temp->vehicle_type;

      if(type == VEHICLE_TYPE_CAR)
         vect4_copy(tires, temp->car_tire_status);
      else if(type == VEHICLE_TYPE_BIKE)
         vect2_copy(tires, temp->bike_tire_status);

      if(!set.trailer_support)
         break;
   }
}


int vehicle_contains_trailer(struct vehicle_info *info, const struct vehicle_info *trailer)
{
   struct vehicle_info *temp;

   for(temp = info; temp != NULL; temp = temp->trailer)
   {
      if(temp == trailer)
         return 1;
   }

   return 0;
}


int vehicle_contains_actor(struct vehicle_info *vehicle, const struct actor_info *actor)
{
   struct actor_info *passengers[VEHICLE_MAX_PASSENGERS];
   struct actor_info *temp;

   if(actor == NULL)
      return 0;

   memcpy(passengers, vehicle->passengers, sizeof(uint32_t) * VEHICLE_MAX_PASSENGERS);

   int i;
   for(i = 0; i < VEHICLE_MAX_PASSENGERS; i++)
   {
      temp = passengers[i];

      if(temp == actor)
         return 1;
   }

   return 0;
}


void vehicle_state_store(const struct vehicle_info *info, struct vehicle_state *state)
{
   if(info == NULL)
      return;

   vect3_copy(info->speed, state->speed);
   vect3_copy(info->spin, state->spin);
   matrix_copy(info->base.matrix, state->matrix);

   if(!set.trailer_support)
      return;

   state->trailerPtr = info->trailer;
   state->trailer = new struct vehicle_state;
   vehicle_state_store(state->trailerPtr, state->trailer); /* XXX - recursion sucks */
}


void vehicle_state_restore(struct vehicle_info *info, const struct vehicle_state *state)
{
   if(info == NULL)
      return;

   vect3_copy(state->speed, info->speed);
   vect3_copy(state->spin, info->spin);
   matrix_copy(state->matrix, info->base.matrix);

   if(set.trailer_support)
      vehicle_state_restore(state->trailerPtr, state->trailer); /* XXX - recursion sucks */
}


/* prevents the vehicle from going below a certain point. doesn't always work
   for some reason ;) */
void vehicle_prevent_below_height(struct vehicle_info *info, float height)
{
   float z = info->base.matrix[4*3+2];
   float zspeed = info->speed[2];   /* XXX: this is wrong */

   if(z < height || (zspeed < 0.0f && (z + zspeed) < height))
   {
      float vect[3] = { info->base.matrix[4*3+0], info->base.matrix[4*3+1], height };

      vehicle_detachables_teleport(info, &info->base.matrix[4*3], vect);

      vect3_copy(vect, &info->base.matrix[4*3]);

      info->speed[2] = 0.05f;
      vect3_zero(info->spin);
   }
}


/* returns the id of the next vehicle or actor. Scans in direction <dir> (1, -1)  */
static int __generic_find(int do_vehicles, int id, int dir, int flags)
{
   const struct pool *pool = do_vehicles ? pool_vehicle : pool_actor;
   int i, n = id;


   if(pool_vehicle == NULL)
      return -1;

   /* loop through the vehicle pool until we find a vehicle */
   for(i=0; i<pool->size; i++)
   {
      n += dir;

      if(n > pool->size - 1)
         n = 0;
      if(n < 0)
         n = pool->size - 1;

      if(do_vehicles)
      {
         if(vehicle_info_get(n, flags) != NULL)
         {
            if(vehicle_info_get(n, flags)->base.matrix != vehicle_info_get(VEHICLE_SELF, 0)->base.matrix)
               return n;
         }
      }
      else
      {
         if(actor_info_get(n, flags) != NULL)
         {
            if(actor_info_get(n, flags)->base.matrix != actor_info_get(ACTOR_SELF, 0)->base.matrix)
               return n;
         }
      }
   }

   return -1;
}

int vehicle_find(int id, int dir, int flags)
{
   return __generic_find(1, id, dir, flags);
}

int actor_find(int id, int dir, int flags)
{
   return __generic_find(0, id, dir, flags);
}

/* XXX make these generic */

/* returns the id of the nearest vehicle */
int vehicle_find_nearest(int flags)
{
   const struct vehicle_info *vehicle_self, *info;
   const struct actor_info *actor_self;
   float dist = -1.0f;
   int id_nearest = -1;
   int n;


   if(pool_vehicle == NULL)
      return -1;

   vehicle_self = vehicle_info_get(VEHICLE_SELF, 0);

   if((actor_self = actor_info_get(ACTOR_SELF, 0)) == NULL)
      return -1;

   for(n=0; n<pool_vehicle->size; n++)
   {
      float vect[3];

      if((info = vehicle_info_get(n, flags)) == NULL)
         continue;

      if(vehicle_self != NULL && vehicle_self->base.matrix == info->base.matrix)
         continue;

      vect3_vect3_sub(&actor_self->base.matrix[4*3], &info->base.matrix[4*3], vect);

      vect[2] += 2.0f;

      if(dist < 0.0f || vect3_length(vect) < dist)
      {
         dist = vect3_length(vect);
         id_nearest = n;
      }
   }

   return id_nearest;
}


/* returns the id of the nearest actor */
int actor_find_nearest(int flags)
{
   const struct actor_info *self, *info;
   float dist = -1.0f;
   int id_nearest = -1;
   int n;


   if(pool_actor == NULL)
      return -1;

   if((self = actor_info_get(ACTOR_SELF, 0)) == NULL)
      return -1;

   for(n=0; n<pool_actor->size; n++)
   {
      float vect[3];

      if((info = actor_info_get(n, flags)) == NULL)
         continue;

      if(self->base.matrix == info->base.matrix)
         continue;

      vect3_vect3_sub(&self->base.matrix[4*3], &info->base.matrix[4*3], vect);

      if(dist < 0.0f || vect3_length(vect) < dist)
      {
         dist = vect3_length(vect);
         id_nearest = n;
      }
   }

   return id_nearest;
}


struct vehicle_info *actor_vehicle_get(const struct actor_info *info)
{
   if(info->state == ACTOR_STATE_DRIVING && info->vehicle != NULL)
      return info->vehicle;

   return NULL;
}


const float *safe_haven_nearest_pos_get(float pos[3])
{
   float *nearest_pos = NULL;
   float nearest_length = -1.0f;
   int i;


   if(safe_haven_list == NULL)
      return NULL;

   for(i=0; i<safe_haven_list_size; i++)
   {
      float vect[3] = {
         pos[0] - safe_haven_list[i].pos[0],
         pos[1] - safe_haven_list[i].pos[1],
         pos[2] - safe_haven_list[i].pos[2]
      };
      float length = vect3_length(vect);

      if(nearest_length < 0.0f || length < nearest_length)
      {
         nearest_length = length;
         nearest_pos = safe_haven_list[i].pos;
      }
   }

   return nearest_pos;
}


static int __page_size_get(void)
{
   static int page_size = -1;
   SYSTEM_INFO si;

   if(page_size == -1)
   {
      GetSystemInfo(&si);
      page_size = (int)si.dwPageSize;
   }

   return page_size;
}


static int __page_write(void *_dest, const void *_src, uint32_t len)
{
   static int page_size = __page_size_get();
   uint8_t *dest = (uint8_t *)_dest;
   const uint8_t *src = (const uint8_t *)_src;
   DWORD prot_prev = 0;
   int prot_changed = 0;
   SIZE_T write_len;
   int ret = 1;


   while(len > 0)
   {
      int page_offset = (int)((UINT_PTR)dest % page_size);
      int page_remain = page_size - page_offset;
      int this_len = len;

      if(this_len > page_remain)
         this_len = page_remain;

      if(IsBadWritePtr(dest, this_len))
      {
         if(!VirtualProtect((void *)dest, this_len, PAGE_EXECUTE_READWRITE, &prot_prev))
            ret = 0;
         else
            prot_changed = 1;
      }

      if(!WriteProcessMemory(GetCurrentProcess(), dest, (void *)src, this_len, &write_len))
         write_len = 0;

      if(prot_changed)
      {
         DWORD dummy;
         if(!VirtualProtect((void *)dest, this_len, prot_prev, &dummy))
            log_debug("__page_write() could not restore original permissions for ptr %p", dest);
      }

      if((int)write_len != this_len)
         ret = 0;

      dest += this_len;
      src += this_len;
      len -= this_len;
   }

   return ret;
}


static int __page_read(void *_dest, const void *_src, uint32_t len)
{
   static int page_size = __page_size_get();
   uint8_t *dest = (uint8_t *)_dest;
   const uint8_t *src = (const uint8_t *)_src;
   DWORD prot_prev = 0;
   int prot_changed = 0;
   SIZE_T read_len;
   int ret = 1;


   while(len > 0)
   {
      int page_offset = (int)((UINT_PTR)src % page_size);
      int page_remain = page_size - page_offset;
      int this_len = len;

      if(this_len > page_remain)
         this_len = page_remain;

      if(IsBadReadPtr(src, this_len))
      {
         if(!VirtualProtect((void *)src, this_len, PAGE_EXECUTE_READWRITE, &prot_prev))
            ret = 0;
         else
            prot_changed = 1;
      }

      if(!ReadProcessMemory(GetCurrentProcess(), src, dest, this_len, &read_len))
         read_len = 0;

      if(prot_changed)
      {
         DWORD dummy;
         if(!VirtualProtect((void *)src, this_len, prot_prev, &dummy))
            log_debug("__page_read() could not restore original permissions for ptr %p", src);
      }

      if((int)read_len != this_len)
      {
         memset(dest + read_len, 0, this_len - read_len);
         ret = 0;
      }

      dest += this_len;
      src += this_len;
      len -= this_len;
   }

   return ret;
}


int memcpy_safe(void *_dest, const void *_src, uint32_t len)
{
   static int page_size = __page_size_get();
   static int recurse_ok = 1;
   uint8_t buf[4096];
   uint8_t *dest = (uint8_t *)_dest;
   const uint8_t *src = (const uint8_t *)_src;
   int ret = 1;


   while(len > 0)
   {
      uint32_t this_len = sizeof(buf);

      if(this_len > len)
         this_len = len;

      if(!__page_read(buf, src, this_len))
         ret = 0;

      if(!__page_write(dest, buf, this_len))
         ret = 0;

      len -= this_len;
      src += this_len;
      dest += this_len;
   }

   return ret;
}


int memset_safe(void *_dest, int c, uint32_t len)
{
   uint8_t *dest = (uint8_t *)_dest;
   uint8_t buf[4096];

   memset(buf, c, (len > 4096) ? 4096 : len);

   for(;;)
   {
      if(len > 4096)
      {
         if(memcpy_safe(dest, buf, 4096) != 1)
            return 0;
         dest += 4096;
         len -= 4096;
      }
      else
      {
         if(memcpy_safe(dest, buf, len) != 1)
            return 0;
         break;
      }
   }

   return 1;
}


int memcmp_safe(const void *_s1, const void *_s2, uint32_t len)
{
   const uint8_t *s1 = (const uint8_t *)_s1;
   const uint8_t *s2 = (const uint8_t *)_s2;
   uint8_t buf[4096];

   for(;;)
   {
      if(len > 4096)
      {
         if(memcpy_safe(buf, s1, 4096) != 1)
            return 0;
         if(memcmp(buf, s2, 4096) != 0)
            return 0;
         s1 += 4096;
         s2 += 4096;
         len -= 4096;
      }
      else
      {
         if(memcpy_safe(buf, s1, len) != 1)
            return 0;
         if(memcmp(buf, s2, len) != 0)
            return 0;
         break;
      }
   }

   return 1;
}


void *dll_baseptr_get(const char *dll_name)
{
   return GetModuleHandle(dll_name);
}


void str_split_free(struct str_split *split)
{
   if(split != NULL)
   {
      if(split->str != NULL)
         free(split->str);
      if(split->argv != NULL)
         free(split->argv);
      free(split);
   }
}


struct str_split *str_split(const char *str, const char *ch)
{
   struct str_split *split;
   char *prev, *next;
   char *dest;
   void *tmp;


   /* left trim */
   while(*str && strchr(ch, *str) != NULL)
      str++;

   split = (struct str_split *)calloc(1, sizeof(struct str_split));
   if(split == NULL)
      return NULL;

   split->str = _strdup(str);
   if(split->str == NULL)
   {
      free(split);
      return NULL;
   }

   for(prev=split->str; ; prev=next)
   {
      if(*prev == '"')
      {
         /* find the ending " */
         for(dest=next=++prev; *next; next++)
         {
            if(*next == '"')
            {
               next++;
               break;
            }
            else if(*next == '\\' && next[1] != 0)
               *dest++ = *++next;
            else
               *dest++ = *next;
         }
         *dest = 0;
      }
      else
      {
         next = prev;
      }

      /* find next value */
      for(; *next && strchr(ch, *next)==NULL; next++);
      next = *next ? next : NULL;

      if((tmp = realloc(split->argv, (split->argc + 1) * sizeof(char *))) == NULL)
         goto out;
      split->argv = (char **)tmp;

      split->argv[split->argc] = prev;
      split->argc++;

      if(next == NULL)
         break;

      for(*next++=0; *next && strchr(ch, *next)!=NULL; next++);

      if(*next == 0)
         break;
   }

   tmp = realloc(split->argv, (split->argc + 1) * sizeof(char *));
   if(tmp == NULL)
      goto out;
   split->argv = (char **)tmp;

   split->argv[split->argc] = NULL;

   return split;

out:;
   str_split_free(split);
   return NULL;
}


size_t strlcpy(char *dst, const char *src, size_t size)
{
   size_t len = strlen(src);

   if(size == 0)
      return len;

   if(len >= size)
   {
      size--;
      memcpy(dst, src, size);
      dst[size] = 0;
   }
   else if(size > 0)
   {
      strcpy(dst, src);
   }

   return len;
}


size_t strlcat(char *dst, const char *src, size_t size)
{
   size_t dlen = strlen(dst);
   size_t slen = strlen(src);

   if(size == 0)
      return dlen + slen;

   if(dlen + slen >= size)
   {
      size -= dlen - 1;
      memcpy(dst + dlen, src, size);
      dst[dlen+size] = 0;
   }
   else if(size > 0)
   {
      strcpy(dst + dlen, src);
   }

   return dlen + slen;
}


void *memdup(const void *src, int len)
{
   void *dest = malloc(len);

   if(dest != NULL)
      memcpy(dest, src, len);

   return dest;
}


static signed char hex_to_dec(signed char ch)
{
   if(ch >= '0' && ch <= '9')
      return ch - '0';
   if(ch >= 'A' && ch <= 'F')
      return ch - 'A' + 10;
   if(ch >= 'a' && ch <= 'f')
      return ch - 'A' + 10;

   return -1;
}


uint8_t *hex_to_bin(const char *str)
{
   int len = (int)strlen(str);
   uint8_t *buf, *sbuf;

   if(len == 0 || len % 2 != 0)
      return NULL;

   sbuf = buf = (uint8_t *)malloc(len / 2);

   while(*str)
   {
      signed char bh = hex_to_dec(*str++);
      signed char bl = hex_to_dec(*str++);

      if(bl == -1 || bh == -1)
      {
         free(sbuf);
         return NULL;
      }

      *buf++ = (uint8_t)(bl | (bh << 4));
   }

   return sbuf;
}

