| W3C | XHTML 1.0 |
√ |
|---|
| W3C | CSS 3.0 |
√ |
|---|
2008.09.20
Содержимое это страницы сильно устарело и не поддерживается!
Все замечания по поводу содержимого будут игнорироваться.
Страница оставлена исключительно только для истории.
В данной статье будут рассматриваться архивы от игр, т. н. "без сжатия", где куча файлов просто сливается в один (в играх Doom такие архивы остроумно назвали WAD - т.е. "комок").
Распаковать архив обычно не сложно, т.к. почти все они устроены одинаково:
Важно: "FAT архива" или, проще говоря, заголовки файлов, могут шифровать (в играх Max Payne .RAS архивы), чтобы кто-нибудь умный не вытащил. Но если не шифровали сами файлы, то их нетрудно достать. Например, .WAV, .BMP, .AVI - содержат в начале описания свой размер.
В архивах Quake-подобных игр (.PAK, .WAD) раздел-описание файлов находится не в начале, а в конце архива. В начале стоит ссылка (4 байта) на место в файле, с которого начинается раздел-описание файлов.
Рассмотрим формат архива от игры C&C: Generals. Возьмём файл MUSIC.BIG и будем тащить оттуда музыку (заодно и послушаем в удовольствие). Откройте файл каким-нибудь HEX редактором. Смотрим:
Пункты 5-7 повторить пока не пройдёте все файлы в архиве (generals.asc, который распакуется, можете переименовать в .TGA и полюбоваться на него).
--- Листинг программы для вытаскивания музыки из MUSIC.BIG файла ---
(готовую программу можно взять из раздела B3/\OM)
{ Распаковщик BIGF файлов из игр от Electronic Arts }
{ Написана на Delphi }
Program EAUnpack;
{$APPTYPE CONSOLE} { консольное приложение }
Uses SysUtils;
Var
TF, FPos, Sz, FSz, FOffs, I: LongInt;
S, St, Sd: String;
P: Pointer;
Fl, F: File;
B: Byte;
SChar: Char; { Slash char - for NFSU2 }
{ процедура для "разворота" типа LongInt }
Function ReadLong(Var F: File; VIV: Boolean): LongInt;
Var K, Bl: Byte;
L: LongInt;
Begin
L:=0;
If VIV=False Then BlockRead(F, L, 4)
Else
For K:=3 DownTo 0 Do
Begin
BlockRead(F, Bl, 1);
L:=L+(Bl Shl (8*K));
End;
ReadLong:=L;
End;
Begin
WriteLn('EA Unpacker (for BIGF)');
WriteLn('For .BIG (C&C: Generals) / .VIV (Need For Speed)');
WriteLn('T#i$ PR0GR@M bY -=CHE@TER=-');
WriteLn('http://CTPAX-CHEATER.losthost.org');
WriteLn;
If ParamCount<>1 Then
Begin
WriteLn('Usage: eaunpack filename.ext');
Exit;
End;
If FileExists(ParamStr(1))=False Then
Begin
WriteLn('Input file not found!');
Exit;
End;
AssignFile(Fl, ParamStr(1));
Reset(Fl, 1);
SetLength(S, 4);
BlockRead(Fl, S[1], 4); { читаем сигнатуру }
If (S<>'BIGF') And (S<>'BIG4') Then { 'BIG4' for NFSU2 }
Begin
CloseFile(Fl);
WriteLn('This is not BIG/VIV archive!'); { Упс! Это - не BIGF! }
Exit;
End;
Sz:=ReadLong(Fl, False); { Читаем нормальные 4 байта }
If Sz<>FileSize(Fl) Then { Если не равны размеру файла, то пытаемся }
Begin { прочитать как "развёрнутые" }
Seek(Fl, 4);
Sz:=ReadLong(Fl, True);
End;
If Sz<>FileSize(Fl) Then { если и тогда не совпало - то это не BIGF }
Begin
CloseFile(Fl);
WriteLn('This is not BIG/VIV archive!');
Exit;
End;
TF:=ReadLong(Fl, True); { Total Files - количество файлов в архиве }
Seek(Fl, FilePos(Fl)+4);{ Пропускаем 4 байта - размер заголовка }
For I:=1 To TF Do { от 1 до количества_файлов_в_архиве делать: }
Begin
FOffs:=ReadLong(Fl, True); { читаем смещение файла }
FSz:=ReadLong(Fl, True); { читаем размер файла }
S:=''; { чистим строчку }
Repeat
BlockRead(Fl, B, 1); { читаем по байту, пока не встретился 0 }
If B<>0 Then S:=S+Chr(B); { и добавляем в строку формируя имя }
Until B=0;
{ запоминаем текущую позицию в заголовке, чтобы вернуться позже }
FPos:=FilePos(Fl);
St:=S;
{ эта проверка определяет символ-разделитель каталогов (NFSU2) }
If Pos('/', St)<>0 Then SChar:='/' Else SChar:='\';
While (Length(St)>0) And (St[Length(St)]<>SChar) Do
Delete(St, Length(St), 1); { вычленяем путь, без имени файла }
If Length(St)>0 Then
Begin
Sd:='.';
While Length(St)<>0 Do
Begin
{ по очереди удлиняем имя пути ... }
Sd:=Sd+'\'+Copy(St, 1, Pos(SChar,St));
Delete(Sd, Length(Sd), 1);
{ ... и создаём его, если его нет }
If DirectoryExists(Sd)=False Then CreateDir(Sd);
St:=Copy(St, Pos(SChar, St)+1, Length(St));
End;
End;
{ переходим на место в файле-архиве, где начинается файл }
Seek(Fl, FOffs);
GetMem(P, FSz); { выделяем кусок памяти под его размер }
BlockRead(Fl, P^, FSz); { читаем его в память }
AssignFile(F, S); { создаём новый файл }
ReWrite(F, 1);
BlockWrite(F, P^, FSz); { и засовываем туда содержимое буфера }
CloseFile(F);
FreeMem(P, FSz); { чистим память }
Seek(Fl, FPos); { переходим обратно к заголовку, где остановились }
WriteLn(S); { для отчётности выводим имя распакованного файла }
End;
CloseFile(Fl);
WriteLn;
WriteLn('Total files: ', TF); { общее количество распакованных файлов }
End. { всё... }