You are not logged in.
в первую очередь, хотелось нормально методы CScriptThread обозвать нормально
.text:00469F00 ParseThread
движок перебирает опкоды, пока не найдет 0001: wait
.text:00464080 ; int __stdcall CollectNumberParams(__int16 ParamsNumber)
движок читает опр. число числовых параметров (константы, переменные, массивы) из SCM. Параметры пишутся в ParamsArray[32].
.text:00464DA0 ; int __cdecl j_SetJumpLocation(int JumpLocation)
thread->IP ставится в указанную позицию (опкод 0002: jump и подобные)
.text:00464790 ; int __cdecl GetVariablePos(int VarType)
функция читает следующий параметр (предполагается, что там переменная) и возвращает адрес переменной в варспейсе. Используется в основном в выражениях
Vartype:
1 - local var
2 - global var
Параметр не используется.
.text:004859D0 ; int __stdcall SetConditionResult(boolean ConditionResult)
устанавливает значение условия в потоке (Thread->If_result)
.text:00464500 ; int __stdcall GetThreadExtraParams(int *NewThreadStruct)
собирает параметры, переданные в новый поток (опкод 004F). Останавливается после типа данных 0. Параметры пишутся в ParamsArray[32].
.text:00464370 ; int __stdcall WriteResult2Variable(__int16 VariablesCount)
записывает N значений из ParamsArray[32] в переменные, которые идут в SCM. Используется в основном для записи результата (конструкторов).
.text:00463D50 ; int __stdcall GetStringParam(int MoveAddress_ptr,byte MaxStringLength)
тоже что CollectNumberParams только для строковых параметров (строк, строковых переменных).
.text:00465AA0 EndThread
заканчивает поток (опкод 004E)
Offline
2Seemann:
.text:00464080 ; int __stdcall CollectNumberParams(__int16 ParamsNumber)
.text:00464370 ; int __stdcall WriteResult2Variable(__int16 VariablesCount)
У меня они сначала назывались примерно так же. Потом я решил их унифицировать и обозвал их
CScriptThread::loadNumberParams и CScriptThread::storeNumberParams.
Обсуждабельно, но хотелось бы прийти к какому-нибудь унифицированному наименованию.
.text:00464DA0 ; int __cdecl j_SetJumpLocation(int JumpLocation)
У меня CScriptThread::goto. Опять-таки, обсуждабельно.
Была идея называть подобные методы opЧтоТо (она, соответственно, была opJump).
.text:00464790 ; int __cdecl GetVariablePos(int VarType)
До этой я пока не добрался.
.text:004859D0 ; int __stdcall SetConditionResult(boolean ConditionResult)
Предполагаtncz CScriptThread::storeConditionResult (для унификации с storeNumberParams).
.text:00464500 ; int __stdcall GetThreadExtraParams(int *NewThreadStruct)
Угу. А это как раз то, над чем я задумался. Параметром должен, по идее, передаваться CScriptThread * newThread.
Что касается того, что он заполняет. У меня это обозвано tls (Thread Local Storage) - по аналогии с "реальными потоками". Массив там 34 элемента, два последних - таймеры. Если есть лучшие идеи - переобзову.
.text:00463D50 ; int __stdcall GetStringParam(int MoveAddress_ptr,byte MaxStringLength)
Опять же, по аналогии - CScriptThread::loadStringParam (хотелось бы договориться о какой-то паре слов для обзывания этих функций: либо get/put, либо load/store, чтобы, в дальнейшем, не было разногласий. IMHO, load/store чуть логичнее, но, по большому счету, мне без разницы).
.text:00465AA0 EndThread
Если нет возражений, CScriptThread::terminate - два Thread в одном имени - слегка нелогично.
Еще немного:
.text:00463CA0 ; int __stdcall CScriptThread__localPtr(int idx)
Возвращает указатель на локальную переменную по указанному индексу.
.text:00463CC0 ; int __stdcall CScriptThread__localArrayPtr(int off,__int16 idx,char mul)
Возвращает указатель на элемент массива в локальных переменных. Параметры - смещение первого элемента массива, индекс в массива и размер элемента.
Возможно, правильнее будет CScriptThread::getLocalArrayPtr - но как-то длинно получается.
.text:00463CF0 ; int __stdcall CScriptThread__getArrayIndex(int move,__int16 *pOff,__int32 *pIdx)
Получить смещение первого элемента массива и индекс в нем из параметра.
.text:00464250 ; int __cdecl CScriptThread__peekNumber()
Получить следующий numberParam, не перемещая instructionPointer потока.
.text:004648E0 ; void __cdecl CScriptThread__init()
Проинициализировать поля объекта. (начальная инициализация)
Есть еще несколько методов, но они пока еще окончательно не оформлены.
Offline
class CScriptThread { // только поля, без методов CScriptThread * prev; // _f00: предыдущий поток в очереди исполнения CScriptThread * next; // _f04: следующий поток в очереди исполнения char threadName[8]; // _f08: имя потока (8 символов, включая завершающий \0) u32 _f10; // _f10: u08 * ip; // _f14: instruction pointer - указатель на текущий обрабатываемый байт u32 stack[8]; // _f18: стек (для возвратов из процедур) u16 sp; // _f38: stack pointer - текущий элемент стека (индекс) u16 _f3A; // _f3A: u32 tls[34]; // _f3C: thread local storage - локальные переменные потока (два последних элемента - таймеры) u08 _fC4; // _fC4: u08 _fC5; // _fC5: u08 _fC6; // _fC6: u08 _fC7; // _fC7: u08 _fC8; // _fC8: u08 _fC9; // _fC9: u08 _fCA; // _fCA: u08 _fCB; // _fCB: u32 wakeTime; // _fCC: время пробуждения потока (используется для команды WAIT) u16 _fD0; // _fD0: u08 _fD2; // _fD2: u08 _fD3; // _fD3: u08 _fD4; // _fD4: u08 _fD5; // _fD5: u16 _fD6; // _fD6: u32 _fD8; // _fD8: u08 _fDC; // _fDC: 0 - в качестве локальных переменных используется TLS, 1 - используется scriptLocals u08 pad[3]; // заглушка для выравнивания };
Размер структуры - 240 байт. Поля, относящиеся к логическим операциям, найдены, но еще не поименованы.
Сами структуры размещаются в:
.data:00A8B430 ; CScriptThread scriptThreads[96]; .data:00A8B430 _scriptThreads CScriptThread 60h dup(<?>)
Перед ними, в 0xA8B428 и 0xA8B42С находятся два указателя, один из них (пока не ясно, какой), указывает на активный поток. Для чего нужен другой - я пока не понял, возможно, это очередь неактивных потоков. Разберу подробно старт/завершения потока - будет ясно.
Last edited by listener (25-04-2007 15:57)
Offline
00000000 THREAD struc ; (sizeof=0xE0) 00000000 @Active dd ? 00000004 @Inactive dd ? 00000008 Name dd 2 dup(?) 00000010 BaseIP dd ? 00000014 CurrentIP dd ? 00000018 ReturnStack dd 8 dup(?) 00000038 StackCounter dw ? 0000003A field_3A dw ? 0000003C LocalVars dd 32 dup(?) 000000BC TimerA dd ? 000000C0 TimerB dd ? 000000C4 _isThreadActive db ? 000000C5 IF_result db ? 000000C6 __mission_cleanup db ? 000000C7 ExternalFlag db ? 000000C8 InMenu db ? 000000C9 UnknownScriptAssigned db ? 000000CA field_CA dw ? 000000CC wakeup_time dd ? 000000D0 if_Number dw ? 000000D2 not_flag db ? 000000D3 WBcheckFlag db ? 000000D4 isWastedOrBusted db ? 000000D5 field_D5 db ? 000000D6 field_D6 db ? 000000D7 field_D7 db ? 000000D8 SkipScenePos dd ? 000000DC MissionFlag db ? 000000DD field_DD db ? 000000DE field_DE dw ? 000000E0 THREAD ends
первые 2 поля указатели на след. активный (используется при парсинге потоков при переходе от одного к другому), и след. пустой (используется при создании нового потока).
Offline
У меня CScriptThread::goto.
скорее CScriptThread::SetIP
хотелось бы договориться о какой-то паре слов для обзывания этих функций: либо
get/put, либо load/store
мне нравится пара Get/Set Хотя значения особого не имеет.
Еще есть процедура, связанная с потоками:
.text:00464D40 j_CreateThreadFromStartScm
Создает поток с базой 0xA49960 (т.е. с начала main.scm). Этот поток запускается всегда при начале новой игры (загрузке). Именно благодаря нему создается игрок (CLEO2 тоже запускается из этого потока). Стартовый поток заканчивается после первого wait.
Last edited by Seemann (25-04-2007 16:33)
Offline
первые 2 поля указатели на след. активный (используется при парсинге потоков при переходе от одного к другому), и след. пустой (используется при создании нового потока).
Проверил еще раз. У меня получается четко, что потоки собираются в двусвязный список, и это указатели на предыдущий и следующий элемент списка (возможно, я их перепутал между собой, но это малокритично).
Есть две очереди: активных и неактивных потоков.
.data:00A8B428 ; CScriptThread *inactiveThreadQueue
.data:00A8B42C ; CScriptThread *activeThreadQueue
.text:00464C00 ; int __stdcall CScriptThread__addToQueue(CScriptThread **queue)
.text:00464BD0 ; int __stdcall CScriptThread__removeFromQueue(CScriptThread **queue)
По коду получается так.
В 004E: end_thread получается:
.... // сбрасывается какой-то флаг removeFromQueue (&activeThreadQueue); addToQueue (&inactiveThreadQueue); endThread (); // да, terminate в качестве названия этой функции подойдет не очень - если не писать явно this->, не ясно, кого терминировать return true;
Last edited by listener (25-04-2007 17:01)
Offline
У меня получается четко, что потоки собираются в двусвязный список, и это указатели на предыдущий и следующий элемент списка (возможно, я их перепутал между собой, но это малокритично).
да, у меня тоже сначала эти поля называлиcь @Next, @Prev.
сбрасывается какой-то флаг
.text:004667DB mov al, [esi+THREAD.MissionFlag] .text:004667E1 test al, al .text:004667E3 jz short _not_mission .text:004667E5 mov ds:bContinueMission, 0 .text:004667EC .text:004667EC _not_mission: .text:004667EC push offset @ActiveThread
не помню уже почему назвал bContinueMission
Offline
Все-таки _f0 - next, а _f4 - prev.
// >> 464BD0 - исключить поток из очереди void CScriptThread::removeFromQueue (CScriptThread ** queue) { if (prev) prev->next = next; else *queue = next; if (next) next->prev = prev; } // >> 464C00 - добавить поток в очередь void CScriptThread::addToQueue(CScriptThread **queue) { next = *queue; prev = NULL; if (next) next->prev = this; *queue = this; }
PS. А на gtaforums народ радостно публикует адрес WinMain ... Жуть какая. Зарегистриться там что-ли и закинуть не только адрес, но и весь декомпилированный WinMain...
Я, кстати, ни из чего найденного не делаю секрета, так что, если кто-нибудь закинет туда что-нибудь из накопанного - я буду только рад.
PPS. Кстати, какие-нибудь C++ - ные куски нужны? А то у меня их уже уже килобайт сто получается.
Offline
Зарегистриться там что-ли и закинуть не только адрес, но и весь декомпилированный WinMain...
ты бы лучше сразу сюда добавлял http://www.gtamodding.com/index.php?tit … _Addresses
Кстати, какие-нибудь C++ - ные куски нужны?
выкладывай, могут пригодиться (лучше наверно в текстовом файле, чтобы простыней не постить).
Offline
выкладывай, могут пригодиться (лучше наверно в текстовом файле, чтобы простыней не постить).
Ну, постить такие куски в форум - это техномазохизм какой-то получается.
Если все пойдет нормально - за выходные причешу текст (за три месяца, накопилось много изменений, без них оно (по сравнению с тем, что есть) практически нечитабельно) и выложу, возможно вместе со свежей базой.
А пока, вот что полностью разобрано по CScriptThread:
// >> 463CA0 получить указатель на локальную переменную (с учетом того, используется TLS или нет) inline u32 * localPtr (u32 idx) { return (_fDC ? scriptLocals : tls)+idx; } // >> 464700 получить смещение в scmBlock следующего операнда (типы 2, 7) // т.е., возвращается не операнд, а только его смещение в scmBlock u32 getGlobalOffset (); // >> 463CC0 получить индексированный указатель на локальную переменную (с учетом того, используется TLS или нет) inline u32 * localArrayPtr (u32 off, u16 idx, u08 mul) { return (_fDC ? scriptLocals : tls)+off+idx*mul; } // >> 463CF0 получить индекс и смещение для индексированного операнда. Первый параметр - выполнять переход к следующему байту или нет void getArrayIndex (bool move, u16 * off, u32 * idx); void getNumberParams (u16 count); // >> 464080 получить числовые параметры команды void getStringParam (char * ptr, u32 len); // >> 463D50 получить строковый параметр команды u32 peekNumber (); // >> 464250 получить числовой операнд по текущему IP. IP не меняется void setNumberParams (u16 count); // >> 464370 записать числовые значения (из opcodeParams в указанные переменные (т.е., indirect значения невозможны)) void getThreadParams (CScriptThread * thread); // >> 464500 получить набор переменных для TLS создаваемого потока void init (); // >> 4648E0 - проинициализировать объект void removeFromQueue (CScriptThread ** queue); // >> 464BD0 - исключить поток из очереди void addToQueue (CScriptThread ** queue); // >> 464C00 - добавить поток в очередь // >> 464C20 - взять поток из очереди неактивных потоков, проинициализировать и добавить в очередь активных потоков static CScriptThread * allocThread (u08 * startIP); // >> 464C90 - аналогично предыдущему, только указывается индекс объекта в scriptThreads (проверяется, что он находится в inactiveThreadQueue) static CScriptThread * allocThreadByIndex (u08 * startIP, u32 index); void * getPointerParam (); // >> 464790 void setIP (s32 newIP); // 464DA0 - перейти к указанному адресу
Дальше можно, прикола ради и проверки для, вкомпилить это в DLL и попробовать подменить то, что в exe-шнике.
Offline
2Seemann:
2listener:
Нельзя ли в exe'шник встроить что-то типа эксепшинхендлера, чтобы при падении игры можно было увидеть хоть какую-нибудь информацию о причинах, или место скрипта где произошел баг ???
ps: Это резко снизило бы кол-во багов в разных модах...
Сначала ты надежда и гордость,
Потом о спину ломают аршин. (c)БГ
Offline
2Capushon:
дык, это... CLEO2 + ScmLog. Берутся с http://cleo.sannybuilder.com
Offline
2listener:
2Seemann:
Выложите структуру CPlayer и CWanted, если такая имеется.
Last edited by Sanchez (28-04-2007 08:55)
Offline
Offline
2listener:
Есть идеи как при старте игры пропустить главное меню и сразу начать новую игру?
Пробовал 2 способа:
1. занопить строки в 0x0053E799, 0x00576C34, 0x576C41
глюк: в игре по понятной причине не открывается меню
2. заменить в 00748C90
mov ds:dwLoadingStage, 7
на
mov ds:dwLoadingStage, 8
глюк: если в игре выбрать новую игру - вылетает.
Offline
2. заменить в 00748C90
mov ds:dwLoadingStage, 7
на
mov ds:dwLoadingStage, 8глюк: если в игре выбрать новую игру - вылетает.
Так в САМП сделали , тоже новая игра не запускается ...
Offline
База listener'а по его же компактному gta_sa.exe.
[You must login to view hidden text.]
Must have для всех кто интересуется устройством игры. Надеюсь, мы еще увидим обновление для этого.
Offline
2listener:
Рад вновь видеть тебя Надеюсь время у тебя появится и ты нас еще чем-нибудь порадуешь.
Если можешь, ответь пожалуйста на пост 66 в этой теме.
Offline
Не совсем в рамках темы, но всё таки...
В ходе разработки своей программы SFX Editor(http://rapidshare.com/files/44154049/SF … r.rar.html) у меня возникла мысль заглянуть в дебри опкода 03CF и в ходе разбора кода я нарыл много полезной информации. Думаю она вам пригодится.
Вот функции и структуры связанные со звуком (в основном звуковые эффекты):
CODE: 004D88A0 void CAudio->AudioSFX::LoadBankWave (int16 BankID,BankSlotID) 004D8ED0 void CAudio->AudioSFX::LoadWave (int16 BankID,WaveID,BankSlotID) 004D9930 bool CAudio::Create() 004D9CC0 bool CAudioEvents::Buffer::LoadInfo (int32* pAudioEvent,pBankID,pWaveID,EBufferID) 004E01B0 BankLookup* CAudioSFX::GetBankLookupAddress (uint8 BankID) 004E0220 bool CAudioSFX::LoadedBankToSlot (uint16 BankID; int16 BankSlotID) 004EBFE0 bool CAudioEvents::Buffer::LoadedSlot (uint8 BufferID) 004EF520 int8 CSoundManager::LoadedBank (int16 BankSlotID) 00507290 void CAudioEvents::Buffer::Load (uint8 BufferId; int32 AudioEvent) 005B9A60 void CAudioEvents::LoadExplosionSounds() 005B9690 bool CSoundManager::Init(?) 005B9A60 void CAudioEvents::LoadExplosionSounds 005B9C60 bool CAudioEvents::Create() DATA: 00B5F8B8 CAudio +D98 CAudioSFX* // здесь лежит указатель, адрес динамический, обычно 021F0000, насчёт класса не уверен 00B62CB0 CSoundManager 00B6BC90 CAudioEvents CAudioSFX +0 dd* pBankSlotData // BankSlot.dat +4 dd* pBankLookupData // BankLkup.dat +8 dd* pPakFilesData // PakFiles.dat +C dw NumSlots +E dw NumBanks +10 db NumPakFiles +14 db BufferFlag? +24 CAudioSFX::Buffer[50] // С этим буфером до конца ещё не разобрался +0 dd* pBankSlot +4 dd BankLookup.WaveOffset +8 dd BankLookup.WaveLenght +14 dd UnkFlag? +18 dw BankID +1A dw BankSlotID +20 dw WaveID +22 db BankLookup.PackID +668 dw InitBufferSlotsNum // количество проинициализированных слотов буфера
Ниже следующий кусок кода из моей программы. Он отражает назначение некоторых структур и переменных, которые используются в вышеперечисленных функциях.
{ === Wave Audio Bank Structure === } TWaveInfo = record HeaderOffset: Cardinal; StartLoop: Integer; SampleRate: Word; UnknownFlag1: Byte; // Pan?? UnknownFlag2: Shortint; // Loops??? end; TWaveBankHeader = record WaveCount: Cardinal; WaveInfoList: array[0..399] of TWaveInfo; end; TWaveBank = record Header: TWaveBankHeader; Data: array of Byte; end; { === SFX PACK Structure === } TSFX_Pack = array of TWaveBank; { === BankLkup.dat Structure === } TWaveBankInfo = record PackFileID: Cardinal; // ($CCCCCC00 or $XX), $XX - File ID Offset: Cardinal; DataSize: Cardinal; end; TWaveBanks = array of TWaveBankInfo; { === PakFiles.dat Structure === } TPackName = array of array[0..51] of Char;
Здесь я хочу показать алгоритм работы опкода 03CF. Некоторые куски кода мне не совсем понятны, их назначение.
03CF: load_in (AUDIO_EVENT_BUFFER:unsigned_byte) audio_event (AUDIO_EVENT:int) AUDIO_EVENT_BUFFER(MAX 4 SLOTS) 1,2,3,4: для звуков (SOUNDs 2000..45400,-1) 3,4: для набора звуков (BANKs 1800..1829) ========================================================================================================================= <gta_sa._opcode_03CF> ========================================================================================================================= 0048519B > > 6A 02 PUSH 2 ; _opcode_03CF; Case 3CF of switch 00483BF2 0048519D . 8BCE MOV ECX,ESI 0048519F . E8 DCEEFDFF CALL <gta_sa.CScriptThread::LoadNumParams> 004851A4 . 8B0D 7C3CA400 MOV ECX,DWORD PTR DS:[<OpcodeParams[1]>] 004851AA . 33D2 XOR EDX,EDX 004851AC . 8A15 783CA400 MOV DL,BYTE PTR DS:[<OpcodeParams[0]>] 004851B2 . FECA DEC DL 004851B4 . 51 PUSH ECX ; 2: int32 AudioEvent 004851B5 . B9 90BCB600 MOV ECX,OFFSET <gta_sa.CAudioEvent> 004851BA . 52 PUSH EDX ; 1: uint8 BufferID 004851BB . E8 D0200800 CALL <gta_sa.CAudioEvent::Load> 004851C0 . 32C0 XOR AL,AL ========================================================================================================================= <gta_sa.CAudioEvent::Load> ========================================================================================================================= 00507290 > $ 81C1 A0020000 ADD ECX,2A0 ; Buffer& = CAudioEvents::Buffer 00507296 .^E9 F54EFEFF JMP gta_sa.004EC190 ......................................................................................................................... 004EC190 > 83EC 0C SUB ESP,0C 004EC193 . 53 PUSH EBX 004EC194 . 8B5C24 14 MOV EBX,DWORD PTR SS:[ESP+14] ; EBX = BufferID 004EC198 . 80FB 04 CMP BL,4 004EC19B . 56 PUSH ESI 004EC19C . 8BF1 MOV ESI,ECX 004EC19E . 0F83 C0000000 JNB gta_sa.004EC264 ; exit if BufferID >= 4 004EC1A4 . 53 PUSH EBX 004EC1A5 . E8 36FEFFFF CALL <gta_sa.CAudioEvents::Buffer::LoadedSlot> 004EC1AA . 84C0 TEST AL,AL 004EC1AC . 0F84 B2000000 JE gta_sa.004EC264 ; exit if prc = false 004EC1B2 . 0FB6C3 MOVZX EAX,BL 004EC1B5 . 55 PUSH EBP 004EC1B6 . 8BC8 MOV ECX,EAX 004EC1B8 . 57 PUSH EDI 004EC1B9 . C1E1 05 SHL ECX,5 ; BufferID * 32 004EC1BC . 03F1 ADD ESI,ECX 004EC1BE . 50 PUSH EAX ; 4: int32 EBufferID = BufferID 004EC1BF . 8DBE AC000000 LEA EDI,DWORD PTR DS:[ESI+AC] 004EC1C5 . 57 PUSH EDI ; 3: int32* pWaveID = *Buffer::Slot[BufferId].WaveID 004EC1C6 . 8DAE A8000000 LEA EBP,DWORD PTR DS:[ESI+A8] 004EC1CC . 8D5424 2C LEA EDX,DWORD PTR SS:[ESP+2C] 004EC1D0 . 55 PUSH EBP ; 2: int32* pBankID = *Buffer::Slot[BufferId].BankID 004EC1D1 . 52 PUSH EDX ; 1: int32* pAudioEvent = *AudioEvent 004EC1D2 . E8 E9DAFEFF CALL <gta_sa.CAudioEvents::Buffer::LoadInfo> 004EC1D7 . 83C4 10 ADD ESP,10 004EC1DA . 84C0 TEST AL,AL 004EC1DC . 0F84 80000000 JE gta_sa.004EC262 ; exit if PROC = false 004EC1E2 . 8B07 MOV EAX,DWORD PTR DS:[EDI] 004EC1E4 . 33C9 XOR ECX,ECX 004EC1E6 . 85C0 TEST EAX,EAX 004EC1E8 . 66:0FB6C3 MOVZX AX,BL 004EC1EC . 7C 1B JL SHORT gta_sa.004EC209 ; jump if WaveID < 0 004EC1EE . 66:8B0F MOV CX,WORD PTR DS:[EDI] 004EC1F1 . 33D2 XOR EDX,EDX 004EC1F3 . 66:8B55 00 MOV DX,WORD PTR SS:[EBP] 004EC1F7 . 83C0 1A ADD EAX,1A 004EC1FA . 50 PUSH EAX ; 3: int16 BankSlotID 004EC1FB . 51 PUSH ECX ; 2: int16 WaveID 004EC1FC . B9 B8F8B500 MOV ECX,<gta_sa.CAudio> 004EC201 . 52 PUSH EDX ; 1: int16 BankID 004EC202 . E8 C9CCFEFF CALL <gta_sa.CAudio->CAudioSFX::LoadWave> 004EC207 . EB 13 JMP SHORT gta_sa.004EC21C 004EC209 > 66:8B4D 00 MOV CX,WORD PTR SS:[EBP] 004EC20D . 83C0 1A ADD EAX,1A 004EC210 . 50 PUSH EAX ; 2: int16 BankSlotID 004EC211 . 51 PUSH ECX ; 1: int16 BankID 004EC212 . B9 B8F8B500 MOV ECX,<gta_sa.CAudio> 004EC217 . E8 84C6FEFF CALL <gta_sa.CAudio->AudioSFX::LoadBankWave> 004EC21C > 8B5424 24 MOV EDX,DWORD PTR SS:[ESP+24] 004EC220 . 8996 A4000000 MOV DWORD PTR DS:[ESI+A4],EDX ; Buffer::Slot[BufferId].AudioEvent = AudioEvent 004EC226 . C786 9C000000 >MOV DWORD PTR DS:[ESI+9C],0 004EC230 . C74424 10 0000>MOV DWORD PTR SS:[ESP+10],C47A0000 004EC238 . 8B4424 10 MOV EAX,DWORD PTR SS:[ESP+10] 004EC23C . 81C6 90000000 ADD ESI,90 ; CurSlot& = Buffer::Slot[BufferId] 004EC242 . C74424 14 0000>MOV DWORD PTR SS:[ESP+14],C47A0000 004EC24A . 8B4C24 14 MOV ECX,DWORD PTR SS:[ESP+14] 004EC24E . 8906 MOV DWORD PTR DS:[ESI],EAX ; CurSlot.ddvar1 = 0xC47A0000 004EC250 . C74424 18 0000>MOV DWORD PTR SS:[ESP+18],C47A0000 004EC258 . 8B5424 18 MOV EDX,DWORD PTR SS:[ESP+18] 004EC25C . 894E 04 MOV DWORD PTR DS:[ESI+4],ECX ; CurSlot.ddvar2 = 0xC47A0000 004EC25F . 8956 08 MOV DWORD PTR DS:[ESI+8],EDX ; CurSlot.ddvar3 = 0xC47A0000 004EC262 > 5F POP EDI 004EC263 . 5D POP EBP 004EC264 > 5E POP ESI 004EC265 . 5B POP EBX 004EC266 . 83C4 0C ADD ESP,0C 004EC269 . C2 0800 RETN 8 ========================================================================================================================= GetAudioEventInfo для 03CF опкода: загружает в буфер информацию о аудио событии. Буфер находится по адресу 0x00B6BFC0 и содержит четыре элемента размером 32 байта. AudioEventBuffer [0x00B6BFC0, 32 bytes, 4 elements] +0 dd ?C4A70000 +4 dd ?C4A70000 +8 dd ?C4A70000 +14 dd AudioEventId +18 dd BankId +20 dd WaveId EBufferID - Extended Buffer Identificator. Он может содержитать не только номер буфера, но и дополнительную информацию. Pascal: function CAudioEvents::Buffer::LoadInfo(var pAudioEvent,pBankID,pWaveID: Integer; int EBufferID): Boolean; cdecl; C/C++: __cdecl int CAudioEvents::Buffer::LoadInfo(int* pAudioEvent,pBankID,pWaveID; int EBufferID); ------------------------- [ESP+8] pAudiEvent [ESP+C] pBankId [ESP+10] pWaveId [ESP+14] EBufferID ------------------------- ========================================================================================================================= 004D9CC0 >/$ 56 PUSH ESI ; (* CAudioEvents::Buffer::LoadInfo *) 004D9CC1 |. 8B7424 08 MOV ESI,DWORD PTR SS:[ESP+8] 004D9CC5 |. 8B06 MOV EAX,DWORD PTR DS:[ESI] 004D9CC7 |. 3D 08070000 CMP EAX,708 ; if pAudioEvent < 1800 then 004D9CCC |. 7D 04 JGE SHORT gta_sa.004D9CD2 ; begin 004D9CCE |. 32C0 XOR AL,AL ; Result := False; 004D9CD0 |. 5E POP ESI ; Exit; 004D9CD1 |. C3 RETN ; end; 004D9CD2 |> 3D D0070000 CMP EAX,7D0 ; if pAudioEvent < 2000 then 004D9CD7 |. 7D 1B JGE SHORT gta_sa.004D9CF4 ; begin 004D9CD9 |. 8B0485 70BC8A00 MOV EAX,DWORD PTR DS:[EAX*4+8ABC70] ; // BankEvent[AudioEvent - 1800] 004D9CE0 |. 8B4C24 0C MOV ECX,DWORD PTR SS:[ESP+C] 004D9CE4 |. 8B5424 10 MOV EDX,DWORD PTR SS:[ESP+10] 004D9CE8 |. 8901 MOV DWORD PTR DS:[ECX],EAX ; pBankID := BankIdEvent[pAudioEvent - 1800]; 004D9CEA |. C702 FFFFFFFF MOV DWORD PTR DS:[EDX],-1 ; pWaveID := -1; 004D9CF0 |. B0 01 MOV AL,1 ; Result := True; 004D9CF2 |. 5E POP ESI ; Exit; 004D9CF3 |. C3 RETN ; end; 004D9CF4 |> 3D FFFF0000 CMP EAX,0FFFF ; if pAudioEvent = -1 then // Для чего??? 004D9CF9 |. 75 3D JNZ SHORT gta_sa.004D9D38 ; begin 004D9CFB |. 8B4424 0C MOV EAX,DWORD PTR SS:[ESP+C] ; // SOUND_BANK_NULL = 291 004D9CFF |. C700 23010000 MOV DWORD PTR DS:[EAX],123 ; pBankID := SOUND_BANK_NULL; 004D9D05 |. 8B4424 14 MOV EAX,DWORD PTR SS:[ESP+14] 004D9D09 |. 85C0 TEST EAX,EAX ; if (EBufferID >= 0) and 004D9D0B |. 7C 1D JL SHORT gta_sa.004D9D2A 004D9D0D |. 83F8 04 CMP EAX,4 ; (EBufferID < 4) then 004D9D10 |. 7D 18 JGE SHORT gta_sa.004D9D2A ; begin 004D9D12 |. 25 01000080 AND EAX,80000001 ; EBufferID := EBufferID and $80000001; 004D9D17 |. 79 05 JNS SHORT gta_sa.004D9D1E ; if EBufferID < 0 then // Зачем? Смысл? 004D9D19 |. 48 DEC EAX ; begin 004D9D1A |. 83C8 FE OR EAX,FFFFFFFE ; EBufferID := (EBufferID - 1) or $FFFFFFFE; 004D9D1D |. 40 INC EAX ; Inc(EBufferID); 004D9D1E |> 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+10] ; end; 004D9D22 |. D1E0 SHL EAX,1 ; pWaveID := EBufferID * 2; //всегда 0 или 2 004D9D24 |. 8901 MOV DWORD PTR DS:[ECX],EAX ; Result := True; 004D9D26 |. B0 01 MOV AL,1 ; Exit; 004D9D28 |. 5E POP ESI ; end; 004D9D29 |. C3 RETN 004D9D2A |> 8B5424 10 MOV EDX,DWORD PTR SS:[ESP+10] ; 004D9D2E |. C702 00000000 MOV DWORD PTR DS:[EDX],0 ; pWaveID := 0; 004D9D34 |. B0 01 MOV AL,1 ; Result := True; 004D9D36 |. 5E POP ESI ; Exit; 004D9D37 |. C3 RETN ; end 004D9D38 |> 05 30F8FFFF ADD EAX,-7D0 004D9D3D |. 894424 08 MOV DWORD PTR SS:[ESP+8],EAX ; pAudioEvent := pAudioEvent - 2000; 004D9D41 |. DB4424 08 FILD DWORD PTR SS:[ESP+8] 004D9D45 |. 83EC 08 SUB ESP,8 004D9D48 |. D80D 4C8B8500 FMUL DWORD PTR DS:[858B4C] ; // CONST [858B4C] = 0.005 (1/200) 004D9D4E |. DD1C24 FSTP QWORD PTR SS:[ESP] 004D9D51 |. E8 9A7C3400 CALL <gta_sa.floor> 004D9D56 |. 83C4 08 ADD ESP,8 004D9D59 |. E8 E27D3400 CALL <gta_sa.__ftol2> 004D9D5E |. 8B4C24 0C MOV ECX,DWORD PTR SS:[ESP+C] 004D9D62 |. 05 93000000 ADD EAX,93 004D9D67 |. 8901 MOV DWORD PTR DS:[ECX],EAX ; pBankID := Floor(pAudioEvent/200) + 147 004D9D69 |. 8B06 MOV EAX,DWORD PTR DS:[ESI] ; // pBankID := AudioEvent div 200 + 147 004D9D6B |. 2D D0070000 SUB EAX,7D0 ; // 147 - SCRIPT_FIRST_BANK_ID 004D9D70 |. 99 CDQ 004D9D71 |. B9 C8000000 MOV ECX,0C8 004D9D76 |. F7F9 IDIV ECX 004D9D78 |. 8B4424 10 MOV EAX,DWORD PTR SS:[ESP+10] 004D9D7C |. 5E POP ESI 004D9D7D |. 8910 MOV DWORD PTR DS:[EAX],EDX ; pWaveID := pAudioEvent mod 200 004D9D7F |. B0 01 MOV AL,1 ; Result := True; 004D9D81 \. C3 RETN ; Exit; ========================================================================================================================= CAudio->AudioSFX::LoadBankWave (int16 BankID,BankSlotID) ========================================================================================================================= 004D88A0 > $ 8A41 01 MOV AL,BYTE PTR DS:[ECX+1] ; 004D88A3 . 84C0 TEST AL,AL 004D88A5 . 75 0B JNZ SHORT gta_sa.004D88B2 004D88A7 . 8B89 980D0000 MOV ECX,DWORD PTR DS:[ECX+D98] ; CAudioSFX& = *CAudio->AudioSFX 004D88AD . E9 BE7D0000 JMP gta_sa.004E0670 004D88B2 > C2 0800 RETN 8 ......................................................................................................................... 004E0670 > 56 PUSH ESI 004E0671 . 8BF1 MOV ESI,ECX ; ESI = CAudioSFX 004E0673 . 8A46 14 MOV AL,BYTE PTR DS:[ESI+14] 004E0676 . 84C0 TEST AL,AL 004E0678 . 0F84 15010000 JE gta_sa.004E0793 ; exit if CAudioSFX::UnkFlag1 = 0 004E067E . 0FBF46 0E MOVSX EAX,WORD PTR DS:[ESI+E] 004E0682 . 53 PUSH EBX 004E0683 . 8B5C24 0C MOV EBX,DWORD PTR SS:[ESP+C] 004E0687 . 55 PUSH EBP 004E0688 . 0FB7EB MOVZX EBP,BX 004E068B . 3BE8 CMP EBP,EAX 004E068D . 57 PUSH EDI 004E068E . 0F8F FC000000 JG gta_sa.004E0790 ; exit if BankID > CAudioSFX::NumBanks 004E0694 . 8B7C24 18 MOV EDI,DWORD PTR SS:[ESP+18] 004E0698 . 66:85FF TEST DI,DI 004E069B . 0F8C EF000000 JL gta_sa.004E0790 ; exit if BankID < 0 004E06A1 . 66:3B7E 0C CMP DI,WORD PTR DS:[ESI+C] 004E06A5 . 0F8F E5000000 JG gta_sa.004E0790 ; exit if BankSlotID > CAudioSFX::NumBankSlots 004E06AB . 57 PUSH EDI ; 2: int16 BankSlotID 004E06AC . 53 PUSH EBX ; 1: int16 BankID 004E06AD . E8 6EFBFFFF CALL <gta_sa.CAudioSFX::LoadedBankToSlot> ; <|=== LoadedBankToSlot??? === 004E06B2 . 84C0 TEST AL,AL 004E06B4 . 0F85 D6000000 JNZ gta_sa.004E0790 ; exit if proc = true 004E06BA . 33C9 XOR ECX,ECX 004E06BC . 8D46 3E LEA EAX,DWORD PTR DS:[ESI+3E] 004E06BF . 90 NOP ; /=== LoadedBankToBuffer??? === 004E06C0 > 0FBF50 FE MOVSX EDX,WORD PTR DS:[EAX-2] ; |!loop ECX = 0..31 004E06C4 . 3BD5 CMP EDX,EBP ; | 004E06C6 . 75 09 JNZ SHORT gta_sa.004E06D1 ; |jump if Buffer[ECX].BankID <> BankID 004E06C8 . 66:3938 CMP WORD PTR DS:[EAX],DI ; | 004E06CB . 0F84 BF000000 JE gta_sa.004E0790 ; |exit if Buffer[ECX].BankSlotID = BankSlotID 004E06D1 > 41 INC ECX ; | 004E06D2 . 83C0 20 ADD EAX,20 ; | 004E06D5 . 83F9 32 CMP ECX,32 ; | 004E06D8 .^7C E6 JL SHORT gta_sa.004E06C0 ; \jump !loop if ECX < 32 004E06DA . 53 PUSH EBX ; 1: BankID 004E06DB . 8BCE MOV ECX,ESI 004E06DD . E8 CEFAFFFF CALL <gta_sa.CAudioSFX::GetBankLookupAddress>; BankLookup* PROC 004E06E2 . 0FBF8E 6806000>MOVSX ECX,WORD PTR DS:[ESI+668] 004E06E9 . C1E1 05 SHL ECX,5 ; InitBufferSlotsNum*32 004E06EC . 66:895C31 3C MOV WORD PTR DS:[ECX+ESI+3C],BX ; Buffer[InitBufferSlotsNum].BankID = BankID 004E06F1 . 0FBF96 6806000>MOVSX EDX,WORD PTR DS:[ESI+668] 004E06F8 . C1E2 05 SHL EDX,5 004E06FB . 66:897C32 3E MOV WORD PTR DS:[EDX+ESI+3E],DI ; Buffer[InitBufferSlotsNum].BankSlotID = BankSlotID 004E0700 . 0FBF8E 6806000>MOVSX ECX,WORD PTR DS:[ESI+668] 004E0707 . 83C1 02 ADD ECX,2 004E070A . C1E1 05 SHL ECX,5 004E070D . 66:C70431 FFFF MOV WORD PTR DS:[ECX+ESI],0FFFF ; Buffer[InitBufferSlotsNum].Unk1 = -1 004E0713 . 0FBF8E 6806000>MOVSX ECX,WORD PTR DS:[ESI+668] 004E071A . 8B1E MOV EBX,DWORD PTR DS:[ESI] 004E071C . 0FBFD7 MOVSX EDX,DI 004E071F . 69D2 D4120000 IMUL EDX,EDX,12D4 004E0725 . 03D3 ADD EDX,EBX 004E0727 . C1E1 05 SHL ECX,5 004E072A . 895431 24 MOV DWORD PTR DS:[ECX+ESI+24],EDX ; Buffer[InitBufferSlotsNum].pBankSlot = *BankSlot[BankSlotID] 004E072E . 8A08 MOV CL,BYTE PTR DS:[EAX] 004E0730 . 0FBF96 6806000>MOVSX EDX,WORD PTR DS:[ESI+668] 004E0737 . C1E2 05 SHL EDX,5 004E073A . 884C32 42 MOV BYTE PTR DS:[EDX+ESI+42],CL ; Buffer[InitBufferSlotsNum].PackID = BankLookup.PackID 004E073E . 8B48 04 MOV ECX,DWORD PTR DS:[EAX+4] 004E0741 . 0FBF96 6806000>MOVSX EDX,WORD PTR DS:[ESI+668] 004E0748 . C1E2 05 SHL EDX,5 004E074B . 894C32 28 MOV DWORD PTR DS:[EDX+ESI+28],ECX ; Buffer[InitBufferSlotsNum].PackID = BankLookup.WaveDataOffset 004E074F . 0FBF96 6806000>MOVSX EDX,WORD PTR DS:[ESI+668] 004E0756 . 8B40 08 MOV EAX,DWORD PTR DS:[EAX+8] 004E0759 . C1E2 05 SHL EDX,5 004E075C . 894432 2C MOV DWORD PTR DS:[EDX+ESI+2C],EAX ; Buffer[InitBufferSlotsNum].PackID = BankLookup.WaveLenght 004E0760 . 0FBF8E 6806000>MOVSX ECX,WORD PTR DS:[ESI+668] 004E0767 . C1E1 05 SHL ECX,5 004E076A . C74431 38 0100>MOV DWORD PTR DS:[ECX+ESI+38],1 ; Buffer[InitBufferSlotsNum].bUnkFlag1 = 1 004E0772 . 0FBF86 6806000>MOVSX EAX,WORD PTR DS:[ESI+668] 004E0779 . 66:FF86 660600>INC WORD PTR DS:[ESI+666] ; CAudioSFX::InitBankBufNum++ 004E0780 . 40 INC EAX 004E0781 . 99 CDQ 004E0782 . B9 32000000 MOV ECX,32 004E0787 . F7F9 IDIV ECX 004E0789 . 66:8996 680600>MOV WORD PTR DS:[ESI+668],DX ; CAudioSFX::InitBufNum++ %= 50 004E0790 > 5F POP EDI 004E0791 . 5D POP EBP 004E0792 . 5B POP EBX 004E0793 > 5E POP ESI 004E0794 . C2 0800 RETN 8
Ещё мне не совсем понятно назначение файла BankSlot.dat.
Наверное этот файл хранит глобальный буффер, его размер и начальные значения.
Вообще, я так понимаю в игре три буффера для звуковых эффектов.
Первый, и наверное самый главный - это буффер BankSlot. Количество слотов зависит от размера файла. Обычно 45.
Второй - это CAudiSFX::Buffer[]. Имеет 50 слотов.
И третий, наверное для скриптов - это CAudioEvent::Buffer. Он самый маленький имеет всего только 4 слота.
Но фишка в том, что в слот может загружатся как целый банк, так и отдельный файл!
А некоторые загружаются ещё в начале игры. Их не нужно загружать повторно.
Моей программой можно посмотреть EventId, BankId, WaveId и прослушать необходимые звуки.
Думаю, если Seemann согласится встроить SFX плагин, то в SB3 мы сможем прослушивать звуки,
не отвлекаясь от написания скриптов. Думаю, что в мой плагин можно будет добавить функцию,
которая возвращает имя аудио события, если в SB3 не будут именованы аудио-константы.
Ладно хватит. А то уже оффтоп пошёл
А, чуть не забыл.
По файловому адресу 004AB490 идут структуры размером 4 байта и в каждой из них номер банка,
положение который соотвествует скриптовым событиям, начиная с 1800 и до 2000.
Это те аудио события, которые загружают банки, а не звуки
352: 1800 310: 1801 296: 1802 291: 1803,1805,1808,1815,1816 // считается как NULL, -1 указывает тоже на этот банк 242: 1804 222: 1806 209: 1807 167: 1809 163: 1810 160: 1811 265: 1812 304: 1813 158: 1814 292: 1817 171: 1818 198: 1819 226: 1820 293: 1821 319: 1822 345: 1823 351: 1824 266: 1825 183: 1826 153: 1827 298: 1828 339: 1829
Кстати, аудио события, начинающие с 1000 и до 1800, это звуки банков, которые доступны только при загрузке банков.
Они проигрываются только с помощью следующих опкодов:
097A: play_at 0.0 0.0 0.0 loaded_audio_event 1132 097B=2,play_audio_at_object %1d% event %2d%
Но некоторые из этих событий не надо загружать, т. к. они уже либо загружены,
либо загружаются и проигрываются вышеупомянутыми опкодами.
Last edited by San'OK (21-07-2007 11:54)
Offline
Некоторую информацию по SA SFX можно почерпнуть отсюда:
http://www.gtaforums.com/index.php?showtopic=225049
http://pdescobar.home.comcast.net/gta/saat/sfx_dir.html
и посмотреть исходники SAAT.
За описания процедур спасибо. Насчет плагина еще обсудим
Edit:
На всякий случай скажу, что 0xC47A0000 = -100.0
Last edited by Seemann (21-07-2007 14:41)
Offline
На закуску:)
018c=4,play_sound %4d% at %1d% %2d% %3d% 097A=4,play_audio_at %1d% %2d% %3d% event %4d% 00507340 CAudioEvent::Buffer::PlayAt (t_coords Coords; int16 EventID)
Покапавшись в ассемблере отличий у этих опкодов не нашёл. Они положи почти как две капли воды. Хотя последний, наверное, должен использоваться после 03CF, который загружает банки.
Я думаю, так будет логичнее их назвать:
018c=4,play_at %1d% %2d% %3d% loaded_audio_event %4d% // проигрывает уже загруженные события (1000..1800) 097A=4,play_at %1d% %2d% %3d% loaded_bank_audio_event %4d% // проигрывает уже загруженные события (1000..1800) из банков (1800..2000) %4d% = -1 // возможно проигрывает последний загруженный звук, но неуверен. Надо проверить в игре.
Edit:
На всякий случай скажу, что 0xC47A0000 = -100.0
Почему -100 ?
(Добавлено) А точно это ж float! Блин, это значит, что в буфере содержутся координаты воспроизведения звука!
...посмотреть исходники SAAT.
Зачем? Я смотрел раньше, но ничего нового я не узнал.
А программа работает нормально? В будущем я ещё и для AudioStreams что-то подобное сделаю.
Last edited by San'OK (21-07-2007 18:43)
Offline
Кстати, может тебе пригодится вот этот кусок кода с одной программки, а то я смотрю у вас невнятно эта структрура описана здесь на форуме:
struct GTASA_SCRIPT_THREAD // 0xE0 bytes total { void* pNext; // 0x00 void* pPrev; // 0x04 char strName[8]; // 0x08 DWORD dwBaseIP; // 0x10 DWORD dwScriptIP; // 0x14 DWORD dwReturnStack[8]; // 0x18 WORD dwStackPointer; // 0x38 DWORD dwLocalVar[34]; // 0x3C BYTE bStartNewScript; // 0xC4 BYTE bJumpFlag; // 0xC5 BYTE bIsMissionThread; // 0xC6 BYTE bIsExternalScript; // 0xC7 BYTE bInMenu; // 0xC8 BYTE bUnknown; // 0xC9 DWORD dwWakeTime; // 0xCC WORD wIfParam; // 0xD0 BYTE bNotFlag; // 0xD2 BYTE bWastedBustedCheck;// 0xD3 BYTE bWastedBustedFlag; // 0xD4 DWORD dwSceneSkipIP; // 0xD8 BYTE bMissionThread; // 0xDC };
Offline
Добавил обновленные списки функций. Ссылки в первом посте.
Offline
Вот, немного покопался в новой базе, сравнивая некоторые функции с исходниками RenderWare'вского "скелета":
файл: renderware/skel/skeleton.c 6193F0 RsKeyFromScanCode 619410 RsTimer 619420 RsWindowSetText 619430 RsPathGetSeparator 619440 RsCameraShowRaster 619460 RsAlwaysOnTop 619470 RsRegisterImageLoader 619480 RsSetDebug 619490 RsMouseSetVisibility 6194A0 RsMouseSetPos 6194B0 RsSelectDevice 6194C0 RsInputDeviceAttach 619510 rsCommandLine 619530 rsPreInitCommandLine 619560 RsKeyboardEventHandler 619580 RsMouseEventHandler 6195A0 RsPadEventHandler 6195E0 RsRwTerminate 6195F0 RsTerminate 619600 RsInitialize 619670 RsSetModelTexturePath 619780 RsSetPresetView 619840 RsSetNextPresetView 619880 RsSetPreviousPresetView 6198C0 RsDestroyPresetViews 619910 CameraElAzPosFromLTM 619AB0 RsGetPresetViewDescription 619AF0 RsGrabScreen 619B00 RsErrorMessage 619B30 RsWarningMessage 619C90 RsRwInitialize 619D60 RsLoadPresetViews 619FA0 RsSavePresetView файл: renderware/skel/win/win.c 7451B0 psWindowSetText (gta_SetWindowText) 7451D0 psErrorMessage (_ShowMessageBoxHand) 7451F0 psWarningMessage 745240 psCameraShowRaster 745270 psTimer 7452B0 psGrabScreen 7453E0 psMouseSetVisibility 7453F0 psMouseSetPos 745500 psPathGetSeparator 745540 psDebugMessageHandler 7458A0 psTerminate 7458B0 psAlwaysOnTop 745CC0 dialogInit 746190 psSelectDevice 747420 psInitialize RW: 802EA0 RwImageSetPath Несколько переменных: dword_C1707C PresetViews dword_C17080 NumPresetViews dword_C92104 ClocksStart_msb dword_C92108 ClocksStart_lsb array_8D2C00 KeysNormal array_8D2D00 KeysShifted struct_8D2E00 Xaxis struct_8D2E0C Yaxis struct_8D2E18 Zaxis string_8D2E24 ViewsFileName dword_8D2E30 CurrentPresetView dword_8D2E34 DefaultVideoMode
PS: в базе RwEngineInstance и RwGlobals представлены как две разных структуры, на самом деле RwEngineInstance это указатель на структуру типа RwGlobals.
PPS: если нужно, могу выложить исходники этого скелетного приложения.:cool:
[large][acronym=Завтра, завтра, постоянно завтра, так проходит жизнь]Cras, cras, semper cras, sic evadit aetas[/acronym][/large]
Offline