// Open SanAndreas
// (c) 2006-2008 Stanislav "listener" Golovin
// 
// system part (WinMain, etc)

#include "stdafx.h"
#include <control.h>
#include "ogMath.h"
#include "ogRW.h"
#include "ogMenu.h"
#include "ogFiles.h"
#include "ogInput.h"

// TODO: interface to gta_sa.exe
// TODO: PsGlobalType должен войти как часть в CGtaApp
// TODO: объявить статически после того, как будет готов CPad::processMouse
#pragma pack(push,1)
#pragma pack(pop)
//PsGlobalType ps;	// TODO: этому место в CGtaApp или чем-то аналогичном (за компанию с rsGlobals)
PsGlobalType * ps = (PsGlobalType *)0xC8CF88;
CMenuManager * menu = (CMenuManager *)0xBA6748;

static bool rwInitialized = false;	//	>> C920E8

/// int * rwInitialized = (int *)0xC920E8;	// TODO: объявить как static после того, как будет готова MainWndProc
/// LRESULT	(__stdcall *mainWndProc)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) = (LRESULT (__stdcall *)(HWND, UINT, WPARAM, LPARAM))0x747EB0;
/// void (*initDIMouse)(BYTE exclusive) = (void (__cdecl *)(BYTE))0x7469A0;
/// void (*initDIPad)() = (void (__cdecl *)())0x7485C0;
/// void (*DIRelease)() = (void (__cdecl *)())0x747000;
/// static BYTE (*gtaTranslateKey)(LPARAM *, LPARAM, WPARAM) = (BYTE (__cdecl *)(LPARAM *, LPARAM, WPARAM))0x747820;
/// void (*gtaTranslateShiftKey)(LPARAM *, bool keydown) = (void (__cdecl *)(LPARAM *, bool))0x747CD0;
/// void (*initProgramPath)() = (void (__cdecl *)())0x5386F0;
/// void (*chdirProgramDir)(char * relPath) = (void (__cdecl *)(char *))0x5387D0;
/// static void (*rsInitialize)() = (void (__cdecl *)())0x619600;
/// static void (*psInitialize)() = (void (__cdecl *)())0x619600;
/// HWND (*initInstance)(HINSTANCE hInstance) = (HWND (__cdecl *)(HINSTANCE))0x745560;	// TODO: заинлайнить в функцию инициализации
/// void (__thiscall * inputEvents__mapMouse)(DWORD _this, DWORD, DWORD) = (void (__thiscall *)(DWORD, DWORD, DWORD))0x52F6F0;	// TODO: CInputEvents::mapMouse
/// void (__thiscall * inputEvents__init)(DWORD _this) = (void (__thiscall *)(DWORD))0x530500;			// TODO: CInputEvents::init
/// void (__thiscall * inputEvents__mapKeyboard)(DWORD _this) = (void (__thiscall *)(DWORD))0x530640;	// TODO: CInputEvents::mapKeyboard
/// void (__thiscall * inputEvents__mapAllButtons)(DWORD _this) = (void (__thiscall *)(DWORD))0x531F20;	// TODO: CInputEvents::mapAllButtons
/// void (__thiscall * inputEvents__mapPadButtons)(DWORD _this, int count) = (void (__thiscall *)(DWORD, int))0x530B00;	// TODO: CInputEvents::mapPadButtons


RsGlobalType * rsGlobal = (RsGlobalType *)0xC17040;
int * gameState = (int *)0xC8D4C0;	// (19)
int * isForeground = (int *)0xC920EC;
int * foregroundApp = (int *)0x8D621C;

int * timerDivider = (int *)0xB7CB2C;
BYTE * replayInProgress = (BYTE *)0xA43088;

BYTE *	nastyGame = (BYTE *)0xB9B7EC;
BYTE *	germanGame = (BYTE *)0xB9B7ED;
BYTE *	_B9B7EE = (BYTE *)0xB9B7EE;
int * windowed = (int *)0xC920CC;

BYTE * gammaChanged = (BYTE *)0xC920C8;	

int * videoModeNotSelected = (int *)0x8D6218;
int * gCurSelVM = (int *)0x8D6220;
IMediaControl ** mediaControl = (IMediaControl **)0xC920D8;

BYTE * anisotropySupported = (BYTE *)0xC87FFC;	// used by readTextureNative
D3DCAPS9 * d3dCaps = (D3DCAPS9 *)0xC9BF00;

DWORD * currentTime = (DWORD *)0xB7CB84;

LPDIRECT3DDEVICE9 (*RwD3D9GetCurrentD3DDevice)() = (LPDIRECT3DDEVICE9 (__cdecl *)())0x7F9D50;
D3DGAMMARAMP * savedGamma = (D3DGAMMARAMP *)0xC8D4C8;
D3DGAMMARAMP * gammaTable = (D3DGAMMARAMP *)0xC8DAC8;

int (*RsKeyboardEventHandler)(int event, void * param) = (int (__cdecl *)(int, void *))0x619560;	// TODO: заменить прямым вызовом хэндлера
int (*RsPadEventHandler)(int event, void * param) = (int (__cdecl *)(int, void *))0x6195A0; // TODO: заменить прямым вызовом хэндлера

BYTE (*isCurrentModeFullscreen)() = (BYTE (__cdecl *)())0x745CA0;

void (*chdirToUserPath)() = (void (__cdecl *)())0x538860;
void (*initStreaming)(int) = (void (__cdecl *)(int))0x0406B70;
void (*initPad)() = (void (__cdecl *)())0x541D90;	

bool (__thiscall * inputEvents__readSettingsFile)(DWORD _this, FILE * file) = (bool (__thiscall *)(DWORD, FILE *))0x530530;	// TODO: CInputEvents::readSettingsFile
///BYTE * (__stdcall *getMouseButtonMask)(BYTE *) = (BYTE * (__stdcall *)(BYTE *))0x53F2D0;	// TODO: на самом деле, это, похоже, метод CMouse, не использующий this

void (__stdcall *pad__update)() = (void (__stdcall *)())0x541DD0;	// TODO: CPad::update
void (__thiscall * pad__initialize)(DWORD _this, int, int) = (void (__thiscall *)(DWORD, int, int))0x541A70;	// TODO: CPad::initialize
DWORD getPad(int padNum) { return 0xB73458 + padNum*0x134; }	// TODO: static CPad * CPad::getPad ();	// TODO: поскольку пад всегда используется только один, поддержку второго можно спокойно срезать

void (*initL10N)() = (void (__cdecl *)())0x7465B0;
/// int * windowsVersion = (int *)0xC8CF68;	// TODO: убрать как явление после того, будут включены translateKey и translateShiftKey (поддержка win98 убирается нафиг)
void (*ddrawGetAvailableVidMem)(DWORD, DWORD) = (void (__cdecl *)(DWORD, DWORD))0x7455E0;

void (*rsMouseSetPos)(V2D * pos) = (void (__cdecl *)(V2D *))0x6194A0;

int (*initialize3D)(HWND) = (int (__cdecl *)(HWND))0x5BF390;
void (*terminate)() = (void (__cdecl *)())0x53BC30;
void (*terminate3D)() = (void (__cdecl *)())0x53D910;
void (*cameraSize)(DWORD camera, RECT * rect, float viewWindow, float aspectRatio) = (void (__cdecl *)(DWORD, RECT *, float, float))0x72FC70;	// TODO: CCamera *; сделать методом CCamera
void (*freeVideoModeList)() = (void (__cdecl *)())0x745A80;
void (*idle)(int) = (void (__cdecl *)(int))0x53E920;
void (*render)() = (void (__cdecl *)())0x53E770;

void (*_7043D0)() = (void (__cdecl *)())0x7043D0;	// setCameraRasterSize

void (__thiscall * gamma__init)(DWORD _this) = (void (__thiscall *)(DWORD))0x747180;	// TODO: CGamma::init

void (*_53C900)() = (void (__cdecl *)())0x53C900;	// TODO: сделать большую функцию очистки и заинлайнить _53C900 туда
void (*initCheats)() = (void (__cdecl *)())0x438450;	// TODO: CCheats::init 
void (*resetTimers)() = (void (__cdecl *)())0x561AA0;	// TODO: CTimer::init
void (*_53C550)() = (void (__cdecl *)())0x53C550;	// TODO: CGame method ?
void (*_53C680)() = (void (__cdecl *)())0x53C680;
void (*_53E580)() = (void (__cdecl *)())0x53E580;	// load game

void (*readLoadingTextures)(int, int) = (void (__cdecl *)(int, int))0x5900B0;
void (*startLoadingInterval)(int, int) = (void (__cdecl *)(int, int))0x5902B0;
void (*showLoadingTexture)() = (void (__cdecl *)())0x590860;
void (*showLoadingProgress)() = (void (__cdecl *)())0x590990;
void (*cleanLoadingTexture)() = (void (__cdecl *)())0x58FF10;

void (*createVideoPlayer)(char * movieName) = (void (__cdecl *)(char *))0x0747660;
void (*releaseVideoPlayer)() = (void (__cdecl *)())0x746740;
void (*videoPlayerEvent)() = (void (__cdecl *)())0x745FB0;
void (*videoPlayerWaitState)(int state) = (void (__cdecl *)(int))0x745F90;
void (*videoPlayerShow)() = (void (__cdecl *)())0x7466D0;

int (*initGame)() = (int (__cdecl *)())0x5BA160;	// TODO: inline it?
void (*initGame2)() = (void (__cdecl *)())0x5BFA90;

DWORD (*getTimeFromRenderStart)() = (DWORD (__cdecl *)())0x561A80;


void (__thiscall * audioEvent__507CB0)(DWORD _this) = (void (__thiscall *)(DWORD))0x507CB0;	// TODO: CAudioManager::terminate
int (__thiscall * audioEvent__5071D0)(DWORD _this) = (int (__thiscall *)(DWORD))0x5071D0;	// TODO: CAudioManager method
void (__thiscall * audioManager__507410)(DWORD _this) = (void (__thiscall *)(DWORD))0x507410;
void (__thiscall * audioManager__5078F0)(DWORD _this) = (void (__thiscall *)(DWORD))0x5078F0;
void (__thiscall * audioManager__507430)(DWORD _this) = (void (__thiscall *)(DWORD))0x507430;
void (__thiscall * audioManager__507440)(DWORD _this) = (void (__thiscall *)(DWORD))0x507440;

void (__thiscall *menu__unloadTextures)(CMenuManager * _this) = (void (__thiscall *)(CMenuManager *))0x574630;	// TODO: CMenuManager::unloadTextures
void (__thiscall * menu__readSettingsFile)(DWORD _this) = (void (__thiscall *)(DWORD))0x57C8F0;	// TODO: CMenuManager::readSettingsFile
// 

const char * appName = "Open SanAndreas";
const char * appClassName = "Open SanAndreas";

//=============================================================================
// direct input	// TODO: вынести в ogInput
CPadConfig * padConfig = (CPadConfig *)0xC92144;	// TODO: make static!	TODO: достаточно только одной структуры, т.к. больше одного пада все равно не поддерживается

///static LPDIENUMDEVICESCALLBACK	diPadEnum = (LPDIENUMDEVICESCALLBACK)0x747020;	// TODO: сделать!
///void (*diPadSetRanges)(LPDIRECTINPUTDEVICE8 pad, int num) = (void (__cdecl *)(LPDIRECTINPUTDEVICE8, int))0x746D80;

void diMouseInit (BYTE exclusive) { // >> 7469A0
	if (DI_OK != ps->diInterface->CreateDevice (GUID_SysMouse, &(ps->diMouse), NULL))	
		return;
	if (DI_OK != ps->diMouse->SetDataFormat (&c_dfDIMouse2))
		return;
	if (DI_OK != ps->diMouse->SetCooperativeLevel (ps->window, DISCL_FOREGROUND|(exclusive ? DISCL_EXCLUSIVE : DISCL_NONEXCLUSIVE)))
		return;
	ps->diMouse->Acquire ();		
}

static BOOL WINAPI diPadEnum (LPCDIDEVICEINSTANCE lpddi, LPVOID) {	// >> 747020
	if (DI_OK != ps->diInterface->CreateDevice (lpddi->guidInstance, &(ps->diDevice1), NULL))
		return TRUE;
	// TODO: свернуть два if через &&
	if (DI_OK != ps->diDevice1->SetDataFormat (&c_dfDIJoystick2)) {
		ps->diDevice1->Release();
		return TRUE;
	}
	if (DI_OK != ps->diDevice1->SetCooperativeLevel (ps->window, DISCL_FOREGROUND|DISCL_NONEXCLUSIVE)) {
		ps->diDevice1->Release ();
		return TRUE;
	}
	return FALSE;	// устройство найдено и проинициализировано. Дальше можно не искать
}

static void diPadSetRanges (LPDIRECTINPUTDEVICE8 lpddi, int num) {	// >> 746D80
	DIDEVICEOBJECTINSTANCE dido;
	dido.dwSize = sizeof (DIDEVICEOBJECTINSTANCE);	

	DIPROPRANGE dipr;
    dipr.diph.dwSize       = sizeof(DIPROPRANGE); 
    dipr.diph.dwHeaderSize = sizeof(DIPROPHEADER); 
    dipr.diph.dwObj        = 0;
	dipr.diph.dwHow        = DIPH_BYOFFSET; 
    dipr.lMin              = -2000; 
    dipr.lMax              = +2000; 

	// set range for XAxis
	if (lpddi && lpddi->GetObjectInfo (&dido, 0, DIPH_BYOFFSET) >= 0) {
		if (lpddi->SetProperty (DIPROP_RANGE, (LPDIPROPHEADER)&dipr) < 0)
			return;
	}
	// set range for YAxis
	if (lpddi && lpddi->GetObjectInfo (&dido, 4, DIPH_BYOFFSET) >= 0) {
		dipr.diph.dwObj = 4;
		if (lpddi->SetProperty (DIPROP_RANGE, (LPDIPROPHEADER)&dipr) < 0)
			return;
	}
	// try to set range for ZAxis
	if (lpddi && lpddi->GetObjectInfo (&dido, 8, DIPH_BYOFFSET) >= 0) {
		dipr.diph.dwObj = 8;
		if (lpddi->SetProperty (DIPROP_RANGE, (LPDIPROPHEADER)&dipr) < 0)
			return;
		padConfig[num].zAxisPresent = true;
	}
	// try to set range for RZAxis
	if (lpddi && lpddi->GetObjectInfo (&dido, 20, DIPH_BYOFFSET) >= 0) {
		dipr.diph.dwObj = 20;
		if (lpddi->SetProperty (DIPROP_RANGE, (LPDIPROPHEADER)&dipr) < 0)
			return;
		padConfig[num].rzAxisPresent = true;
	}	
}

static void diPadInit () { // >> 7485C0
	// clear pad configs
	for (int i =0; i < 2; i++) {
		padConfig[i]._f0 = 0;
		padConfig[i].present = padConfig[i].zAxisPresent = padConfig[i].rzAxisPresent = 0;
	}

	// >> 7477C0 -- inlined initPads
	if (0 != ps->diInterface->EnumDevices (DI8DEVCLASS_GAMECTRL, diPadEnum, NULL, DIEDFL_ATTACHEDONLY))
		return;
	if (!ps->diDevice1)	// no pads present
		return ;
	diPadSetRanges (ps->diDevice1, 0);
	/// setPadRanges (ps->diDevice2, 1); // NOTE: second pad not used anyway
	// NOTE: end of inlined initPads

	DIDEVCAPS didc;
	didc.dwSize = sizeof (DIDEVCAPS);

	DIPROPDWORD dipdw;
	dipdw.diph.dwSize = sizeof (DIPROPDWORD);
	dipdw.diph.dwHeaderSize = sizeof (DIPROPHEADER);
	dipdw.diph.dwHow = 0;
	dipdw.diph.dwObj = 0;
	if (ps->diDevice1) {
		padConfig[0].present = true;
		ps->diDevice1->GetCapabilities (&didc);
		ps->diDevice1->GetProperty (DIPROP_VIDPID, (LPDIPROPHEADER)&dipdw);
		padConfig[0].vendorID = dipdw.dwData & 0xFFFF;
		padConfig[0].productID = dipdw.dwData >> 10;
		/// inputEvents__mapPadButtons (0xB70198, 32); // TODO: inputEvents.mapPadButtons (32);
		inputEvents->mapPadButtons (16);
		// debug ("DI: pad vendor = %08x, product = %08x, Z = %d, RZ = %d\n", padConfig[0].vendorID, padConfig[0].productID, padConfig[0].zAxisPresent, padConfig[0].rzAxisPresent);
	}
/*
	if (PsGlobal.diDevice2) {	// TODO: remove it
		padConfig[1].present = true;
		PsGlobal.diDevice2->GetCapabilities (&didc);
		PsGlobal.diDevice2->GetProperty (DIPROP_VIDPID, (LPDIPROPHEADER)&dipdw);
		padConfig[1].vendorId = dipdw.dwData & 0xFFFF;
		padConfig[1].productId = dipdw.dwData >> 10;
		inputEvents.mapPadButtons (32);
	}
*/
}

static void diRelease () {	// >> 747000
	if (ps->diInterface) {
		ps->diInterface->Release ();
		ps->diInterface = NULL;
	}
}

// ### CPad ### 
// TODO: move to ogInput.cpp
/*
void CPad::update() {	// >> 541DD0 -- all in one input processing
	pad->processMouse ();
	pad->processPad (0);

	memset (inputEvents->_f12D0, 0, 16);
	inputEvents->processKeyboard ();
	inputEvents->processMouse ();
	pad->processKeyboard (0);
	*prevKeyState = *currKeyState;
	*currKeyState = *activeKeyState;
}
*/

// TODO: функция находится здесь временно - пока не разнесены глобальные переменные (psGlobals) и функции (diXXXXX)
void CPad::processMouse () {	// >> 53F3C0 (CPad::update)
	if (!*foregroundApp)
		return;

	if (!ps->diMouse) {
		BYTE exclusive = (!menu->menuActive) && isCurrentModeFullscreen () ? 1 : 0;
		diMouseInit (exclusive);
		if (!ps->diMouse) {
			debug ("ERROR: CPad::processMouse - unable to obtain mouse\n");
			return;
		}
	}	

	//debug ("Process Mouse\n");
	DIMOUSESTATE2 dm;
	if (getMouseState (&dm) < 0)
		return;

	int X = 1;
	int Y = 1;
	if (!menu->menuActive) {
		if (*mouseInvertX)
			X = -1;
		if (*mouseInvertY)
			Y = -1;
	}
	activeMouseState->X = (float)(dm.lX*X);
	activeMouseState->Y = (float)(dm.lY*Y);
	activeMouseState->Z = (float)dm.lZ;
	
	activeMouseState->lmb	= dm.rgbButtons[0]&0x80;	// LMB
	activeMouseState->rmb	= dm.rgbButtons[1]&0x80;	// RMB
	activeMouseState->mmb	= dm.rgbButtons[2]&0x80;	// MMB
	activeMouseState->bmx1	= dm.rgbButtons[3]&0x80;	// X1
	activeMouseState->bmx2	= dm.rgbButtons[4]&0x80;	// X2	
	activeMouseState->wheelUp = dm.lZ > 0 ? 1 : 0;	// WheelUp	
	activeMouseState->wheelDn = dm.lZ < 0 ? 1 : 0;	// WheelDn
	// debug ("Process Mouse: %f-%f-%f (%d, %d)\n", activeMouseState
	*prevMouseState = *currMouseState;
	*currMouseState = *activeMouseState;

	if (isMouseUsed ())
		padQueryTime = *currentTime;
}

void CPad::processPad (int padNum) {	// >> 746A10
	if (padNum != 0)	// NOTE: only one pad supported
		return;
	LPDIRECTINPUTDEVICE8 device = ps->diDevice1;
	if (!device)
		return;

	while (device->Poll () < 0) 
		while (device->Acquire () == DIERR_INPUTLOST)
			;

	// TODO: упростить конструкцию, чтобы избежать лишнего копирования структур
	DIJOYSTATE2	dj;
	if (FAILED (device->GetDeviceState (sizeof (DIJOYSTATE2), &dj)))
		return; 

	// если это первый опрос - инициализируем обе структуры. 
	if (inputEvents->_f0 == 1) {
		inputEvents->currPadState = dj;
		inputEvents->prevPadState = dj;
		inputEvents->_f0 = 0;
	} else {
		inputEvents->prevPadState = inputEvents->currPadState;
		inputEvents->currPadState = dj;
	}

	RsPadEventHandler (32, &padNum);	// TODO: заменить прямым вызовом (rsPADBUTTONUP)
	
	// получим текущие значения стиков
	float padX1 = (float)dj.lX/2000.0f;
	float padY1 = (float)dj.lY/2000.0f;
	float padX2 = 0.0f;
	float padY2 = 0.0f;
	if (dj.rgdwPOV[0] != 0xFFFF) {
		float angle = (float)dj.rgdwPOV[0] / 100.0f * ((float)M_PI / 180.0f);
		padX1 = sinf (angle);
		padY1 = -cosf (angle);
	}
	if (padConfig[padNum].rzAxisPresent && padConfig[padNum].zAxisPresent) {
		padX2 = (float)dj.lZ/2000.0f;
		padY2 = (float)dj.lRz/2000.0f;
	}
	
	RsPadEventHandler (32, &padNum);	// TODO: заменить прямым вызовом (rsPADBUTTONUP)
	RsPadEventHandler (31, &padNum);	// TODO: заменить прямым вызовом (rsPADBUTTONDOWN)
	
	// заполним значения с учетом настроек меню
	// TODO: сделать отдельную переменную для хранения порога чувствительности
	if (padX1 >= 0.3f) {
		if (menu->invertPadX1)
			padX1 = -padX1;
		_fA8.keys[menu->swapPadAxis1 ? 1 : 0] = (short)(padX1*128.0f);
	}
	if (padY1 >= 0.3f) {
		if (menu->invertPadY1)
			padY1 = -padY1;
		_fA8.keys[menu->swapPadAxis1 ? 0 : 1] = (short)(padY1*128.0f);
	}
	if (padX2 >= 0.3f) {
		if (menu->invertPadX2)
			padX2 = -padX2;
		_fA8.keys[menu->swapPadAxis2 ? 3 : 2] = (short)(padX2*128.0f);
	}
	if (padY2 >= 0.3f) {
		if (menu->invertPadY2)
			padY2 = -padY2;
		_fA8.keys[menu->swapPadAxis2 ? 2 : 3] = (short)(padY2*128.0f);
	}
	return;
}

// returns true, if any changes detected in activeMouseState
bool CPad::isMouseUsed() {	// >> 53F270
	if (activeMouseState->lmb ||
		activeMouseState->rmb ||
		activeMouseState->mmb ||
		activeMouseState->wheelUp ||
		activeMouseState->wheelDn ||
		activeMouseState->bmx1 ||
		activeMouseState->bmx2 ||
		activeMouseState->X != 0.0f ||
		activeMouseState->Y != 0.0f)
		return true;
	return false;
}

// TODO: private method
HRESULT	CPad::getMouseState(DIMOUSESTATE2 *dm) {	// >> 746ED0
	memset (dm, 0, sizeof (DIMOUSESTATE2));

	if (ps->diMouse->GetDeviceState (sizeof (DIMOUSESTATE2), dm) >= 0)
		return 0;
	// mouse error. try to reinitialize
	while (ps->diMouse->Acquire () == DIERR_INPUTLOST)
		;

	memset (dm, 0, sizeof (DIMOUSESTATE2));
	return ps->diMouse->GetDeviceState (sizeof (DIMOUSESTATE2), dm);
}

//=================================================================================================================================
// TODO: собрать эти две функции в одну, сделать вызов keyboard handler только из нее
bool translateKey (LPARAM * outKey, LPARAM flags, WPARAM virtKey) {	// >> 747820
	UINT k;
	*outKey = rsNULL;
	switch (virtKey) {
	case VK_SHIFT:
		return false;
		//*outKey = rsSHIFT;	// работало только в win98
		// break;
	case VK_RETURN:
		*outKey = flags&0x1000000 ?  rsENTER : rsEXTENTER;
		break;
	case VK_CONTROL:
		*outKey = flags&0x1000000 ?  rsRCTRL : rsLCTRL;
		break;
	case VK_MENU:
		*outKey = flags&0x1000000 ?  rsRMENU : rsLMENU;
		break;
	case VK_APPS:
		*outKey = rsAPPS;
		break;
	case VK_PAUSE:
		*outKey = rsPAUSE;
		break;
	case VK_CAPITAL:
		*outKey = rsCAPSLOCK;
		break;
	case VK_ESCAPE:
		*outKey = rsESC;
		break;
	case VK_PRIOR:
		*outKey = flags&0x1000000 ?  rsPGUP : rsEXTPGUP;
		break;
	case VK_NEXT:
		*outKey = flags&0x1000000 ?  rsPGDN : rsEXTPGDN;
		break;
	case VK_END:
		*outKey = flags&0x1000000 ?  rsEND : rsEXTEND;
		break;
	case VK_HOME:
		*outKey = flags&0x1000000 ?  rsHOME : rsEXTHOME;
		break;
	case VK_LEFT:
		*outKey = flags&0x1000000 ?  rsLEFT : rsEXTLEFT;
		break;
	case VK_UP:
		*outKey = flags&0x1000000 ?  rsUP : rsEXTUP;
		break;
	case VK_RIGHT:
		*outKey = flags&0x1000000 ?  rsRIGHT : rsEXTRIGHT;
		break;
	case VK_DOWN:
		*outKey = flags&0x1000000 ?  rsDOWN : rsEXTDOWN;
		break;
	case VK_INSERT:
		*outKey = flags&0x1000000 ?  rsINSERT : rsEXTINSERT;
		break;
	case VK_DELETE:
		*outKey = flags&0x1000000 ?  rsDEL : rsEXTDEL;
		break;	
	case VK_LWIN:
		*outKey = rsLWIN;
		break;
	case VK_RWIN:
		*outKey = rsRWIN;
		break;
	case VK_NUMPAD0:
		*outKey = rsNUM0;
		break;
	case VK_NUMPAD1:
		*outKey = rsNUM1;
		break;
	case VK_NUMPAD2:
		*outKey = rsNUM2;
		break;
	case VK_NUMPAD3:
		*outKey = rsNUM3;
		break;
	case VK_NUMPAD4:
		*outKey = rsNUM4;
		break;
	case VK_NUMPAD5:
		*outKey = rsNUM5;
		break;
	case VK_NUMPAD6:
		*outKey = rsNUM6;
		break;
	case VK_NUMPAD7:
		*outKey = rsNUM7;
		break;
	case VK_NUMPAD8:
		*outKey = rsNUM8;
		break;
	case VK_NUMPAD9:
		*outKey = rsNUM9;
		break;
	case VK_MULTIPLY:
		*outKey = rsMUL;
		break;
	case VK_DIVIDE:
		*outKey = rsDIV;
		break;
	case VK_ADD:
		*outKey = rsADD;
		break;
	case VK_SUBTRACT:
		*outKey = rsSUB;
		break;
	case VK_DECIMAL:
		*outKey = rsDECIMAL;
		break;
	case VK_F1:
		*outKey = rsF1;
		break;
	case VK_F2:
		*outKey = rsF2;
		break;
	case VK_F3:
		*outKey = rsF3;
		break;
	case VK_F4:
		*outKey = rsF4;
		break;
	case VK_F5:
		*outKey = rsF5;
		break;
	case VK_F6:
		*outKey = rsF6;
		break;
	case VK_F7:
		*outKey = rsF7;
		break;
	case VK_F8:
		*outKey = rsF8;
		break;
	case VK_F9:
		*outKey = rsF9;
		break;
	case VK_F10:
		*outKey = rsF10;
		break;
	case VK_F11:
		*outKey = rsF11;
		break;
	case VK_F12:
		*outKey = rsF12;
		break;
	case VK_NUMLOCK:
		*outKey = rsNUMLOCK;
		break;
	case VK_SCROLL:
		*outKey = rsSCROLL;
		break;
	case VK_BACK:
		*outKey = rsBACK;
		break;
	case VK_TAB:
		*outKey = rsTAB;
		break;

	default:
		k = MapVirtualKey ((UINT)virtKey, 2); // MAPVK_VK_TO_CHAR
		if (k < 0xFF)
			*outKey = k;
		return *outKey != rsNULL;
	}	
	return true;
}

void translateShiftKey (LPARAM * keyCode, bool down) {	// >> 747CD0
	*keyCode = rsNULL;
	if (GetKeyState (VK_LSHIFT) & 0x8000) {
		*keyCode = rsLSHIFT;
		RsKeyboardEventHandler (0x1C, keyCode);	// rsKEYDOWN
	}
	*keyCode = rsNULL;
	if (GetKeyState (VK_RSHIFT) & 0x8000) {
		*keyCode = rsRSHIFT;
		RsKeyboardEventHandler (0x1C, keyCode);
	}
	*keyCode = rsNULL;
	if (!(GetKeyState (VK_LSHIFT) & 0x8000)) {
		*keyCode = rsLSHIFT;
		RsKeyboardEventHandler (0x1D, keyCode);// rsKEYUP
	}
	*keyCode = rsNULL;
	if (!(GetKeyState (VK_RSHIFT) & 0x8000)) {
		*keyCode = rsRSHIFT;
		RsKeyboardEventHandler (0x1D, keyCode);
	}
	// NOTE: зачем они здесь еще раз пытались вызвать RsKeyboardEventHandler ? непонятно, наверное был макрос какой-то
}

//=============================================================================
// main window function
//-----------------------------------------------------------------------------
static LRESULT WINAPI mainWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {	// >> 747EB0
	static BOOL noMemory = false;
	
	RECT r;
	RECT * newPos;

	switch (msg) {
	case WM_SIZE:		// TODO: возможно, это сообщение можно благополучно проигнорировать (размер окна просто так поменять не получится, даже если оно будет исполняться в окне)
		r.left = 0;
        r.top = 0;
        r.right = LOWORD(lParam);
        r.bottom = HIWORD(lParam);

        if (rwInitialized && r.right > 0 && r.bottom > 0) {
            cameraSize (*(DWORD *)0xC1703C, &r, tanf (45.0f*(float)M_PI/360.0f), 4.0f/3.0f);	// TODO: gtaCamera.setSize (...)

            if (r.right != LOWORD(lParam) && r.bottom != HIWORD(lParam)) {
                noMemory = true; // failed to create window of required size 

                WINDOWPLACEMENT wp;
                ReleaseCapture();	// stop re-sizing
                GetWindowPlacement(hWnd, &wp);	// handle maximised window
                if (wp.showCmd == SW_SHOWMAXIMIZED)                    
                    SendMessage(hWnd, WM_WINDOWPOSCHANGED, 0, 0);                 
            } else
                noMemory = FALSE;
        }
		return 0;

	case WM_ACTIVATE:
        if (LOWORD(wParam) == WA_INACTIVE) {
			audioManager__507430 (0xB6BC90);	// TODO: CAudioManager method
			SetTimer(hWnd, 1, (UINT)(1000.0f/rsGlobal->frameLimit), NULL);	// TODO: а это нужно? WM_TIMER все равно не обрабатывается 
            *foregroundApp = false;            
        } else {
			audioManager__507440 (0xB6BC90);	// TODO: CAudioManager method
			ShowCursor (false);
            *foregroundApp = true;            
        }		
		pad__initialize (getPad (0), 0, 1);	// TODO: CPad::getPad()->initialize (0, 1);
		/// pad__initialize (getPad (1), 0, 1);	// TODO: убрать нафиг
		break;

	case WM_DESTROY:
    case WM_CLOSE:
        ClipCursor(NULL);
		diRelease ();
        PostQuitMessage(0);
        return 0;

	case WM_KILLFOCUS:
		*foregroundApp = FALSE;
		break;

	case WM_SETFOCUS:
		if (menu->_fE9 == 0 && menu->menuActive == 0)	// open menu on app restore
			menu->activateMenu = 1;        
		*foregroundApp = TRUE;
		break;

	case WM_ACTIVATEAPP:
		// если игра меняла гамму, то ее нужно устанавливать/восстанавливать при каждой активации/деактивации приложения
		if (*gammaChanged) {
			LPDIRECT3DDEVICE9 d3d9 = RwD3D9GetCurrentD3DDevice ();
			d3d9->SetGammaRamp (0, 1, wParam ? gammaTable : savedGamma);			
		}

		switch (*gameState) {
		case 0:
			if (!ps->fullscreen)
				*windowed = true;
			break;
		case 2:	// проигрываются ролики?
		case 4:
			OAFilterState fs;
			(*mediaControl)->GetState (10, &fs);
			if (!wParam) {	// сворачиваем проигрывание, пауза				
				if ((*mediaControl) && fs == 2) {		// playing
					if (FAILED ((*mediaControl)->Pause ())) 
						OutputDebugString ("Failed to pause the MPEG");
					else 
						videoPlayerWaitState (1);									
				}
			} else { // восстанавливаем проигрывание				
				videoPlayerShow ();
				if ((*mediaControl) && fs != 2) {
					if (FAILED ((*mediaControl)->Run()))
						OutputDebugString ("Failed to run the MPEG");
					else {
						videoPlayerWaitState (2);									
						SetFocus(ps->window);	
					}
				}
			}
			break;
		}
		pad__initialize (getPad (0), 0, 1);	// TODO: CPad::getPad()->initialize (0, 1);
		pad__initialize (getPad (1), 0, 1);	// TODO: убрать нафиг
		return 0;

	case WM_SETCURSOR:
		ShowCursor (false);
		SetCursor (NULL);
		break;

	case WM_KEYDOWN:
	case WM_SYSKEYDOWN:
		if (translateKey (&lParam, lParam, wParam))				
			RsKeyboardEventHandler (0x1C, &lParam);		// (rsKEYDOWN) TODO: вызвать обработчик напрямую
		if (wParam == VK_SHIFT) 
			translateShiftKey (&lParam, true);
		return 0;

	case WM_KEYUP:
	case WM_SYSKEYUP:
		if (translateKey (&lParam, lParam, wParam))
			RsKeyboardEventHandler (0x1D, &lParam);	// (rsKEYUP) TODO: вызвать обработчик напрямую
		if (wParam == VK_SHIFT) 
			translateShiftKey (&lParam, false);
		return 0;

	case WM_SIZING:
		// redraw window
		if (rwInitialized && *gameState == 9) 
			idle (1);
        newPos = (LPRECT) lParam;
		SetWindowPos(hWnd, HWND_TOP, 0, 0, newPos->right-newPos->left, newPos->bottom-newPos->top, SWP_NOMOVE);
		_7043D0 ();	// TODO: setCameraRasterSize ()		
		return 0;

	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_MBUTTONDOWN:
		SetCapture (hWnd);
		return 0;

	case WM_MOUSEMOVE:
		menu->mousePosLeftA = LOWORD (lParam);
		menu->mousePosTopA = HIWORD (lParam);		
		return 0;

	case WM_LBUTTONUP:
	case WM_RBUTTONUP:
	case WM_MBUTTONUP:
		ReleaseCapture ();
		return 0;

	case WM_DEVICECHANGE:	// CD/DVD removed
		debug ("Unsupported message WM_DEVICECHANGE\n");
		// TODO: здесь достаточно большая проверка на наличие DVD в приводе
		// пропущено до разбора соответствующих объектов
		break;

	case WM_USER+13:	// unknown videoplayer message
		if (*gameState == 2 || *gameState == 4) 
			videoPlayerEvent ();	
		break;

    case WM_TIMER:
		return 0;

	}
	return DefWindowProc (hWnd, msg, wParam, lParam);
}
//=============================================================================
// main function
//-----------------------------------------------------------------------------
// check second instance
static bool isAlreadyRunning () {
	CreateEvent (NULL, false, true, appName);
	if (ERROR_ALREADY_EXISTS != GetLastError ()) 
		return false;
	
	HWND w = FindWindow (appClassName, rsGlobal->appName);
	if (w)
		SetForegroundWindow (w);

	return true;
}
//-----------------------------------------------------------------------------
// WinMain
int APIENTRY ogWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	debug ("WinMain started\n");

	SystemParametersInfo (SPI_SETFOREGROUNDLOCKTIMEOUT, 0, 0, SPIF_SENDCHANGE);
	if (isAlreadyRunning ()) 
		return 0;

	// => RsEventHandler (rsINITIALIZE, 0)
	// =>> Initialize ();
	*nastyGame = 1;
	*germanGame = *_B9B7EE = 0;
	// =>>> initProgramPath TODO: заинлайнить сюда, чтобы не дергать два раза
	fileManager.initialize ();	// NOTE: сюда переехала вся инициализация путей и относящегося к файловой системе
	initStreaming (5);	// TODO:: CStreaming::init
	initPad ();			// TODO:: CPad::init
	
	// rsInitialize ();	// NOTE: проверялся код возврата - по идее, ничего плохого здесь произойти не должно, но нужно уточнить
	// TODO: вынести в отдельный метод RsGlobalType. Позже - перенести в конструктор RsGlobalType или объекта, в который он войдет
	// TODO: возможно, хэндлеры будут прописываться при инициализации (или без них вообще можно обойтись)
	rsGlobal->appName = (char *)appName;
	rsGlobal->maximumWidth = 640;
	rsGlobal->maximumHeight = 480;
	rsGlobal->frameLimit = 30;
	rsGlobal->quit = false;
	rsGlobal->keyboard.type = 0;	// keyboard
	rsGlobal->keyboard.used = false;
	rsGlobal->keyboard.handler = NULL;
	rsGlobal->mouse.type = 1;	// mouse
	rsGlobal->mouse.used = false;
	rsGlobal->mouse.handler = NULL;
	rsGlobal->pad.type = 2;	// pad
	rsGlobal->pad.used = false;
	rsGlobal->pad.handler = NULL;

	// psInitialize inlined >> 747420
	// TODO: перенести в конструктор PsGlobalType
	ps->lastMousePosX = ps->lastMousePosY = 0;
	ps->fullscreen = 0;
	ps->diInterface = NULL;
	ps->diMouse = NULL;
	ps->diDevice1 = ps->diDevice2 = NULL;

	initL10N ();
	*gameState = 0;
	// NOTE: здесь были проверки возможностей процессора, версии DirectX и т.д. 
	menu__readSettingsFile (0xBA6748);	// TODO: menu->readSettingsFile ();
	ddrawGetAvailableVidMem (0xC8CF80, 0xC8CF84);	// TODO: сейчас это используется при установке видеорежима. Посмотреть, как можно обойтись без этого
	debug ("Available vidmem: %d, %d\n", *(int *)0xC8CF80, *(int *)0xC8CF84);
	// TODO: end of the constructor
	rsGlobal->ps = ps;

	debug ("WinMain: rsInitialize done\n");

	// => InitApplication	-- register window class
	// TODO: вынести отдельную функцию инициализации оконной системы (InitApplication, InitInstance и т.д.), 
	//		возможно, инициализация DirectX будет в ней же, возможно, отдельной функцией 

	// регистрируем класс для окна ...
	WNDCLASS	wndclass;
	wndclass.style = CS_BYTEALIGNWINDOW;
	wndclass.lpfnWndProc = mainWndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon (hInstance, (LPCSTR)100);
	wndclass.hCursor = LoadCursor (hInstance, (LPCSTR)32512);
	wndclass.hbrBackground = 0;
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = appClassName;
	if (!RegisterClass (&wndclass))
		return 0;

	// ... и создаем само окно
	rsGlobal->ps->instance = hInstance;
	RECT rr;
	rr.left = rr.top = 0;
	rr.right = rsGlobal->maximumWidth;
	rr.bottom = rsGlobal->maximumHeight; 
	AdjustWindowRect (&rr, WS_OVERLAPPEDWINDOW, false);
	HWND window = CreateWindowEx (0, appClassName, rsGlobal->appName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rr.right-rr.left, rr.bottom-rr.top, 
		NULL, NULL, hInstance, NULL);
	rsGlobal->ps->window = window;

	debug ("WinMain: create window: %08x\n", GetLastError ());

	if (!window) 
		return 0;
	
	debug ("WinMain: instance initialized OK\n");

	// TODO: preprocess command line
	//		до этой точки повлиять ни на что было нельзя. Дальше, можно получить из комендной строки все, что передали:
	//		видеорежим (возможно, оконный), флаги для пропуска интро, флаг автостарта игры/загрузки последнего сохранения и т.д.

	// initialize DirectInput
	inputEvents->initialize ();
	inputEvents->mapKeyboard ();
	
	if (DI_OK == DirectInput8Create (GetModuleHandle (NULL), 0x800, IID_IDirectInput8A, (LPVOID *)&(rsGlobal->ps->diInterface), NULL)) {
		diMouseInit (0);
		diPadInit ();
	} else 
		debug ("ERROR: Unable to get DirectInput interface\n");

	debug ("WinMain: DirectInput init OK\n");
	menu->menuActive = true;	// чтобы избежать обращения к неинициализированному rwEngineInstance

	/// inputEvents__mapMouse (0xB70198, getMouseButtonMask (mouseMask), menu->useKeyboardAndMouse == 0 ? 1 : 0); // TODO: inputEvents.mapMouseButtons
	/// inputEvents->mapMouseButtons (getMouseButtonMask (mouseMask));
	inputEvents->mapMouseButtons ();

	// initialize RenderWare Graphics
	if (!initialize3D (window)) {
		DestroyWindow (window);
		terminate ();
		return 0;
	}

	if (rsGlobal->ps->fullscreen) {
		SetWindowLong (window, 0xFFFFFFF0, 0x80000000);
		SetWindowPos (window, 0, 0, 0, 0, 0, 0x27);
	}

	debug ("WinMain: 3D initialized OK\n");

	// TODO: process command line

	RECT r;
	r.left = r.top = 0;
	r.right = rsGlobal->maximumWidth;
	r.bottom = rsGlobal->maximumHeight;
	cameraSize (*(DWORD *)0xC1703C, &r, tanf (45.0f*(float)M_PI/360.0f), 4.0f/3.0f);	// TODO: gtaCamera.setSize (...)

	// change some system parameters
	STICKYKEYS skeys;
    STICKYKEYS newSkeys;
    skeys.cbSize = sizeof (STICKYKEYS);
    newSkeys.cbSize = sizeof (STICKYKEYS);
    newSkeys.dwFlags = 0x100;
	SystemParametersInfo (SPI_SETSCREENSAVEACTIVE, 0, 0, SPIF_SENDCHANGE);
    SystemParametersInfo (SPI_SETPOWEROFFACTIVE, 0, 0, SPIF_SENDCHANGE);
    SystemParametersInfo (SPI_SETLOWPOWERACTIVE, 0, 0, SPIF_SENDCHANGE);
    SystemParametersInfo (SPI_GETSTICKYKEYS, sizeof (STICKYKEYS), &skeys, SPIF_SENDCHANGE);
    SystemParametersInfo (SPI_SETSTICKYKEYS, sizeof (STICKYKEYS), &newSkeys, SPIF_SENDCHANGE);

	debug ("WinMain: before show window: window = %08x, nShowCmd = %d\n", window, nCmdShow);

	ShowWindow (window, nCmdShow);
    UpdateWindow (window);

	chdirToUserPath ();
	FILE * file = fopen ("gta_sa.set", "rb");
	if (file) {
		if (inputEvents__readSettingsFile (0xB70198, file))	// TODO: inputEvents.readFromSettingsFile			
			// inputEvents__mapAllButtons (0xB70198);	// TODO: inputEvents.mapAllButtons
			inputEvents->mapAllButtons ();
		fclose (file);
	}	
	chdirProgramDir ("");
	SetErrorMode (SEM_FAILCRITICALERRORS);

	bool _v81 = true;	// TODO: зачем это?

	debug ("WinMain: before main loop... (Q = %d, S = %d)\n", rsGlobal->quit, *gameState);
	
	while (true) {	// 7489F0: main loop
		rwInitialized = true;

		V2D	pos;
		pos.x = (float)rsGlobal->maximumWidth/2.0f;
		pos.y = (float)rsGlobal->maximumHeight/2.0f;
		rsMouseSetPos (&pos);

		gamma__init (0xC92134);	// TODO: gamma.init ();

		WINDOWPLACEMENT wpl;
		wpl.length = sizeof (WINDOWPLACEMENT);
		MSG message;

		while (!rsGlobal->quit) {	// game loop
			if (menu->doGameReload)
				break;
			if (PeekMessage (&message, NULL, 0, 0, 3)) {
				if (message.message == WM_QUIT)
					break;
				TranslateMessage(&message);
				DispatchMessage (&message);
				continue;
			}

			if (!*foregroundApp) {
				*isForeground = false;
				Sleep (100);
				continue;
			}

			switch (*gameState) {	// 748AA8
			case 0:
				readLoadingTextures (1, 0);
				startLoadingInterval (1, 1);
				showLoadingTexture ();
				showLoadingProgress ();
				cleanLoadingTexture ();

				readLoadingTextures (1, 1);
				startLoadingInterval (1, 1);
				showLoadingTexture ();
				showLoadingProgress ();
				cleanLoadingTexture ();

				*gameState = 5;	// TODO: 1
				break;
			case 1:
				if (!windowed)
					createVideoPlayer ("movies/logo.mpg");
				*gameState = 2;
				break;
			case 2:
			case 4:
				pad__update ();	// TODO: pad.update ();
				/*if (windowed ||
					inputEvents.getFirstPadButtonJustPressed() ||
					pads->isAnyButtonPressed ();
					playerControls.isAnyControlPressed () ||
					playerControls.isLmbJustPressed () ||
					playerControls.isEnterJustPressed () ||
					playerControls.isStandardKeyJustPressed(' ') ||
					playerControls.isMenuKeyJustPresssed () ||
					playerControls.isTabJustPressed ()) 
					(*gameState)++;*/
				break;
			case 3:
				if (*mediaControl)
					(*mediaControl)->Stop ();
				releaseVideoPlayer ();
				createVideoPlayer ((menu->textLanguage == 7 || menu->titleLanguage == 12) ? "movies/GTATitlesGER.mpg" : "movies/GTATitles.mpg");
				*gameState = 4;
				break;
			case 5:
				if (*mediaControl)
					(*mediaControl)->Stop();
				releaseVideoPlayer ();
				startLoadingInterval (1, 0);
				showLoadingTexture ();
				if (!initGame ()) // 5BA160
					rsGlobal->quit = true;
				initGame2 (); // 5BFA90
				*gameState = 6;
				*anisotropySupported = (d3dCaps->RasterCaps & D3DPRASTERCAPS_ANISOTROPY) != 0;
				break;
			case 6:
				menu->_fE9 = true;
				menu->activateMenu = true;
				if (*videoModeNotSelected) {
					menu->videoMode = menu->prevVideoMode = *gCurSelVM;
					*videoModeNotSelected = false;
				}
				*gameState = 7;
				showLoadingProgress ();
				break;
			case 7:
				GetWindowPlacement (window, &wpl);
				if (wpl.showCmd != SW_SHOWMINIMIZED)
					render ();										
				if (!menu->menuActive)
					*gameState = 8;
				if (!menu->_f60)
					break;
				// fall-through
			case 8:
				audioManager__507410 (0xB6BC90);	// TODO: audioManager._507410 ();				
				_53E580 (); // load game (TODO: inline it?)
				menu->_fE9 = false;
				*gameState = 9;
				audioManager__5078F0 (0xB6BC90);	// TODO: audioManager._5078F0 ();				
				break;
			case 9:
				if (!rwInitialized)
					break;				
				if (menu->frameLimiterOn || *replayInProgress || audioEvent__5071D0 (0xB6BC90)) {	// TODO audioManager._5071D0 ()
					if (1000.0f/rsGlobal->frameLimit > getTimeFromRenderStart ()/(*timerDivider)) 
						break;
				}
				idle (1);				
				break;
			}
			*isForeground = true;
		}

		// 748DD5 ...
		rwInitialized = false;
		menu__unloadTextures (menu); // TODO: menu.unloadTextures ();
		if (!menu->doGameReload)
			break;	
		initCheats ();	// CCheats::init ();		
		resetTimers ();	// CTimer::reset ();		
		if (menu->_f60)  {
			_53C550 ();
			_53C680 ();
			menu->_f60 = false;
		} else {
			if (_v81) {				
				resetTimers ();	// CTimer::reset ();
				*gameState = menu->_f5E ? 6 : 8;	// TODO: check it!				
			} else {
				initCheats ();	// CCheats::init ();		
				_53C550 ();
				resetTimers ();	// CTimer::reset ();
				_53C680 ();				
			}
		}

		_v81 = false;
		menu->_f5E = false;
		menu->doGameReload = false;
	}

	// 748E62 ...
	if (*gameState == 9)
		_53C900 (); // cleanAll ();

	audioEvent__507CB0 (0xB6BC90);	// audioManager.terminate ();	
	freeVideoModeList ();
	terminate3D ();
	DestroyWindow (window);
	terminate ();	
	/// free (argv);	// TODO: потребуется после того, как будет сделана обработка командной строки

	// restore system parameters
	ShowCursor (true);
    SystemParametersInfo (SPI_SETSTICKYKEYS, sizeof (STICKYKEYS), &skeys, SPIF_SENDCHANGE);
    SystemParametersInfo (SPI_SETPOWEROFFACTIVE, 1, 0, SPIF_SENDCHANGE);
    SystemParametersInfo (SPI_SETLOWPOWERACTIVE, 1, 0, SPIF_SENDCHANGE);
    SystemParametersInfo (SPI_SETSCREENSAVEACTIVE, 1, 0, SPIF_SENDCHANGE);

	SetErrorMode (0);
	return 0;
}