Между делом набросал шаблон 010 Editor для работы с опкодами main.scm в Stories.
Эскиз кода (вывод не оформлен, может не хватать нескольких проверок, но логика выдержана):
typedef struct Opcode{
byte btOpcodeByte;
local string sOpcode_1, sOpcode_2, sOpcode;
SPrintf (sOpcode_1, "%x", btOpcodeByte);
if (Strlen(sOpcode_1) == 1) sOpcode_1="0"+sOpcode_1;
byte btOpcodeByte;
SPrintf (sOpcode_2, "%x", btOpcodeByte);
if (Strlen(sOpcode_2) == 1) sOpcode_2="0"+sOpcode_2;
sOpcode=sOpcode_1+sOpcode_2;
local int iParamsCount;
iParamsCount = OpcodeParamsCount_read(sOpcode);
if (iParamsCount!=-1) {
local int i;
for(i=0; i<iParamsCount; i++){
ubyte btParamType;
switch (btParamType){
case 0x01: break; //null
case 0x02: break; //null
case 0x0B: break; //null
case 0x03: //Packed float (1 byte)
<... some code skipped ...>
break;
case 0x04: //Packed float (2 byte)
<... some code skipped ...>
break;
case 0x05: //Packed float (3 byte)
<... some code skipped ...>
break;
case 0x06: //label (4 byte)
<... some code skipped ...>
break;
case 0x07: //Byte
<... some code skipped ...>
break;
case 0x08: //Integer (4 bute)
<... some code skipped ...>
break;
case 0x09: //Float (4 byte)
<... some code skipped ...>
break;
case 0x0A: //String (null-terminated)
<... some code skipped ...>
break;
}
if ((btParamType>=0x0C)&(btParamType<=0x6B)){ //Local Variable
<... some code skipped ...>
}
if ((btParamType>=0xCC)&(btParamType<=0xE5)){ //Global Variable
<... some code skipped ...>
}
if ((btParamType>=0xE6)&(btParamType<=0xFF)){ //Array
<... some code skipped ...>
}
}
}
}
Суть примерно в следующем: сперва читаются два байта опкода (например, 0x01 и 0x00), из них формируется соответствующая строка ("0001"), передаваемая в процедуру получения количества параметров опкода, после чего исходя из полученного значения читаются все параметры - сначала каждый соответствующий байт-определитель типа, а затем и сами величины.
Пример кода:
шестнадцатиричный код | опкод | число параметров | определитель типа | значение | эквивалентный код
01 00 01 | 0x01 0x00 | 1 | 0x01 - автоматически возвращает 0 | auto - 0 | 0001: wait 0
01 00 07 01 | 0x01 0x00 | 1 | 0x07 - целое число, 1 байт | 0x01 - 1 | 0001: wait 1
01 00 08 E8 03 00 00 | 0x01 0x00 | 1 | 0x08 - целое число, 4 байта | 0x03E8 - 1000 | 0001: wait 1000
Процедуру OpcodeParamsCount_read я перенес в отдельный *.bt, поскольку вид она имеет достаточно внушительный. Пример для LCS:
byte OpcodeParamsCount_read (string s){
switch (s) {
case "0001": return 1; break;
case "0002": return 1; break;
case "0003": return 1; break;
case "0004": return 2; break;
case "0005": return 2; break;
<...>
/* ~ 1200 opcodes skipped */
<...>
case "0671": return 3; break;
case "0674": return 1; break;
case "0676": return 1; break;
case "0677": return 2; break;
case "0678": return 2; break;
default: return -1;
};
Раньше мне приходилось действовать методом проб и ошибок, все время дополнительно сверяясь с декомпилированным через Sanny Builder кодом, теперь же работать стало гораздо проще. Достаточно просто запустить чтение структур на конкретном участке кода и можно редактировать.
Тривиальный пример чтения структуры opcode:
FSeek(<оффсет>);
Opcode m_Opcode[<число опкодов>] <optimize=false>;
Можно сделать и что-то чуть более интересное - например, циклически обойти какой-либо поток руководствуясь оффсетом его начала и проверкой на end_thread (0x4E00).