Цитата
Вот это верно. Но я не понимаю, почему h3m - это нормальный gz-архив, а gm1 - некорректный. Это уж кому нужно было?
Хиппо, а ты подумай, нужны ли разрабам читеры, что сразу же распакуют и начнут менять сейвы. А так, сейв не распаковывается как бы. Карты тоже в формате GZ, но там верная crc пишется. Разрабы просто попытались защитить сейвы хотя бы от совсем несведущих. Хотя и не ахти вышло.
Цитата
И ещё - я понимаю, что со столькими коментариями не понять - признак тупости, но почему для записи используется триггер музыки?
В Эре триггер SN используется для новых событий. Ресивер SN - для команд.
Вот что было в оригинальной концепции, позже наноассемблер был исключён:
Код
DLL с дополнительным функционалом называется ANGEL.DLL.
Её загрузка происходит сразу после инициализации ВОГа и патчинга нужных функций.
DLL патчит zPlaySound так, что v50..v99 становятся ключевыми переменными.
v50 - номер команды
v51..v99 - параметры.
v99 - результат вызова функции API (Call Proc)
0 - стандартный ZPlaySound
1 - LoadLibrary (команда обнуляется)
2 - GetProcAddress (команда обнуляется)
3 - Call Proc (команда обнуляется)
4 - Call Nasm Function (команда обнуляется)
5 - вызов zPlaySound без ЕРМ-триггера. (команда обнуляется)
Другое: - вызвать !?SN-триггер без вызова функции zPlaySound (команда не обнуляется)
Что происходит?
На zPlaySound, иначе процедуре проигрывания звука, стоит мой перехватчик, а до этого был перехватчик ZVS. Перехватчик анализирует v50 и если это 0, то реакция стандартна, иначе см. выше.
Как программировать. Значение команды (v50) варьируется в небольшом диапазоне:
Код
0 - стандартный ZPlaySound]
[1 - LoadLibrary (команда обнуляется)
Name: ZIndex; VAR Result: VIndex;
[2 - GetProcAddress (команда обнуляется)]
hModule: INTEGER; ProcName: ZIndex; Result: VIndex;
[3 - Call Proc (команда обнуляется)]
Proc: POINTER; PushType: TPushType; Params: TParams;
TParms = RECORD
Number: INTEGER;
Params: ARRAY Number OF INTEGER;
END;
Через !!VR:C мы сразу настраиваем команду и параметры. Инициализируем, если нужно, строковые ерм переменные и выполняем команду: !!SN:Pz1; Звука не будет, ЕРМ триггер тоже не активируется. Вдобавок, ядро само обнулит v50.
TParams, если говорить проще - это: кол-во параметров/параметр1/параметр2/...;
В демке Era использует PASCAL-соглашение о передаче параметров в стёк и очистке мусора. В более поздних вариантах соглашение (CDECL, STDCALL, PASCAL) нужно будет указывать после адреса вызываемой функции. Дело в том, что большинство кода Героев юзает STDCALL/REGISTER, Слава - CDECL, а я - PASCAL. Единственное, DELPHI юзает убойный REGISTER (передача параметров через регистры), что, увы, не поддерживается.
Приведу для примера кусок из демки:
Цитата
!!VRz1:S^Angel.dll^; Скрипт получает дескриптор библиотеки ядра технической платформы
!!VRv50:C1/1/2; ...
!!SN:Pz1; ... v2 = дескриптор
!!VRv50:C/1/2;
Если бы демка была под SCVS, что возможно и будет в будущем (скомпилированный вариант тоже будет), то шаблон выглядел бы так:
Код
!!VR$Команда$:C(Загрузить библиотеку)/(Индекс z-переменной с названием)/(индекс v-переменной, куда поместить результат)
Что касается новых событий, то теперь должно быть понятно, что их довольно просто вводить. Я вставляю перехватчики в определённых местах, сохраняю регистры и флаги, устанавливаю v50 в номер события (должен быть > 4, >5 в демке) и вызываю zPlaySound. Ессно, звука нет, а вот ЕРМ триггер ловит событие. Соответственно в скрипте мы пишем:
!?SN&v50=(Событие_Х);
!!VRv50:S0; - это важно, иначе вместо звука (ведь скрипты часто проигрывают реальный звук) мы ещё раз сгенерируем это событие.
Как вывод, всё в принципе довольно стандартно.
Код
!!VRv50:Cкоманды и параметры
!!SN:Pz1; выполнили
....
!?SN&v50=событие_х;
!!VRv50:S0;
....
Например, ты можешь написать тестовую DLL, экспортировать функцию по имени:
Код
EXPORTS
TestFunc NAME 'TestFunc';
По аналогии подгрузить DLL, получить адрес функции и вызвать её. Не забудь только при объявлении:
Код
FUNCTION TestFunc(i: INTEGER); PASCAL;
Поставить тип соглашения. А то Делфи будет ожидать параметров в регистрах, а мы ей стёк