You are not logged in.
А можно в SA сделать автосейв, к примеру при появлении надписи "Mission Passed", скажем, в последний слот?
Offline
Offline
BoPoH, show_save_screen
Offline
0AA5: call 0x619060 num_params 1 pop 0 2
Автосейв в третий слот. Чуть позже может выложу в разделе модификаций.
Last edited by Sergey81 (28-09-2010 20:29)
Offline
Не в третий, а в последний загруженный/сохраненный. Номер текущего сохранения хранится где-то в структуре CMenuManager. Если по этому адресу записать, скажем, 8, то игра сохранится в несущуствующий 9-й слот (GTASAsf9.b). Загрузить его можно будет так же через вызов соответствующей функции.
Offline
Не в третий, а в последний загруженный/сохраненный. Номер текущего сохранения хранится где-то в структуре CMenuManager. Если по этому адресу записать, скажем, 8, то игра сохранится в несущуствующий 9-й слот (GTASAsf9.b). Загрузить его можно будет так же через вызов соответствующей функции.
Эм, если написать вместо двойки 7, сохранит в последний слот у меня. Кстати как лучше сделать проверку на выполнение миссии? В идеале тот момент, когда текст миссия выполнена исчезает.
Offline
Кстати как лучше сделать проверку на выполнение миссии? В идеале тот момент, когда текст миссия выполнена исчезает.
перехватывать исполнение опкода 0112. он используется в самом конце миссии. это универсальный способ на все миссии в игре (для специфических миссий можно дополнительно проверять название потока).
Проверять нужно конечно запуск самого опкода, а не его флага. Базы под рукой нет, но навскидку приходит на ум следующий способ: в таблицах указателей на обработчики опкодов найти нужный опкод, туда прописать адрес внутри тела скрипта на свой код на асме (конструкция hex..end). После кода поставить переход на реальный обработчик опкода 0112. в коде асма записывать в какой-то специфический адрес ( например, адрес переменной 0@) определенное значение. Сам скрипт будет в бесконечном цикле проверять этот адрес, и если он изменился, значит был выполнен опкод 0112 и, соответственно, пройдена миссия. Остальное дело техники.
Есть правда один скользкий момент: опкод 0112 может запускаться и при смерти/аресте игрока (т.е. провале миссии). Этот момент нужно дополнительно проверять.
Как это примерно делается можно посмотреть в исходниках CLEO 1.
http://sannybuilder.com/dev/cleo.txt
Offline
Кстати как лучше сделать проверку на выполнение миссии? В идеале тот момент, когда текст миссия выполнена исчезает.
перехватывать исполнение опкода 0112. он используется в самом конце миссии. это универсальный способ на все миссии в игре (для специфических миссий можно дополнительно проверять название потока).
Проверять нужно конечно запуск самого опкода, а не его флага. Базы под рукой нет, но навскидку приходит на ум следующий способ: в таблицах указателей на обработчики опкодов найти нужный опкод, туда прописать адрес внутри тела скрипта на свой код на асме (конструкция hex..end). После кода поставить переход на реальный обработчик опкода 0112. в коде асма записывать в какой-то специфический адрес ( например, адрес переменной 0@) определенное значение. Сам скрипт будет в бесконечном цикле проверять этот адрес, и если он изменился, значит был выполнен опкод 0112 и, соответственно, пройдена миссия. Остальное дело техники.
Есть правда один скользкий момент: опкод 0112 может запускаться и при смерти/аресте игрока (т.е. провале миссии). Этот момент нужно дополнительно проверять.
Как это примерно делается можно посмотреть в исходниках CLEO 1.
http://sannybuilder.com/dev/cleo.txt
Спасибо, гляну, незнаю правда дойдёт дело до реализации или нет.
Пока пришёл на ум простенький метод: http://sannybuilder.com/forums/viewtopi … 535#p12535
Offline
Честно говоря не совсем понял что нужно делать, а точнее сказать как делать.
в таблицах указателей на обработчики опкодов найти нужный опкод
Что имеется в виду? В базе этот опкод я нашёл:
.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
Offline
Найди не сам обработчик, а ссылку на него в списке переходов. в процедуре _opcode_handler_02 в самом начале есть switch(), который по номеру опкода выбирает из списка в конце процедуры адрес нужного обработчика (в нашем случае 00469A9B). Тебе нужно заменить этот адрес 00469A9B на адрес инжекта в теле твоего скрипта (чтобы switch "прыгнул" на твой асм, а не на обработчик опкода). А в скрипте в конце асма прописать переход назад на опкод 0112 (mov eax, 00469A9B; jmp eax), можно пока ограничиться этим.
получится такая схема:
игра->switch()->наш код->опкод 0112
Offline
Примерно вроде всё ясно.
Тебе нужно заменить этот адрес 00469A9B на адрес инжекта в теле твоего скрипта (чтобы switch "прыгнул" на твой асм, а не на обработчик опкода).
Как именно делается вот этот момент?
Offline
Примерно вроде всё ясно.
Seemann wrote:Тебе нужно заменить этот адрес 00469A9B на адрес инжекта в теле твоего скрипта (чтобы switch "прыгнул" на твой асм, а не на обработчик опкода).
Как именно делается вот этот момент?
1. Узнаешь глобальный адрес твоего асма в памяти игры
(читаешь thread.base_address, к нему плюсуешь label offset). Пример есть в скрипте Hello world комплекта CLEO 3.
2. Находишь в таблице переходов адрес опкода 0112 и туда прописываешь этот адрес.
Блин, нет под рукой иды и базы, так бы сказал куда прописывать.
Offline
Это и есть ссылка на обработчик, я правильно понимаю?
(читаешь thread.base_address, к нему плюсуешь label offset). Пример есть в скрипте Hello world комплекта CLEO 3.
0A9F: 0@ = current_thread_pointer
0@ += label offset
потом записать по адресу 0x469A9B 0@ через 0A8C?
Label offset это что и как найти его значение?
Offline
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
@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
Ага, спасибо за разъяснения, теперь всё понятно.
Адрес я нашёл.
Только вот скомпилированный код 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
Вылетает на опкоде 0AC6? Если да, то претензии к CLEO 4
Попробуй поубирать опкоды по одному, чтобы найти проблемный. И попробуй вместо 0a93 поставить бесконечный цикл (он по идее там и должен быть).
Offline
Вылетает на опкоде 0AC6? Если да, то претензии к CLEO 4
Попробуй поубирать опкоды по одному, чтобы найти проблемный. И попробуй вместо 0a93 поставить бесконечный цикл (он по идее там и должен быть).
Не, cleo 3 у меня. Цикл попробую.
UPD. Всё тоже самое, если убрать 0AC6, грузится. К слову, пример из Cleo3 работает.
Last edited by Sergey81 (30-09-2010 22:38)
Offline
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
0AC6 - это опкод из CLEO 4
CLEO4 не работает корректно у меня с текущим набором скриптов
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
Не все так просто)))
С этим кодом
{$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
Можно в принципе использовать обычные адреса, например
0x969164 - дефолтное значение 0, значение 0x000100 даёт всем машинам нитро, 0x000001 или что то подобное делает машину игрока танком(если задеть другую машину та взрывается). 0x100000 вроде бы не оказывает влияния на игру, мб лучше воспользоваться этим. Ну и адрес можно другой поискать если что.
Offline
Можно и так. Но с локальной переменной код более независим. Зачем идти коротким путем?) Единожды написав такой скрипт, можно использовать его в дальнейшем для других случаев (перехватывать исполнение вообще любого опкода), а не искать каждый раз новый свободный адрес. Тем более, что вопрос сводится к написанию на асме нескольких строчек
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
Ещё надо как-нибудь отключить функцию 0AA5: call 0x618F50 num_params 0 pop 0, которая двигает время на 6 часов вперёд, только я незнаю, насовсем её убрать или лучше отключить для текущего скрипта(т.е. двигаем время только при обычном сейве, а при автосейве время не меняется)?
Offline