www.allods2.com http://archive.allods2.eu/forum/ |
|
разбор а2с http://archive.allods2.eu/forum/viewtopic.php?f=37&t=604 |
Страница 1 из 1 |
Автор: | petRUShka [ 26 сен 2008, 03:15 ] |
Заголовок сообщения: | разбор а2с |
Код: #!/usr/bin/perl
my @arr; my $keysec; my @sps = ( "None", # incorrect spell "Fire Arrow", # огненная стрела "Fire Ball", # огненный шар "Fire Wall", # огненная стена "Protection from Fire", # защита от магии огня "Ice Missile", # ледяная стрела "Poison Cloud", # ядовитый туман "Blizzard", # град "Protection from Water", # защита от магии воды "Acid Stream", # кислотная атака "Lightning", # молния "Prismatic Spray", # радужная молния "Invisibility", # невидимость "Protection from Air", # защита от магии воздуха "Darkness", # тьма "Light", # свечение "Diamond Dust", # каменная стрела "Wall of Earth", # каменная стена "Stone Curse", # каменное проклятие "Protection from Earth", # защита от магии огня "Bless", # благословение "Haste", # ускорение "Control Spirit", # перерождение "Teleport", # телепорт "Heal", # исцеление "Summon", # зов "Drain Life", # вампиризм "Shield", # магический щит "Curse", # проклятие "Slow"); # замедление # Все тоже, что и в предыдущем, только чуть хитрее записано. Считывает данные из некого массива @arr начиная с места $idx, которое передается в качестве параметра sub readil($) { my $idx = shift; $a = @arr[$idx]; $b = @arr[$idx+1]; $c = @arr[$idx+2]; $d = @arr[$idx+3]; return $a | ($b << 8) | ($c << 16) | ($d << 24); } #аналогично, но читает 2 байта sub readwl($) { my $idx = shift; $a = @arr[$idx]; $b = @arr[$idx+1]; return $a | ($b << 8); } #а тут байт sub readbl($) { my $idx = shift; $a = @arr[$idx]; return $a; } #а тут читает из файла 4 байта sub readi($) { my $f = shift; read($f, $a, 1); read($f, $b, 1); read($f, $c, 1); read($f, $d, 1); return ord($a) | (ord($b) << 8) | (ord($c) << 16) | (ord($d) << 24); } #а тут байт из файла sub readb($) { my $f = shift; read($f, $a, 1); return ord($a); } # Расшифровывает каждый байт секции sub decrypt { $keysec = (($keysec >> 0x10) & 0xFFFF); $k = $keysec | ($keysec << 0x10); # print($kh); $c = 0; foreach $i(@arr) { $i = $i & 0xFF; # $ih1 = sprintf("0x%2.2X", $i); $i = (($k >> 0x10) ^ $i) & 0xFF; $k = ($k << 1) & 0xFFFFFFFF; if (($c & 0x0F) == 0x0F) { $k |= $keysec; } $c+=1; # $kh = sprintf("0x%8.8X", $k); # $ih = sprintf("0x%2.2X", $i); # print("$ih1 $ih $kh\n"); } } sub testchecksum { my $ch = 0; foreach $i(@arr) { $ch = (($ch << 1) & 0xFFFFFFFF) + ($i & 0xFF); # print "ch=$ch, i=$i\n"; } return $ch & 0xFFFFFFFF; } sub readpack { #Пак находится в @arr начиная с 9_го байта $idx = 9; $size = scalar(@arr); while ($idx < $size) { #считаем 2_ухбайтный id вещи $itid = readwl($idx); $idx+=2; #этот байт $b0 = readbl($idx); $idx++; $IDh = sprintf("%.4Xh", $itid); if (($b0 & 0x80) == 0x80) { #Если 24_ый бит в $b0 равен 1, то... #...$cc - это $b0 с обнуленным битом и вещь простая - немагическая, а сам $cc - её количество $cc = $b0-0x80; print(" found simple object: ID=$IDh, count=$cc\n"); } elsif (($b0 & 0x20) == 0x20) { #Если иначе 18-ый бит равен 1, то считывается 4 байта - это цена магической вещи $price = readil($idx); $idx+=4; # $ccc - это двухбайтная переменная, младший, байт из 2_ух байт - $b0. $ccc - это количество магий, навешанных на шмотку $ccc = $b0 & 0xF; print(" found magic object: ID=$IDh, price=$price gold\n"); $i = 0; while ($i++ < $ccc) { #считываем магии, висящие на шмотке. Первый байт - код магии (например, сила+), второй ее значние (2, итого сила +2) $eid = readbl($idx); $idx++; $eIDh = sprintf("%.2Xh", $eid); $evl = readbl($idx); $idx++; print(" effect ID=$eIDh, value=$evl\n"); # if ($eid == 0x29 || ($eid >= 0x2C && $eid <=0x30)) { # $ccc--; # } } } elsif (!$b0) { #если $b0 нулевой, то количество простых шмоток лежит в следующих двух байтах $ccc = readwl($idx); $idx+=2; print(" found simple object: ID=$IDh, count=$ccc\n"); } } } $fn = shift; if (!$fn) { $fn = "test.a2c"; } open(C, $fn) || die "Can't open character file: $!!\n"; binmode C; # Считываем 4 байта. По всей видимости, впустую $sig=readi(C); #sisec - сигнатура секции while($sigsec=readi(C) & 0xFFFFFFFF) { #размер секции $sizsec=readi(C) & 0xFFFFFFFF; $keysec=readi(C) & 0xFFFFFFFF; # crc (контрольная сумма секции) $crcsec=readi(C) & 0xFFFFFFFF; $sigh = sprintf("%.8Xh", $sigsec); print("section found: $sigh\n size: $sizsec bytes\n"); #Разбор по типу сигнатуры if ($sigsec == 0xAAAAAAAA) { print(" main:\n"); read(C, $body, $sizsec); # @arr - массив, в который загнали данные секции. @acc = unpack ("c*", $body) означает, что мы побайтно загнали $body в @acc, только и всего @arr = unpack("c*", $body); #расшифруем зашифрованную секцию decrypt(); #считаем id, он лежит в начале секции main $id1 = readil(0); $id2 = readil(4); print(" ID=$id1/$id2\n"); $idxxx = 0; $s = ""; # После, начиная с 12_го байта лежит имя персонажа. имя персонажа кончается байтом со значением в 0 while ($c = readbl(12+$idxxx++)) { $s = $s . chr($c & 0xFF); } print(" nick: '$s'\n"); #проверим контрольную сумму секции if ($crcsec!=testchecksum()) { die "bad chksum!\n"; } } elsif ($sigsec == 0x55555555) { #некая additional секция, где хз какие данные хранятся print(" additional:\n"); read(C, $body, $sizsec); @arr = unpack("c*", $body); decrypt(); if ($crcsec!=testchecksum()) { die "bad chksum!\n"; } } elsif ($sigsec == 0x40A40A40) { #аналогично print(" memory:\n"); read(C, $body, $sizsec); @arr = unpack("c*", $body); decrypt(); if ($crcsec!=testchecksum()) { die "bad chksum!\n"; } } elsif ($sigsec == 0x3A5A3A5A) { # секция, в которой хранится пак персонажа print(" in-the-pack:\n"); read(C, $body, $sizsec); @arr = unpack("c*", $body); decrypt(); if ($crcsec!=testchecksum()) { die "bad chksum!\n"; } readpack(); } elsif ($sigsec == 0xDE0DE0DE) { #аналогично, но хранятся вещи на персонаже print(" on-the-body:\n"); read(C, $body, $sizsec); @arr = unpack("c*", $body); decrypt(); if ($crcsec!=testchecksum()) { die "bad chksum!\n"; } readpack(); } elsif ($sigsec == 0x41392521) { # Секция с характеристиками персонажа $keysec = $keysec >> 0x10; # $keysec - ключ к расшифровке параметров персонажа. Видимо создатели а2 сильно постарались и завязали один параметр на другой, # что б с полпинка не расшивровали. Хз как ленд допер, но факт остается фактом, расшифровка ниже if ($keysec & 0x0001) { $tmp=readb(C);} $monsters_kills=(readi(C) ^ 0x1529251) & 0xFFFFFFFF; if ($keysec & 0x0002) {$tmp=readb(C);} $players_kills=(readi(C)+ $monsters_kills *5+0x13141516) & 0xFFFFFFFF; if ($keysec & 0x0004) {$tmp=readb(C);} $frags = (readi(C) + $players_kills * 7 + 0xABCDEF) & 0xFFFFFFFF; if ($keysec & 0x0008) {$tmp=readb(C);} $deaths = (readi(C) ^ 0x17FF12AA ) & 0xFFFFFFFF; if ($keysec & 0x0010) {$tmp=readb(C);} $money = (readi(C) + $monsters_kills *3 - 0x21524542) & 0xFFFFFFFF; if ($keysec & 0x0020) {$tmp=readb(C);} $body_level = (readb(C) + $money * 0x11 + $monsters_kills * 0x13) & 0xFF; if ($keysec & 0x0040) {$tmp=readb(C);} $reaction_level = (readb(C) + $body_level*3) & 0xFF; if ($keysec & 0x0080) {$tmp=readb(C);} $mind_level = (readb(C) + $body_level + $reaction_level*5) & 0xFF; if ($keysec & 0x0100) {$tmp=readb(C);} $spirit_level = (readb(C) + $body_level * 7 + $mind_level*9) & 0xFF; if ($keysec & 0x4000) {$tmp=readb(C);} $spells = (readi(C) - 0x10121974) & 0xFFFFFFFF; if ($keysec & 0x2000) {$tmp=readb(C);} $active_spell = (readi(C)) & 0xFFFFFFFF; if ($keysec & 0x0200) {$tmp=readb(C);} $exp_fire_blade = (readi(C) ^ 0xDADEDADE) & 0xFFFFFFFF; if ($keysec & 0x0400) {$tmp=readb(C);} $exp_water_axe = (readi(C) - $exp_fire_blade*0x771) & 0xFFFFFFFF; if ($keysec & 0x0800) {$tmp=readb(C);} $exp_air_bludgeon = (readi(C) - $exp_water_axe*0x771) & 0xFFFFFFFF; if ($keysec & 0x1000) {$tmp=readb(C);} $exp_earth_pike = (readi(C) - $exp_air_bludgeon*0x771) & 0xFFFFFFFF; if ($keysec & 0x2000) {$tmp=readb(C);} $exp_astral_shooting = (readi(C) - $exp_earth_pike*0x771) & 0xFFFFFFFF; $idxx = 0; print(" other settings:\n"); print(" spell-list:\n"); foreach $i (@sps) { # Если каждый байт переменной $spell отвечает за выученное заклинание, список которых выше if ($spells & (1 << $idxx)) { print(" $i\n"); } $idxx++; } print(" fire/blade exp: $exp_fire_blade\n"); print(" water/axe exp: $exp_water_axe\n"); print(" air/bludgeon exp: $exp_air_bludgeon\n"); print(" earth/pike exp: $exp_earth_pike\n"); print(" astral/shooting exp: $exp_astral_shooting\n"); } else { die "invalid section's signature!\n"; } } |
Автор: | petRUShka [ 26 сен 2008, 03:30 ] |
Заголовок сообщения: | Re: разбор а2с |
Резюмирую: 1) Файл a2c делится на секции. 2) Секций есть несколько типов (main, additional и т.п.). Тип секции можно определить по первым 4_ем байтам - сигнатура секции 3) После сигнатуры секции идут 4 байта - размер секции, затем 4 байта - ключ разбора секции, следующие 4 байта - crc, контрольная сумма секции. 4) Каждая секция зашифрована в соответствии с ключом секции. Зашифрованная побайтно. Теперь по типам секции: 1) Секция с сигнатурой 0xAAAAAAAA. У ленда названа main, что логично. В ней первые 4 байта id1, вторые 4 байта id2, байты начиная с 12_го - имя персонажа 2) Секция с сигнатурой 0x3A5A3A5A - это пак персонажа 3) Секция с сигнатурой 0xDE0DE0DE - то, что на персонаже - разбор происходит также, как и пака 4) Секция с сигнатурой 0x41392521 - характеристики персонажа. Причем мало того, что секция зашифрована, так и еще и все параметры (деньги, изученные заклинания, количество убитых монстров) жестко повязаны на ключ секции и друг на друга. Это ниваловцы так все шифровали, что бы всякие хитрецы не могли сходу расшифровать формат a2c. Пак персонажа: Пак состоит из подряд идущих данных каждой вещи. Первые два байта - всегда id вещи. Обо всем подробнее код выше расскажет лучше меня. Тем более, я его достаточно подробно откомментировал |
Страница 1 из 1 | Часовой пояс: UTC + 4 часа |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |