Помомгите с расшифровкой файла w3g

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
Miniben86
Начинающий
Начинающий
 
Сообщения: 2
Зарегистрирован: 22.12.2008 (Пн) 3:12

Помомгите с расшифровкой файла w3g

Сообщение Miniben86 » 22.12.2008 (Пн) 3:44

Имеется файл формата w3g (это реплей от игры warcraft3). В нём полно необходимой информации, но проблема заключается в том, что этот реплей состоит из частей - заголовок, подзаголовок, и небольшие архивы данных (запакованных zlib'ом). В заголовке содержится информация о размере этих блоков, и начало первого блока (сдвиг).
Мне удалось прочитать заголовок и его подзаголовки, т.е. по байтам читаю начало файла, и не могу понять где точно начало первого блока, не получается разжать блоки zlib'ом. Уже много чего перепробовал, по инструкции к формату файла делал, но не выходит ничего путнего, то не расжимает блоки, то каракули выходят. Руки просто опускаются со всеми этими "сдвигами по байтам" :(
Помогите расшифровать, пример мелкого реплея, пару текстов по формату файла и реализованный на php пример работы (может кто сможет "перевести" с php на vb6).
Очень нужна помощь, посмотрите вложение! :|
Вложения
w3g_format.zip
(425.01 Кб) Скачиваний: 95

Miniben86
Начинающий
Начинающий
 
Сообщения: 2
Зарегистрирован: 22.12.2008 (Пн) 3:12

Re: Помомгите с расшифровкой файла w3g

Сообщение Miniben86 » 22.12.2008 (Пн) 10:04

Вот этот необходимый "момент" на php:
Код: Выделить всё
   // 2.0 [Header]
   function parseheader() {
      $data = fread($this->fp, 48);
      $this->header = @unpack('a28intro/Vheader_size/Vc_size/Vheader_v/Vu_size/Vblocks', $data);
   
      if ($this->header['intro'] != "Warcraft III recorded game\x1A") {
         exit('Not a replay file');
      }
   
      if ($this->header['header_v'] == 0) {
         $data = fread($this->fp, 16);
         $this->header = array_merge($this->header, unpack('vminor_v/vmajor_v/vbuild_v/vflags/Vlength/Vchecksum', $data));
         $this->header['ident'] = 'WAR3';
      } elseif ($this->header['header_v']==1) {
         $data = fread($this->fp, 20);
         $this->header = array_merge($this->header, unpack('a4ident/Vmajor_v/vbuild_v/vflags/Vlength/Vchecksum', $data));
         $this->header['minor_v'] = 0;
         $this->header['ident'] = strrev($this->header['ident']);
      }
   }
   
   function parsedata() {
      fseek($this->fp, $this->header['header_size']);
      $blocks_count = $this->header['blocks'];
      for ($i=0; $i<$blocks_count; $i++) {
         // 3.0 [Data block header]
         $block_header = @unpack('vc_size/vu_size/Vchecksum', fread($this->fp, 8));
         $temp = fread($this->fp, $block_header['c_size']);
         $temp = substr($temp, 2, -4);
         // the first bit must be always set, but already set in replays with modified chatlog (why?)
         $temp{0} = chr(ord($temp{0}) | 1);
         if ($temp = gzinflate($temp)) {
            $this->data .= $temp;
         } else {
            exit($this->filename.': Incomplete replay file');
         }
   
         // 4.0 [Decompressed data]
         if ($i == 0) {
            $this->data = substr($this->data, 4);
            $this->loadplayer();
            $this->loadgame();
         } elseif ($blocks_count - $i < 2) {
            $this->max_datablock = 0;
         }
   
         if ($this->parse_chat || $this->parse_actions) {
            $this->parseblocks();
         } else {
            break;
         }
      }
   }
   


Или вот код на Си:
Код: Выделить всё

TW3RepHeader::TW3RepHeader()
{
    m_loaded = false;
    m_version = 0;
    memset(&Header, 0, sizeof(Header));
    memset(&SubHeader, 0, sizeof(SubHeader));
}

bool TW3RepHeader::Load(FILE * fp)
{
    if(!fp)
        return false;

    if(m_loaded)
        Unload();

    fread(&Header, 48, 1, fp);
    switch(Header.header_version)
    {
        case 0: fread(&SubHeader.v0, 16, 1, fp); m_version = (((WRL_DWord)SubHeader.v0.version1) << 16) + (WRL_DWord)SubHeader.v0.version2; break;
        case 1: fread(&SubHeader.v1, 20, 1, fp); m_version = (((WRL_DWord)SubHeader.v1.version1) << 16) + (WRL_DWord)SubHeader.v1.version2; break;
        default: return false;
    }

    m_loaded = true;
    return true;
}

void TW3RepHeader::Unload()
{
    if(!m_loaded)
        return;
    m_loaded = false;
    memset(&Header, 0, sizeof(Header));
    memset(&SubHeader, 0, sizeof(SubHeader));
    m_version = 0;
}

bool TW3RepHeader::Loaded() const
{
    return m_loaded;
}

const char * TW3RepHeader::GetVersionWRL_String(char * vstring, bool longversion) const
{
    if(longversion)
        sprintf(vstring, "1.%u build %u", m_version, GetBuild());
    else
        sprintf(vstring, "1.0.%u.%u", m_version>>16, m_version & 0xffff, GetBuild());
    return vstring;
}

WRL_DWord TW3RepHeader::GetVersion() const
{
    return m_version;
}

WRL_Word TW3RepHeader::GetBuild() const
{
    switch(Header.header_version)
    {
    case 0: return SubHeader.v0.buildnum;
    case 1: return SubHeader.v1.buildnum;
    }
    return 0;
}

WRL_DWord TW3RepHeader::GetLength() const
{
    switch(Header.header_version)
    {
    case 0: return SubHeader.v0.length;
    case 1: return SubHeader.v1.length;
    }
    return 0;
}

TW3RepGameData::TW3RepGameData()
{
    m_loaded = false;
    m_gamename = NULL;
    m_gameflag = 0;
    m_mapname = NULL;
    m_creator = NULL;
    m_playercount = 0;
    m_gametype = gametype_unknown;
    m_private = false;
}

TW3RepGameData::~TW3RepGameData()
{
    Unload();
}

bool TW3RepGameData::Load(WRL_String &data)
{
    if(m_loaded)
        Unload();
/*
    Read game name
*/
    m_gamename = strdup((const char *)data);
    data += (strlen(m_gamename) + 2);

/*
    Read game flag
*/
    if((*data) & 0x4)
        m_gameflag &= gameflag_hideterrain;
    if((*data) & 0x10)
        m_gameflag &= gameflag_fullshare;
    data ++;
    if((*data) & 0x2)
        m_gameflag &= gameflag_speedfast;
    data ++;
    if((*data) & 0x2)
        m_gameflag &= gameflag_mapexplored;
    if((*data) & 0x4)
        m_gameflag &= gameflag_alwaysvisible;
    if((*data) & 0x8)
        m_gameflag &= gameflag_defaultvisible;
    if((*data) & 0x10)
        m_gameflag &= gameflag_fullobserver;
    if((*data) & 0x20)
        m_gameflag &= gameflag_observerondefeat;
    if((*data) & 0x40)
        m_gameflag &= gameflag_teamtogether;
    data ++;
    if((*data) & 0x2)
        m_gameflag &= gameflag_fixedteams;
    data ++;
    if((*data) & 0x2)
        m_gameflag &= gameflag_randomhero;
    if((*data) & 0x4)
        m_gameflag &= gameflag_randomraces;
    if((*data) & 0x40)
        m_gameflag &= gameflag_referee;
    data += 11;

/*
    Read game map and creator name
*/
    DecryptInfo(data, m_mapname, m_creator);

/*
    Player Count
*/
    m_playercount = *((unsigned int *)data);
    data += 4;

/*
    Game Type
*/
    switch(*data)
    {
    case 0x1: break;
    case 0x9: m_gametype = gametype_custom; break;
    case 0x1D: m_gametype = gametype_single; break;
    case 0x20: m_gametype = gametype_team; break;
    default: m_gametype = gametype_unknown;
    }
    data ++;

/*
    Game Private Flag
*/
    if((*data) & 0x8)
        m_private = true;
    else
        m_private = false;
    data +=7;

    m_loaded = true;
    return true;
}


Пытаюсь это повторить в VB (примерно):
Код: Выделить всё
Open FileName For Binary As #1
    For sdvig = 36 To 33 Step -1 'Читаем байты задом наперед в VB
        Get #1, sdvig, bait
        buf = buf & String((Len(Hex(bait)) - 2) * -1, 0) & Hex(bait) 'что получаем в конечном итоге, перевернув и преобразовав в DEC из HEX
    Next sdvig
    Text1.Text = Text1.Text & "Size: " & ConvertDec(buf) & vbCrLf 'вывод в Text1.text инфо о размере файла из заголовка (он совпадает с реальным)
'...так и далее вынимаем необходимую информацию из заголовка, особенно размер блока, и его начало
    '...достаю первый блок, и...
    'пытаюсь реализовать распаковку блока компонентом (пока лучше вариант не нашел) CompressZIt.
     result = CompressZIt1.DecompressData(Block(), CompressZIt1.OriginalSize)
'...но ничего путнего получается... как сделать проще и правильно - не в курсе...
Close #1


Судя по примерам на других языках - всё сделано достаточно просто, и всё с помошью вложенных циклов (что весь реплей мгновенно весь разбивается на кусочки), вынимается необходимая информация из заголовков, подзаголовков, расжимаются блоки, далее разбираются блоки по частям на загловки и инфу - вот и составляется информация из реплея...

Что пишут в описании формата w3g:
Код: Выделить всё
===============================================================================
3.0 [Data block header]
===============================================================================

Each compressed data block consists of a header followed by compressed data.
The first data block starts at the address denoted in the replay file header.
All following addresses are relative to the start of the data block header.
The decompressed data blocks append to a single continueous data stream
(disregarding the block headers). The content of this stream (see section 4) is
completely independent of the original block boundaries.

offset | size/type | Description
-------+-----------+-----------------------------------------------------------
0x0000 |  1  word  | size n of compressed data block (excluding header)
0x0002 |  1  word  | size of decompressed data block (currently 8k)
0x0004 |  1 dword  | unknown (probably checksum)
0x0008 |  n bytes  | compressed data (decompress using zlib)

To decompress one block with zlib:
1. call 'inflate_init'
2. call 'inflate' with Z_SYNC_FLUSH for the block

The last block is padded with 0 bytes up to the 8K border. These bytes can
be disregarded.

//TODO: add decompression details and move explanation to seperate file


===============================================================================
4.0 [Decompressed data]
===============================================================================

Decompressed data is a collection of data items that appear back to back in
the stream. The offsets for these items vary depending on the size of every
single item.

This section describes the records that always appear at the beginning of
a replay data stream. They hold information about settings and players right
before the start of the game. Data about the game in progress is described
in section 5.

The order of the start up items is as follows:

# |   Size   | Name
---+----------+--------------------------
1 |   4 byte | Unknown (0x00000110 - another record id?)
2 | variable | PlayerRecord (see 4.1)
3 | variable | GameName (null terminated string) (see 4.2)
4 |   6 byte | MapSettings (see 4.3)
5 |  10 byte | MapRecord (see 4.4)
6 | variable | Map&CreatorName  (encrypted null terminated string) (see 4.5)
7 |   4 byte | PlayerCount (see 4.6)
8 |   4 byte | GameType (see 4.7)
9 |   4 byte | LanguageID (see 4.8)
10 | variable | PlayerList (see 4.9)
11 | variable | GameStartRecord (see 4.11)

The following sections describe these items in detail.
After the static items (as described above) there follow variable information
organized in blocks that are described in section 5.


Вернуться в Visual Basic 1–6

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

Сейчас этот форум просматривают: Google-бот и гости: 91

    TopList