Инженерный анализ, Reverse Engineering |
Здравствуйте, гость ( Вход | Регистрация )
Инженерный анализ, Reverse Engineering |
27 Jan 2019, 11:05
(Сообщение отредактировал XEPOMAHT - 27 Jan 2019, 11:07)
Сообщение
#121
|
|
---------------------- New_Life_of_Heroes ---------------------- Сообщений: 226 Спасибо сказали: 345 раз |
Какие диапазоны могу использовать, чтобы не нарушить код после запуска и во время игры? Все секции памяти используются геройским кодом. В конце воговской секции вроде бы был неиспользованный копеечный участок, но и туда бездумно лезть - тоже извращенство. Необходимо 3800 адресов. Разве проблема создать *.dll под ЭРА и выделить нужное количество памяти под собственные нужды? Либо в самой ЭРЕ слишком сложно использовать SN:M? Опять придумываете велосипед вместе того, чтобы использовать проверенное временем. -------------------- WoG + MoP + HoA + Forge + Bastion = ERA+
|
|
|
27 Jan 2019, 11:28
(Сообщение отредактировал Richter - 27 Jan 2019, 14:50)
Сообщение
#122
|
|
collector of time Сообщений: 159 Спасибо сказали: 73 раза |
Все секции памяти используются геройским кодом. В конце воговской секции вроде бы был неиспользованный копеечный участок, но и туда бездумно лезть - тоже извращенство. То, есть такие нельзя использовать:00639D55 00 DB 00 00639D58 00 DB 00 ...? Разве проблема создать *.dll под ЭРА и выделить нужное количество памяти под собственные нужды? Либо в самой ЭРЕ слишком сложно использовать SN:M? Опять придумываете велосипед вместе того, чтобы использовать проверенное временем. Повторюсь, не являюсь программистом, поэтому dll = проблема. Если не трудно создайте адресов на 80 тыс. Мне это поможет. Может ещё кому пригодится. На счет SN:M не понятно как она поможет в добавлении кода. Код Работа с дополнительной памятью ↑ Описание ^ !!SN:M[...]; ЕРМ переменные статичны и ограничены в количестве. Статичность приводит к невозможности организовывать динамические структуры данных (например, списки), для которых нужны функции выделения и освобождения памяти, а ограниченное количество ведёт к необходимости строгого учёта индексов без возможности выйти за их пределы. Более того, ЕРМ строки в виде z-переменных занимают ровно 512 байт каждая в независимости от размера их содержимого. Эра предоставляет программисту до 2 млрд. слотов под массивы новых переменных (числовых или строковых). Размер массивов может изменяться средствами ЕРМ. Поскольку работа с динамическими структурами предполагает автоматическое выделение номеров слотов, то такая возможность присутствует. Слоты с положительными индексами принадлежат пользователю, а с отрицательными используются при автовыделении памяти. Удаление слота памяти ^ !!SN:M[номер слота, начиная с 0]; Пример: !!SN:M5; удалить слот 5 Получение/установка размера слота ^ !!SN:M[номер слота]/[?][количество элементов]; Размер слота — это количество элементов в массиве. Команда возвращает -1, если слот не существует. Пример: !!SN:M2/5; установить количество элементов в слоте 2 равное 5. !!SN:My1/?y2; получить размер слота y1 в переменную y2. Работа со значениями элементов слотов ^ !!SN:M[номер слота]/[номер элемента, начиная с 0]/[?][значение]; Пример: !!SN:M1/3; размер слота 1—3 элемента !!SN:M1/0/111 M1/1/222 M1/2/333; содержимое слота 1: 111, 222, 333 !!SN:M1/1/?y5; y5 — содержимое 1-го элемента слота 1 !!IF:M^%Y5^; выведет: "222" Получение адреса элемента слота ^ !!SN:M[номер слота]/?[адрес элемента]/[номер элемента]; При удалении слота или изменении его размера, — адрес станет недействительным! Пример: !!SN:M1/?y1/2; y1 содержит адрес 2-го элемента слота 1 Создание нового слота ^ !!SN:M[номер слота]/[количество элементов]/[тип элементов]/[запоминать ли значения в сохранёнках]; Старое содержимое слота (если оно было), уничтожается. *номер слота* — "-1" для автовыделения свободного номера и помещения его в v1. *тип элементов*: - 0 (число) - 1 (строка) *запоминать ли значения в сохранёнках* - 0 (нет, при загрузке игры содержимое элементов будет представлять собой случайный мусор) - 1 (да, содержимое нужно сохранять как есть) При 0 экономится место в файле и возрастает скорость сохранения. Пример: !!SN:M0/4/1/1; выделить массив из 4-х строк в слоте 0. Сохранять их содержимое при загрузке !!SN:M0/2/^привет^; установить значение 2-й строки слота 0 !!SN:M0/3/^мир^; установить значение 3-й строки слота 0 !!SN:M0/2/?z1 M0/3/?z2; получить значения 2-й и третьей строк в z1, z2 !!IF:M^%Z1 %Z2^; выведет "привет мир" !!SN:M0; удалить слот 0 !!SN:M-1/0/0/0; выделить пустой слот под временный массив чисел !!VRy1:S1 R6; сгенерировали случайное число 1..6 !!SN:Mv1/y1; установили размер нового слота в это число !!VRy1:-1; y1 — индекс последнего элемента в слоте !!SN:Mv1/y1/777; значение последнего элемента слота — 777 -------------------- |
|
|
27 Jan 2019, 15:25
Сообщение
#123
|
|
---------------------- New_Life_of_Heroes ---------------------- Сообщений: 226 Спасибо сказали: 345 раз |
Повторюсь, не являюсь программистом, поэтому dll = проблема. Если не трудно создайте адресов на 80 тыс. Возьми, например, исходник Тифона, открой в нём текстовик Virtual.asm и допиши в конце строчку Код Выделенная_память rb 80000 после собери dll. Там же где-нибудь в хуке AfterWoG можешь записать адрес выделенной памяти в какую-нибудь, к примеру, v-переменную, если хочешь ею пользоваться из ERM. Через SN:M ещё проще - !!SN:M0/80000/0/0; -------------------- WoG + MoP + HoA + Forge + Bastion = ERA+
|
|
|
27 Jan 2019, 15:46
Сообщение
#124
|
|
laughed as one fey Сообщений: 12 166 Спасибо сказали: 20581 раз |
Стоп, а что надо-то конкретно? Ну то есть куда эти адреса?
|
|
|
27 Jan 2019, 15:50
(Сообщение отредактировал Richter - 27 Jan 2019, 15:52)
Сообщение
#125
|
|
collector of time Сообщений: 159 Спасибо сказали: 73 раза |
Стоп, а что надо-то конкретно? Ну то есть куда эти адреса? Это обычные команды из Olly беру "кусками" и переношу. Пока не спрашивайте зачем и откуда (статика). Главное, что работает (проверял). Но мало памяти. Такие дела. -------------------- |
|
|
27 Jan 2019, 16:03
Сообщение
#126
|
|
Анти-Всë Сообщений: 2 989 Спасибо сказали: 2376 раз |
Тогда надо говорить не "адреса", а "память", "блок памяти", "свободные байты", "неиспользованное адресное пространство". При чем тут "адреса" и "команды"?
И да: делай так, как я говорю, и не делай так, как я делаю. Патчинг экзешника - путь к стагнации и всё большему запутыванию кодера в собственном проекте. Именно поэтому каждая следующая версия MoP дается мне всё труднее, всё чаще хочется плюнуть и бросить. Используй DLL. -------------------- Circle of destruction, hammer comes crushing
Powerhouse of energy Whipping up a fury, dominating flurry We create the battery |
|
|
27 Jan 2019, 16:21
(Сообщение отредактировал Richter - 27 Jan 2019, 19:26)
Сообщение
#127
|
|
collector of time Сообщений: 159 Спасибо сказали: 73 раза |
Через SN:M ещё проще - !!SN:M0/80000/0/0; А как этим одновременно с Olly пользоваться? !#SN:M1300/80000/0/0; !?PI; !!SN:M1300/?y84/0; !!IF:M^%Y84^; далее открываем Olly и прыгаем на адрес и работаем. Так правильно? Патчинг экзешника - путь к стагнации и всё большему запутыванию кодера в собственном проекте... Используй DLL. Факт, уже столкнулся с этим. Однако у меня пока есть сумасшедший интерес закончить проект, главное чтоб работало как говорится. DLL наверно не сложно написать, однако не умею. -------------------- |
|
|
01 Feb 2019, 20:25
(Сообщение отредактировал planetavril - 01 Feb 2019, 20:27)
Сообщение
#128
|
|
Advanced Member Сообщений: 104 Спасибо сказали: 60 раз |
how to change the projectiles of the creatures,I replaced the elves of the rampart but how to change the bullets / arrows and to set the shots?
|
|
|
01 Feb 2019, 22:15
Сообщение
#129
|
|
laughed as one fey Сообщений: 12 166 Спасибо сказали: 20581 раз |
Опять обсуждаете в справочном треде. Не надо так.
Так, продолжаем вопрос по деревьям отстройки, на сей раз про редактор. Десять блоков двенадцатибайтовых структурок, в каждой пререквизит постройки (или -1 для от корня), указатель на название, указатель на описание. Для отключения достаточно нуля в указатель на название. Как-то так: Код struct _TownBuildingInfo_ { char* short_name; char* long_name; int prerequisit; }; #define o_KnightBuildings ((_TownBuildingInfo_*)0x005A6458) #define o_SorcBuildings ((_TownBuildingInfo_*)0x005A7830) #define o_WizardBuildings ((_TownBuildingInfo_*)0x005A6860) #define o_HereticBuildings ((_TownBuildingInfo_*)0x005A6240) #define o_WarlockBuildings ((_TownBuildingInfo_*)0x005A71D0) #define o_NecromancyBuildings ((_TownBuildingInfo_*)0x005A75F0) #define o_BarbarianBuildings ((_TownBuildingInfo_*)0x005A6AE0) #define o_WitchBuildings ((_TownBuildingInfo_*)0x005A6F30) #define o_DervishBuildings ((_TownBuildingInfo_*)0x005A6D10) #define o_RandomBuildings ((_TownBuildingInfo_*)0x005A6660) #define ME_TOWN_HALL 0 #define ME_CITY_HALL 1 #define ME_CAPITOL 2 #define ME_FORT 3 #define ME_CITADEL 4 #define ME_CASTLE 5 #define ME_TAVERN 6 #define ME_BLACKMITH 7 #define ME_MARKETPLACE 8 #define ME_SILO 9 #define ME_ART_MERCHANT 10 #define ME_MAGE_GUILD_1 11 #define ME_MAGE_GUILD_2 12 #define ME_MAGE_GUILD_3 13 #define ME_MAGE_GUILD_4 14 #define ME_MAGE_GUILD_5 15 #define ME_SHIPYARD 16 #define ME_GRAIL 17 #define ME_SPEC1 18 // #define ME_SPEC2 19 #define ME_SPEC3 20 #define ME_SPEC4 21 #define ME_DWELL1 22 #define ME_DWELL1_UP 23 #define ME_DWELL1_HORDE 24 #define ME_DWELL2 25 #define ME_DWELL2_UP 26 #define ME_DWELL2_HORDE 27 #define ME_DWELL3 28 #define ME_DWELL3_UP 29 #define ME_DWELL3_HORDE 30 #define ME_DWELL4 31 #define ME_DWELL4_UP 32 #define ME_DWELL4_HORDE 33 #define ME_DWELL5 34 #define ME_DWELL5_UP 35 #define ME_DWELL5_HORDE 36 #define ME_DWELL6 37 #define ME_DWELL6_UP 38 #define ME_DWELL7 39 #define ME_DWELL7_UP 40 void CopyBuildingInformation(_TownBuildingInfo_* source, _TownBuildingInfo_* destination) { source->short_name = destination->short_name; source->long_name = destination->long_name; source->prerequisit = destination->prerequisit; } void DisableBuilding(_TownBuildingInfo_* source) { source->short_name = 0; source->long_name = 0; source->prerequisit = -1; } void FlattenBuildingsPrereqs(_TownBuildingInfo_* source) { source->prerequisit = -1; } Пример использования: врезаемся после загрузки текстовиков, когда эти структурки и заполняются, и, например Код //Разрешить верфи и ГМ всем городам CopyBuildingInformation(o_KnightBuildings + ME_MAGE_GUILD_5, o_WarlockBuildings + ME_MAGE_GUILD_5); CopyBuildingInformation(o_WitchBuildings + ME_MAGE_GUILD_4, o_WarlockBuildings + ME_MAGE_GUILD_4); CopyBuildingInformation(o_WitchBuildings + ME_MAGE_GUILD_5, o_WarlockBuildings + ME_MAGE_GUILD_5); CopyBuildingInformation(o_BarbarianBuildings + ME_MAGE_GUILD_4, o_WarlockBuildings + ME_MAGE_GUILD_4); CopyBuildingInformation(o_BarbarianBuildings + ME_MAGE_GUILD_5, o_WarlockBuildings + ME_MAGE_GUILD_5); CopyBuildingInformation(o_SorcBuildings + ME_SHIPYARD, o_KnightBuildings + ME_SHIPYARD); CopyBuildingInformation(o_WizardBuildings + ME_SHIPYARD, o_KnightBuildings + ME_SHIPYARD); CopyBuildingInformation(o_BarbarianBuildings + ME_SHIPYARD, o_KnightBuildings + ME_SHIPYARD); CopyBuildingInformation(o_WarlockBuildings + ME_SHIPYARD, o_KnightBuildings + ME_SHIPYARD); CopyBuildingInformation(o_HereticBuildings + ME_SHIPYARD, o_KnightBuildings + ME_SHIPYARD); Код //запретить граали и разрешить строить все строения независимо друг от друга _TownBuildingInfo_* allfractions[] = {o_KnightBuildings, o_SorcBuildings, o_WizardBuildings, o_HereticBuildings, o_NecromancyBuildings, o_WarlockBuildings, o_BarbarianBuildings, o_WitchBuildings, o_DervishBuildings, o_RandomBuildings}; int dwells[] = {ME_DWELL1, ME_DWELL2, ME_DWELL3, ME_DWELL4, ME_DWELL5, ME_DWELL6, ME_DWELL7}; for(int i = 0; i!=10; i++) { DisableBuilding(allfractions[i]+ME_GRAIL); for(int j = 0; j!=7; j++) { FlattenBuildingsPrereqs(allfractions[i]+dwells[j]); } } Код //переместить фонтан удачи в корень дерева отстройки
o_SorcBuildings[ME_SPEC2].prerequisit = -1; //переместить сокровищницу в дерево отстройки замка после цитадели o_SorcBuildings[ME_SPEC3].prerequisit = ME_CITADEL; //переместить орду дендроидов в дерево отстройки 1 уровня o_SorcBuildings[ME_DWELL5_HORDE].prerequisit = ME_DWELL1; |
|
|
05 Feb 2019, 09:49
Сообщение
#130
|
|
laughed as one fey Сообщений: 12 166 Спасибо сказали: 20581 раз |
It's not a thread for questions, stop spamming.
Also, answer is somwhere ITT on first pages, read carefully. |
|
|
09 Feb 2019, 15:07
Сообщение
#131
|
|
collector of time Сообщений: 159 Спасибо сказали: 73 раза |
Здравствуйте. Вновь приходится задать вопрос в этой теме. Другой подходящей не попалось на глаза.
Где хранится способность героя ходить по воде (арт. сапоги левитации, закл. хождение по воде), можно ли её включить без артефакта и заклинания? -------------------- |
|
|
10 Feb 2019, 05:40
(Сообщение отредактировал MasterOfPuppets - 10 Feb 2019, 08:08)
Сообщение
#132
|
|
Анти-Всë Сообщений: 2 989 Спасибо сказали: 2376 раз |
Исходники MoP (ArtReNumbering.txt - перенумерация артефактов)
Код ;******************************************************************* ; Сапоги Левитации (90 -> 202) ; функция проверки: 2C123 68CA000000; PUSH 0CA 2C128 E833D30A00; CALL mop.004D9460 2C12D C20400; RETN 4 ; Замена вызовов: 7CEE E830440200; CALL mop.0042C123 7ECC E852420200; CALL mop.0042C123 1C8EB E833F80000; CALL mop.0042C123 3026B E8B3BEFFFF; CALL mop.0042C123 8161D E801ABFAFF; CALL mop.0042C123 8168C E892AAFAFF; CALL mop.0042C123 B1749 E8D5A9F7FF; CALL mop.0042C123 B299F E87F97F7FF; CALL mop.0042C123 16B796 E88809ECFF; CALL mop.0042C123 16B989 E89507ECFF; CALL mop.0042C123 16BB7A E8A405ECFF; CALL mop.0042C123 ;******************************************************************* То есть, на этот артефакт 11 проверок. Тут все они заменены на вызов одной функции, куда, в принципе, можно добавить проверки на что угодно. Здравствуйте. Вновь приходится задать вопрос в этой теме. Другой подходящей не попалось на глаза. Вот же эта тема! -------------------- Circle of destruction, hammer comes crushing
Powerhouse of energy Whipping up a fury, dominating flurry We create the battery |
|
|
11 Feb 2019, 00:43
(Сообщение отредактировал Richter - 11 Feb 2019, 00:58)
Сообщение
#133
|
|
collector of time Сообщений: 159 Спасибо сказали: 73 раза |
То есть, на этот артефакт 11 проверок. Тут все они заменены на вызов одной функции, куда, в принципе, можно добавить проверки на что угодно. Master of puppets, спасибо за указанное направление. Попробую разобраться. Здравствуйте. Вновь приходится задать вопрос в этой теме. Другой подходящей не попалось на глаза. Вот же эта тема! Думал это просто обсуждение уже имеющегося в "Инженерном анализе". Теперь буду знать. -------------------- |
|
|
22 Feb 2019, 16:57
Сообщение
#134
|
|
пират с чёрной меткой Сообщений: 23 599 Спасибо сказали: 12880 раз |
Провёл мониторинг процессов по snd файлам.
ХотА, например, грузит: - Data\heroes3.snd - Data\hota.snd и пытается: - Heroes3\Data\heroes3.snd Взял и поменял в экзешнике строку "heroes3" на "heroes4" ВО ВСЕХ случаях, не только для heroes3.snd В результате, получается следующее: - Data\heroes4.snd - Data\hota.snd и пытается: - Data\heroes3.snd (капслоком) - Heroes3\Data\heroes3.snd Откуда вообще берутся эти пути и имена файлов? Я уже все dll проверил, реестр проверил! -------------------- Давший быка на убой не выпросил и печень на жаркое.
|
|
|
22 Feb 2019, 21:04
Сообщение
#135
|
|
---------------------- New_Life_of_Heroes ---------------------- Сообщений: 226 Спасибо сказали: 345 раз |
Взял и поменял в экзешнике строку "heroes3" на "heroes4" ВО ВСЕХ случаях, не только для heroes3.snd Значит не во всех. Пробуй менять в самой памяти (например HD-мод всегда подгружает Heroes3.exe четвёртой версии из папки _HD3_Data, игнорируя штатный геройский exe, за исключением ВоГа). -------------------- WoG + MoP + HoA + Forge + Bastion = ERA+
|
|
|
22 Feb 2019, 21:35
Сообщение
#136
|
|
пират с чёрной меткой Сообщений: 23 599 Спасибо сказали: 12880 раз |
Значит не во всех. Пробуй менять в самой памяти (например HD-мод всегда подгружает Heroes3.exe четвёртой версии из папки _HD3_Data, игнорируя штатный геройский exe, за исключением ВоГа). Да мне это не нужно в принципе. Если мне нужно подгрузить свой snd я его могу положить и как Data\heroes3.snd (переименовав штатный в heroes4.snd) и как Heroes3\Data\heroes3.snd. Но такое впечатление, что строка "heroes3" собирается по кусочкам или лежит заксоренная. -------------------- Давший быка на убой не выпросил и печень на жаркое.
|
|
|
22 Feb 2019, 22:27
(Сообщение отредактировал XEPOMAHT - 22 Feb 2019, 22:30)
Сообщение
#137
|
|
---------------------- New_Life_of_Heroes ---------------------- Сообщений: 226 Спасибо сказали: 345 раз |
и как Data\heroes3.snd (переименовав штатный в heroes4.snd) и как Heroes3\Data\heroes3.snd. Но такое впечатление, что строка "heroes3" собирается по кусочкам или лежит заксоренная. Heroes3 просто плюсуется к началу строки при чтении с CD (т.е. первый путь - <папка_с_игрой>\Data\heroes3.snd, а второй - диск:\Heroes3\Data\heroes3.snd) средствами какой-то сторонней dll, к сожалению, отладчик не пишет какой. Проверил на MoP - первый путь к heroes3.snd без проблем переименовывается и в exe и в памяти. Второй - а нахрена, если CD - пережиток прошлого. -------------------- WoG + MoP + HoA + Forge + Bastion = ERA+
|
|
|
22 Feb 2019, 22:40
Сообщение
#138
|
|||
пират с чёрной меткой Сообщений: 23 599 Спасибо сказали: 12880 раз |
Проверил на MoP - первый путь к heroes3.snd без проблем переименовывается и в exe и в памяти. Второй - а нахрена, если CD - пережиток прошлого. Хотя - нет, вот сравнение TE и MoP:
MoP основан на h3wog358.exe? Не на h3te.exe? -------------------- Давший быка на убой не выпросил и печень на жаркое.
|
||
|
|||
24 Feb 2019, 15:31
Сообщение
#139
|
|
Анти-Всë Сообщений: 2 989 Спасибо сказали: 2376 раз |
На h3te.exe. Правда, после многолетней бомбардировки альфа-частицами и прочего чада кутежа это уже мало похоже на исходный материал.
-------------------- Circle of destruction, hammer comes crushing
Powerhouse of energy Whipping up a fury, dominating flurry We create the battery |
|
|
14 Jul 2019, 22:01
Сообщение
#140
|
|
Immortal Сообщений: 1 468 Спасибо сказали: 1151 раз |
Формат файлов кампаний:
Код Heroes 3 Campaign (h3c) format after unpacking (gz).
================================= byte = 1 byte (0..255) word = 2 bytes (0..65535) integer = 4 bytes (-2147483648..2147483647) boolean = 1 byte (0 = false, 1 = true) --------------------------------- const (* Hardcoded in camptext.txt, differs for each campaign index. Number of zones in current campaign *) NUM_ZONES = ?; var Header: THeader; Zones: array NUM_ZONES of TZone; Maps: array NUM_ZONES of TMap | None; type TString = record Length: integer; Value: array Length of char; end; THeader = record GameVersion: integer = 5 (Armageddon Blade) | 6 (WoG); CampaignInd: byte; // Index of campaign (from 0), see camptext.txt CampaignName: TString; CampaignDesc: TString; // Description UserCanSelectDifficulty: boolean; MusicTheme: byte; end; // THeader TZonePrologue = record HasPrologue: boolean; if HasPrologue then VideoId: byte; MusicId: byte; Text: TString; end; end; TZoneEpilogue = record HasEpilogue: boolean; if HasEpilogue then VideoId: byte; MusicId: byte; Text: TString; end; end; TStartingOpts = record OptsType: byte; // StartingBonus = 1, CrossoverHero = 2, InitialHero = 3 if OptsType = 1 then PlayerColor: byte; NumBonuses: byte; Bonuses: array NumBonuses of record BonusType: byte; if Bonuses = 0 (spell) then Hero: word; 65023 for most powerful hero Spell: byte; end; if Bonuses = 1 (creature) then Creature: word; Number: word; end; if Bonuses = 3 (artifact) then Hero: word; Art: word; end; if Bonuses = 4 (spell scroll) then Hero: word; Spell: byte; end if Bonuses = 5 (primary skills) then Hero: word; PrimSkills: array 4] of byte; end; if Bonuses = 6 (secondary skill) then Hero: word; SecSkill: byte; Level: byte; end; if Bonuses = 7 (resource) then Resource: byte; Quantity: integer; end; end; // Bonuses end; // if if OptsType = 2 then PlayerColor: byte; ZoneIndex: byte; end; end; // .TStartingOpts TZone = record FileName: TString; FileSize: integer; RequiredZones: byte; // Zone prerequisites; bitmask: 1 bit for each zone ZoneColor: byte; DifficultyLevel: byte; RegionRmbText: TString; // Right mouse button hint Prologue: TZonePrologue; Epilogue: TZoneEpilogue; HeroesRetain: byte; // Bitmask of Experience (bit 0), Primary Skills (bit 1), Secondary Skills (bit 2), Spells (bit 3), Artifacts (bit 4) CrossoverCreatures: array of 19 bytes; // 1 bit for every creature. Creature ID: 0..159 CrossoverArts: array of 18 bytes; // 1 bit for every artifact. Artifact ID: 0..143 StartingOpts: TStartingOpts; end; TMap = unpacked h3m (gz) map -------------------- |
|
|
Текстовая версия | Сейчас: 29 March 2024 - 12:13 |
Copyright by Алексей Крючков
Programming by Degtyarev Dmitry |