#1 28-09-2010 09:19

Sergey81
Registered: 19-12-2008
Posts: 654

[WIP] Автосохранение

А можно в SA сделать автосейв, к примеру при появлении надписи "Mission Passed", скажем, в последний слот?

Offline

#2 28-09-2010 12:46

BoPoH
From: Каушаны
Registered: 21-02-2009
Posts: 77

Re: [WIP] Автосохранение

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


userbar.png

Offline

#3 28-09-2010 13:50

~AquaZ~
Registered: 01-03-2010
Posts: 726

Re: [WIP] Автосохранение

BoPoH, show_save_screen

Offline

#4 28-09-2010 19:44

Sergey81
Registered: 19-12-2008
Posts: 654

Re: [WIP] Автосохранение

0AA5: call 0x619060 num_params 1 pop 0 2

Автосейв в третий слот. Чуть позже может выложу в разделе модификаций.

Last edited by Sergey81 (28-09-2010 20:29)

Offline

#5 28-09-2010 20:58

Alien
Registered: 12-10-2008
Posts: 564

Re: [WIP] Автосохранение

Не в третий, а в последний загруженный/сохраненный. Номер текущего сохранения хранится где-то в структуре CMenuManager. Если по этому адресу записать, скажем, 8, то игра сохранится в несущуствующий 9-й слот (GTASAsf9.b). Загрузить его можно будет так же через вызов соответствующей функции.

Offline

#6 28-09-2010 21:21

Sergey81
Registered: 19-12-2008
Posts: 654

Re: [WIP] Автосохранение

Alien wrote:

Не в третий, а в последний загруженный/сохраненный. Номер текущего сохранения хранится где-то в структуре CMenuManager. Если по этому адресу записать, скажем, 8, то игра сохранится в несущуствующий 9-й слот (GTASAsf9.b). Загрузить его можно будет так же через вызов соответствующей функции.

Эм, если написать вместо двойки 7, сохранит в последний слот у меня. Кстати как лучше сделать проверку на выполнение миссии? В идеале тот момент, когда текст миссия выполнена исчезает.

Offline

#7 29-09-2010 00:07

Seemann
Registered: 07-08-2006
Posts: 2,073

Re: [WIP] Автосохранение

Кстати как лучше сделать проверку на выполнение миссии? В идеале тот момент, когда текст миссия выполнена исчезает.

перехватывать исполнение опкода 0112. он используется в самом конце миссии. это универсальный способ на все миссии в игре (для специфических миссий можно дополнительно проверять название потока).

Проверять нужно конечно запуск самого опкода, а не его флага. Базы под рукой нет, но навскидку приходит на ум следующий способ: в таблицах указателей на обработчики опкодов найти нужный опкод, туда прописать адрес внутри тела скрипта на свой код на асме (конструкция hex..end). После кода поставить переход на реальный обработчик опкода 0112. в коде асма записывать в какой-то специфический адрес  ( например, адрес переменной 0@) определенное значение. Сам скрипт будет в бесконечном цикле проверять этот адрес, и если он изменился, значит был выполнен опкод 0112 и, соответственно, пройдена миссия. Остальное дело техники.

Есть правда один скользкий момент: опкод 0112 может запускаться и при смерти/аресте игрока (т.е. провале миссии). Этот момент нужно дополнительно проверять.

Как это примерно делается можно посмотреть в исходниках CLEO 1.
http://sannybuilder.com/dev/cleo.txt

Offline

#8 29-09-2010 00:32

Sergey81
Registered: 19-12-2008
Posts: 654

Re: [WIP] Автосохранение

Seemann wrote:

Кстати как лучше сделать проверку на выполнение миссии? В идеале тот момент, когда текст миссия выполнена исчезает.

перехватывать исполнение опкода 0112. он используется в самом конце миссии. это универсальный способ на все миссии в игре (для специфических миссий можно дополнительно проверять название потока).

Проверять нужно конечно запуск самого опкода, а не его флага. Базы под рукой нет, но навскидку приходит на ум следующий способ: в таблицах указателей на обработчики опкодов найти нужный опкод, туда прописать адрес внутри тела скрипта на свой код на асме (конструкция hex..end). После кода поставить переход на реальный обработчик опкода 0112. в коде асма записывать в какой-то специфический адрес  ( например, адрес переменной 0@) определенное значение. Сам скрипт будет в бесконечном цикле проверять этот адрес, и если он изменился, значит был выполнен опкод 0112 и, соответственно, пройдена миссия. Остальное дело техники.

Есть правда один скользкий момент: опкод 0112 может запускаться и при смерти/аресте игрока (т.е. провале миссии). Этот момент нужно дополнительно проверять.

Как это примерно делается можно посмотреть в исходниках CLEO 1.
http://sannybuilder.com/dev/cleo.txt

Спасибо, гляну, незнаю правда дойдёт дело до реализации или нет.

Пока пришёл на ум простенький метод: http://sannybuilder.com/forums/viewtopi … 535#p12535

Offline

#9 29-09-2010 05:20

Sergey81
Registered: 19-12-2008
Posts: 654

Re: [WIP] Автосохранение

Честно говоря не совсем понял что нужно делать, а точнее сказать как делать.

в таблицах указателей на обработчики опкодов найти нужный опкод

Что имеется в виду? В базе этот опкод я нашёл:

.text:00469A9B     ; __linkproc__ opcode_0112
.text:00469A9B     @@opcode_0112:                          ; CODE XREF: _opcode_handler_02+38j
.text:00469A9B                                             ; DATA XREF: .text:__linkproc__ opcode_tableo
.text:00469A9B 04C                 mov     cl, [esi+CScriptThread.wastedOrBusted] ;   wasted_or_busted ; mission only
.text:00469AA1 04C                 test    cl, cl
.text:00469AA3 04C                 setnz   cl
.text:00469AA6 04C                 jmp     short loc_469AFC

туда прописать адрес внутри тела скрипта на свой код на асме (конструкция hex..end). После кода поставить переход на реальный обработчик опкода 0112. в коде асма записывать в какой-то специфический адрес  ( например, адрес переменной 0@) определенное значение.

Вот тут было бы неплохо расписать поподробнее и желательно с примерами.

Offline

#10 29-09-2010 12:34

BoPoH
From: Каушаны
Registered: 21-02-2009
Posts: 77

Re: [WIP] Автосохранение

Alien wrote:

Если по этому адресу записать, скажем, 8, то игра сохранится в несущуствующий 9-й слот (GTASAsf9.b). Загрузить его можно будет так же через вызов соответствующей функции.

Какой именно функции?


userbar.png

Offline

#11 29-09-2010 13:32

Seemann
Registered: 07-08-2006
Posts: 2,073

Re: [WIP] Автосохранение

Найди не сам обработчик, а ссылку на него в списке переходов. в процедуре _opcode_handler_02 в самом начале есть switch(), который по номеру опкода выбирает из списка в конце процедуры адрес нужного обработчика (в нашем случае 00469A9B). Тебе нужно заменить этот адрес 00469A9B на адрес инжекта в теле твоего скрипта (чтобы switch "прыгнул" на твой асм, а не на обработчик опкода). А в скрипте в конце асма прописать переход назад на опкод 0112 (mov eax, 00469A9B; jmp eax), можно пока ограничиться этим.

получится такая схема:

игра->switch()->наш код->опкод 0112

Offline

#12 30-09-2010 13:15

Sergey81
Registered: 19-12-2008
Posts: 654

Re: [WIP] Автосохранение

Примерно вроде всё ясно.

Seemann wrote:

Тебе нужно заменить этот адрес 00469A9B на адрес инжекта в теле твоего скрипта (чтобы switch "прыгнул" на твой асм, а не на обработчик опкода).

Как именно делается вот этот момент?

Offline

#13 30-09-2010 16:06

Seemann
Registered: 07-08-2006
Posts: 2,073

Re: [WIP] Автосохранение

Sergey81 wrote:

Примерно вроде всё ясно.

Seemann wrote:

Тебе нужно заменить этот адрес 00469A9B на адрес инжекта в теле твоего скрипта (чтобы switch "прыгнул" на твой асм, а не на обработчик опкода).

Как именно делается вот этот момент?

1. Узнаешь глобальный адрес твоего асма в памяти игры
(читаешь thread.base_address, к нему плюсуешь label offset). Пример есть в скрипте Hello world комплекта CLEO 3.

2. Находишь в таблице переходов адрес опкода 0112 и туда прописываешь этот адрес.

Блин, нет под рукой иды и базы, так бы сказал куда прописывать.

Offline

#14 30-09-2010 17:20

Sergey81
Registered: 19-12-2008
Posts: 654

Re: [WIP] Автосохранение

72504be4078e60ba63cf537687df8873.jpeg 
Это и есть ссылка на обработчик, я правильно понимаю?

(читаешь thread.base_address, к нему плюсуешь label offset). Пример есть в скрипте Hello world комплекта CLEO 3.

0A9F: 0@ = current_thread_pointer
0@ += label offset
потом записать по адресу 0x469A9B 0@ через 0A8C?
Label offset это что и как найти его значение?

Offline

#15 30-09-2010 18:11

Seemann
Registered: 07-08-2006
Posts: 2,073

Re: [WIP] Автосохранение

00469A9B это адрес самого обработчика. Тебе надо перезаписать ссылку на него. Т.е. заменить адрес со словами offset 0112 (в районе 469DD4) (там будет число 00469A9B).

0A9F: 0@ = current_thread_pointer
это указатель на структуру потока. по смещению 0x10 хранится адрес начала кода скрипта. К этому числу надо прибавить @label (точнее вычесть, т.к. в клео скриптах метки имеют отрицательное смещение)

В CLEO 3 есть пример (test_example.txt):

// calculates absolute address of the text (opcode 0900) in game memory
0A9F: 0@ = current_thread_pointer
0@ += 0x10
0A8D: 0@ = read_memory 0@ size 4 virtual_protect 0
0@ -= @TEXT_PTR  // as all offsets are local (with negative values)

Или просто используй опкод 0AC6: 0@ = label @label offset

0AC6: 1@ = label @asmcode offset // это число записывается вместо 00469A9B в таблицу обработчиков


:asmcode
hex

end

Offline

#16 30-09-2010 18:26

Sanchez
Registered: 18-08-2006
Posts: 280

Re: [WIP] Автосохранение

@Sergey81 -

{$CLEO}
0AC6: 0@ = label @lbl offset
0A8C: write_memory 0x00469E2C size 4 value 0@ virtual_protect 1
0A93: end_custom_thread

:lbl
hex
    B8 9B9A4600     // MOV EAX,00469A9B
    FFE0            // JMP EAX
end

Offline

#17 30-09-2010 19:30

Sergey81
Registered: 19-12-2008
Posts: 654

Re: [WIP] Автосохранение

Ага, спасибо за разъяснения, теперь всё понятно.
24eec3b303a7dbba1ce997066fb80b56.jpeg 
Адрес я нашёл.
Только вот скомпилированный код Sanchez'а вылетает, даже с прописанными wait 0.
В чём причина?
scmlog:

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
********************************************

00000004: 0AC6

Last edited by Sergey81 (30-09-2010 19:30)

Offline

#18 30-09-2010 21:14

Seemann
Registered: 07-08-2006
Posts: 2,073

Re: [WIP] Автосохранение

Вылетает на опкоде 0AC6? Если да, то претензии к CLEO 4

Попробуй поубирать опкоды по одному, чтобы найти проблемный. И попробуй вместо 0a93 поставить бесконечный цикл (он по идее там и должен быть).

Offline

#19 30-09-2010 21:59

Sergey81
Registered: 19-12-2008
Posts: 654

Re: [WIP] Автосохранение

Seemann wrote:

Вылетает на опкоде 0AC6? Если да, то претензии к CLEO 4

Попробуй поубирать опкоды по одному, чтобы найти проблемный. И попробуй вместо 0a93 поставить бесконечный цикл (он по идее там и должен быть).

Не, cleo 3 у меня. Цикл попробую.
UPD. Всё тоже самое, если убрать 0AC6, грузится. К слову, пример из Cleo3 работает.

Last edited by Sergey81 (30-09-2010 22:38)

Offline

#20 30-09-2010 22:39

Seemann
Registered: 07-08-2006
Posts: 2,073

Re: [WIP] Автосохранение

0AC6 - это опкод из CLEO 4

Раз у тебя CLEO 3 то используй вместо этого опкода код

0A9F: 0@ = current_thread_pointer
0@ += 0x10
0A8D: 0@ = read_memory 0@ size 4 virtual_protect 0
0@ -= @lbl

Offline

#21 30-09-2010 22:46

Sergey81
Registered: 19-12-2008
Posts: 654

Re: [WIP] Автосохранение

Seemann wrote:

0AC6 - это опкод из CLEO 4

CLEO4 не работает корректно у меня с текущим набором скриптов sad

0A9F: 0@ = current_thread_pointer
0@ += 0x10
0A8D: 0@ = read_memory 0@ size 4 virtual_protect 0
0@ -= @lbl

С этим всё ок, не вылетает. Сам код автосейва прописывать надо после 0@ -= @lbl?

Last edited by Sergey81 (30-09-2010 23:03)

Offline

#22 30-09-2010 23:29

Seemann
Registered: 07-08-2006
Posts: 2,073

Re: [WIP] Автосохранение

Не все так просто)))

С этим кодом

{$CLEO}
0A9F: 0@ = current_thread_pointer
0@ += 0x10
0A8D: 0@ = read_memory 0@ size 4 virtual_protect 0
0@ -= @lbl
0A8C: write_memory 0x00469E2C size 4 value 0@ virtual_protect 1
while true
 wait 0

end

:lbl
hex
    B8 9B9A4600     // MOV EAX,00469A9B
    FFE0            // JMP EAX
end

мы добились только того, что игра при исполнении опкода 0112 прыгает в тело нашего скрипта (на асм).

Теперь нужно чтобы код на асме давал нашему скрипту понять, что игра на него зашла. Я предлагал установить значение локальной переменной (например 1@) в определенное значение (1). Сам же скрипт в бесконечном цикле, который я добавил, должен эту переменную проверять. После этого можно запускать автосохранение и сбрасывать 1@ в 0.

Осталась одна сложная задача: написать на асме код получения адреса локальной переменной 1@. Для этого нужно, чтобы внутри hex..end мы знали адрес структуры нашего скрипта. Этот адрес хранится игрой в регистре esi. Трудность заключается в следующем: мы не можем напрямую использовать регистр esi для получения указателя на наш скрипт, потому что наш код на асме по сути является продолжением запущенной миссии с опкодом 0112. И esi в нем указывает на поток миссии, а не CLEO-скрипта.

Поэтому предлагаю привлечь еще одну хитрость - буфер для хранения этого указателя в теле скрипта. Берем все тот же опкод 0A9F: 0@ = current_thread_pointer

0@ хранит указатель на структуру нашего скрипта, то, что нам нужно. Это значение нужно записать в отдельный буфер вида

:buf
hex
00 00 00 00
end

В него записать адрес переменной 1@, значение которой нужно изменить

Примерно код будет выглядеть так

{$CLEO}
// получаем адрес текущего CLEO скрипта в памяти игры
0A9F: 0@ = current_thread_pointer
0A8E: 10@ = 0@ + 0x10
0A8D: 10@ = read_memory 10@ size 4 virtual_protect 0

// вычисляем адрес метки lbl в памяти игры
0A8F: 11@ = 10@ - @lbl    // 11@ = lbl's global address

// подсовываем игре вместо обработчика 0112 наш код на асме
0A8C: write_memory 0x00469E2C size 4 value 11@ virtual_protect 1

// вычисляем адрес переменной 1@
0@ += 0x44 // 0@ = 1@'s global address

// записываем этот адрес в буфер в теле скрипта
0A8F: 11@ = 10@ - @buf  // 11@ = buf's global addresss
0A8C: write_memory 11@ size 4 value 0@ virtual_protect 0

while true
    wait 0
    if
       1@ == 1
    then
    //здесь можно делать автосохранение
    1@ = 0
    end 
end

:buf
hex
 00 00 00 00
end

:lbl
hex
    B8 9B9A4600     // MOV EAX,00469A9B
    FFE0            // JMP EAX
end

Теперь осталось придумать как в блоке lbl прочитать эти 4 байта из блока buf.  Вероятно, смещение в байтах придется считать вручную и добавлять его к значению EIP Требуется прочитать адрес 0x00469E2C, в котором мы прописали глобальный адрес метки lbl, от прочитанного значения отнять 4 (размер буфера) и все. Без программ под рукой помочь не смогу(

Last edited by Seemann (01-10-2010 02:39)

Offline

#23 01-10-2010 01:55

Sergey81
Registered: 19-12-2008
Posts: 654

Re: [WIP] Автосохранение

Можно в принципе использовать обычные адреса, например
0x969164 - дефолтное значение 0, значение 0x000100 даёт всем машинам нитро, 0x000001 или что то подобное делает машину игрока танком(если задеть другую машину та взрывается). 0x100000 вроде бы не оказывает влияния на игру, мб лучше воспользоваться этим. Ну и адрес можно другой поискать если что.

Offline

#24 01-10-2010 02:05

Seemann
Registered: 07-08-2006
Posts: 2,073

Re: [WIP] Автосохранение

Можно и так. Но с локальной переменной код более независим. Зачем идти коротким путем?) Единожды написав такой скрипт, можно использовать его в дальнейшем для других случаев (перехватывать исполнение вообще любого опкода), а не искать каждый раз новый свободный адрес. Тем более, что вопрос сводится к написанию на асме нескольких строчек

mov eax, [0x00469E2C]
sub eax, 4
mov eax, [eax]
mov [eax], 1

EDIT

Собственно раскинул мозгами и пришел к выводу, что можно обойтись вообще без буфера и этих танцев с бубном. Адрес локальной переменной известен. Код на асме тоже имеется. Так почему бы просто не прописать напрямую этот адрес в код асма

{$CLEO}
// получаем адрес текущего CLEO скрипта в памяти игры
0A9F: 0@ = current_thread_pointer
0A8E: 10@ = 0@ + 0x10
0A8D: 10@ = read_memory 10@ size 4 virtual_protect 0

// вычисляем адрес метки lbl в памяти игры
0A8F: 11@ = 10@ - @lbl    // 11@ = lbl's global address

// подсовываем игре вместо обработчика 0112 наш код на асме
0A8C: write_memory 0x00469E2C size 4 value 11@ virtual_protect 1

// вычисляем адрес переменной 1@
0@ += 0x44 // 0@ = 1@'s global address

// записываем этот адрес в буфер в теле скрипта
11@ += xxxxx // прописываем сколько байтов между началом lbl и местом для записи адреса
0A8C: write_memory 11@ size 4 value 0@ virtual_protect 0

while true
    wait 0
    if
       1@ == 1
    then
    //здесь можно делать автосохранение
    1@ = 0
    end 
end

:lbl
hex
    xx 00 00 00 00      // MOV eax, ADDR (сюда запишется адрес 1@)
    xxxxxxxx             // MOV [eax], 1
    B8 9B9A4600        // MOV EAX,00469A9B
    FFE0                      // JMP EAX
end

Код на асме можно сделать короче, если реализовать команду MOV [1@_ADDR], 1

Как все это пишется на асме, лучше спросить у Санчеза))

Last edited by Seemann (01-10-2010 03:00)

Offline

#25 01-10-2010 03:28

Sergey81
Registered: 19-12-2008
Posts: 654

Re: [WIP] Автосохранение

Ещё надо как-нибудь отключить функцию  0AA5: call 0x618F50 num_params 0 pop 0, которая двигает время на 6 часов вперёд, только я незнаю, насовсем её убрать или лучше отключить для текущего скрипта(т.е. двигаем время только при обычном сейве, а при автосейве время не меняется)?

Offline

Board footer

Powered by FluxBB