View Single Post
  #1  
Old 03-14-2004, 03:40 AM
daeken_bb
Discordant
 
Join Date: Mar 2003
Location: Chambersburg, PA
Posts: 469
Default It's been a while.

Well, it's been a while since I've coded anything related to EQ, so I decided that since I started playing eqlive again, an S3D extractor would be a perfect test for my new unpack_struct() function (hopefully in core php soon).

Code:
<?php
define('NL', chr(10));
$struct_s3d_header = array('offset' => 'long',
                   'magicCookie' => 'char[4]',
                   'unknown' => 'long'); // 12 bytes

$struct_s3d_directory_header = array('count' => 'long'); // 4 bytes

$struct_s3d_directory = array('crc' => 'long',
                              'offset' => 'long',
                              'size' => 'long'); // 12 bytes

$struct_s3d_footer = array('steveCookie' => 'char[5]',
                           'crc' => 'long'); // 9 bytes

$struct_data_block = array('defLen' => 'long',
                           'infLen' => 'long'); // 8 bytes

$struct_fn_header = array('fncount' => 'long'); // 4 bytes

$struct_fn_entry = array('fnlen' => 'long'); // 4 bytes;

if($_SERVER['argc'] > 1) {
  $s3d = &$_SERVER['argv'][1];
}
else {
  echo 'Usage: ', $_SERVER['argv'][0], ' [s3d filename]', NL;
  exit;
}
if(!file_exists($s3d)) {
  echo 'File does not exist.', NL;
  exit;
}
@mkdir($s3d . '_cont');
$file = file_get_contents($s3d);
$header = unpack_struct($struct_s3d_header, $file);
if($header->magicCookie != 'PFS ') {
  echo 'Magic Cookie != `PFS \', dieing.', NL;
  exit;
}
$dhead = unpack_struct($struct_s3d_directory_header, substr($file, $header->offset, 4));
$pos = $header->offset + 4;
$filenames = array();
$files = array();
for($i = 0; $i < $dhead->count; ++$i) {
  $ent = unpack_struct($struct_s3d_directory, substr($file, $pos, 12));
  if($ent->crc == 0x61580AC9) {
    $fnames = substr($file, $ent->offset);
    $fpos = 0;
    $inf = (string) null;
    while(strlen($inf) < $ent->size) {
      $block = unpack_struct($struct_data_block, substr($fnames, $fpos, 8));
      $inf .= gzuncompress(substr($fnames, $fpos + 8, $block->defLen));
      $fpos += 8 + $block->defLen;
    }
    $fnhead = unpack_struct($struct_fn_header, $inf);
    $fpos = 4;
    for($j = 0; $j < $fnhead->fncount; ++$j) {
      $fnent = unpack_struct($struct_fn_entry, substr($inf, $fpos, 4));
      $filenames[] = substr($inf, $fpos + 4, $fnent->fnlen - 1);
      $fpos += 4 + $fnent->fnlen;
    }
    $pos += 12;
    continue;
  }
  $fblock = substr($file, $ent->offset);
  $fpos = 0;
  $inf = (string) null;
  while(strlen($inf) < $ent->size) {
    $block = unpack_struct($struct_data_block, substr($fblock, $fpos, 8));
    $inf .= gzuncompress(substr($fblock, $fpos + 8, $block->defLen));
    $fpos += 8 + $block->defLen;
  }
  $files[$ent->offset] = $inf;
  $pos += 12;
}
if(count($files) != count($filenames)) {
  echo 'Dieing: The count of files and the count of filenames don\'t match.', NL;
  exit;
}
ksort($files);
$i = 0;
foreach($files as $data) {
  $fp = fopen($s3d . '_cont/' . $filenames[$i++], 'wb');
  fwrite($fp, $data);
  fclose($fp);
}
?>
To run the script, use the PHP CLI (NOT CGI!) as follows 'php s3dread.php foobar.s3d' and you'll get a directory called foobar.s3d_cont/ with the content in it.

There's the source for the script itself, and the source for the patched php is available at http://archshadow.com/standard.tar.bz2

Note, I have only tested both of these pieces of code (the new functions and the script itself) on Linux with PHP 4.3.4-dev, with snapshot php4-STABLE-200409101930.

Happy Hacking,
Lord Daeken M. BlackBlade
(Cody Brocious)
__________________
Keep me unemployed and working on OpenEQ, PM me about donating

Check out my deviantART page at http://daeken.deviantart.com/
Reply With Quote