Рейтинг - 5.0 (1)

В этом уроке мы познакомимся с одной возможностью блока HEX-END - буффером данных.

Здравствуйте, друзья! Очень давно я планировал сделать урок по HEX, но никак руки не доходили. Я не знаю всех возможможностей этого блока, но использовать его как буфер я умею.

Давайте определим понятие "Буфер", так как не все понимают что это такое. Я находил в интернете значение слова "Buffer", но все они такто туманно описывают это слово. Что же оно значит с точки зрения программирования ( скриптинга ) ? Если говорить простым языком, буфер - это произвольный набор данных, который имеет свою структуру.

Сами структуры Вы могли наблюдать в уроках о выделении памяти и форматированном считывании с файла. В первом случае у нас была простая структура - набор актёров. Во втором ( последний пример ) : цепочка данных с целых и дробных чисел.

Как видим, структуру определяет разработчик, в зависимости от потребности. Но если в ситуации с файлами мы могли наблюдать данные в явном виде, то с выделением памяти всё выглядело более абстрактно. HEX-буфер в этой ситуации является гибридом - данные "вшиваются" в скрипт в явном виде, но дальнейшая работа переходит в абстрактную, так как мы не будем знать какие там данные будут находится в процессе записи.

Для того, чтобы работать с буфером, нужно научится его создавать. Для этого нужно точно знать какие данные он будет хранить. Пусть это будут актёры, как в ситуации с выделением памяти. Узнаём размер ( в байтах ) одной ячейки этой структуры, после чего узнаём максимальное количество элементов такой структуры. Табличка с размерами приведена ниже:

Тип данных Размеры ( байт )
float 4
int 1, 2, 3 или 4
sString 8
vString 16
Actor, Car, Objet и т.п. 4

Как видим, тип int - очень универсален. Размер зависит от диапазона чисел, которое будет использоваться, что вполне логично. Если нам нужен маленький дипазон, то не нужно выделять больших объёмов памяти ( всё равно, что стрелять с гранатомёта по воробьях :) ). Приведу табличку диапазонов:

Размер Диапазон #1 Диапазон #2*
1 [-128;128) [0;256)
2 [-32768;32768) [0;65536)
3 [-8388608;8388607) [0;16777216)
4 [-2147483648;2147483648) [0;4294967296)
Синий - то, что тестировал;
Розовый - тестировал, но не весь диапазон;
Белый - не тестировал;
* - диапазон Unsigned;

Имея размер ячейки и максимальное количество структур, мы можем определить размер буфера. Я буду использовать 20 актёров, значит мне нужно выделить:

20 * 4 = 80 байт

Дальше уже нужно написать сам блок, и метку перед его началом. Вопрос: "Как записывать буфер в HEX?". Это отличный вопрос! Дело в том, что буфер требует первоначальных данных, размер которых должет быть равен размеру буфера. Обычно его заполняют нулями, хотя это нельзя назвать правилом. Количество нулей должно быть в 2 раза больше, чем размер буфера. Дело в том, что 1 байт информации записывается здесь в шестнадцатеричной системе счисления. Следовательно, диапазон битов может быть от 0 до FF включительно. Для удобства я разделяю байты пробелами ( см. картинку ниже ).

Обычно, при разработке скриптов, я делаю перенос строки, если структура 1-й ячейки полностью описана, но Sanny Builder при декомпиляции приведёт к виду 16 байт на строку. Это значит, что нам неважно в каком виде писать нолики, так как SB разместит данные слева на права и сверху вниз. На картинке, в сумме я выделил 34 байта, в который явно не разместить 20 актёров. Допишем недостающие элементы:

:ACTOR_ARRAY 
hex 
00 00 00 00 // Actor 0, offset +0 
00 00 00 00 // Actor 1, offset +4 
00 00 00 00 // Actor 2, offset +8 
00 00 00 00 // Actor 3, offset +12 
00 00 00 00 // Actor 4, offset +16 
00 00 00 00 // Actor 5, offset +20 
00 00 00 00 // Actor 6, offset +24 
00 00 00 00 // Actor 7, offset +28 
00 00 00 00 // Actor 8, offset +32 
00 00 00 00 // Actor 9, offset +36 
00 00 00 00 // Actor 10, offset +40 
00 00 00 00 // Actor 11, offset +44 
00 00 00 00 // Actor 12, offset +48 
00 00 00 00 // Actor 13, offset +52 
00 00 00 00 // Actor 14, offset +56 
00 00 00 00 // Actor 15, offset +60 
00 00 00 00 // Actor 16, offset +64 
00 00 00 00 // Actor 17, offset +68 
00 00 00 00 // Actor 18, offset +72 
00 00 00 00 // Actor 19, offset +76 
end

В комментариях я написал смещение структур относительно метки "ACTOR_ARRAY". Следовательно, если мы получим оффсет этой метки через опкод 0AC6, то мы получим адрес первого элемента буфера. Чтобы записать или прочитать что-то, нужно использовать знакомые опкоды чтения/записи памяти:

0AC6: $ARRAY_START = label @ACTOR_ARRAY offset 

0A8C: write_memory { $ARRAY_START + OFFSET } size { ITEM SIZE } value $VALUE virtual_protect 0 
0A8D: $VALUE = read_memory { $ARRAY_START + OFFSET } size { ITEM SIZE } virtual_protect 0

Давайте создадим 20 актёров, дадим им случайным образом M4 и запишем их в буфер:

{$CLEO} 
0AC6: 0@ = label @ACTOR_ARRAY offset 
Model.Load(#ARMY) 
Model.Load(#M4) 
038B: load_requested_models 

4@ = 2500.0 // spawn X coord 
for 1@ = 0 to 76 step 4 
0A8E: 2@ = 0@ + 1@ // label offset + item offset 
gosub @ACTOR_CREATE 
0A8C: write_memory 2@ size 4 value 3@ virtual_protect 0 
4@ -= 2.0 
end 

wait 1000 

for 1@ = 0 to 76 step 4 
0A8E: 2@ = 0@ + 1@ // label offset + item offset 
0A8D: 3@ = read_memory 2@ size 4 virtual_protect 0 
if 
056D: actor 3@ defined 
then 
01B2: give_actor 3@ weapon 31 ammo 400 
0209: 5@ = random_int_in_ranges 0 2 
if 
5@ == 1 
then 
01B9: set_actor 3@ armed_weapon_to 31 
else 
01B9: set_actor 3@ armed_weapon_to 0 
end 
end 
end 

Model.Destroy(#ARMY) 
Model.Destroy(#M4) 
0A93: end_custom_thread 

:ACTOR_CREATE 
3@ = Actor.Create(24, #ARMY, 4@, -1657.7767, 12.3935) 
Actor.Angle(3@) = 180.0 
return 

:ACTOR_ARRAY 
hex 
00 00 00 00 // Actor 0, offset +0 
00 00 00 00 // Actor 1, offset +4 
00 00 00 00 // Actor 2, offset +8 
00 00 00 00 // Actor 3, offset +12 
00 00 00 00 // Actor 4, offset +16 
00 00 00 00 // Actor 5, offset +20 
00 00 00 00 // Actor 6, offset +24 
00 00 00 00 // Actor 7, offset +28 
00 00 00 00 // Actor 8, offset +32 
00 00 00 00 // Actor 9, offset +36 
00 00 00 00 // Actor 10, offset +40 
00 00 00 00 // Actor 11, offset +44 
00 00 00 00 // Actor 12, offset +48 
00 00 00 00 // Actor 13, offset +52 
00 00 00 00 // Actor 14, offset +56 
00 00 00 00 // Actor 15, offset +60 
00 00 00 00 // Actor 16, offset +64 
00 00 00 00 // Actor 17, offset +68 
00 00 00 00 // Actor 18, offset +72 
00 00 00 00 // Actor 19, offset +76 
end

Результат:

Обратите внимание, что цикл for я осуществляю по смещению. Если использовать индекс в качестве итератора, то цикл был бы таким:

for 1@ = 0 to 19 
0A90: 2@ = 1@ * 4 // array index * item size 
0A8E: 2@ = 0@ + 2@ // label offset + item offset 
// ... 
end

Можете использовать тот цикл, который Вам более понятен.

Простой пример мы разобрали, теперь перейдём к более сложному. Если в предыдущем примере термин "масив" отлично подходил, то в следующем примере уместно называть данные набором структур, так как "структура" - это набор данных произвольного типа.

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

handle, model, x, y, z
4+4+4+4+4=20 байт

Далее нужно получить размер буфера. Пусть буфер будет хранить 5 таких элементов:

5*20=100 байт

Заполним нулями структуру. Я приведу 2 варианта для примера.

Вариант 1:

:STRUCTURES 
hex 
{object handle} 00 00 00 00 {model id} 00 00 00 00 {x} 00 00 00 00 {y} 00 00 00 00 {z} 00 00 00 00 // item 0, offset +0 
{object handle} 00 00 00 00 {model id} 00 00 00 00 {x} 00 00 00 00 {y} 00 00 00 00 {z} 00 00 00 00 // item 1, offset +20 
{object handle} 00 00 00 00 {model id} 00 00 00 00 {x} 00 00 00 00 {y} 00 00 00 00 {z} 00 00 00 00 // item 2, offset +40 
{object handle} 00 00 00 00 {model id} 00 00 00 00 {x} 00 00 00 00 {y} 00 00 00 00 {z} 00 00 00 00 // item 3, offset +60 
{object handle} 00 00 00 00 {model id} 00 00 00 00 {x} 00 00 00 00 {y} 00 00 00 00 {z} 00 00 00 00 // item 4, offset +80 
end

Вариант 2:

:STRUCTURES 
hex 
// item 0, offset +0 
00 00 00 00 // handle, item offset +0 
00 00 00 00 // model id, item offset +4 
00 00 00 00 // x, item offset +8 
00 00 00 00 // y, item offset +12 
00 00 00 00 // z, item offset +16 

// item 1, offset +20 
00 00 00 00 // handle, item offset +0 
00 00 00 00 // model id, item offset +4 
00 00 00 00 // x, item offset +8 
00 00 00 00 // y, item offset +12 
00 00 00 00 // z, item offset +16 

// item 2, offset +40 
00 00 00 00 // handle, item offset +0 
00 00 00 00 // model id, item offset +4 
00 00 00 00 // x, item offset +8 
00 00 00 00 // y, item offset +12 
00 00 00 00 // z, item offset +16 

// item 3, offset +60 
00 00 00 00 // handle, item offset +0 
00 00 00 00 // model id, item offset +4 
00 00 00 00 // x, item offset +8 
00 00 00 00 // y, item offset +12 
00 00 00 00 // z, item offset +16 

// item 4, offset +80 
00 00 00 00 // handle, item offset +0 
00 00 00 00 // model id, item offset +4 
00 00 00 00 // x, item offset +8 
00 00 00 00 // y, item offset +12 
00 00 00 00 // z, item offset +16 
end

Отлично! В своих примерах я буду использовать 2-й вариант. Сначала я сгенерирую координаты и модель, после чего запишу данные в буфер. После этого буду читать данные с буфера и на их основе создам объекты. После чего хэндл объекта запишу в буфер. Затем получим хендлы объектов с буфера и установим маркеры над объектами. Вот код:

{$CLEO} 
0AC6: 0@ = label @STRUCTURES offset 

8@ = 2500.0 // spawn X coord 
for 1@ = 0 to 80 step 20 
0A8E: 2@ = 0@ + 1@ // label offset + item offset 

0A8E: 3@ = 2@ + 4 // Model ID: item offset +4 
0A8E: 4@ = 2@ + 8 // X: item offset +8 
0A8E: 5@ = 2@ + 12 // Y: item offset +12 
0A8E: 6@ = 2@ + 16 // Z: item offset +16 

0209: 7@ = random_int_in_ranges 2995 3001 // random model 

0A8C: write_memory 3@ size 4 value 7@ virtual_protect 0 
0A8C: write_memory 4@ size 4 value 8@ virtual_protect 0 
0A8C: write_memory 5@ size 4 value -1657.7767 virtual_protect 0 
0A8C: write_memory 6@ size 4 value 13.3935 virtual_protect 0 

8@ -= 2.0 
end 

wait 1000 

for 10@ = 2995 to 3001 
Model.Load(10@) 
end 
038B: load_requested_models 

for 1@ = 0 to 80 step 20 
0A8E: 2@ = 0@ + 1@ // label offset + item offset 

0A8E: 3@ = 2@ + 4 // Model ID: item offset +4 
0A8E: 4@ = 2@ + 8 // X: item offset +8 
0A8E: 5@ = 2@ + 12 // Y: item offset +12 
0A8E: 6@ = 2@ + 16 // Z: item offset +16 

0A8D: 3@ = read_memory 3@ size 4 virtual_protect 0 
0A8D: 4@ = read_memory 4@ size 4 virtual_protect 0 
0A8D: 5@ = read_memory 5@ size 4 virtual_protect 0 
0A8D: 6@ = read_memory 6@ size 4 virtual_protect 0 
9@ = Object.Create(3@, 4@, 5@, 6@) 
Object.CollisionDetection(9@) = false 
0A8C: write_memory 2@ size 4 value 9@ virtual_protect 0 // object handle, item offset +0 
end 

for 10@ = 2995 to 3001 
Model.Destroy(10@) 
end 

10@ = 0 // marker array index 
for 1@ = 0 to 80 step 20 
0A8E: 2@ = 0@ + 1@ // label offset + item offset 
0A8D: 9@ = read_memory 2@ size 4 virtual_protect 0 // object handle, item offset +0 
if 
03CA: object 9@ exists 
then 
20@(10@,5i) = Marker.CreateAboveObject(9@) 
end 
10@ += 1 
end 

0A93: end_custom_thread 

:STRUCTURES 
hex 
// item 0, offset +0 
00 00 00 00 // handle, item offset +0 
00 00 00 00 // model id, item offset +4 
00 00 00 00 // x, item offset +8 
00 00 00 00 // y, item offset +12 
00 00 00 00 // z, item offset +16 

// item 1, offset +20 
00 00 00 00 // handle, item offset +0 
00 00 00 00 // model id, item offset +4 
00 00 00 00 // x, item offset +8 
00 00 00 00 // y, item offset +12 
00 00 00 00 // z, item offset +16 

// item 2, offset +40 
00 00 00 00 // handle, item offset +0 
00 00 00 00 // model id, item offset +4 
00 00 00 00 // x, item offset +8 
00 00 00 00 // y, item offset +12 
00 00 00 00 // z, item offset +16 

// item 3, offset +60 
00 00 00 00 // handle, item offset +0 
00 00 00 00 // model id, item offset +4 
00 00 00 00 // x, item offset +8 
00 00 00 00 // y, item offset +12 
00 00 00 00 // z, item offset +16 

// item 4, offset +80 
00 00 00 00 // handle, item offset +0 
00 00 00 00 // model id, item offset +4 
00 00 00 00 // x, item offset +8 
00 00 00 00 // y, item offset +12 
00 00 00 00 // z, item offset +16 
end

Результат:

Третий пример будет связан с SCM-строками. Именно с ними чаще всего возникают ошибки, так как их размеры отличаются по длинне. Любая переменная в скрипте имеет размер 4 байта. Следовательно мы не можем записывать/читать данные с длинной более 4 байт. Поэтому приходится делить стоки на части и записывать/читать их отдельно ( как и в SCM-функциях ).

Тем не менее в буфере мы обязаны указывать реальный размер строк в байтах. Допустим, мы выделим память под 1 строку s-типа и 1 строку v-типа. Размер буфера составит:

8+16=24 байта

А вот и наш буфер:

:STRINGS 
hex 
00 00 00 00 00 00 00 00 // sString, offset +0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // vString, offset +8
end

Мы можем условно "разбить" буфер на части ( это чисто для удобства просмотра ). Примерно так:

:STRINGS 
hex 
// sString, offset +0
00 00 00 00 // part 1, offset+0 
00 00 00 00 // part 2, offset+4

// vString, offset +8
00 00 00 00 // part 1, offset+0 
00 00 00 00 // part 2, offset+4 
00 00 00 00 // part 3, offset+8 
00 00 00 00 // part 4, offset+12
end

Поскольку мы не можем записать в переменную более 4 байт, будем записывать и читать буфер по 4 байта:

{$CLEO}

0AC6: 0@ = label @STRINGS offset // buffer offset + 0, sString, part 1, item offset +0
0A8E: 1@ = 0@ + 4 // sString, part 2, item offset +4 
 
0A8E: 5@ = 0@ + 8 // buffer offset +8, vString, part 1, item offset +0
0A8E: 6@ = 5@ + 4 // vString, part 2, item offset +4 
0A8E: 7@ = 5@ + 8 // vString, part 3, item offset +8 
0A8E: 8@ = 5@ + 12 // vString, part 4, item offset +12 

// --- --- ---
 
10@s = 'HELP101'
// 10@: part 1 
// 11@: part 2

12@v = "Hello, world!" 
// 12@: part 1 
// 13@: part 2
// 14@: part 3 
// 15@: part 4
 
// write sString 10@s
0A8C: write_memory 0@ size 4 value 10@ virtual_protect 0 
0A8C: write_memory 1@ size 4 value 11@ virtual_protect 0 
 
// write vString 12@v 
0A8C: write_memory 5@ size 4 value 12@ virtual_protect 0 
0A8C: write_memory 6@ size 4 value 13@ virtual_protect 0 
0A8C: write_memory 7@ size 4 value 14@ virtual_protect 0 
0A8C: write_memory 8@ size 4 value 15@ virtual_protect 0 

wait 2000

// read sString to 20@s
0A8D: 20@ = read_memory 0@ size 4 virtual_protect 0
0A8D: 21@ = read_memory 1@ size 4 virtual_protect 0
 
// read vString to 22@v
0A8D: 22@ = read_memory 5@ size 4 virtual_protect 0
0A8D: 23@ = read_memory 6@ size 4 virtual_protect 0 
0A8D: 24@ = read_memory 7@ size 4 virtual_protect 0
0A8D: 25@ = read_memory 8@ size 4 virtual_protect 0

03E5: show_text_box 20@s
0AD0: show_formatted_text_lowpriority 22@v time 8000

0A93: end_custom_thread 

:STRINGS 
hex 
// sString, buffer offset +0
00 00 00 00 // part 1, item offset+0 
00 00 00 00 // part 2, item offset+4

// vString, buffer offset +8
00 00 00 00 // part 1, item offset+0 
00 00 00 00 // part 2, item offset+4 
00 00 00 00 // part 3, item offset+8 
00 00 00 00 // part 4, item offset+12
end

В результате мы получим это:

Последний пример, который мы рассмотрим в этом уроке - чтение и запись данных с меньшим количеством байт, а также изменим фиксированное значение буфера с 0 на другие данные. Очень хороший пример можно наблюдать в моей мини-игре "Сокобан". Писать игру здесь я, конечно, не буду, но, используя этот урок, Вы сможете сделать нечто подобное.

Для началя я загружу нужные мне тестуры и напишу условия их удаления:

while true
wait 0
 
 while 8AB0: not key_pressed 49 // key 1
 wait 0 
 end
 
 0390: load_txd_dictionary 'LD_BEAT'
 038F: load_texture "cross" as 1 
 038F: load_texture "cring" as 2 
 038F: load_texture "circle" as 3 
 038F: load_texture "chit" as 4 
 03F0: enable_text_draw 1
 Player.CanMove($PLAYER_CHAR) = false 
 0ACE: show_formatted_text_box "ON" 
 wait 250
 
 while 8AB0: not key_pressed 49 // key 1
 wait 0
 
 // script ...
 
 end
 
 Player.CanMove($PLAYER_CHAR) = true
 03F0: enable_text_draw 0
 0391: release_textures 
 0ACE: show_formatted_text_box "OFF"
 wait 250

end

0A93: end_custom_thread

Каждой текстуре мы присваиваем некий номер. Всего в нас 4 номера, диапазон значений которых не превышает 255. Значит целесообразно использовать размер ячейки в 1 байт. Давайте зададим в буффере значения от 1 до 4 любым образом. У меня например так:

:MAP
hex 
01 04 02 03 00 03 02 04 01
end

Размер буфера - 9 байт. Байт, который равен 0 я буду использовать как пробел при выводе текстур на дисплей. А вот, собственно, и сам вывод:

{$CLEO} 
0AC6: 0@ = label @MAP offset // buffer offset

while true
wait 0
 
 while 8AB0: not key_pressed 49 // key 1
 wait 0 
 end
 
 0390: load_txd_dictionary 'LD_BEAT'
 038F: load_texture "cross" as 1 
 038F: load_texture "cring" as 2 
 038F: load_texture "circle" as 3 
 038F: load_texture "chit" as 4 
 03F0: enable_text_draw 1
 Player.CanMove($PLAYER_CHAR) = false 
 0ACE: show_formatted_text_box "ON" 
 wait 250
 
 while 8AB0: not key_pressed 49 // key 1
 wait 0
 
 4@ = 180.0 
 for 1@ = 0 to 8 
 4@ += 36.0 
 0A8E: 2@ = 0@ + 1@ // int
 0A8D: 3@ = read_memory 2@ size 1 virtual_protect 0 // read 1 byte
 if
 3@ == 0
 then 
 continue
 end
 03E3: set_texture_to_be_drawn_antialiased 1
 038D: draw_texture 3@ position 4@ 140.0 size 32.0 32.0 RGBA 255 255 255 255
 end 

 end
 
 Player.CanMove($PLAYER_CHAR) = true 
 03F0: enable_text_draw 0
 0391: release_textures 
 0ACE: show_formatted_text_box "OFF" 
 wait 250

end

0A93: end_custom_thread 

:MAP
hex 
01 04 02 03 00 03 02 04 01
end

Результат:

Таким образом, если мне захочется сменить местоположение текстур, мне достаточно отредактировать буффер, не изменяя другой код, что очень удобно!К тому же, этот метод очень стабильный, в отличии от того же выделения памаяти через опкод 0AC8, который часто приводит к вылетам.

Я сделал небольшую программу, которая должна упростить процесс написание HEX-буфера. Скачать её можно здесь.

Естественно, HEX имеет многие другие возможности, которые Вы можете обнаружить в справке SB.Тем не менее то, что имеется, уже существенно помогает в разработке сложных скриптов, и надеюсь, что и Вам он понадобится в Ваших проектах. Спасибо за внимание! С Вами был wmysterio.


Теги: hex

Evgeny2000   (16.07.17 14:58)
Через HEX-блок можно организовывать даже полноценные ассемблерные вставки:
Код
{$CLEO}

//Создаем буфер на 8 байтов, чтобы хранить разряды шестнадцатеричного числа.
0AC8: 0@ = allocate_memory_size 8

//Получаем адрес ассемблерной процедуры.
0AC6: 1@ = label @ASM offset

//Вызываем процедуру и передаем адрес буфера и число, которое надо разбить на разряды.
0AA5: call 1@ num_params 2 pop 2 0@ 0x8C0AD39E

//Копируем буфер в массив локальных переменных с 1@ до 8@.
for 9@ = 0 to 7

  0A8E: 10@ = 0@ + 9@ // int
  0A8D: 1@(9@,8i) = read_memory 10@ size 1 virtual_protect 0  
  
end

//Выводим массив на экран.
while true
  wait 0
  
  0AD1: show_formatted_text_highpriority "%.1X %.1X %.1X %.1X %.1X %.1X %.1X %.1X" time 200 1@ 2@ 3@ 4@ 5@ 6@ 7@ 8@
  
end

:ASM
hex
  B9 08 00 00 00  //MOV  ECX, 8  
  //loop_lable:
  49  //DEC  ECX
  B0 07  //MOV  AL, 7
  2A C1  //SUB  AL, Cl  
  6B C0 04  //IMUL  EAX, 4
  8B 5C 24 04  //MOV  EBX, [ESP+4]
  51  //PUSH  ECX
  8A C8  //MOV  CL, AL
  D3 EB  //SHR  EBX, CL
  59  //POP  ECX
  83 E3 0F  //AND  EBX, 0Fh
  8B 44 24 08  //MOV  EAX, [ESP+8]
  03 C1  //ADD  EAX, ECX
  88 18  //MOV  [EAX], BL
  85 C9  //TEST  ECX, ECX
  75 DF  //JNZ  loop_lable
  C3  //RET
end

Dyabda   (05.08.17 08:42)
Уважаемый Wmysterio.
Возможно вопрос прозвучит глупо и не по теме.
Можно ли при помощи HEX в cleo изменить название папки в которую сохраняется игра?
К примеру: не "GTA San Andreas User files", а "GTA Undeground User files"?

SanKing   (08.08.17 13:36)
Dyabda, для этого нужно редактировать сам .exe-файл. Это не так сложно, туториал по этому делу есть на libertycity.ru. Удачного моддинга!

Dyabda   (02.09.17 07:45)
Дело в том, что я хочу это сделать без редактирования EXE, я уже видел модификацию, которая не имеет своего EXE, используя стандартный EXE 1.0 US, но папка сохранений у этой модификации имеет другое имя. Похоже, мне нужно обращаться к ASI скриптингу, чтобы реализовать это.
Вход на сайт

Поиск
Категории раздела
Мини-чат
Пожалуйста, все вопросы по скриптингу задавать на форуме!
Наш опрос
Какие моды Вы хотите видеть на сайте?
Всего ответов: 6
Активность на сайте
Пожертвования
Кошельки WebMoney:
U859420971000
R407741810602
Z331072372430
E314272616890
Друзья сайта
Полезные ресурсы
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Сегодня нас посетили:
wmysterio, eglive
Рекомендую
Реклама