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. { всё... }