code:
#include <csignal>
#define WITHWINDOWS
#include "common.h"
#if defined DETECT_JOYSTICK_MENU && defined XINPUT
#include <xinput.h>
#if !defined(PSAPI_VERSION) || (PSAPI_VERSION > 1)
#pragma comment( lib, "Xinput9_1_0.lib" )
#else
#pragma comment( lib, "Xinput.lib" )
#endif
#endif
#include "Renderer.h"
#include "Credits.h"
#include "Camera.h"
#include "Weather.h"
#include "Clock.h"
#include "World.h"
#include "Vehicle.h"
#include "ModelIndices.h"
#include "Streaming.h"
#include "PathFind.h"
#include "Boat.h"
#include "Heli.h"
#include "Automobile.h"
#include "Console.h"
#include "Debug.h"
#include "Hud.h"
#include "SceneEdit.h"
#include "Pad.h"
#include "PlayerPed.h"
#include "Radar.h"
#include "debugmenu.h"
#include "Frontend.h"
#include "WaterLevel.h"
#include "main.h"
#include "Script.h"
#include "postfx.h"
#include "custompipes.h"
#include "MemoryHeap.h"
#include "FileMgr.h"
#include "Camera.h"
#include "MBlur.h"
#include "ControllerConfig.h"
#include "CarCtrl.h"
#include "Population.h"
#include "IniFile.h"
#include "Zones.h"

#include "crossplatform.h"

#ifndef _WIN32
#include "assert.h"
#include <stdarg.h>
#endif

#ifdef RWLIBS
extern "C" int vsprintf(char* const _Buffer, char const* const _Format, va_list  _ArgList);
#endif

#ifdef USE_PS2_RAND
unsigned long long myrand_seed = 1;
#else
unsigned long int myrand_seed = 1;
#endif

int
myrand(void)
{
#ifdef USE_PS2_RAND
	// Use our own implementation of rand, stolen from PS2
	myrand_seed = 0x5851F42D4C957F2D * myrand_seed + 1;
	return ((myrand_seed >> 32) & 0x7FFFFFFF);
#else
	// or original codewarrior rand
	myrand_seed = myrand_seed * 1103515245 + 12345;
	return((myrand_seed >> 16) & 0x7FFF);
#endif
}

void
mysrand(unsigned int seed)
{
	myrand_seed = seed;
}

#ifdef CUSTOM_FRONTEND_OPTIONS
#include "frontendoption.h"

#ifdef MORE_LANGUAGES
void LangPolSelect(int8 action)
{
	if (action == FEOPTION_ACTION_SELECT) {
		FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_POLISH;
		FrontEndMenuManager.m_bFrontEnd_ReloadObrTxtGxt = true;
		FrontEndMenuManager.InitialiseChangedLanguageSettings();
		FrontEndMenuManager.SaveSettings();
	}
}

void LangRusSelect(int8 action)
{
	if (action == FEOPTION_ACTION_SELECT) {
		FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_RUSSIAN;
		FrontEndMenuManager.m_bFrontEnd_ReloadObrTxtGxt = true;
		FrontEndMenuManager.InitialiseChangedLanguageSettings();
		FrontEndMenuManager.SaveSettings();
	}
}

void LangJapSelect(int8 action)
{
	if (action == FEOPTION_ACTION_SELECT) {
		FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_JAPANESE;
		FrontEndMenuManager.m_bFrontEnd_ReloadObrTxtGxt = true;
		FrontEndMenuManager.InitialiseChangedLanguageSettings();
		FrontEndMenuManager.SaveSettings();
	}
}
#endif

void
CustomFrontendOptionsPopulate(void)
{
	// Most of custom options are done statically in MenuScreensCustom.cpp, we add them here only if they're dependent to extra files

	// These work only if we have neo folder
	int fd;
#ifdef EXTENDED_PIPELINES
	const char *vehPipelineNames[] = { "FED_MFX", "FED_NEO" };
	const char *off_on[] = { "FEM_OFF", "FEM_ON" };
	fd = CFileMgr::OpenFile("neo/neo.txd","r");
	if (fd) {
#ifdef GRAPHICS_MENU_OPTIONS
		FrontendOptionSetCursor(MENUPAGE_GRAPHICS_SETTINGS, -3, false);
		FrontendOptionAddSelect("FED_VPL", vehPipelineNames, ARRAY_SIZE(vehPipelineNames), (int8*)&CustomPipes::VehiclePipeSwitch, false, nil, "Graphics", "VehiclePipeline");
		FrontendOptionAddSelect("FED_PRM", off_on, 2, (int8*)&CustomPipes::RimlightEnable, false, nil, "Graphics", "NeoRimLight");
		FrontendOptionAddSelect("FED_WLM", off_on, 2, (int8*)&CustomPipes::LightmapEnable, false, nil, "Graphics", "NeoLightMaps");
		FrontendOptionAddSelect("FED_RGL", off_on, 2, (int8*)&CustomPipes::GlossEnable, false, nil, "Graphics", "NeoRoadGloss");
#else
		FrontendOptionSetCursor(MENUPAGE_DISPLAY_SETTINGS, -3, false);
		FrontendOptionAddSelect("FED_VPL", vehPipelineNames, ARRAY_SIZE(vehPipelineNames), (int8*)&CustomPipes::VehiclePipeSwitch, false, nil, "Graphics", "VehiclePipeline");
		FrontendOptionAddSelect("FED_PRM", off_on, 2, (int8*)&CustomPipes::RimlightEnable, false, nil, "Graphics", "NeoRimLight");
		FrontendOptionAddSelect("FED_WLM", off_on, 2, (int8*)&CustomPipes::LightmapEnable, false, nil, "Graphics", "NeoLightMaps");
		FrontendOptionAddSelect("FED_RGL", off_on, 2, (int8*)&CustomPipes::GlossEnable, false, nil, "Graphics", "NeoRoadGloss");
#endif
		CFileMgr::CloseFile(fd);
	}
#endif

	// Add outsourced language translations, if files are found
#ifdef MORE_LANGUAGES
	int fd2;
	FrontendOptionSetCursor(MENUPAGE_LANGUAGE_SETTINGS, 5, false);
	if (fd = CFileMgr::OpenFile("text/polish.gxt","r")) {
		if (fd2 = CFileMgr::OpenFile("models/fonts_p.txd","r")) {
			FrontendOptionAddDynamic("FEL_POL", nil, nil, LangPolSelect, nil, nil);
			CFileMgr::CloseFile(fd2);
		}
		CFileMgr::CloseFile(fd);
	}

	if (fd = CFileMgr::OpenFile("text/russian.gxt","r")) {
		if (fd2 = CFileMgr::OpenFile("models/fonts_r.txd","r")) {
			FrontendOptionAddDynamic("FEL_RUS", nil, nil, LangRusSelect, nil, nil);
			CFileMgr::CloseFile(fd2);
		}
		CFileMgr::CloseFile(fd);
	}

	if (fd = CFileMgr::OpenFile("text/japanese.gxt","r")) {
		if (fd2 = CFileMgr::OpenFile("models/fonts_j.txd","r")) {
			FrontendOptionAddDynamic("FEL_JAP", nil, nil, LangJapSelect, nil, nil);
			CFileMgr::CloseFile(fd2);
		}
		CFileMgr::CloseFile(fd);
	}
#endif

}
#endif

#ifdef LOAD_INI_SETTINGS
#define MINI_CASE_SENSITIVE
#include "ini.h"

mINI::INIFile ini("re3.ini");
mINI::INIStructure cfg;

bool ReadIniIfExists(const char *cat, const char *key, uint32 *out)
{
	mINI::INIMap<std::string> section = cfg.get(cat);
	if (section.has(key)) {
		char *endPtr;
		*out = strtoul(section.get(key).c_str(), &endPtr, 0);
		return true;
	}
	return false;
}

bool ReadIniIfExists(const char *cat, const char *key, uint8 *out)
{
	mINI::INIMap<std::string> section = cfg.get(cat);
	if (section.has(key)) {
		char *endPtr;
		*out = strtoul(section.get(key).c_str(), &endPtr, 0);
		return true;
	}
	return false;
}

bool ReadIniIfExists(const char *cat, const char *key, bool *out)
{
	mINI::INIMap<std::string> section = cfg.get(cat);
	if (section.has(key)) {
		char *endPtr;
		*out = strtoul(section.get(key).c_str(), &endPtr, 0);
		return true;
	}
	return false;
}

bool ReadIniIfExists(const char *cat, const char *key, int32 *out)
{
	mINI::INIMap<std::string> section = cfg.get(cat);
	if (section.has(key)) {
		char *endPtr;
		*out = strtol(section.get(key).c_str(), &endPtr, 0);
		return true;
	}
	return false;
}

bool ReadIniIfExists(const char *cat, const char *key, int8 *out)
{
	mINI::INIMap<std::string> section = cfg.get(cat);
	if (section.has(key)) {
		char *endPtr;
		*out = strtol(section.get(key).c_str(), &endPtr, 0);
		return true;
	}
	return false;
}

bool ReadIniIfExists(const char *cat, const char *key, float *out)
{
	mINI::INIMap<std::string> section = cfg.get(cat);
	if (section.has(key)) {
		char *endPtr;
		*out = strtof(section.get(key).c_str(), &endPtr);
		return true;
	}
	return false;
}

bool ReadIniIfExists(const char *cat, const char *key, char *out, int size)
{
	mINI::INIMap<std::string> section = cfg.get(cat);
	if (section.has(key)) {
		strncpy(out, section.get(key).c_str(), size - 1);
		out[size - 1] = '\0';
		return true;
	}
	return false;
}

void StoreIni(const char *cat, const char *key, uint32 val)
{
	char temp[11];
	sprintf(temp, "%u", val);
	cfg[cat][key] = temp;
}

void StoreIni(const char *cat, const char *key, uint8 val)
{
	char temp[11];
	sprintf(temp, "%u", val);
	cfg[cat][key] = temp;
}

void StoreIni(const char *cat, const char *key, int32 val)
{
	char temp[11];
	sprintf(temp, "%d", val);
	cfg[cat][key] = temp;
}

void StoreIni(const char *cat, const char *key, int8 val)
{
	char temp[11];
	sprintf(temp, "%d", val);
	cfg[cat][key] = temp;
}

void StoreIni(const char *cat, const char *key, float val)
{
	char temp[50];
	sprintf(temp, "%f", val);
	cfg[cat][key] = temp;
}

void StoreIni(const char *cat, const char *key, char *val, int size)
{
	cfg[cat][key] = val;
}

const char *iniControllerActions[] = { "PED_FIREWEAPON", "PED_CYCLE_WEAPON_RIGHT", "PED_CYCLE_WEAPON_LEFT", "GO_FORWARD", "GO_BACK", "GO_LEFT", "GO_RIGHT", "PED_SNIPER_ZOOM_IN",
	"PED_SNIPER_ZOOM_OUT", "VEHICLE_ENTER_EXIT", "CAMERA_CHANGE_VIEW_ALL_SITUATIONS", "PED_JUMPING", "PED_SPRINT", "PED_LOOKBEHIND",
#ifdef BIND_VEHICLE_FIREWEAPON
	"VEHICLE_FIREWEAPON",
#endif
	"VEHICLE_ACCELERATE", "VEHICLE_BRAKE", "VEHICLE_CHANGE_RADIO_STATION", "VEHICLE_HORN", "TOGGLE_SUBMISSIONS", "VEHICLE_HANDBRAKE", "PED_1RST_PERSON_LOOK_LEFT",
	"PED_1RST_PERSON_LOOK_RIGHT", "VEHICLE_LOOKLEFT", "VEHICLE_LOOKRIGHT", "VEHICLE_LOOKBEHIND", "VEHICLE_TURRETLEFT", "VEHICLE_TURRETRIGHT", "VEHICLE_TURRETUP", "VEHICLE_TURRETDOWN",
	"PED_CYCLE_TARGET_LEFT", "PED_CYCLE_TARGET_RIGHT", "PED_CENTER_CAMERA_BEHIND_PLAYER", "PED_LOCK_TARGET", "NETWORK_TALK", "PED_1RST_PERSON_LOOK_UP", "PED_1RST_PERSON_LOOK_DOWN",
	"_CONTROLLERACTION_36", "TOGGLE_DPAD", "SWITCH_DEBUG_CAM_ON", "TAKE_SCREEN_SHOT", "SHOW_MOUSE_POINTER_TOGGLE" };

const char *iniControllerTypes[] = { "kbd:", "2ndKbd:", "mouse:", "joy:" };

const char *iniMouseButtons[] = {"LEFT","MIDDLE","RIGHT","WHLUP","WHLDOWN","X1","X2"};

const char *iniKeyboardButtons[] = {"ESC","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12",
	"INS","DEL","HOME","END","PGUP","PGDN","UP","DOWN","LEFT","RIGHT","DIVIDE","TIMES","PLUS","MINUS","PADDEL",
	"PADEND","PADDOWN","PADPGDN","PADLEFT","PAD5","NUMLOCK","PADRIGHT","PADHOME","PADUP","PADPGUP","PADINS",
	"PADENTER", "SCROLL","PAUSE","BACKSP","TAB","CAPSLK","ENTER","LSHIFT","RSHIFT","SHIFT","LCTRL","RCTRL","LALT",
	"RALT", "LWIN", "RWIN", "APPS", "NULL"};

void LoadINIControllerSettings()
{
#ifdef DETECT_JOYSTICK_MENU
#ifdef XINPUT
	int storedJoy1 = -1;
	if (ReadIniIfExists("Controller", "JoystickName", &storedJoy1)) {
		CPad::XInputJoy1 = -1;
		CPad::XInputJoy2 = -1;
		XINPUT_STATE xstate;
		memset(&xstate, 0, sizeof(XINPUT_STATE));

		// Firstly confirm & set joy 1
		if (XInputGetState(storedJoy1, &xstate) == ERROR_SUCCESS) {
			CPad::XInputJoy1 = storedJoy1;
		}

		for (int i = 0; i <= 3; i++) {
			if (XInputGetState(i, &xstate) == ERROR_SUCCESS) {
				if (CPad::XInputJoy1 == -1)
					CPad::XInputJoy1 = i;
				else if (CPad::XInputJoy2 == -1 && i != CPad::XInputJoy1)
					CPad::XInputJoy2 = i;
			}
		}

		// There is no plug event on XInput, so let's leave XInputJoy1/2 as 0/1 respectively, and hotplug will be possible.
		if (CPad::XInputJoy1 == -1) {
			CPad::XInputJoy1 = 0;
			CPad::XInputJoy2 = 1;
		} else if (CPad::XInputJoy2 == -1) {
			CPad::XInputJoy2 = (CPad::XInputJoy1 + 1) % 4;
		}
	}
#else
	ReadIniIfExists("Controller", "JoystickName", gSelectedJoystickName, 128);
#endif
#endif
	// force to default GTA behaviour (never overwrite bindings on joy change/initialization) if user init'ed/set bindings before we introduced that
	if (!ReadIniIfExists("Controller", "PadButtonsInited", &ControlsManager.ms_padButtonsInited)) {
		ControlsManager.ms_padButtonsInited = cfg.get("Bindings").size() != 0 ? 16 : 0;
	}

	for (int32 i = 0; i < MAX_CONTROLLERACTIONS; i++) {
		char value[128];
		if (ReadIniIfExists("Bindings", iniControllerActions[i], value, 128)) {
			for (int32 j = 0; j < MAX_CONTROLLERTYPES; j++){
				ControlsManager.ClearSettingsAssociatedWithAction((e_ControllerAction)i, (eControllerType)j);
			}

			for (char *binding = strtok(value,", "); binding != nil; binding = strtok(nil, ", ")) {
				int contType = -1;
				for (int32 k = 0; k < ARRAY_SIZE(iniControllerTypes); k++) {
					int len = strlen(iniControllerTypes[k]);
					if (strncmp(binding, iniControllerTypes[k], len) == 0) {
						contType = k;
						binding += len;
						break;
					}
				}
				if (contType == -1)
					continue;

				int contKey;
				if (contType == JOYSTICK) {
					char *temp;
					contKey = strtol(binding, &temp, 0);

				} else if (contType == KEYBOARD || contType == OPTIONAL_EXTRA) {
					if (strlen(binding) == 1) {
						contKey = binding[0];
					} else if(strcmp(binding, "SPC") == 0) {
						contKey = ' ';
					} else {
						for (int32 k = 0; k < ARRAY_SIZE(iniKeyboardButtons); k++) {
							if(strcmp(binding, iniKeyboardButtons[k]) == 0) {
								contKey = 1000 + k;
								break;
							}
						}
					}
				} else if (contType == MOUSE) {
					for (int32 k = 0; k < ARRAY_SIZE(iniMouseButtons); k++) {
						if(strcmp(binding, iniMouseButtons[k]) == 0) {
							contKey = 1 + k;
							break;
						}
					}
				}

				ControlsManager.SetControllerKeyAssociatedWithAction((e_ControllerAction)i, contKey, (eControllerType)contType);
			}
		}
	}
}

void SaveINIControllerSettings()
{
	for (int32 i = 0; i < MAX_CONTROLLERACTIONS; i++) {
		char value[128] = { '\0' };

		// upper limit should've been GetNumOfSettingsForAction(i), but sadly even R* doesn't use it's own system correctly, and there are gaps between orders.
		for (int32 j = SETORDER_1; j < MAX_SETORDERS; j++){

			// We respect the m_ContSetOrder, and join/implode/order the bindings according to that; using comma as seperator.
			for (int32 k = 0; k < MAX_CONTROLLERTYPES; k++){
				if (ControlsManager.m_aSettings[i][k].m_ContSetOrder == j) {
					char next[32];
					if (k == JOYSTICK) {
						snprintf(next, 32, "%s%d,", iniControllerTypes[k], ControlsManager.m_aSettings[i][k].m_Key);

					} else if (k == KEYBOARD || k == OPTIONAL_EXTRA) {
						if (ControlsManager.m_aSettings[i][k].m_Key == ' ')
							snprintf(next, 32, "%sSPC,", iniControllerTypes[k]);
						else if (ControlsManager.m_aSettings[i][k].m_Key < 256)
							snprintf(next, 32, "%s%c,", iniControllerTypes[k], ControlsManager.m_aSettings[i][k].m_Key);
						else
							snprintf(next, 32, "%s%s,", iniControllerTypes[k], iniKeyboardButtons[ControlsManager.m_aSettings[i][k].m_Key - 1000]);

					} else if (k == MOUSE) {
						snprintf(next, 32, "%s%s,", iniControllerTypes[k], iniMouseButtons[ControlsManager.m_aSettings[i][k].m_Key - 1]);
					}
					strcat(value, next);
					break;
				}
			}
		}
		int len = strlen(value);
		if (len > 0)
			value[len - 1] = '\0'; // to remove comma

		StoreIni("Bindings", iniControllerActions[i], value, 128);
	}

#ifdef DETECT_JOYSTICK_MENU
#ifdef XINPUT
	StoreIni("Controller", "JoystickName", CPad::XInputJoy1);
#else
	StoreIni("Controller", "JoystickName", gSelectedJoystickName, 128);
#endif
#endif
	StoreIni("Controller", "PadButtonsInited", ControlsManager.ms_padButtonsInited);

	ini.write(cfg);
}

bool LoadINISettings()
{
	if (!ini.read(cfg))
		return false;

#ifdef IMPROVED_VIDEOMODE
	ReadIniIfExists("VideoMode", "Width", &FrontEndMenuManager.m_nPrefsWidth);
	ReadIniIfExists("VideoMode", "Height", &FrontEndMenuManager.m_nPrefsHeight);
	ReadIniIfExists("VideoMode", "Depth", &FrontEndMenuManager.m_nPrefsDepth);
	ReadIniIfExists("VideoMode", "Subsystem", &FrontEndMenuManager.m_nPrefsSubsystem);
	// Windowed mode is loaded below in CUSTOM_FRONTEND_OPTIONS section
#else
	ReadIniIfExists("Graphics", "VideoMode", &FrontEndMenuManager.m_nDisplayVideoMode);
#endif
	ReadIniIfExists("Controller", "HeadBob1stPerson", &TheCamera.m_bHeadBob);
	ReadIniIfExists("Controller", "VerticalMouseSens", &TheCamera.m_fMouseAccelVertical);
	ReadIniIfExists("Controller", "HorizantalMouseSens", &TheCamera.m_fMouseAccelHorzntl);
	ReadIniIfExists("Controller", "InvertMouseVertically", &MousePointerStateHelper.bInvertVertically);
	ReadIniIfExists("Controller", "DisableMouseSteering", &CVehicle::m_bDisableMouseSteering);
	ReadIniIfExists("Controller", "Vibration", &FrontEndMenuManager.m_PrefsUseVibration);
	ReadIniIfExists("Audio", "SfxVolume", &FrontEndMenuManager.m_PrefsSfxVolume);
	ReadIniIfExists("Audio", "MusicVolume", &FrontEndMenuManager.m_PrefsMusicVolume);
	ReadIniIfExists("Audio", "Radio", &FrontEndMenuManager.m_PrefsRadioStation);
#ifdef EXTERNAL_3D_SOUND
	ReadIniIfExists("Audio", "SpeakerType", &FrontEndMenuManager.m_PrefsSpeakers);
	ReadIniIfExists("Audio", "Provider", &FrontEndMenuManager.m_nPrefsAudio3DProviderIndex);
#endif
	ReadIniIfExists("Audio", "DynamicAcoustics", &FrontEndMenuManager.m_PrefsDMA);
	ReadIniIfExists("Display", "Brightness", &FrontEndMenuManager.m_PrefsBrightness);
	ReadIniIfExists("Display", "DrawDistance", &FrontEndMenuManager.m_PrefsLOD);
	ReadIniIfExists("Display", "Subtitles", &FrontEndMenuManager.m_PrefsShowSubtitles);
	ReadIniIfExists("Graphics", "AspectRatio", &FrontEndMenuManager.m_PrefsUseWideScreen);
	ReadIniIfExists("Graphics", "VSync", &FrontEndMenuManager.m_PrefsVsyncDisp);
	ReadIniIfExists("Graphics", "FrameLimiter", &FrontEndMenuManager.m_PrefsFrameLimiter);
	ReadIniIfExists("Graphics", "Trails", &CMBlur::BlurOn);
	ReadIniIfExists("General", "SkinFile", FrontEndMenuManager.m_PrefsSkinFile, 256);
	ReadIniIfExists("Controller", "Method", &FrontEndMenuManager.m_ControlMethod);
	ReadIniIfExists("General", "Language", &FrontEndMenuManager.m_PrefsLanguage);

#ifdef EXTENDED_COLOURFILTER
	ReadIniIfExists("CustomPipesValues", "PostFXIntensity", &CPostFX::Intensity);
#endif
#ifdef EXTENDED_PIPELINES
	ReadIniIfExists("CustomPipesValues", "NeoVehicleShininess", &CustomPipes::VehicleShininess);
	ReadIniIfExists("CustomPipesValues", "NeoVehicleSpecularity", &CustomPipes::VehicleSpecularity);
	ReadIniIfExists("CustomPipesValues", "RimlightMult", &CustomPipes::RimlightMult);
	ReadIniIfExists("CustomPipesValues", "LightmapMult", &CustomPipes::LightmapMult);
	ReadIniIfExists("CustomPipesValues", "GlossMult", &CustomPipes::GlossMult);
#endif
#ifdef NEW_RENDERER
	ReadIniIfExists("Rendering", "NewRenderer", &gbNewRenderer);
#endif

#ifdef PROPER_SCALING
	ReadIniIfExists("Draw", "ProperScaling", &CDraw::ms_bProperScaling);
#endif
#ifdef FIX_RADAR
	ReadIniIfExists("Draw", "FixRadar", &CDraw::ms_bFixRadar);
#endif
#ifdef FIX_SPRITES
	ReadIniIfExists("Draw", "FixSprites", &CDraw::ms_bFixSprites);
#endif
#ifdef DRAW_GAME_VERSION_TEXT
	ReadIniIfExists("General", "DrawVersionText", &gbDrawVersionText);
#endif
#ifdef NO_MOVIES
	ReadIniIfExists("General", "NoMovies", &gbNoMovies);
#endif

#ifdef CUSTOM_FRONTEND_OPTIONS
	bool migrate = cfg.get("FrontendOptions").size() != 0;
	for (int i = 0; i < MENUPAGES; i++) {
		for (int j = 0; j < NUM_MENUROWS; j++) {
			CMenuScreenCustom::CMenuEntry &option = aScreens[i].m_aEntries[j];
			if (option.m_Action == MENUACTION_NOTHING)
				break;

			// CFO check
			if (option.m_Action < MENUACTION_NOTHING && option.m_CFO->save) {
				// Migrate from old .ini to new .ini
				// Old values can only be int8, new ones can contain float if it is slider
				if (migrate && ReadIniIfExists("FrontendOptions", option.m_CFO->save, (int8*)option.m_CFO->value))
					cfg["FrontendOptions"].remove(option.m_CFO->save);
				else if (option.m_Action == MENUACTION_CFO_SLIDER)
					ReadIniIfExists(option.m_CFO->saveCat, option.m_CFO->save, (float*)option.m_CFO->value);
				else
					ReadIniIfExists(option.m_CFO->saveCat, option.m_CFO->save, (int8*)option.m_CFO->value);

				if (option.m_Action == MENUACTION_CFO_SELECT) {
					option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue = *(int8*)option.m_CFO->value;
				}
			}
		}
	}
#endif

	// Fetched in above block, but needs evaluation
#ifdef PED_CAR_DENSITY_SLIDERS
	CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS * CIniFile::PedNumberMultiplier;
	CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS * CIniFile::CarNumberMultiplier;
#endif

	return true;
}

void SaveINISettings()
{
#ifdef IMPROVED_VIDEOMODE
	StoreIni("VideoMode", "Width", FrontEndMenuManager.m_nPrefsWidth);
	StoreIni("VideoMode", "Height", FrontEndMenuManager.m_nPrefsHeight);
	StoreIni("VideoMode", "Depth", FrontEndMenuManager.m_nPrefsDepth);
	StoreIni("VideoMode", "Subsystem", FrontEndMenuManager.m_nPrefsSubsystem);
	// Windowed mode is loaded below in CUSTOM_FRONTEND_OPTIONS section
#else
	StoreIni("Graphics", "VideoMode", FrontEndMenuManager.m_nDisplayVideoMode);
#endif
	StoreIni("Controller", "HeadBob1stPerson", TheCamera.m_bHeadBob);
	StoreIni("Controller", "VerticalMouseSens", TheCamera.m_fMouseAccelVertical);
	StoreIni("Controller", "HorizantalMouseSens", TheCamera.m_fMouseAccelHorzntl);
	StoreIni("Controller", "InvertMouseVertically", MousePointerStateHelper.bInvertVertically);
	StoreIni("Controller", "DisableMouseSteering", CVehicle::m_bDisableMouseSteering);
	StoreIni("Controller", "Vibration", FrontEndMenuManager.m_PrefsUseVibration);
	StoreIni("Audio", "SfxVolume", FrontEndMenuManager.m_PrefsSfxVolume);
	StoreIni("Audio", "MusicVolume", FrontEndMenuManager.m_PrefsMusicVolume);
	StoreIni("Audio", "Radio", FrontEndMenuManager.m_PrefsRadioStation);
#ifdef EXTERNAL_3D_SOUND
	StoreIni("Audio", "SpeakerType", FrontEndMenuManager.m_PrefsSpeakers);
	StoreIni("Audio", "Provider", FrontEndMenuManager.m_nPrefsAudio3DProviderIndex);
#endif
	StoreIni("Audio", "DynamicAcoustics", FrontEndMenuManager.m_PrefsDMA);
	StoreIni("Display", "Brightness", FrontEndMenuManager.m_PrefsBrightness);
	StoreIni("Display", "DrawDistance", FrontEndMenuManager.m_PrefsLOD);
	StoreIni("Display", "Subtitles", FrontEndMenuManager.m_PrefsShowSubtitles);
	StoreIni("Graphics", "AspectRatio", FrontEndMenuManager.m_PrefsUseWideScreen);
	StoreIni("Graphics", "VSync", FrontEndMenuManager.m_PrefsVsyncDisp);
	StoreIni("Graphics", "FrameLimiter", FrontEndMenuManager.m_PrefsFrameLimiter);
	StoreIni("Graphics", "Trails", CMBlur::BlurOn);
	StoreIni("General", "SkinFile", FrontEndMenuManager.m_PrefsSkinFile, 256);
	StoreIni("Controller", "Method", FrontEndMenuManager.m_ControlMethod);
	StoreIni("General", "Language", FrontEndMenuManager.m_PrefsLanguage);

#ifdef EXTENDED_COLOURFILTER
	StoreIni("CustomPipesValues", "PostFXIntensity", CPostFX::Intensity);
#endif
#ifdef EXTENDED_PIPELINES
	StoreIni("CustomPipesValues", "NeoVehicleShininess", CustomPipes::VehicleShininess);
	StoreIni("CustomPipesValues", "NeoVehicleSpecularity", CustomPipes::VehicleSpecularity);
	StoreIni("CustomPipesValues", "RimlightMult", CustomPipes::RimlightMult);
	StoreIni("CustomPipesValues", "LightmapMult", CustomPipes::LightmapMult);
	StoreIni("CustomPipesValues", "GlossMult", CustomPipes::GlossMult);
#endif
#ifdef NEW_RENDERER
	StoreIni("Rendering", "NewRenderer", gbNewRenderer);
#endif

#ifdef PROPER_SCALING
	StoreIni("Draw", "ProperScaling", CDraw::ms_bProperScaling);
#endif
#ifdef FIX_RADAR
	StoreIni("Draw", "FixRadar", CDraw::ms_bFixRadar);
#endif
#ifdef FIX_SPRITES
	StoreIni("Draw", "FixSprites", CDraw::ms_bFixSprites);
#endif
#ifdef DRAW_GAME_VERSION_TEXT
	StoreIni("General", "DrawVersionText", gbDrawVersionText);
#endif
#ifdef NO_MOVIES
	StoreIni("General", "NoMovies", gbNoMovies);
#endif
#ifdef CUSTOM_FRONTEND_OPTIONS
	for (int i = 0; i < MENUPAGES; i++) {
		for (int j = 0; j < NUM_MENUROWS; j++) {
			CMenuScreenCustom::CMenuEntry &option = aScreens[i].m_aEntries[j];
			if (option.m_Action == MENUACTION_NOTHING)
				break;

			if (option.m_Action < MENUACTION_NOTHING && option.m_CFO->save) {
				if (option.m_Action == MENUACTION_CFO_SLIDER)
					StoreIni(option.m_CFO->saveCat, option.m_CFO->save, *(float*)option.m_CFO->value);
				else
					StoreIni(option.m_CFO->saveCat, option.m_CFO->save, *(int8*)option.m_CFO->value);
			}
		}
	}
#endif

	ini.write(cfg);
}

#endif

#ifdef DEBUGMENU
void WeaponCheat();
void HealthCheat();
void TankCheat();
void BlowUpCarsCheat();
void ChangePlayerCheat();
void MayhemCheat();
void EverybodyAttacksPlayerCheat();
void WeaponsForAllCheat();
void FastTimeCheat();
void SlowTimeCheat();
void MoneyCheat();
void ArmourCheat();
void WantedLevelUpCheat();
void WantedLevelDownCheat();
void SunnyWeatherCheat();
void CloudyWeatherCheat();
void RainyWeatherCheat();
void FoggyWeatherCheat();
void FastWeatherCheat();
void OnlyRenderWheelsCheat();
void ChittyChittyBangBangCheat();
void StrongGripCheat();
void NastyLimbsCheat();

DebugMenuEntry *carCol1;
DebugMenuEntry *carCol2;

void
SpawnCar(int id)
{
	CVector playerpos;
	CStreaming::RequestModel(id, 0);
	CStreaming::LoadAllRequestedModels(false);
	if(CStreaming::HasModelLoaded(id)){
		playerpos = FindPlayerCoors();
		int node;
		if(!CModelInfo::IsBoatModel(id)){
			node = ThePaths.FindNodeClosestToCoors(playerpos, 0, 100.0f, false, false);
			if(node < 0)
				return;
		}

		CVehicle *v;
		if(CModelInfo::IsBoatModel(id))
			v = new CBoat(id, RANDOM_VEHICLE);
		else
			v = new CAutomobile(id, RANDOM_VEHICLE);

		v->bHasBeenOwnedByPlayer = true;
		if(carCol1)
			DebugMenuEntrySetAddress(carCol1, &v->m_currentColour1);
		if(carCol2)
			DebugMenuEntrySetAddress(carCol2, &v->m_currentColour2);

		if(CModelInfo::IsBoatModel(id))
			v->SetPosition(TheCamera.GetPosition() + TheCamera.GetForward()*15.0f);
		else
			v->SetPosition(ThePaths.m_pathNodes[node].GetPosition());

		v->GetMatrix().GetPosition().z += 4.0f;
		v->SetOrientation(0.0f, 0.0f, 3.49f);
		v->SetStatus(STATUS_ABANDONED);
		v->m_nDoorLock = CARLOCK_UNLOCKED;
		CWorld::Add(v);
	}
}

static void
FixCar(void)
{
	CVehicle *veh = FindPlayerVehicle();
	if(veh == nil)
		return;
	veh->m_fHealth = 1000.0f;
	if(!veh->IsCar())
		return;
	((CAutomobile*)veh)->Damage.SetEngineStatus(0);
	((CAutomobile*)veh)->Fix();
}

#ifdef MENU_MAP
static void
TeleportToWaypoint(void)
{
	if (CRadar::TargetMarkerId == -1)
		return;
	CEntity* pEntityToTeleport = FindPlayerEntity();
	CVector vNewPos = CRadar::TargetMarkerPos;
	CGame::currLevel = CTheZones::GetLevelFromPosition(&vNewPos);
	CCollision::SortOutCollisionAfterLoad();
	CStreaming::LoadScene(vNewPos);
	vNewPos.z = CWorld::FindGroundZForCoord(vNewPos.x, vNewPos.y) + pEntityToTeleport->GetDistanceFromCentreOfMassToBaseOfModel();
	pEntityToTeleport->Teleport(vNewPos);
}
#endif

static void
SwitchCarCollision(void)
{
	if (FindPlayerVehicle() && FindPlayerVehicle()->IsCar())
		FindPlayerVehicle()->bUsesCollision = !FindPlayerVehicle()->bUsesCollision;
}

static void
ToggleComedy(void)
{
	CVehicle *veh = FindPlayerVehicle();
	if(veh == nil)
		return;
	veh->bComedyControls = !veh->bComedyControls;
}

static void
PlaceOnRoad(void)
{
	CVehicle *veh = FindPlayerVehicle();
	if(veh == nil)
		return;

	if(veh->IsCar())
		((CAutomobile*)veh)->PlaceOnRoadProperly();
}

static void
ResetCamStatics(void)
{
	TheCamera.Cams[TheCamera.ActiveCam].ResetStatics = true;
}

#ifdef MISSION_SWITCHER
int8 nextMissionToSwitch = 0;
static void
SwitchToMission(void)
{
	CTheScripts::SwitchToMission(nextMissionToSwitch);
}
#endif

#ifdef USE_CUSTOM_ALLOCATOR
static void ParseHeap(void) { gMainHeap.ParseHeap(); }
#endif

static const char *carnames[] = {
	"landstal", "idaho", "stinger", "linerun", "peren", "sentinel", "patriot", "firetruk", "trash", "stretch", "manana", "infernus", "blista", "pony",
	"mule", "cheetah", "ambulan", "fbicar", "moonbeam", "esperant", "taxi", "kuruma", "bobcat", "mrwhoop", "bfinject", "corpse", "police", "enforcer",
	"securica", "banshee", "predator", "bus", "rhino", "barracks", "train", "chopper", "dodo", "coach", "cabbie", "stallion", "rumpo", "rcbandit",
	"bellyup", "mrwongs", "mafia", "yardie", "yakuza", "diablos", "columb", "hoods", "airtrain", "deaddodo", "speeder", "reefer", "panlant", "flatbed",
	"yankee", "escape", "borgnine", "toyz", "ghost",
};

//#include <list>

static CTweakVar** TweakVarsList;
static int TweakVarsListSize = -1;
static bool bAddTweakVarsNow = false;
static const char *pTweakVarsDefaultPath = NULL;

void CTweakVars::Add(CTweakVar *var)
{
	if(TweakVarsListSize == -1) {
		TweakVarsList = (CTweakVar**)malloc(64 * sizeof(CTweakVar*));
		TweakVarsListSize = 0;
	}
	if(TweakVarsListSize > 63)
		TweakVarsList = (CTweakVar**) realloc(TweakVarsList, (TweakVarsListSize + 1) * sizeof(CTweakVar*));

	TweakVarsList[TweakVarsListSize++] = var;
//	TweakVarsList.push_back(var);

	if ( bAddTweakVarsNow )
		var->AddDBG(pTweakVarsDefaultPath);
}

void CTweakVars::AddDBG(const char *path)
{
	pTweakVarsDefaultPath = path;

	for(int i = 0; i < TweakVarsListSize; ++i)
		TweakVarsList[i]->AddDBG(pTweakVarsDefaultPath);

	bAddTweakVarsNow = true;
}

void CTweakSwitch::AddDBG(const char *path)
{
	DebugMenuEntry *e = DebugMenuAddVar(m_pPath == NULL ? path : m_pPath, m_pVarName, (int32_t *)m_pIntVar, m_pFunc, 1, m_nMin, m_nMax, m_aStr);
	DebugMenuEntrySetWrap(e, true);
}

void CTweakFunc::AddDBG  (const char *path) { DebugMenuAddCmd     (m_pPath == NULL ? path : m_pPath, m_pVarName, m_pFunc); }
void CTweakBool::AddDBG  (const char *path) { DebugMenuAddVarBool8(m_pPath == NULL ? path : m_pPath, m_pVarName, (int8_t *)m_pBoolVar,  NULL); }
void CTweakInt8::AddDBG  (const char *path) { DebugMenuAddVar     (m_pPath == NULL ? path : m_pPath, m_pVarName, (int8_t *)m_pIntVar,   NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); }
void CTweakUInt8::AddDBG (const char *path) { DebugMenuAddVar     (m_pPath == NULL ? path : m_pPath, m_pVarName, (uint8_t *)m_pIntVar,  NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); }
void CTweakInt16::AddDBG (const char *path) { DebugMenuAddVar     (m_pPath == NULL ? path : m_pPath, m_pVarName, (int16_t *)m_pIntVar,  NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); }
void CTweakUInt16::AddDBG(const char *path) { DebugMenuAddVar     (m_pPath == NULL ? path : m_pPath, m_pVarName, (uint16_t *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); }
void CTweakInt32::AddDBG (const char *path) { DebugMenuAddVar     (m_pPath == NULL ? path : m_pPath, m_pVarName, (int32_t *)m_pIntVar,  NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); }
void CTweakUInt32::AddDBG(const char *path) { DebugMenuAddVar     (m_pPath == NULL ? path : m_pPath, m_pVarName, (uint32_t *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); }
void CTweakFloat::AddDBG (const char *path) { DebugMenuAddVar     (m_pPath == NULL ? path : m_pPath, m_pVarName, (float *)m_pIntVar,    NULL, m_nStep, m_nLoawerBound, m_nUpperBound); }

/*
static const char *wt[] = {
			"Sunny", "Cloudy", "Rainy", "Foggy"
		};

SETTWEAKPATH("TEST");
TWEAKSWITCH(CWeather::NewWeatherType, 0, 3, wt, NULL);
*/

void
DebugMenuPopulate(void)
{
	if(1){
		static const char *weathers[] = {
			"Sunny", "Cloudy", "Rainy", "Foggy"
		};
		DebugMenuEntry *e;
		e = DebugMenuAddVar("Time & Weather", "Current Hour", &CClock::GetHoursRef(), nil, 1, 0, 23, nil);
		DebugMenuEntrySetWrap(e, true);
		e = DebugMenuAddVar("Time & Weather", "Current Minute", &CClock::GetMinutesRef(),
			[](){ CWeather::InterpolationValue = CClock::GetMinutes()/60.0f; }, 1, 0, 59, nil);
			DebugMenuEntrySetWrap(e, true);
		e = DebugMenuAddVar("Time & Weather", "Old Weather", (int16*)&CWeather::OldWeatherType, nil, 1, 0, 3, weathers);
		DebugMenuEntrySetWrap(e, true);
		e = DebugMenuAddVar("Time & Weather", "New Weather", (int16*)&CWeather::NewWeatherType, nil, 1, 0, 3, weathers);
		DebugMenuEntrySetWrap(e, true);
		DebugMenuAddVar("Time & Weather", "Wind", (float*)&CWeather::Wind, nil, 0.1f, 0.0f, 1.0f);
		DebugMenuAddVar("Time & Weather", "Time scale", (float*)&CTimer::GetTimeScale(), nil, 0.1f, 0.0f, 10.0f);

		DebugMenuAddCmd("Cheats", "Weapons", WeaponCheat);
		DebugMenuAddCmd("Cheats", "Money", MoneyCheat);
		DebugMenuAddCmd("Cheats", "Health", HealthCheat);
		DebugMenuAddCmd("Cheats", "Wanted level up", WantedLevelUpCheat);
		DebugMenuAddCmd("Cheats", "Wanted level down", WantedLevelDownCheat);
		DebugMenuAddCmd("Cheats", "Tank", TankCheat);
		DebugMenuAddCmd("Cheats", "Blow up cars", BlowUpCarsCheat);
		DebugMenuAddCmd("Cheats", "Change player", ChangePlayerCheat);
		DebugMenuAddCmd("Cheats", "Mayhem", MayhemCheat);
		DebugMenuAddCmd("Cheats", "Everybody attacks player", EverybodyAttacksPlayerCheat);
		DebugMenuAddCmd("Cheats", "Weapons for all", WeaponsForAllCheat);
		DebugMenuAddCmd("Cheats", "Fast time", FastTimeCheat);
		DebugMenuAddCmd("Cheats", "Slow time", SlowTimeCheat);
		DebugMenuAddCmd("Cheats", "Armour", ArmourCheat);
		DebugMenuAddCmd("Cheats", "Sunny weather", SunnyWeatherCheat);
		DebugMenuAddCmd("Cheats", "Cloudy weather", CloudyWeatherCheat);
		DebugMenuAddCmd("Cheats", "Rainy weather", RainyWeatherCheat);
		DebugMenuAddCmd("Cheats", "Foggy weather", FoggyWeatherCheat);
		DebugMenuAddCmd("Cheats", "Fast weather", FastWeatherCheat);
		DebugMenuAddCmd("Cheats", "Only render wheels", OnlyRenderWheelsCheat);
		DebugMenuAddCmd("Cheats", "Chitty chitty bang bang", ChittyChittyBangBangCheat);
		DebugMenuAddCmd("Cheats", "Strong grip", StrongGripCheat);
		DebugMenuAddCmd("Cheats", "Nasty limbs", NastyLimbsCheat);

		static int spawnCarId = MI_LANDSTAL;
		e = DebugMenuAddVar("Spawn", "Spawn Car ID", &spawnCarId, nil, 1, MI_LANDSTAL, MI_GHOST, carnames);
		DebugMenuEntrySetWrap(e, true);
		DebugMenuAddCmd("Spawn", "Spawn Car", [](){
			if(spawnCarId == MI_TRAIN ||
			   spawnCarId == MI_CHOPPER ||
			   spawnCarId == MI_AIRTRAIN ||
			   spawnCarId == MI_DEADDODO ||
			   spawnCarId == MI_ESCAPE)
				return;
			SpawnCar(spawnCarId);
		});
		static uint8 dummy;
		carCol1 = DebugMenuAddVar("Spawn", "First colour", &dummy, nil, 1, 0, 255, nil);
		carCol2 = DebugMenuAddVar("Spawn", "Second colour", &dummy, nil, 1, 0, 255, nil);
		DebugMenuAddCmd("Spawn", "Spawn Stinger", [](){ SpawnCar(MI_STINGER); });
		DebugMenuAddCmd("Spawn", "Spawn Infernus", [](){ SpawnCar(MI_INFERNUS); });
		DebugMenuAddCmd("Spawn", "Spawn Cheetah", [](){ SpawnCar(MI_CHEETAH); });
		DebugMenuAddCmd("Spawn", "Spawn Esperanto", [](){ SpawnCar(MI_ESPERANT); });
		DebugMenuAddCmd("Spawn", "Spawn Stallion", [](){ SpawnCar(MI_STALLION); });
		DebugMenuAddCmd("Spawn", "Spawn Kuruma", [](){ SpawnCar(MI_KURUMA); });
		DebugMenuAddCmd("Spawn", "Spawn Taxi", [](){ SpawnCar(MI_TAXI); });
		DebugMenuAddCmd("Spawn", "Spawn Police", [](){ SpawnCar(MI_POLICE); });
		DebugMenuAddCmd("Spawn", "Spawn Enforcer", [](){ SpawnCar(MI_ENFORCER); });
		DebugMenuAddCmd("Spawn", "Spawn Banshee", [](){ SpawnCar(MI_BANSHEE); });
		DebugMenuAddCmd("Spawn", "Spawn Yakuza", [](){ SpawnCar(MI_YAKUZA); });
		DebugMenuAddCmd("Spawn", "Spawn Yardie", [](){ SpawnCar(MI_YARDIE); });
		DebugMenuAddCmd("Spawn", "Spawn Dodo", [](){ SpawnCar(MI_DODO); });
		DebugMenuAddCmd("Spawn", "Spawn Rhino", [](){ SpawnCar(MI_RHINO); });
		DebugMenuAddCmd("Spawn", "Spawn Firetruck", [](){ SpawnCar(MI_FIRETRUCK); });
		DebugMenuAddCmd("Spawn", "Spawn Predator", [](){ SpawnCar(MI_PREDATOR); });

		DebugMenuAddVarBool8("Render", "Draw hud", &CHud::m_Wants_To_Draw_Hud, nil);

#ifdef PROPER_SCALING
		DebugMenuAddVarBool8("Render", "Proper Scaling", &CDraw::ms_bProperScaling, nil);
#endif
#ifdef FIX_RADAR
		DebugMenuAddVarBool8("Render", "Fix Radar", &CDraw::ms_bFixRadar, nil);
#endif
#ifdef FIX_SPRITES
		DebugMenuAddVarBool8("Render", "Fix Sprites", &CDraw::ms_bFixSprites, nil);
#endif
		DebugMenuAddVarBool8("Render", "PS2 Alpha test Emu", &gPS2alphaTest, nil);
		DebugMenuAddVarBool8("Render", "Frame limiter", &FrontEndMenuManager.m_PrefsFrameLimiter, nil);
		DebugMenuAddVarBool8("Render", "VSynch", &FrontEndMenuManager.m_PrefsVsync, nil);
		DebugMenuAddVar("Render", "Max FPS", &RsGlobal.maxFPS, nil, 1, 1, 1000, nil);
#ifdef NEW_RENDERER
		DebugMenuAddVarBool8("Render", "New Renderer", &gbNewRenderer, nil);
extern bool gbRenderRoads;
extern bool gbRenderEverythingBarRoads;
//extern bool gbRenderFadingInUnderwaterEntities;
extern bool gbRenderFadingInEntities;
extern bool gbRenderWater;
extern bool gbRenderBoats;
extern bool gbRenderVehicles;
extern bool gbRenderWorld0;
extern bool gbRenderWorld1;
extern bool gbRenderWorld2;
		DebugMenuAddVarBool8("Debug Render", "gbRenderRoads", &gbRenderRoads, nil);
		DebugMenuAddVarBool8("Debug Render", "gbRenderEverythingBarRoads", &gbRenderEverythingBarRoads, nil);
//		DebugMenuAddVarBool8("Debug Render", "gbRenderFadingInUnderwaterEntities", &gbRenderFadingInUnderwaterEntities, nil);
		DebugMenuAddVarBool8("Debug Render", "gbRenderFadingInEntities", &gbRenderFadingInEntities, nil);
		DebugMenuAddVarBool8("Debug Render", "gbRenderWater", &gbRenderWater, nil);
		DebugMenuAddVarBool8("Debug Render", "gbRenderBoats", &gbRenderBoats, nil);
		DebugMenuAddVarBool8("Debug Render", "gbRenderVehicles", &gbRenderVehicles, nil);
		DebugMenuAddVarBool8("Debug Render", "gbRenderWorld0", &gbRenderWorld0, nil);
		DebugMenuAddVarBool8("Debug Render", "gbRenderWorld1", &gbRenderWorld1, nil);
		DebugMenuAddVarBool8("Debug Render", "gbRenderWorld2", &gbRenderWorld2, nil);
#endif

#ifdef EXTENDED_COLOURFILTER
		static const char *filternames[] = { "None", "Simple", "Normal", "Mobile" };
		e = DebugMenuAddVar("Render", "Colourfilter", &CPostFX::EffectSwitch, nil, 1, CPostFX::POSTFX_OFF, CPostFX::POSTFX_MOBILE, filternames);
		DebugMenuEntrySetWrap(e, true);
		DebugMenuAddVar("Render", "Intensity", &CPostFX::Intensity, nil, 0.05f, 0, 10.0f);
		DebugMenuAddVarBool8("Render", "Motion Blur", &CPostFX::MotionBlurOn, nil);
#endif
#ifdef LIBRW
		DebugMenuAddVarBool32("Render", "MatFX env map apply light", &rw::MatFX::envMapApplyLight, nil);
		DebugMenuAddVarBool32("Render", "MatFX env map flip U", &rw::MatFX::envMapFlipU, nil);
		DebugMenuAddVarBool32("Render", "MatFX env map use matcolor", &rw::MatFX::envMapUseMatColor, nil);
#endif
#ifdef EXTENDED_PIPELINES
		static const char *vehpipenames[] = { "MatFX", "Neo" };
		e = DebugMenuAddVar("Render", "Vehicle Pipeline", &CustomPipes::VehiclePipeSwitch, nil,
			1, CustomPipes::VEHICLEPIPE_MATFX, CustomPipes::VEHICLEPIPE_NEO, vehpipenames);
		DebugMenuEntrySetWrap(e, true);
		DebugMenuAddVar("Render", "Neo Vehicle Shininess", &CustomPipes::VehicleShininess, nil, 0.1f, 0, 1.0f);
		DebugMenuAddVar("Render", "Neo Vehicle Specularity", &CustomPipes::VehicleSpecularity, nil, 0.1f, 0, 1.0f);
		DebugMenuAddVarBool8("Render", "Neo Ped Rim light enable", &CustomPipes::RimlightEnable, nil);
		DebugMenuAddVar("Render", "Mult", &CustomPipes::RimlightMult, nil, 0.1f, 0, 1.0f);
		DebugMenuAddVarBool8("Render", "Neo World Lightmaps enable", &CustomPipes::LightmapEnable, nil);
		DebugMenuAddVar("Render", "Mult", &CustomPipes::LightmapMult, nil, 0.1f, 0, 1.0f);
		DebugMenuAddVarBool8("Render", "Neo Road Gloss enable", &CustomPipes::GlossEnable, nil);
		DebugMenuAddVar("Render", "Mult", &CustomPipes::GlossMult, nil, 0.1f, 0, 1.0f);
#endif
		DebugMenuAddVarBool8("Debug Render", "Show Ped Paths", &gbShowPedPaths, nil);
		DebugMenuAddVarBool8("Debug Render", "Show Car Paths", &gbShowCarPaths, nil);
		DebugMenuAddVarBool8("Debug Render", "Show Car Path Links", &gbShowCarPathsLinks, nil);
		DebugMenuAddVarBool8("Debug Render", "Show Ped Road Groups", &gbShowPedRoadGroups, nil);
		DebugMenuAddVarBool8("Debug Render", "Show Car Road Groups", &gbShowCarRoadGroups, nil);
		DebugMenuAddVarBool8("Debug Render", "Show Collision Lines", &gbShowCollisionLines, nil);
		DebugMenuAddVarBool8("Debug Render", "Show Collision Polys", &gbShowCollisionPolys, nil);
		DebugMenuAddVarBool8("Debug Render", "Don't render Buildings", &gbDontRenderBuildings, nil);
		DebugMenuAddVarBool8("Debug Render", "Don't render Big Buildings", &gbDontRenderBigBuildings, nil);
		DebugMenuAddVarBool8("Debug Render", "Don't render Peds", &gbDontRenderPeds, nil);
		DebugMenuAddVarBool8("Debug Render", "Don't render Vehicles", &gbDontRenderVehicles, nil);
		DebugMenuAddVarBool8("Debug Render", "Don't render Objects", &gbDontRenderObjects, nil);
		DebugMenuAddVarBool8("Debug Render", "Don't Render Water", &gbDontRenderWater, nil);

#ifdef DRAW_GAME_VERSION_TEXT
		DebugMenuAddVarBool8("Debug", "Version Text", &gbDrawVersionText, nil);
#endif
		DebugMenuAddVarBool8("Debug", "Show DebugStuffInRelease", &gbDebugStuffInRelease, nil);
#ifdef TIMEBARS
		DebugMenuAddVarBool8("Debug", "Show Timebars", &gbShowTimebars, nil);
#endif
#ifndef FINAL
		DebugMenuAddVarBool8("Debug", "Use debug render groups", &bDebugRenderGroups, nil);
		DebugMenuAddVarBool8("Debug", "Print Memory Usage", &gbPrintMemoryUsage, nil);
#ifdef USE_CUSTOM_ALLOCATOR
		DebugMenuAddCmd("Debug", "Parse Heap", ParseHeap);
#endif
#endif
		DebugMenuAddVarBool8("Debug", "Show cullzone debug stuff", &gbShowCullZoneDebugStuff, nil);
		DebugMenuAddVarBool8("Debug", "Disable zone cull", &gbDisableZoneCull, nil);

		DebugMenuAddVarBool8("Debug", "pad 1 -> pad 2", &CPad::m_bMapPadOneToPadTwo, nil);
#ifdef GTA_SCENE_EDIT
		DebugMenuAddVarBool8("Debug", "Edit on", &CSceneEdit::m_bEditOn, nil);
#endif
		//DebugMenuAddCmd("Debug", "Start Credits", CCredits::Start);
		//DebugMenuAddCmd("Debug", "Stop Credits", CCredits::Stop);

#ifdef MENU_MAP
		DebugMenuAddCmd("Game", "Teleport to map waypoint", TeleportToWaypoint);
#endif
		DebugMenuAddCmd("Game", "Fix Car", FixCar);
		DebugMenuAddCmd("Game", "Place Car on Road", PlaceOnRoad);
		DebugMenuAddCmd("Game", "Switch car collision", SwitchCarCollision);
		DebugMenuAddCmd("Game", "Toggle Comedy Controls", ToggleComedy);

		DebugMenuAddVarBool8("Game", "Toggle popping heads on headshot", &CPed::bPopHeadsOnHeadshot, nil);

#ifdef MISSION_SWITCHER
		DebugMenuEntry *missionEntry;
		static const char* missions[] = {
			"Intro Movie", "Hospital Info Scene", "Police Station Info Scene",
			"RC Diablo Destruction", "RC Mafia Massacre", "RC Rumpo Rampage", "RC Casino Calamity",
			"Patriot Playground", "A Ride In The Park", "Gripped!", "Multistorey Mayhem",
			"Paramedic", "Firefighter", "Vigilante", "Taxi Driver",
			"The Crook", "The Thieves", "The Wife", "Her Lover",
			"Give Me Liberty and Luigi's Girls", "Don't Spank My Bitch Up", "Drive Misty For Me", "Pump-Action Pimp", "The Fuzz Ball",
			"Mike Lips Last Lunch", "Farewell 'Chunky' Lee Chong", "Van Heist", "Cipriani's Chauffeur", "Dead Skunk In The Trunk", "The Getaway",
			"Taking Out The Laundry", "The Pick-Up", "Salvatore's Called A Meeting", "Triads And Tribulations", "Blow Fish", "Chaperone", "Cutting The Grass",
			"Bomb Da Base: Act I", "Bomb Da Base: Act II", "Last Requests", "Turismo", "I Scream, You Scream", "Trial By Fire", "Big'N'Veiny", "Sayonara Salvatore",
			"Under Surveillance", "Paparazzi Purge", "Payday For Ray", "Two-Faced Tanner", "Kanbu Bust-Out", "Grand Theft Auto", "Deal Steal", "Shima", "Smack Down",
			"Silence The Sneak", "Arms Shortage", "Evidence Dash", "Gone Fishing", "Plaster Blaster", "Marked Man",
			"Liberator", "Waka-Gashira Wipeout!", "A Drop In The Ocean", "Bling-Bling Scramble", "Uzi Rider", "Gangcar Round-Up", "Kingdom Come",
			"Grand Theft Aero", "Escort Service", "Decoy", "Love's Disappearance", "Bait", "Espresso-2-Go!", "S.A.M.",
			"Uzi Money", "Toyminator", "Rigged To Blow", "Bullion Run", "Rumble", "The Exchange"
		};

		missionEntry = DebugMenuAddVar("Game", "Select mission", &nextMissionToSwitch, nil, 1, 0, ARRAY_SIZE(missions) - 1, missions);
		DebugMenuEntrySetWrap(missionEntry, true);
		DebugMenuAddCmd("Game", "Start selected mission ", SwitchToMission);
#endif

		extern bool PrintDebugCode;
		extern int16 DebugCamMode;
		DebugMenuAddVarBool8("Cam", "Use mouse Cam", &CCamera::m_bUseMouse3rdPerson, nil);
#ifdef FREE_CAM
		DebugMenuAddVarBool8("Cam", "Free Cam", &CCamera::bFreeCam, nil);
#endif
		DebugMenuAddVarBool8("Cam", "Print Debug Code", &PrintDebugCode, nil);
		DebugMenuAddVar("Cam", "Cam Mode", &DebugCamMode, nil, 1, 0, CCam::MODE_EDITOR, nil);
		DebugMenuAddCmd("Cam", "Normal", []() { DebugCamMode = 0; });
	//	DebugMenuAddCmd("Cam", "Follow Ped With Bind", []() { DebugCamMode = CCam::MODE_FOLLOW_PED_WITH_BIND; });
	//	DebugMenuAddCmd("Cam", "Reaction", []() { DebugCamMode = CCam::MODE_REACTION; });
	//	DebugMenuAddCmd("Cam", "Chris", []() { DebugCamMode = CCam::MODE_CHRIS; });
		DebugMenuAddCmd("Cam", "Reset Statics", ResetCamStatics);

		CTweakVars::AddDBG("Debug");
	}
}
#endif

#ifndef __MWERKS__
#ifndef MASTER
const int   re3_buffsize = 1024;
static char re3_buff[re3_buffsize];
#endif

#ifndef MASTER
void re3_assert(const char *expr, const char *filename, unsigned int lineno, const char *func)
{
#ifdef _WIN32
	int nCode;

	strcpy_s(re3_buff, re3_buffsize, "Assertion failed!" );
	strcat_s(re3_buff, re3_buffsize, "\n" );

	strcat_s(re3_buff, re3_buffsize, "File: ");
	strcat_s(re3_buff, re3_buffsize, filename );
	strcat_s(re3_buff, re3_buffsize, "\n" );

	strcat_s(re3_buff, re3_buffsize, "Line: " );
	_itoa_s( lineno, re3_buff + strlen(re3_buff), re3_buffsize - strlen(re3_buff), 10 );
	strcat_s(re3_buff, re3_buffsize, "\n");

	strcat_s(re3_buff, re3_buffsize, "Function: ");
	strcat_s(re3_buff, re3_buffsize, func );
	strcat_s(re3_buff, re3_buffsize, "\n" );

	strcat_s(re3_buff, re3_buffsize, "Expression: ");
	strcat_s(re3_buff, re3_buffsize, expr);
	strcat_s(re3_buff, re3_buffsize, "\n");

	strcat_s(re3_buff, re3_buffsize, "\n" );
	strcat_s(re3_buff, re3_buffsize, "(Press Retry to debug the application)");

	nCode = ::MessageBoxA(nil, re3_buff, "RE3 Assertion Failed!",
		MB_ABORTRETRYIGNORE|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL);

	if (nCode == IDABORT)
	{
		raise(SIGABRT);
		_exit(3);
	}

	if (nCode == IDRETRY)
	{
		__debugbreak();
		return;
	}

	if (nCode == IDIGNORE)
		return;

	abort();
#else
	// TODO
	printf("\nRE3 ASSERT FAILED\n\tFile: %s\n\tLine: %d\n\tFunction: %s\n\tExpression: %s\n",filename,lineno,func,expr);
	assert(false);
#endif
}
#endif

void re3_debug(const char *format, ...)
{
#ifndef MASTER
	va_list va;
	va_start(va, format);
#ifdef _WIN32
	vsprintf_s(re3_buff, re3_buffsize, format, va);
#else
	vsprintf(re3_buff, format, va);
#endif
	va_end(va);

	printf("%s", re3_buff);
	CDebug::DebugAddText(re3_buff);
#endif
}

#ifndef MASTER
void re3_trace(const char *filename, unsigned int lineno, const char *func, const char *format, ...)
{
	char buff[re3_buffsize *2];
	va_list va;
	va_start(va, format);
#ifdef _WIN32
	vsprintf_s(re3_buff, re3_buffsize, format, va);
	va_end(va);

	sprintf_s(buff, re3_buffsize * 2, "[%s.%s:%d]: %s", filename, func, lineno, re3_buff);
#else
	vsprintf(re3_buff, format, va);
	va_end(va);

	sprintf(buff, "[%s.%s:%d]: %s", filename, func, lineno, re3_buff);
#endif

	OutputDebugString(buff);
}
#endif

#ifndef MASTER
void re3_usererror(const char *format, ...)
{
	va_list va;
	va_start(va, format);
#ifdef _WIN32
	vsprintf_s(re3_buff, re3_buffsize, format, va);
	va_end(va);

	::MessageBoxA(nil, re3_buff, "RE3 Error!",
		MB_OK|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL);

	raise(SIGABRT);
	_exit(3);
#else
	vsprintf(re3_buff, format, va);
	printf("\nRE3 Error!\n\t%s\n",re3_buff);
	assert(false);
#endif
}
#endif
#endif

#ifdef VALIDATE_SAVE_SIZE
int32 _saveBufCount;
#endif


code:
#pragma once

template<typename T, int32 n>
class CStore
{
public:
	int32 allocPtr;
	T store[n];

	T *Alloc(void){
		if(allocPtr >= n){
			printf("Size of this thing:%d needs increasing\n", n);
			assert(0);
		}
		return &store[allocPtr++];
	}
	void Clear(void){
		allocPtr = 0;
	}
	int32 GetIndex(T *item){
		assert(item >= &store[0]);
		assert(item < &store[n]);
		return item - store;
	}
	T *GetItem(int32 index){
		assert(index >= 0);
		assert(index < n);
		return &store[index];
	}
};

#define POOLFLAG_ID     0x7f
#define POOLFLAG_ISFREE 0x80

template<typename T, typename U = T>
class CPool
{
	U     *m_entries;
	uint8 *m_flags;
	int32  m_size;
	int32  m_allocPtr;

public:
	CPool(int32 size){
		m_entries = (U*)new uint8[sizeof(U)*size];
		m_flags = new uint8[size];
		m_size = size;
		m_allocPtr = 0;
		for(int i = 0; i < size; i++){
			SetId(i, 0);
			SetIsFree(i, true);
		}
	}

	int GetId(int i) const
	{
		return m_flags[i] & POOLFLAG_ID;
	}

	bool GetIsFree(int i) const
	{
		return !!(m_flags[i] & POOLFLAG_ISFREE);
	}

	void SetId(int i, int id)
	{
		m_flags[i] = (m_flags[i] & POOLFLAG_ISFREE) | (id & POOLFLAG_ID);
	}

	void SetIsFree(int i, bool isFree)
	{
		if (isFree)
			m_flags[i] |= POOLFLAG_ISFREE;
		else
			m_flags[i] &= ~POOLFLAG_ISFREE;
	}

	~CPool() {
		Flush();
	}
	void Flush() {
		if (m_size > 0) {
			delete[] (uint8*)m_entries;
			delete[] m_flags;
			m_entries = nil;
			m_flags = nil;
			m_size = 0;
			m_allocPtr = 0;
		}
	}
	int32 GetSize(void) const { return m_size; }
	T *New(void){
		bool wrapped = false;
		do
#ifdef FIX_BUGS
			if (++m_allocPtr >= m_size) {
				m_allocPtr = 0;
				if (wrapped)
					return nil;
				wrapped = true;
			}
#else
			if(++m_allocPtr == m_size){
				if(wrapped)
					return nil;
				wrapped = true;
				m_allocPtr = 0;
			}
#endif
		while(!GetIsFree(m_allocPtr));
		SetIsFree(m_allocPtr, false);
		SetId(m_allocPtr, GetId(m_allocPtr)+1);
		return (T*)&m_entries[m_allocPtr];
	}
	T *New(int32 handle){
		T *entry = (T*)&m_entries[handle>>8];
		SetNotFreeAt(handle);
		return entry;
	}
	void SetNotFreeAt(int32 handle){
		int idx = handle>>8;
		SetIsFree(idx, false);
		SetId(idx, handle & POOLFLAG_ID);
		for(m_allocPtr = 0; m_allocPtr < m_size; m_allocPtr++)
			if(GetIsFree(m_allocPtr))
				return;
	}
	void Delete(T *entry){
		int i = GetJustIndex(entry);
		SetIsFree(i, true);
		if(i < m_allocPtr)
			m_allocPtr = i;
	}
	T *GetSlot(int i){
		return GetIsFree(i) ? nil : (T*)&m_entries[i];
	}
	T *GetAt(int handle){
#ifdef FIX_BUGS
		if (handle == -1)
			return nil;
#endif
		return m_flags[handle>>8] == (handle & 0xFF) ?
		       (T*)&m_entries[handle >> 8] : nil;
	}
	int32 GetIndex(T *entry){
		int i = GetJustIndex_NoFreeAssert(entry);
		return m_flags[i] + (i<<8);
	}
	int32 GetJustIndex(T *entry){
		int index = GetJustIndex_NoFreeAssert(entry);
		assert(!GetIsFree(index));
		return index;
	}
	int32 GetJustIndex_NoFreeAssert(T* entry){
		int index = ((U*)entry - m_entries);
		assert((U*)entry == (U*)&m_entries[index]); // cast is unsafe - check required
		return index;
	}
	int32 GetNoOfUsedSpaces(void) const{
		int i;
		int n = 0;
		for(i = 0; i < m_size; i++)
			if(!GetIsFree(i))
				n++;
		return n;
	}
	void ClearStorage(uint8 *&flags, U *&entries){
		delete[] flags;
		delete[] (uint8*)entries;
		flags = nil;
		entries = nil;
	}
	uint32 GetMaxEntrySize() const { return sizeof(U); }
	void CopyBack(uint8 *&flags, U *&entries){
		memcpy(m_flags, flags, sizeof(uint8)*m_size);
		memcpy(m_entries, entries, sizeof(U)*m_size);
		debug("Size copied:%d (%d)\n", sizeof(U)*m_size, m_size);
		m_allocPtr = 0;
		ClearStorage(flags, entries);
		debug("CopyBack:%d (/%d)\n", GetNoOfUsedSpaces(), m_size); /* Assumed inlining */
	}
	void Store(uint8 *&flags, U *&entries){
		flags = (uint8*)new uint8[sizeof(uint8)*m_size];
		entries = (U*)new uint8[sizeof(U)*m_size];
		memcpy(flags, m_flags, sizeof(uint8)*m_size);
		memcpy(entries, m_entries, sizeof(U)*m_size);
		debug("Stored:%d (/%d)\n", GetNoOfUsedSpaces(), m_size); /* Assumed inlining */
	}
};

template<typename T>
class CLink
{
public:
	T item;
	CLink<T> *prev;
	CLink<T> *next;

	void Insert(CLink<T> *link){
		link->next = this->next;
		this->next->prev = link;
		link->prev = this;
		this->next = link;
	}
	void Remove(void){
		this->prev->next = this->next;
		this->next->prev = this->prev;
	}
};

template<typename T>
class CLinkList
{
public:
	CLink<T> head, tail;
	CLink<T> freeHead, freeTail;
	CLink<T> *links;

	void Init(int n){
		links = new CLink<T>[n];
		head.next = &tail;
		tail.prev = &head;
		freeHead.next = &freeTail;
		freeTail.prev = &freeHead;
		while(n--)
			freeHead.Insert(&links[n]);
	}
	void Shutdown(void){
		delete[] links;
		links = nil;
	}
	void Clear(void){
		while(head.next != &tail)
			Remove(head.next);
	}
	CLink<T> *Insert(T const &item){
		CLink<T> *node = freeHead.next;
		if(node == &freeTail)
			return nil;
		node->item = item;
		node->Remove();		// remove from free list
		head.Insert(node);
		return node;
	}
	CLink<T> *InsertSorted(T const &item){
		CLink<T> *sort;
		for(sort = head.next; sort != &tail; sort = sort->next)
			if(sort->item.sort >= item.sort)
				break;
		CLink<T> *node = freeHead.next;
		if(node == &freeTail)
			return nil;
		node->item = item;
		node->Remove();		// remove from free list
		sort->prev->Insert(node);
		return node;
	}
	void Remove(CLink<T> *link){
		link->Remove();		// remove from list
		freeHead.Insert(link);	// insert into free list
	}
	int32 Count(void){
		int n = 0;
		CLink<T> *lnk;
		for(lnk = head.next; lnk != &tail; lnk = lnk->next)
			n++;
		return n;
	}
};

license: mit


code:
#include "common.h"
#ifndef MASTER
#include "Font.h"
#include "Frontend.h"
#include "Timer.h"
#include "Text.h"

#define MAX_TIMERS (50)
#define MAX_MS_COLLECTED (40)

// enables frame time output
#define FRAMETIME

struct sTimeBar
{
	char name[20];
	float startTime;
	float endTime;
	int32 unk;
};

struct
{
	sTimeBar Timers[MAX_TIMERS];
	uint32 count;
} TimerBar;
float MaxTimes[MAX_TIMERS];
float MaxFrameTime;

uint32 curMS;
uint32 msCollected[MAX_MS_COLLECTED];
#ifdef FRAMETIME
float FrameInitTime;
#endif

void tbInit()
{
	TimerBar.count = 0;
	uint32 i = CTimer::GetFrameCounter() & 0x7F;
	if (i == 0) {
		do
			MaxTimes[i++] = 0.0f;
		while (i != MAX_TIMERS);
#ifdef FRAMETIME
		MaxFrameTime = 0.0f;
#endif
	}
#ifdef FRAMETIME
	FrameInitTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame();
#endif
}

void tbStartTimer(int32 unk, Const char *name)
{
	strcpy(TimerBar.Timers[TimerBar.count].name, name);
	TimerBar.Timers[TimerBar.count].unk = unk;
	TimerBar.Timers[TimerBar.count].startTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame();
	TimerBar.count++;
}

void tbEndTimer(Const char* name)
{
	uint32 n = 1500;
	for (uint32 i = 0; i < TimerBar.count; i++) {
		if (strcmp(name, TimerBar.Timers[i].name) == 0)
			n = i;
	}
	assert(n != 1500);
	TimerBar.Timers[n].endTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame();
}

float Diag_GetFPS()
{
	return 39000.0f / (msCollected[(curMS - 1) % MAX_MS_COLLECTED] - msCollected[curMS % MAX_MS_COLLECTED]);
}

void tbDisplay()
{
	char temp[200];
	wchar wtemp[200];

#ifdef FRAMETIME
	float FrameEndTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame();
#endif

	msCollected[(curMS++) % MAX_MS_COLLECTED] = RsTimer();
	CFont::SetBackgroundOff();
	CFont::SetBackgroundColor(CRGBA(0, 0, 0, 128));
	CFont::SetScale(0.48f, 1.12f);
	CFont::SetCentreOff();
	CFont::SetJustifyOff();
	CFont::SetWrapx(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH));
	CFont::SetRightJustifyOff();
	CFont::SetPropOn();
	CFont::SetFontStyle(FONT_BANK);
	sprintf(temp, "FPS: %.2f", Diag_GetFPS());
	AsciiToUnicode(temp, wtemp);
	CFont::SetColor(CRGBA(255, 255, 255, 255));
	if (!CMenuManager::m_PrefsMarketing || !CMenuManager::m_PrefsDisableTutorials) {
		CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * (4.0f / DEFAULT_SCREEN_HEIGHT), wtemp);

#ifndef FINAL
		// Timers output (my own implementation)
		for (uint32 i = 0; i < TimerBar.count; i++) {
			MaxTimes[i] = Max(MaxTimes[i], TimerBar.Timers[i].endTime - TimerBar.Timers[i].startTime);
			sprintf(temp, "%s: %.2f", &TimerBar.Timers[i].name[0], MaxTimes[i]);
			AsciiToUnicode(temp, wtemp);
			CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * ((8.0f * (i + 2)) / DEFAULT_SCREEN_HEIGHT), wtemp);
		}

#ifdef FRAMETIME
		MaxFrameTime = Max(MaxFrameTime, FrameEndTime - FrameInitTime);
		sprintf(temp, "Frame Time: %.2f", MaxFrameTime);
		AsciiToUnicode(temp, wtemp);

		CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * ((8.0f * (TimerBar.count + 4)) / DEFAULT_SCREEN_HEIGHT), wtemp);
#endif // FRAMETIME
#endif // !FINAL
	}
}
#endif // !MASTER


code:
#pragma once

#ifdef TIMEBARS
void tbInit();
void tbStartTimer(int32, Const char*);
void tbEndTimer(Const char*);
void tbDisplay();
#else
#define tbInit()
#define tbStartTimer(a, b)
#define tbEndTimer(a)
#define tbDisplay()
#endif


code:
#include "common.h"

#include "Pools.h"
#include "World.h"
#include "Dummy.h"

void *CDummy::operator new(size_t sz) throw() { return CPools::GetDummyPool()->New();  }
void CDummy::operator delete(void *p, size_t sz) throw() { CPools::GetDummyPool()->Delete((CDummy*)p); }

void
CDummy::Add(void)
{
	int x, xstart, xmid, xend;
	int y, ystart, ymid, yend;
	CSector *s;
	CPtrList *list;

	CRect bounds = GetBoundRect();
	xstart = CWorld::GetSectorIndexX(bounds.left);
	xend   = CWorld::GetSectorIndexX(bounds.right);
	xmid   = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f);
	ystart = CWorld::GetSectorIndexY(bounds.top);
	yend   = CWorld::GetSectorIndexY(bounds.bottom);
	ymid   = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f);
	assert(xstart >= 0);
	assert(xend < NUMSECTORS_X);
	assert(ystart >= 0);
	assert(yend < NUMSECTORS_Y);

	for(y = ystart; y <= yend; y++)
		for(x = xstart; x <= xend; x++){
			s = CWorld::GetSector(x, y);
			if(x == xmid && y == ymid)
				list = &s->m_lists[ENTITYLIST_DUMMIES];
			else
				list = &s->m_lists[ENTITYLIST_DUMMIES_OVERLAP];
			CPtrNode *node = list->InsertItem(this);
			assert(node);
			m_entryInfoList.InsertItem(list, node, s);
		}
}

void
CDummy::Remove(void)
{
	CEntryInfoNode *node, *next;
	for(node = m_entryInfoList.first; node; node = next){
		next = node->next;
		node->list->DeleteNode(node->listnode);
		m_entryInfoList.DeleteNode(node);
	}
}


code:
#pragma once

#include "Lists.h"
#include "Entity.h"

class CDummy : public CEntity
{
public:
	CEntryInfoList m_entryInfoList;

	CDummy(void) { m_type = ENTITY_TYPE_DUMMY; }
	void Add(void);
	void Remove(void);

	static void *operator new(size_t) throw();
	static void operator delete(void*, size_t) throw();
};

VALIDATE_SIZE(CDummy, 0x68);


code:
#include "common.h"

#include "General.h"
#include "RwHelper.h"
#include "ModelIndices.h"
#include "Timer.h"
#include "Entity.h"
#include "Object.h"
#include "World.h"
#include "Camera.h"
#include "Glass.h"
#include "Weather.h"
#include "Timecycle.h"
#include "TrafficLights.h"
#include "Coronas.h"
#include "PointLights.h"
#include "Shadows.h"
#include "Pickups.h"
#include "SpecialFX.h"
#include "TxdStore.h"
#include "Zones.h"
#include "MemoryHeap.h"
#include "Bones.h"
#include "Debug.h"
#include "SaveBuf.h"

int gBuildings;

CEntity::CEntity(void)
{
	m_type = ENTITY_TYPE_NOTHING;
	m_status = STATUS_ABANDONED;

	bUsesCollision = false;
	bCollisionProcessed = false;
	bIsStatic = false;
	bHasContacted = false;
	bPedPhysics = false;
	bIsStuck = false;
	bIsInSafePosition = false;
	bUseCollisionRecords = false;

	bWasPostponed = false;
	bExplosionProof = false;
	bIsVisible = true;
	bHasCollided = false;
	bRenderScorched = false;
	bHasBlip = false;
	bIsBIGBuilding = false;
	bRenderDamaged = false;

	bBulletProof = false;
	bFireProof = false;
	bCollisionProof = false;
	bMeleeProof = false;
	bOnlyDamagedByPlayer = false;
	bStreamingDontDelete = false;
	bZoneCulled = false;
	bZoneCulled2 = false;

	bRemoveFromWorld = false;
	bHasHitWall = false;
	bImBeingRendered = false;
	bTouchingWater = false;
	bIsSubway = false;
	bDrawLast = false;
	bNoBrightHeadLights = false;
	bDoNotRender = false;

	bDistanceFade = false;
	m_flagE2 = false;

	m_scanCode = 0;
	m_modelIndex = -1;
	m_rwObject = nil;
	m_randomSeed = CGeneral::GetRandomNumber();
	m_pFirstReference = nil;
}

CEntity::~CEntity(void)
{
	DeleteRwObject();
	ResolveReferences();
}

void
CEntity::SetModelIndex(uint32 id)
{
	m_modelIndex = id;
	CreateRwObject();
}

void
CEntity::SetModelIndexNoCreate(uint32 id)
{
	m_modelIndex = id;
}

void
CEntity::CreateRwObject(void)
{
	CBaseModelInfo *mi;

	mi = CModelInfo::GetModelInfo(m_modelIndex);

	PUSH_MEMID(MEMID_WORLD);
	m_rwObject = mi->CreateInstance();
	POP_MEMID();

	if(m_rwObject){
		if(IsBuilding())
			gBuildings++;
		if(RwObjectGetType(m_rwObject) == rpATOMIC)
			GetMatrix().AttachRW(RwFrameGetMatrix(RpAtomicGetFrame((RpAtomic *)m_rwObject)), false);
		else if(RwObjectGetType(m_rwObject) == rpCLUMP)
			GetMatrix().AttachRW(RwFrameGetMatrix(RpClumpGetFrame((RpClump *)m_rwObject)), false);
		mi->AddRef();
	}
}

void
CEntity::AttachToRwObject(RwObject *obj)
{
	m_rwObject = obj;
	if(m_rwObject){
		if(RwObjectGetType(m_rwObject) == rpATOMIC)
			GetMatrix().Attach(RwFrameGetMatrix(RpAtomicGetFrame((RpAtomic *)m_rwObject)), false);
		else if(RwObjectGetType(m_rwObject) == rpCLUMP)
			GetMatrix().Attach(RwFrameGetMatrix(RpClumpGetFrame((RpClump *)m_rwObject)), false);
		CModelInfo::GetModelInfo(m_modelIndex)->AddRef();
	}
}

void
CEntity::DetachFromRwObject(void)
{
	if(m_rwObject)
		CModelInfo::GetModelInfo(m_modelIndex)->RemoveRef();
	m_rwObject = nil;
	GetMatrix().Detach();
}

#ifdef PED_SKIN
RpAtomic*
AtomicRemoveAnimFromSkinCB(RpAtomic *atomic, void *data)
{
	if(RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic))){
		RpHAnimHierarchy *hier = RpSkinAtomicGetHAnimHierarchy(atomic);
#ifdef LIBRW
		if(hier && hier->interpolator->currentAnim){
			RpHAnimAnimationDestroy(hier->interpolator->currentAnim);
			hier->interpolator->currentAnim = nil;
		}
#else
		if(hier && hier->pCurrentAnim){
			RpHAnimAnimationDestroy(hier->pCurrentAnim);
			hier->pCurrentAnim = nil;
		}
#endif
	}
	return atomic;
}
#endif

void
CEntity::DeleteRwObject(void)
{
	RwFrame *f;

	GetMatrix().Detach();
	if(m_rwObject){
		if(RwObjectGetType(m_rwObject) == rpATOMIC){
			f = RpAtomicGetFrame((RpAtomic*)m_rwObject);
			RpAtomicDestroy((RpAtomic*)m_rwObject);
			RwFrameDestroy(f);
		}else if(RwObjectGetType(m_rwObject) == rpCLUMP){
#ifdef PED_SKIN
			if(IsClumpSkinned((RpClump*)m_rwObject))
				RpClumpForAllAtomics((RpClump*)m_rwObject, AtomicRemoveAnimFromSkinCB, nil);
#endif
			RpClumpDestroy((RpClump*)m_rwObject);
		}
		m_rwObject = nil;
		CModelInfo::GetModelInfo(m_modelIndex)->RemoveRef();
		if(IsBuilding())
			gBuildings--;
	}
}

CRect
CEntity::GetBoundRect(void)
{
	CRect rect;
	CVector v;
	CColModel *col = CModelInfo::GetColModel(m_modelIndex);

	rect.ContainPoint(GetMatrix() * col->boundingBox.min);
	rect.ContainPoint(GetMatrix() * col->boundingBox.max);

	v = col->boundingBox.min;
	v.x = col->boundingBox.max.x;
	rect.ContainPoint(GetMatrix() * v);

	v = col->boundingBox.max;
	v.x = col->boundingBox.min.x;
	rect.ContainPoint(GetMatrix() * v);

	return rect;
}

CVector
CEntity::GetBoundCentre(void)
{
	return GetMatrix() * CModelInfo::GetColModel(m_modelIndex)->boundingSphere.center;
}

#ifdef GTA_PS2
void
CEntity::GetBoundCentre(CVuVector &out)
{
	TransformPoint(out, GetMatrix(), CModelInfo::GetColModel(m_modelIndex)->boundingSphere.center);
}
#else
void
CEntity::GetBoundCentre(CVector &out)
{
	out = GetMatrix() * CModelInfo::GetColModel(m_modelIndex)->boundingSphere.center;
}
#endif

float
CEntity::GetBoundRadius(void)
{
	return CModelInfo::GetColModel(m_modelIndex)->boundingSphere.radius;
}

void
CEntity::UpdateRwFrame(void)
{
	if(m_rwObject)
		RwFrameUpdateObjects((RwFrame*)rwObjectGetParent(m_rwObject));
}

#ifdef PED_SKIN
void
CEntity::UpdateRpHAnim(void)
{
	RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(GetClump());
	RpHAnimHierarchyUpdateMatrices(hier);

#if 0
	int i;
	char buf[256];
	if(this == (CEntity*)FindPlayerPed())
	for(i = 0; i < hier->numNodes; i++){
		RpHAnimStdInterpFrame *kf = (RpHAnimStdInterpFrame*)rpHANIMHIERARCHYGETINTERPFRAME(hier, i);
		sprintf(buf, "%6.3f %6.3f %6.3f %6.3f  %6.3f %6.3f %6.3f  %d %s",
			kf->q.imag.x, kf->q.imag.y, kf->q.imag.z, kf->q.real,
			kf->t.x, kf->t.y, kf->t.z,
			HIERNODEID(hier, i),
			ConvertBoneTag2BoneName(HIERNODEID(hier, i)));
		CDebug::PrintAt(buf, 10, 1+i*3);

		RwMatrix *m = &RpHAnimHierarchyGetMatrixArray(hier)[i];
		sprintf(buf, "%6.3f %6.3f %6.3f %6.3f",
			m->right.x, m->up.x, m->at.x, m->pos.x);
		CDebug::PrintAt(buf, 80, 1+i*3+0);
		sprintf(buf, "%6.3f %6.3f %6.3f %6.3f",
			m->right.y, m->up.y, m->at.y, m->pos.y);
		CDebug::PrintAt(buf, 80, 1+i*3+1);
		sprintf(buf, "%6.3f %6.3f %6.3f %6.3f",
			m->right.z, m->up.z, m->at.z, m->pos.z);
		CDebug::PrintAt(buf, 80, 1+i*3+2);
	}

	void RenderSkeleton(RpHAnimHierarchy *hier);
	RenderSkeleton(hier);
#endif
}
#endif

void
CEntity::PreRender(void)
{
	switch(m_type){
	case ENTITY_TYPE_BUILDING:
		if(GetModelIndex() == MI_RAILTRACKS){
			CShadows::StoreShadowForPole(this, 0.0f, -10.949f, 5.0f, 8.0f, 1.0f, 0);
			CShadows::StoreShadowForPole(this, 0.0f, 10.949f, 5.0f, 8.0f, 1.0f, 1);
		}else if(IsTreeModel(GetModelIndex())){
			CShadows::StoreShadowForTree(this);
			ModifyMatrixForTreeInWind();
		}else if(IsBannerModel(GetModelIndex())){
			ModifyMatrixForBannerInWind();
		}
		break;
	case ENTITY_TYPE_OBJECT:
		if(GetModelIndex() == MI_COLLECTABLE1){
			CPickups::DoCollectableEffects(this);
			GetMatrix().UpdateRW();
			UpdateRwFrame();
		}else if(GetModelIndex() == MI_MONEY){
			CPickups::DoMoneyEffects(this);
			GetMatrix().UpdateRW();
			UpdateRwFrame();
		}else if(GetModelIndex() == MI_NAUTICALMINE ||
		         GetModelIndex() == MI_CARMINE ||
		         GetModelIndex() == MI_BRIEFCASE){
			if(((CObject*)this)->bIsPickup){
				CPickups::DoMineEffects(this);
				GetMatrix().UpdateRW();
				UpdateRwFrame();
			}
		}else if(IsPickupModel(GetModelIndex())){
			if(((CObject*)this)->bIsPickup){
				CPickups::DoPickUpEffects(this);
				GetMatrix().UpdateRW();
				UpdateRwFrame();
			}else if(GetModelIndex() == MI_GRENADE){
				CMotionBlurStreaks::RegisterStreak((uintptr)this,
					100, 100, 100,
					GetPosition() - 0.07f*TheCamera.GetRight(),
					GetPosition() + 0.07f*TheCamera.GetRight());
			}else if(GetModelIndex() == MI_MOLOTOV){
				CMotionBlurStreaks::RegisterStreak((uintptr)this,
					0, 100, 0,
					GetPosition() - 0.07f*TheCamera.GetRight(),
					GetPosition() + 0.07f*TheCamera.GetRight());
			}
		}else if(GetModelIndex() == MI_MISSILE){
			CVector pos = GetPosition();
			float flicker = (CGeneral::GetRandomNumber() & 0xF)/(float)0x10;
			CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE,
				gpShadowExplosionTex, &pos,
				8.0f, 0.0f, 0.0f, -8.0f,
				255, 200.0f*flicker, 160.0f*flicker, 120.0f*flicker,
				20.0f, false, 1.0f);
			CPointLights::AddLight(CPointLights::LIGHT_POINT,
				pos, CVector(0.0f, 0.0f, 0.0f),
				8.0f,
				1.0f*flicker,
				0.8f*flicker,
				0.6f*flicker,
				CPointLights::FOG_NONE, true);
			CCoronas::RegisterCorona((uintptr)this,
				255.0f*flicker, 220.0f*flicker, 190.0f*flicker, 255,
				pos, 6.0f*flicker, 80.0f, gpCoronaTexture[CCoronas::TYPE_STAR],
				CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
				CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
		}else if(IsGlass(GetModelIndex())){
			PreRenderForGlassWindow();
		}
		// fall through
	case ENTITY_TYPE_DUMMY:
		if(GetModelIndex() == MI_TRAFFICLIGHTS){
			CTrafficLights::DisplayActualLight(this);
			CShadows::StoreShadowForPole(this, 2.957f, 0.147f, 0.0f, 16.0f, 0.4f, 0);
		}else if(GetModelIndex() == MI_SINGLESTREETLIGHTS1)
			CShadows::StoreShadowForPole(this, 0.744f, 0.0f, 0.0f, 16.0f, 0.4f, 0);
		else if(GetModelIndex() == MI_SINGLESTREETLIGHTS2)
			CShadows::StoreShadowForPole(this, 0.043f, 0.0f, 0.0f, 16.0f, 0.4f, 0);
		else if(GetModelIndex() == MI_SINGLESTREETLIGHTS3)
			CShadows::StoreShadowForPole(this, 1.143f, 0.145f, 0.0f, 16.0f, 0.4f, 0);
		else if(GetModelIndex() == MI_DOUBLESTREETLIGHTS)
			CShadows::StoreShadowForPole(this, 0.0f, -0.048f, 0.0f, 16.0f, 0.4f, 0);
		else if(GetModelIndex() == MI_STREETLAMP1 ||
		        GetModelIndex() == MI_STREETLAMP2)
			CShadows::StoreShadowForPole(this, 0.0f, 0.0f, 0.0f, 16.0f, 0.4f, 0);
		break;
	}

	if (CModelInfo::GetModelInfo(GetModelIndex())->GetNum2dEffects() != 0)
		ProcessLightsForEntity();
}

void
CEntity::Render(void)
{
	if(m_rwObject){
		bImBeingRendered = true;
		if(RwObjectGetType(m_rwObject) == rpATOMIC)
			RpAtomicRender((RpAtomic*)m_rwObject);
		else
			RpClumpRender((RpClump*)m_rwObject);
		bImBeingRendered = false;
	}
}

bool
CEntity::GetIsTouching(CVUVECTOR const &center, float radius)
{
	CVUVECTOR boundCenter;
	GetBoundCentre(boundCenter);
	return sq(GetBoundRadius()+radius) > (boundCenter-center).MagnitudeSqr();
}

bool
CEntity::IsVisible(void)
{
	return m_rwObject && bIsVisible && GetIsOnScreen();
}

bool
CEntity::IsVisibleComplex(void)
{
	return m_rwObject && bIsVisible && GetIsOnScreenComplex();
}

bool
CEntity::GetIsOnScreen(void)
{
	return TheCamera.IsSphereVisible(GetBoundCentre(), GetBoundRadius());
}

bool
CEntity::GetIsOnScreenComplex(void)
{
#ifdef GTA_PS2
	CVuVector boundBox[8];
#else
	CVector boundBox[8];
#endif

	if(TheCamera.IsPointVisible(GetBoundCentre(), &TheCamera.GetCameraMatrix()))
		return true;

	CRect rect = GetBoundRect();
	CColModel *colmodel = CModelInfo::GetColModel(m_modelIndex);
	float z = GetPosition().z;
	float minz = z + colmodel->boundingBox.min.z;
	float maxz = z + colmodel->boundingBox.max.z;
	boundBox[0].x = rect.left;
	boundBox[0].y = rect.bottom;
	boundBox[0].z = minz;
	boundBox[1].x = rect.left;
	boundBox[1].y = rect.top;
	boundBox[1].z = minz;
	boundBox[2].x = rect.right;
	boundBox[2].y = rect.bottom;
	boundBox[2].z = minz;
	boundBox[3].x = rect.right;
	boundBox[3].y = rect.top;
	boundBox[3].z = minz;
	boundBox[4].x = rect.left;
	boundBox[4].y = rect.bottom;
	boundBox[4].z = maxz;
	boundBox[5].x = rect.left;
	boundBox[5].y = rect.top;
	boundBox[5].z = maxz;
	boundBox[6].x = rect.right;
	boundBox[6].y = rect.bottom;
	boundBox[6].z = maxz;
	boundBox[7].x = rect.right;
	boundBox[7].y = rect.top;
	boundBox[7].z = maxz;

	return TheCamera.IsBoxVisible(boundBox, &TheCamera.GetCameraMatrix());
}

void
CEntity::Add(void)
{
	int x, xstart, xmid, xend;
	int y, ystart, ymid, yend;
	CSector *s;
	CPtrList *list;

	CRect bounds = GetBoundRect();
	xstart = CWorld::GetSectorIndexX(bounds.left);
	xend   = CWorld::GetSectorIndexX(bounds.right);
	xmid   = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f);
	ystart = CWorld::GetSectorIndexY(bounds.top);
	yend   = CWorld::GetSectorIndexY(bounds.bottom);
	ymid   = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f);
	assert(xstart >= 0);
	assert(xend < NUMSECTORS_X);
	assert(ystart >= 0);
	assert(yend < NUMSECTORS_Y);

	for(y = ystart; y <= yend; y++)
		for(x = xstart; x <= xend; x++){
			s = CWorld::GetSector(x, y);
			if(x == xmid && y == ymid) switch(m_type){
			case ENTITY_TYPE_BUILDING:
				list = &s->m_lists[ENTITYLIST_BUILDINGS];
				break;
			case ENTITY_TYPE_VEHICLE:
				list = &s->m_lists[ENTITYLIST_VEHICLES];
				break;
			case ENTITY_TYPE_PED:
				list = &s->m_lists[ENTITYLIST_PEDS];
				break;
			case ENTITY_TYPE_OBJECT:
				list = &s->m_lists[ENTITYLIST_OBJECTS];
				break;
			case ENTITY_TYPE_DUMMY:
				list = &s->m_lists[ENTITYLIST_DUMMIES];
				break;
			}else switch(m_type){
			case ENTITY_TYPE_BUILDING:
				list = &s->m_lists[ENTITYLIST_BUILDINGS_OVERLAP];
				break;
			case ENTITY_TYPE_VEHICLE:
				list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP];
				break;
			case ENTITY_TYPE_PED:
				list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP];
				break;
			case ENTITY_TYPE_OBJECT:
				list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP];
				break;
			case ENTITY_TYPE_DUMMY:
				list = &s->m_lists[ENTITYLIST_DUMMIES_OVERLAP];
				break;
			}
			list->InsertItem(this);
		}
}

void
CEntity::Remove(void)
{
	int x, xstart, xmid, xend;
	int y, ystart, ymid, yend;
	CSector *s;
	CPtrList *list;

	CRect bounds = GetBoundRect();
	xstart = CWorld::GetSectorIndexX(bounds.left);
	xend   = CWorld::GetSectorIndexX(bounds.right);
	xmid   = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f);
	ystart = CWorld::GetSectorIndexY(bounds.top);
	yend   = CWorld::GetSectorIndexY(bounds.bottom);
	ymid   = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f);
	assert(xstart >= 0);
	assert(xend < NUMSECTORS_X);
	assert(ystart >= 0);
	assert(yend < NUMSECTORS_Y);

	for(y = ystart; y <= yend; y++)
		for(x = xstart; x <= xend; x++){
			s = CWorld::GetSector(x, y);
			if(x == xmid && y == ymid) switch(m_type){
			case ENTITY_TYPE_BUILDING:
				list = &s->m_lists[ENTITYLIST_BUILDINGS];
				break;
			case ENTITY_TYPE_VEHICLE:
				list = &s->m_lists[ENTITYLIST_VEHICLES];
				break;
			case ENTITY_TYPE_PED:
				list = &s->m_lists[ENTITYLIST_PEDS];
				break;
			case ENTITY_TYPE_OBJECT:
				list = &s->m_lists[ENTITYLIST_OBJECTS];
				break;
			case ENTITY_TYPE_DUMMY:
				list = &s->m_lists[ENTITYLIST_DUMMIES];
				break;
			}else switch(m_type){
			case ENTITY_TYPE_BUILDING:
				list = &s->m_lists[ENTITYLIST_BUILDINGS_OVERLAP];
				break;
			case ENTITY_TYPE_VEHICLE:
				list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP];
				break;
			case ENTITY_TYPE_PED:
				list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP];
				break;
			case ENTITY_TYPE_OBJECT:
				list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP];
				break;
			case ENTITY_TYPE_DUMMY:
				list = &s->m_lists[ENTITYLIST_DUMMIES_OVERLAP];
				break;
			}
			list->RemoveItem(this);
		}
}

float
CEntity::GetDistanceFromCentreOfMassToBaseOfModel(void)
{
	return -CModelInfo::GetColModel(m_modelIndex)->boundingBox.min.z;
}

void
CEntity::SetupBigBuilding(void)
{
	CSimpleModelInfo *mi;

	mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(m_modelIndex);
	bIsBIGBuilding = true;
	bStreamingDontDelete = true;
	bUsesCollision = false;
	m_level = CTheZones::GetLevelFromPosition(&GetPosition());
	if(m_level == LEVEL_GENERIC){
		if(mi->GetTxdSlot() != CTxdStore::FindTxdSlot("generic")){
			mi->SetTexDictionary("generic");
			printf("%d:%s txd has been set to generic\n", m_modelIndex, mi->GetModelName());
		}
	}
	if(mi->m_lodDistances[0] > 2000.0f)
		m_level = LEVEL_GENERIC;
}

float WindTabel[] = {
	1.0f, 0.5f, 0.2f, 0.7f, 0.4f, 1.0f, 0.5f, 0.3f,
	0.2f, 0.1f, 0.7f, 0.6f, 0.3f, 1.0f, 0.5f, 0.2f,
};

void
CEntity::ModifyMatrixForTreeInWind(void)
{
	uint16 t;
	float f;
	float strength, flutter;

	if(CTimer::GetIsPaused())
		return;

	CMatrix mat(GetMatrix().m_attachment);

	if(CWeather::Wind >= 0.5){
		t = m_randomSeed + 16*CTimer::GetTimeInMilliseconds();
		f = (t & 0xFFF)/(float)0x1000;
		flutter = f * WindTabel[(t>>12)+1 & 0xF] +
			(1.0f - f) * WindTabel[(t>>12) & 0xF] +
			1.0f;
		strength = CWeather::Wind < 0.8f ? 0.008f : 0.014f;
	}else if(CWeather::Wind >= 0.2){
		t = (uintptr)this + CTimer::GetTimeInMilliseconds();
		f = (t & 0xFFF)/(float)0x1000;
		flutter = Sin(f * 6.28f);
		strength = 0.008f;
	}else{
		t = (uintptr)this + CTimer::GetTimeInMilliseconds();
		f = (t & 0xFFF)/(float)0x1000;
		flutter = Sin(f * 6.28f);
		strength = 0.005f;
	}

	mat.GetUp().x = strength * flutter;
	mat.GetUp().y = mat.GetUp().x;

	mat.UpdateRW();
	UpdateRwFrame();
}

float BannerWindTabel[] = {
	0.0f, 0.3f, 0.6f, 0.85f, 0.99f, 0.97f, 0.65f, 0.15f,
	-0.1f, 0.0f, 0.35f, 0.57f, 0.55f, 0.35f, 0.45f, 0.67f,
	0.73f, 0.45f, 0.25f, 0.35f, 0.35f, 0.11f, 0.13f, 0.21f,
	0.28f, 0.28f, 0.22f, 0.1f, 0.0f, -0.1f, -0.17f, -0.12f
};

void
CEntity::ModifyMatrixForBannerInWind(void)
{
	uint16 t;
	float f;
	float strength, flutter;
	CVector right, up;

	if(CTimer::GetIsPaused())
		return;

	if(CWeather::Wind < 0.1f)
		strength = 0.2f;
	else if(CWeather::Wind < 0.8f)
		strength = 0.43f;
	else
		strength = 0.66f;

	t = ((int)(GetMatrix().GetPosition().x + GetMatrix().GetPosition().y) << 10) + 16*CTimer::GetTimeInMilliseconds();
	f = (t & 0x7FF)/(float)0x800;
	flutter = f * BannerWindTabel[(t>>11)+1 & 0x1F] +
		(1.0f - f) * BannerWindTabel[(t>>11) & 0x1F];
	flutter *= strength;

	right = CrossProduct(GetForward(), GetUp());
	right.z = 0.0f;
	right.Normalise();
	up = right * flutter;
	up.z = Sqrt(sq(1.0f) - sq(flutter));
	GetRight() = CrossProduct(GetForward(), up);
	GetUp() = up;

	GetMatrix().UpdateRW();
	UpdateRwFrame();
}

void
CEntity::PreRenderForGlassWindow(void)
{
	CGlass::AskForObjectToBeRenderedInGlass(this);
	bIsVisible = false;
}

#ifdef COMPATIBLE_SAVES
void
CEntity::SaveEntityFlags(uint8*& buf)
{
	uint32 tmp = 0;
	tmp |= (m_type & (BIT(3) - 1));
	tmp |= (m_status & (BIT(5) - 1)) << 3;

	if (bUsesCollision) tmp |= BIT(8);
	if (bCollisionProcessed) tmp |= BIT(9);
	if (bIsStatic)  tmp |= BIT(10);
	if (bHasContacted) tmp |= BIT(11);
	if (bPedPhysics) tmp |= BIT(12);
	if (bIsStuck) tmp |= BIT(13);
	if (bIsInSafePosition) tmp |= BIT(14);
	if (bUseCollisionRecords) tmp |= BIT(15);

	if (bWasPostponed) tmp |= BIT(16);
	if (bExplosionProof) tmp |= BIT(17);
	if (bIsVisible)  tmp |= BIT(18);
	if (bHasCollided) tmp |= BIT(19);
	if (bRenderScorched) tmp |= BIT(20);
	if (bHasBlip) tmp |= BIT(21);
	if (bIsBIGBuilding) tmp |= BIT(22);
	if (bRenderDamaged) tmp |= BIT(23);

	if (bBulletProof) tmp |= BIT(24);
	if (bFireProof) tmp |= BIT(25);
	if (bCollisionProof)  tmp |= BIT(26);
	if (bMeleeProof) tmp |= BIT(27);
	if (bOnlyDamagedByPlayer) tmp |= BIT(28);
	if (bStreamingDontDelete) tmp |= BIT(29);
	if (bZoneCulled) tmp |= BIT(30);
	if (bZoneCulled2) tmp |= BIT(31);

	WriteSaveBuf(buf, tmp);

	tmp = 0;

	if (bRemoveFromWorld) tmp |= BIT(0);
	if (bHasHitWall) tmp |= BIT(1);
	if (bImBeingRendered)  tmp |= BIT(2);
	if (bTouchingWater) tmp |= BIT(3);
	if (bIsSubway) tmp |= BIT(4);
	if (bDrawLast) tmp |= BIT(5);
	if (bNoBrightHeadLights) tmp |= BIT(6);
	if (bDoNotRender) tmp |= BIT(7);

	if (bDistanceFade) tmp |= BIT(8);
	if (m_flagE2) tmp |= BIT(9);

	WriteSaveBuf(buf, tmp);
}

void
CEntity::LoadEntityFlags(uint8*& buf)
{
	uint32 tmp;
	ReadSaveBuf(&tmp, buf);
	m_type = (tmp & ((BIT(3) - 1)));
	m_status = ((tmp >> 3) & (BIT(5) - 1));

	bUsesCollision = !!(tmp & BIT(8));
	bCollisionProcessed = !!(tmp & BIT(9));
	bIsStatic = !!(tmp & BIT(10));
	bHasContacted = !!(tmp & BIT(11));
	bPedPhysics = !!(tmp & BIT(12));
	bIsStuck = !!(tmp & BIT(13));
	bIsInSafePosition = !!(tmp & BIT(14));
	bUseCollisionRecords = !!(tmp & BIT(15));

	bWasPostponed = !!(tmp & BIT(16));
	bExplosionProof = !!(tmp & BIT(17));
	bIsVisible = !!(tmp & BIT(18));
	bHasCollided = !!(tmp & BIT(19));
	bRenderScorched = !!(tmp & BIT(20));
	bHasBlip = !!(tmp & BIT(21));
	bIsBIGBuilding = !!(tmp & BIT(22));
	bRenderDamaged = !!(tmp & BIT(23));

	bBulletProof = !!(tmp & BIT(24));
	bFireProof = !!(tmp & BIT(25));
	bCollisionProof = !!(tmp & BIT(26));
	bMeleeProof = !!(tmp & BIT(27));
	bOnlyDamagedByPlayer = !!(tmp & BIT(28));
	bStreamingDontDelete = !!(tmp & BIT(29));
	bZoneCulled = !!(tmp & BIT(30));
	bZoneCulled2 = !!(tmp & BIT(31));

	ReadSaveBuf(&tmp, buf);

	bRemoveFromWorld = !!(tmp & BIT(0));
	bHasHitWall = !!(tmp & BIT(1));
	bImBeingRendered = !!(tmp & BIT(2));
	bTouchingWater = !!(tmp & BIT(3));
	bIsSubway = !!(tmp & BIT(4));
	bDrawLast = !!(tmp & BIT(5));
	bNoBrightHeadLights = !!(tmp & BIT(6));
	bDoNotRender = !!(tmp & BIT(7));

	bDistanceFade = !!(tmp & BIT(8));
	m_flagE2 = !!(tmp & BIT(9));
}

#endif


code:
#pragma once

#include "ModelInfo.h"
#include "Placeable.h"

struct CReference;
class CPtrList;

enum eEntityType
{
	ENTITY_TYPE_NOTHING = 0,
	ENTITY_TYPE_BUILDING,
	ENTITY_TYPE_VEHICLE,
	ENTITY_TYPE_PED,
	ENTITY_TYPE_OBJECT,
	ENTITY_TYPE_DUMMY,
};

enum eEntityStatus
{
	STATUS_PLAYER,
	STATUS_PLAYER_PLAYBACKFROMBUFFER,
	STATUS_SIMPLE,
	STATUS_PHYSICS,
	STATUS_ABANDONED,
	STATUS_WRECKED,
	STATUS_TRAIN_MOVING,
	STATUS_TRAIN_NOT_MOVING,
	STATUS_HELI,
	STATUS_PLANE,
	STATUS_PLAYER_REMOTE,
	STATUS_PLAYER_DISABLED,
};

class CEntity : public CPlaceable
{
public:
	RwObject *m_rwObject;
protected:
	uint32 m_type : 3;
private:
	uint32 m_status : 5;
public:
	// flagsA
	uint32 bUsesCollision : 1;			// does entity use collision
	uint32 bCollisionProcessed : 1;		// has object been processed by a ProcessEntityCollision function
	uint32 bIsStatic : 1;				// is entity static
	uint32 bHasContacted : 1;			// has entity processed some contact forces
	uint32 bPedPhysics : 1;
	uint32 bIsStuck : 1;				// is entity stuck
	uint32 bIsInSafePosition : 1;		// is entity in a collision free safe position
	uint32 bUseCollisionRecords : 1;

	// flagsB
	uint32 bWasPostponed : 1;			// was entity control processing postponed
	uint32 bExplosionProof : 1;
	uint32 bIsVisible : 1;				//is the entity visible
	uint32 bHasCollided : 1;
	uint32 bRenderScorched : 1;
	uint32 bHasBlip : 1;
	uint32 bIsBIGBuilding : 1;			// Set if this entity is a big building
	uint32 bRenderDamaged : 1;			// use damaged LOD models for objects with applicable damage

	// flagsC
	uint32 bBulletProof : 1;
	uint32 bFireProof : 1;
	uint32 bCollisionProof : 1;
	uint32 bMeleeProof : 1;
	uint32 bOnlyDamagedByPlayer : 1;
	uint32 bStreamingDontDelete : 1;	// Dont let the streaming remove this
	uint32 bZoneCulled : 1;
	uint32 bZoneCulled2 : 1;    // only treadables+10m

	// flagsD
	uint32 bRemoveFromWorld : 1;		// remove this entity next time it should be processed
	uint32 bHasHitWall : 1;				// has collided with a building (changes subsequent collisions)
	uint32 bImBeingRendered : 1;		// don't delete me because I'm being rendered
	uint32 bTouchingWater : 1;	// used by cBuoyancy::ProcessBuoyancy
	uint32 bIsSubway : 1;	// set when subway, but maybe different meaning?
	uint32 bDrawLast : 1;				// draw object last
	uint32 bNoBrightHeadLights : 1;
	uint32 bDoNotRender : 1;

	// flagsE
	uint32 bDistanceFade : 1;			// Fade entity because it is far away
	uint32 m_flagE2 : 1;

	uint16 m_scanCode;
	uint16 m_randomSeed;
	int16 m_modelIndex;
	uint16 m_level;	// int16
	CReference *m_pFirstReference;

public:
	uint8 GetType() const { return m_type; }
	void SetType(uint8 type) { m_type = type; }
	uint8 GetStatus() const { return m_status; }
	void SetStatus(uint8 status) { m_status = status; }
	CColModel *GetColModel(void) { return CModelInfo::GetModelInfo(m_modelIndex)->GetColModel(); }
	bool GetIsStatic(void) const { return bIsStatic; }
	void SetIsStatic(bool state) { bIsStatic = state; }
#ifdef COMPATIBLE_SAVES
	void SaveEntityFlags(uint8*& buf);
	void LoadEntityFlags(uint8*& buf);
#else
	uint32* GetAddressOfEntityProperties() { /* AWFUL */ return (uint32*)((char*)&m_rwObject + sizeof(m_rwObject)); }
#endif

	CEntity(void);
	~CEntity(void);

	virtual void Add(void);
	virtual void Remove(void);
	virtual void SetModelIndex(uint32 id);
	virtual void SetModelIndexNoCreate(uint32 id);
	virtual void CreateRwObject(void);
	virtual void DeleteRwObject(void);
	virtual CRect GetBoundRect(void);
	virtual void ProcessControl(void) {}
	virtual void ProcessCollision(void) {}
	virtual void ProcessShift(void) {}
	virtual void Teleport(CVector v) {}
	virtual void PreRender(void);
	virtual void Render(void);
	virtual bool SetupLighting(void);
	virtual void RemoveLighting(bool);
	virtual void FlagToDestroyWhenNextProcessed(void) {}

	bool IsBuilding(void) { return m_type == ENTITY_TYPE_BUILDING; }
	bool IsVehicle(void) { return m_type == ENTITY_TYPE_VEHICLE; }
	bool IsPed(void) { return m_type == ENTITY_TYPE_PED; }
	bool IsObject(void) { return m_type == ENTITY_TYPE_OBJECT; }
	bool IsDummy(void) { return m_type == ENTITY_TYPE_DUMMY; }

	RpAtomic *GetAtomic(void) {
		assert(RwObjectGetType(m_rwObject) == rpATOMIC);
		return (RpAtomic*)m_rwObject;
	}
	RpClump *GetClump(void) {
		assert(RwObjectGetType(m_rwObject) == rpCLUMP);
		return (RpClump*)m_rwObject;
	}

	void GetBoundCentre(CVUVECTOR &out);
	CVector GetBoundCentre(void);
	float GetBoundRadius(void);
	float GetDistanceFromCentreOfMassToBaseOfModel(void);
	bool GetIsTouching(CVUVECTOR const &center, float r);
	bool GetIsOnScreen(void);
	bool GetIsOnScreenComplex(void);
	bool IsVisible(void);
	bool IsVisibleComplex(void);
	int16 GetModelIndex(void) const { return m_modelIndex; }
	void UpdateRwFrame(void);
	void SetupBigBuilding(void);

	void AttachToRwObject(RwObject *obj);
	void DetachFromRwObject(void);

	void RegisterReference(CEntity **pent);
	void ResolveReferences(void);
	void PruneReferences(void);

#ifdef PED_SKIN
	void UpdateRpHAnim(void);
#endif

	void PreRenderForGlassWindow(void);
	void AddSteamsFromGround(CVector *unused);
	void ModifyMatrixForTreeInWind(void);
	void ModifyMatrixForBannerInWind(void);
	void ProcessLightsForEntity(void);
};

VALIDATE_SIZE(CEntity, 0x64);