www.allods2.com

Игровой сервер
Текущее время: 28 мар 2024, 16:57

Часовой пояс: UTC + 4 часа




Начать новую тему Ответить на тему  [ Сообщений: 2 ] 
Автор Сообщение
 Заголовок сообщения: разбор а2с
Непрочитанное сообщениеДобавлено: 26 сен 2008, 03:15 
Не в сети
Администратор

Зарегистрирован: 24 июл 2008, 10:19
Сообщения: 211
Код:
#!/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";
   }
}


Вернуться к началу
  Профиль  
 
 Заголовок сообщения: Re: разбор а2с
Непрочитанное сообщениеДобавлено: 26 сен 2008, 03:30 
Не в сети
Администратор

Зарегистрирован: 24 июл 2008, 10:19
Сообщения: 211
Резюмирую:
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 вещи.

Обо всем подробнее код выше расскажет лучше меня. Тем более, я его достаточно подробно откомментировал


Вернуться к началу
  Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 2 ] 

Часовой пояс: UTC + 4 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Русская поддержка phpBB