Перейти к содержанию

[Counter Strike Source] и вывод графики через DirectX


Рекомендуемые сообщения

проблема в том ,что ничего не рисует , но надпись jast do it =) отображает правильно( то есть она отображается , что значит проходит проверку на w2s и когда отворачиваюсь от ботов , исчезает )

код где я все делаю 

Скрытый текст

D3DXVECTOR2 treangle[2];//для рисования
D3DCOLOR cool = D3DCOLOR_ARGB(255, 255, 0, 0);
ID3DXLine *line;

void DrawIndicator(void* self) 
{
	IDirect3DDevice9* dev = (IDirect3DDevice9*)self;
	dev->BeginScene();
	D3DXCreateFont(dev, 16, 0, FW_NORMAL, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, ANTIALIASED_QUALITY, 0 | FF_DONTCARE, TEXT("Arial"), &m_font);
	D3DXCreateFont(dev, 16, 0, FW_NORMAL, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, ANTIALIASED_QUALITY, 0 | FF_DONTCARE, TEXT("Arial"), &m_font1);
	GiveMeLineStandart(dev);
	ViewMatrix = (PD3MATRIX)(engine_dll + v_matrix_on);;
	D3DVIEWPORT9 viewport;
	dev->GetViewport(&viewport);
	FindClientModule();
	FindEngineModule();
	float coordX, coordY, coordZ;
	float pOut[3];
	if (indicator)
	{
		for (int i = 1; i < 15; i++)
		{
			coordX = *reinterpret_cast<float*>(client_dll + player_onl_coordX+i*320);
			coordY = *reinterpret_cast<float*>(client_dll + player_onl_coordY+i*320);
			coordZ = *reinterpret_cast<float*>(client_dll + player_onl_coordZ+i*320);
			if ((coordX == 0) || (coordY == 0)) { continue; }
			if (World_To_Screen(coordX, coordY, coordZ, viewport, pOut) == 1)
			{
				m_font1->DrawTextA(0, "just do it", -1, &fontRect1, 0, D3DCOLOR_ARGB(255, 255, 255, 0));
				treangle[0] = D3DXVECTOR2(pOut[0], pOut[1]);
				treangle[1] = D3DXVECTOR2(pOut[0], pOut[1]+50);
				line->Begin();
				line->Draw(treangle, 2, cool);
				line->End();
			}
		}
	}
	else    
	{
		m_font->DrawTextA(0, "Working?!", -1, &fontRect, 0, D3DCOLOR_ARGB(255, 0, 255, 0));
	}
	dev->Clear(1, &rec, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0,0,0,0), 1.0f, 0);
	m_font->Release();
	m_font1->Release();
	line->Release();
	dev->EndScene();
}

 

ещё возможно не правильно вызвал dev ...

 

Up

функция w2s:

Скрытый текст

typedef struct D3MATRIX
{
	float m[4][4];
	
}*PD3MATRIX;

PD3MATRIX ViewMatrix;


int World_To_Screen(float coordX, float coordY, float coordZ, D3DVIEWPORT9 viewport, float* pOut)
{
	float w = 0.0f;
	pOut[0] = ViewMatrix->m[0][0] * coordX + ViewMatrix->m[0][1] * coordY + ViewMatrix->m[0][2] * coordZ + ViewMatrix->m[0][3];
	pOut[1] = ViewMatrix->m[1][0] * coordX + ViewMatrix->m[1][1] * coordY + ViewMatrix->m[1][2] * coordZ + ViewMatrix->m[1][3];
	w = ViewMatrix->m[3][0] * coordX + ViewMatrix->m[3][1] * coordY + ViewMatrix->m[3][2] * coordZ + ViewMatrix->m[3][3];
	if (w < 0.01f) { return 0; }
	float intw = 1.0f / w;
	pOut[0] *= intw;
	pOut[1] *= intw;
	int weight = (int)(viewport.Width);
	int height = (int)(viewport.Height);
	float x = weight / 2;
	float y = height / 2;
	x += 0.5*pOut[0] + weight + 0.5;
	y -= 0.5*pOut[1] + height + 0.5;
	pOut[0] = x;
	pOut[1] = y;
	return 1;
}

 

 

PS Благодарю за ответы =)

Изменено пользователем IzerodayI
Ссылка на комментарий
Поделиться на другие сайты

1 час назад, IzerodayI сказал:

typedef struct D3MATRIX

 

Возможно из-за этого, хотя может и нет. 

В общем, где-то читал, что struct определяет новый тип, а typedef struct вроде как создает только ссылку на структуру, вместо нового типа. В идеале, стоит для начала под отладкой пропустить свой код, а не гадать что у тебя не так с кодом и почему что-то не рисуется.

Изменено пользователем partoftheworlD
Ссылка на комментарий
Поделиться на другие сайты

56 минут назад, partoftheworlD сказал:

Возможно из-за этого, хотя может и нет

Это я посоветовал так сделать, чтобы читать матрицу сразу по указателю, а не копировать ее в свой массив.

Насколько я понимаю, запись с typedef эквивалентна такому:

/*
typedef struct D3MATRIX
{
	float m[4][4];
} *PD3MATRIX;
*/

struct D3MATRIX
{
	float m[4][4];
};

typedef D3MATRIX *PD3MATRIX;

Тем более, в случае с typedef ты можешь вообще опустить имя структуры, но назначить ей псевдоним (*PD3MATRIX).

waWWMYy.png
Я часто встречал такую запись в тех же хедерах winnt, поэтому уже привык к такой записи. Да и в Си она позволяет опускать слово struct при объявлении переменной.

Скрытый текст

Пример из winnt.h:


typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

 

PS переведите в гугле слово typedef xD

Изменено пользователем uhx
Ссылка на комментарий
Поделиться на другие сайты

10 минут назад, uhx сказал:

Это я посоветовал так сделать

И  корректно работает такой вариант? Я такой способ раз попробовал, но что-то не завелось и стал использовать просто структуру как новый тип.

Изменено пользователем partoftheworlD
Ссылка на комментарий
Поделиться на другие сайты

6 минут назад, partoftheworlD сказал:

И работает такой вариант? Я такой способ раз попробовал, но что-то не завелось и стал использовать просто структуру как новый тип.

Да, конечно. Прежде чем скидывать, я его прогнал сам у себя.

 

Скрытый текст

typedef struct D3MATRIX
{
	float m[4][4];
} *PD3MATRIX;

PD3MATRIX viewMatrix;

int main( int argc, char **argv )
{
	setlocale( 0, "" );

	float arr[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };

	viewMatrix = (PD3MATRIX) arr;

	for( int i = 0; i < 4; i++ )
	{
		for( int j = 0; j < 4; j++ )
		{
			printf( "%6.2f ", viewMatrix->m[i][j] );
		}

		putchar( '\n' );
	}

	system( "pause > nul" );

	return 0;
}

Вывод:


  1,00   2,00   3,00   4,00
  5,00   6,00   7,00   8,00
  9,00  10,00  11,00  12,00
 13,00  14,00  15,00  16,00

Ну а по сути да, в C++ не особо важно как делать. Можно просто объявлять через struct name, и потом заводить как name *pName;

PS только щас догнал, что у себя в проекте я тоже копирую матрицу :D

Изменено пользователем uhx
Ссылка на комментарий
Поделиться на другие сайты

1 час назад, partoftheworlD сказал:

В идеале, стоит для начала под отладкой пропустить свой код, а не гадать что у тебя не так с кодом и почему что-то не рисуется.

В общем-то да, именно этим и стоит заняться. Тут я полностью согласен, никто за тебя твой код дебажить не будет, поэтому вперед.

Проверь, рисуются ли у тебя ВООБЩЕ линии. Правильно ли ты ее рисуешь? Судя по тому, что надпись пропадает/появляется, то дело именно в линии, поэтому копай в ее сторону.

Почему ты не выставляешь ее толщину?

line->SetWidth( 1.0f );

Где ты вообще создаешь ID3DXLine?

Короче, странный ты. Говоришь

2 часа назад, IzerodayI сказал:

проблема в том ,что ничего не рисует

и в то же время пишешь, что надпись-таки верно отображается. Так на кой нам твой W2S, если он верно работает? У тебя что не рисуется то, линия? Значит ее и проверяй.

Почему мы должны гадать - не пойму. Просто проверь свой код хоть как-то... смысл тут кучу тем уже плодить вокруг одного.

Возьми исходники чужие, благо их полным полно, и сверься, проверь тот код, посмотри на свой, сравни и тд.

Изменено пользователем uhx
Ссылка на комментарий
Поделиться на другие сайты

15 минут назад, uhx сказал:

Почему ты не выставляешь ее толщину?

да, в программе есть ширина линии

GiveMeLineStandart(dev);

и ее тело:

void GiveMeLineStandart(IDirect3DDevice9* device)
{
	D3DXCreateLine(device, &line);
	line->SetWidth(4.0f);
	line->SetPattern(0xffffffff);
	line->SetAntialias(FALSE);
}

 

17 минут назад, uhx сказал:

что надпись-таки верно отображается

перепроверил надпись , не всегда правильно пишет слово, видно первый раз повезло....

 

PS при создание консоли AllocConsole()-(консоль создаётся с именем игры) , как перенаправить поток буфера cout в AllocConsole?

 

А вообще я пытался координаты записывать в блокнот 

ofstream fail("Matrix.txt");
fail << ViewMatrix->m[0][0] //и так далее...
fail.close(); 

но dll не создавала этот блокнот. хотя через exe работает.

Ссылка на комментарий
Поделиться на другие сайты

17 минут назад, IzerodayI сказал:

как перенаправить поток буфера cout в AllocConsole

лол

17 минут назад, IzerodayI сказал:

но dll не создавала этот блокнот

она создает его в папке с игрой. Или ты думал где он появится?

17 минут назад, IzerodayI сказал:

перепроверил надпись , не всегда правильно пишет слово, видно первый раз повезло

почему не вывести надпись по координатам из W2S? Почему не вывести на экран те же координаты, что вернул W2S? Ты вообще хоть как-то дебажил свой код, смотрел какие у тебя значения там фигурируют?

Изменено пользователем uhx
Ссылка на комментарий
Поделиться на другие сайты

17 минут назад, uhx сказал:

она создает его в папке с игрой. Или ты думал где он появится?

да я смотрел и поиском пользовался - не нашел.

 

18 минут назад, uhx сказал:

лол

я не так вводил:blink: в Яндексе...(мой косяк)

21 минуту назад, uhx сказал:

почему не вывести надпись по координатам из W2S? Почему не вывести на экран те же координаты, что вернул W2S? Ты вообще хоть как-то дебажил свой код, смотрел вообще какие у тебя значения там фигурируют?

так и хочу сделать , только начну с client.dll и engine.dll, причина выше....

PS извини  :) (писать сюда, формирую мысли, а  в Яндекс нет, так по моей "ахинеи" ничего не находит)

Ссылка на комментарий
Поделиться на другие сайты

16 часов назад, uhx сказал:

смотрел какие у тебя значения там фигурируют?

Просмотрел значения , матрица совпадает со значением матрицы exe , а вот координаты  ботов нет, они 0:0:0.

Есть сомнения по поводу их нахождения(в dll)- в exe  я координаты находил так :

DWORD adres = client_dll + struct_onl_play_of;

и с помощью функции readproccesmemory я искал adres и получал &coords.

А в dll я их нахожу так :

Скрытый текст

const DWORD struct_onl_play_of = 0x4035C0;
const DWORD player_onl_coordX = 0x1a0;
const DWORD player_onl_coordY = 0x1a4;
const DWORD player_onl_coordZ = 0x1a8;

//Код...

	addr_client = client_dll + struct_onl_play_of;
	for (int i = 1; i < 15; i++)
		{
			coordX = *reinterpret_cast<float*>(addr_client + player_onl_coordX+i*320);
			coordY = *reinterpret_cast<float*>(addr_client + player_onl_coordY+i*320);
			coordZ = *reinterpret_cast<float*>(addr_client + player_onl_coordZ+i*320);

 

и находятся не те значения...

 

Ссылка на комментарий
Поделиться на другие сайты

В 20.07.2017 в 22:27, partoftheworlD сказал:

Возможно из-за этого, хотя может и нет

С матрицей все хорошо , а вот с координатами игроков оказывается плохо....

Моя ошибка скорее всего в том , что я в dll складываю оффсеты , а не получаю адрес как в exe с помощью ReadProccesMemory

код dll :

Скрытый текст

const DWORD struct_onl_play_of = 0x4035C0;
const DWORD player_onl_coordX = 0x1a0;
const DWORD player_onl_coordY = 0x1a4;
const DWORD player_onl_coordZ = 0x1a8;

//Код...

	addr_client = client_dll + struct_onl_play_of;
	for (int i = 1; i < 15; i++)
		{
			coordX = *reinterpret_cast<float*>(addr_client + player_onl_coordX+i*320);
			coordY = *reinterpret_cast<float*>(addr_client + player_onl_coordY+i*320);
			coordZ = *reinterpret_cast<float*>(addr_client + player_onl_coordZ+i*320);

 

тогда как в dll получить адрес , а не складывать оффсеты?

Ссылка на комментарий
Поделиться на другие сайты

1 час назад, IzerodayI сказал:

тогда как в dll получить адрес , а не складывать оффсеты?

https://habrahabr.ru/post/116255/

 

 

addr_client = *reinterpret_cast<DWORD*>(client_dll + struct_onl_play_of);

coordX = *reinterpret_cast<float*>(addr_client + player_onl_coordX+i*0x140)

 

Изменено пользователем partoftheworlD
Ссылка на комментарий
Поделиться на другие сайты

Скрытый текст
6 часов назад, partoftheworlD сказал:

https://habrahabr.ru/post/116255/

 

 



addr_client = *reinterpret_cast<DWORD*>(client_dll + struct_onl_play_of);

coordX = *reinterpret_cast<float*>(addr_client + player_onl_coordX+i*0x140)

 

 

не поверишь , но ТЕПЕРЬ показывает координаты:D

я не считал это за ошибку , а почему в exe так работало, не знаешь?

просто я даже не думал , что элемент DWORD нужно явно преобразовывать  в DWORD

PS Большое спасибо

Изменено пользователем IzerodayI
Ссылка на комментарий
Поделиться на другие сайты

6 часов назад, partoftheworlD сказал:

А по этому же принципу происходит обфускация кода(что-бы было ничего не понятно антивирусу и людям смотрящим код(допустим игры , что-бы труднее на неё писать читы^_^)) ?

 

Ссылка на комментарий
Поделиться на другие сайты

39 минут назад, IzerodayI сказал:

я не считал это за ошибку , а почему в exe так работало, не знаешь?

просто я даже не думал , что элемент DWORD нужно явно преобразовывать  в DWORD

PS Большое спасибо

Ну типа ты для начала должен получить указатель на базовый адрес, а после уже работать с ним.

ReadProcessMemory так и делает.

 

22 минуты назад, IzerodayI сказал:

А по этому же принципу происходит обфускация кода(что-бы было ничего не понятно антивирусу и людям смотрящим код(допустим игры , что-бы труднее на неё писать читы^_^)) 

Думаю нет. В дизассемблере иногда логика кода понятней, чем читая тот же код.

Читы, да любые программы легко писать, когда знаешь что хочешь сделать.

Изменено пользователем partoftheworlD
Ссылка на комментарий
Поделиться на другие сайты

2 часа назад, IzerodayI сказал:

просто я даже не думал , что элемент DWORD нужно явно преобразовывать  в DWORD

Нет, ты похоже не понял прикола.

Твой код:

В 21.07.2017 в 19:46, IzerodayI сказал:

DWORD adres = client_dll + struct_onl_play_of;

Он складывает два значения, которые находятся в client_dll и struct_onl_play_of. Пусть это будет, например, 0x400000 и 0x1234 соответственно.

В результате получается 0x401234, и отталкиваясь от этого адреса ты пытался читать позиции игроков.

То, что тебе предложили:

2 часа назад, IzerodayI сказал:

addr_client = *reinterpret_cast<DWORD*>(client_dll + struct_onl_play_of);

Тут складываются опят же два значения, но они приводятся к указателю на DWORD значение. А затем по этому указателю, т.е. по адресу 0x401234, берется значение (видишь звездочку в начале?), которое там записано. Например, 0x500000.

А значит, теперь у тебя addr_client равен не 0x401234, а 0x500000, и уже отталкиваясь от этого адреса ты читаешь позиции игроков.

Так как ты не приложил код из exe, а показал лишь одну строчку из которой нихрена не понятно, то могу предположить что ты читал значение из результата сложения двух адресов, типа так:

ReadProcessMemory(
  m_hProcess,
  (LPCVOID)( client_dll + struct_onl_play_of ),
  (LPVOID) &adres,
  sizeof( adres ),
  NULL
);

Короче, разбирайся с указателями. Лучше вообще начни изучать нормально плюсы, а не пытаться непойми как писать и ловить кучу ошибок из-за этого. Потому что сейчас ты явно не понимаешь что у тебя происходит в коде и как это работает.

Изменено пользователем uhx
  • Плюс 1
Ссылка на комментарий
Поделиться на другие сайты

18 минут назад, uhx сказал:

Тут складываются опят же два значения, но они приводятся к указателю на DWORD значение. А затем по этому указателю, т.е. по адресу 0x401234, берется значение (видишь звездочку в начале?), которое там записано. Например, 0x500000.

А значит, теперь у тебя addr_client равен не 0x401234, а 0x500000, и уже отталкиваясь от этого адреса ты читаешь позиции игроков.

Спасибо за объяснения, для меня сложно объяснить кому-то что-то, я сам просто знаю что так работает, просто потому что работает не вдаваясь в подробности.:D

Ссылка на комментарий
Поделиться на другие сайты

6 часов назад, partoftheworlD сказал:

для меня сложно объяснить кому-то что-то, я сам просто знаю что так работает

Конечно лучше объяснять, как например делает это Keng (дар учителя), нужно добиться понимания, а не знания.

А то часто наблюдаю: выложишь человеку скрипт, который нужно ему сделать под себя (ну например просто заменить регистр), ан нет - не могут.;)  

Ссылка на комментарий
Поделиться на другие сайты

Скрытый текст
17 часов назад, uhx сказал:

понимаешь что у тебя происходит в коде

 

17 часов назад, partoftheworlD сказал:

просто потому что работает не вдаваясь в подробности

 

Я все сделал, но он не рисует линию , хотя он правильно определяет размеры окна, видовую матрицу и координаты игрока (все это проверил)

А вот выходные координаты для обрисовки ботов (такие как pOut_1 и pOut_2):

pOut_1 = 1920

pOut_2 = -300

Что очень странно , если предположить , что левый правый угол это 0:0 , а игра  weight = 1280 ; height =600(с помощью D3DVIEWPORT9 viewport (да , он и в правду работает =)  )),

то с такими выходными координатами как :  pOut_1 = 1920, pOut_2 = -300 , я никакой обрисовки ботов не увижу

screen с док-вами :

[img=https://cloud.mail.ru/public/MJ6r/h18TbRSMK ]

как примерно  можно это исправить?

PS Благодарю за ответы 

Ссылка на комментарий
Поделиться на другие сайты

18 минут назад, IzerodayI сказал:

как примерно  можно это исправить?

PS Благодарю за ответы 

Да ты наверное шутишь? Извини конечно,но ты понимаешь что делаешь?

для отрисовки координаты пропускаются через w2s функцию которая переводит мировые координаты в экранные координаты по которым рисуются боксы, для линии создают вектор от точки а, до точки б по нему рисуют. И если все верно найдено, то все обязательно будет работать.

UPD 

Что отладка показывает при рисовании линий?

Изменено пользователем partoftheworlD
Ссылка на комментарий
Поделиться на другие сайты

27 минут назад, partoftheworlD сказал:

Что отладка показывает?

Да глянь на W2S его ( в начале поста ). Она кривая же. Даже не представляю откуда он ее взял, лол.

 

Если свернуть все в одно выражение, то получается так:

pOut[0] = weight + weight / 2 + 0.5 + ( ViewMatrix->m[0][0] * coordX + ViewMatrix->m[0][1] * coordY + ViewMatrix->m[0][2] * coordZ + ViewMatrix->m[0][3] ) / w / 2

А теперь смотрим на исходные данные:

weight = 1280

Т.е. weight + weight / 2 = 1920, что и у него. 

Во-первых, сама формула неверная. Я не знаю как так ТС ее переписал, но она выглядит по другому. Там явно должны перемножаться размеры экрана на выражение с матрицей, а тут они просто складываются.

Правда странно, почему только левая часть тут работает, почему не суммирует вычисления с матрицей))

Но если честно, то я задолбался конкретно. ТС какой-то непонятливый, как ему еще самому не надоело - не пойму. Разве так сложно понять, что раз у тебя верные координаты, верная видовая матрица, но, мать его, неверные результаты после выполнения W2S, то значит в ней и ошибка? Почему нельзя просто в гугле посмотреть на "эталон" W2S и сравнить со своим? Что не так?? Я когда впервые с видовой матрицей и W2S мучался, то тоже кучу проблем огреб по началу: так же координаты криво определяло и тд, но простой дебаг + интернет взяли свое. Почему ТС по каждой проблеме пишет сюда? Она не такая уж и сложная, опять же: примеров полным полно. Все это банально гуглится и исправляется за 5 минут. Открыл исходник ЛЮБОГО чита - пожалуйста, вот тебе и W2S, и правильный метод для определения размеров окна и куча еще прочего.

Пошел лечить нервы.

Изменено пользователем uhx
Ссылка на комментарий
Поделиться на другие сайты

 

14 минуты назад, uhx сказал:

Она кривая же.

Если ты про функцию, то у меня примерно такая же в коде и рабочая на нескольких играх проверенно

 

 

В 20.07.2017 в 21:23, IzerodayI сказал:

x += 0.5*pOut[0] + weight + 0.5;

y -= 0.5*pOut[1] + height + 0.5;

 

Должно быть

x += 0.5*pOut[0] * weight + 0.5; 
y -= 0.5*pOut[1] * height + 0.5;

 

 

 

Ссылка на комментарий
Поделиться на другие сайты

4 минуты назад, partoftheworlD сказал:

Должно быть

Так я о том же: формулы кривые, это сразу видно. Смотри пост выше.

Я так понимаю он брал функцию у keng'a, но даже у него она по другому выглядит. Как раз то, что ты и написал.

Как можно ошибиться в копипасте?

Изменено пользователем uhx
Ссылка на комментарий
Поделиться на другие сайты

32 минуты назад, partoftheworlD сказал:

Должно быть

 

31 минуту назад, uhx сказал:

Как можно ошибиться в копипасте?

Да не , я вручную переписывая с экрана код (ну или с другого места) , я так его лучше понимаю и вникаю в него , потому,что каждую строчку в уме рассказываю , что и для чего она нужна...

просто когда долго печатаешь , утомляешься и ошибаешься , а проверяю на глаз(примерно).

41 минуту назад, partoftheworlD сказал:

Должно быть


x += 0.5*pOut[0] * weight + 0.5; 
y -= 0.5*pOut[1] * height + 0.5;

 

PS ошибка была в этом и все заработало , всем спасибо и хороших нервов!:)

извини за нервы:mellow: 

Ссылка на комментарий
Поделиться на другие сайты

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.