<?php
/* UnXZP - XZP File Extractor 
Copyright (C) 2006 Andrew Koester <andrew@neocodenetworks.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

define("XZPVERSION","1.0");

function 
headerMsg() {
    print 
"UnXZP - the XZP File Extractor - Version ".XZPVERSION."\r\n";
    print 
"By Andrew Koester <andrew@neocodenetworks.com>\r\n";
    print 
"http://projects.neocodenetworks.com/unxzp/\r\n";
    print 
"---\r\n";
    print 
"UnXZP version ".XZPVERSION.", Copyright (C) 2006 Andrew Koester\r\n";
    print 
"UnXZP comes with ABSOLUTELY NO WARRANTY. This is free software,\r\n";
    print 
"and you are welcome to redistribute it under certain conditions.\r\n";
    print 
"See http://www.gnu.org/licenses/gpl.txt for details.\r\n";
    print 
"\r\n";
}

headerMsg();

if (
$argc == 1) {
    print 
"Usage:\r\n";
    print 
"unxzp <filename>\r\n";
    print 
" - Extracts XZP file to a directory.\r\n";
    die();
}

$file $argv[1];
$path pathinfo($file);
if ((!
file_exists($file)) || ($path["extension"] != "xzp")) {
    die(
"Invalid XZP archive.");
}

$outdir $path["dirname"]."/".str_replace(".xzp","",$path["basename"])."/";
mkdir($outdir);

$xzpfile fopen($file,"rb");

/* === XZP file info ===
Header (36 Bytes long)

[4] Bytes - ID - 70695A78
[4] Bytes - unknown (Compressed = 1 Decompressed = 6?)
[4] Bytes - Number of Files - Uknown (Related to Total Files)
[4] Bytes - Number of Files Text Listing
[4] Bytes - Unknown
[4] Bytes - Uknown
[4] Bytes - Number of Files - Uknown 2 (Related to Number of File Names)
[4] Bytes - Offset to (Table 2) (Take (Number of Files - Uknown 2) * 12 for size)
[4] Bytes - Table 2 + Text String Table Size



After Header take Number of Files - Uknown + Number of Files Text Listing * 12 for entries
Table 1
[4] Bytes - ID ( This references ID in (Table 2 Uknown))
[4] Bytes - File Size
[4] Bytes - Offset


Table 2 Uknown
[4] Bytes - File ID
[4] Bytes - Filename Offset (string, null terminated)
[4] Bytes - Time created?


Table 3 (Strings begins right after Table2)
String~ Null Terminated

~~~~~~~~~~~~~~~~~~
After Table 3 (strings)
[4] Bytes - File Size (End Of File)
[4] Bytes - 74467A58 Always (might indicate the file has ended)
*/

function NextBinaryInt(&$fileptr) {
    
$tmp unpack("i",fread($fileptr,4));
    return 
$tmp[1];
}

function 
NextBinaryChar(&$fileptr) {
    
$tmp unpack("c",fread($fileptr,1));
    return 
chr($tmp[1]);
}

$header["ID"] = NextBinaryInt($xzpfile);
$header["Unknown"] = NextBinaryInt($xzpfile);
$header["NumFiles"] = NextBinaryInt($xzpfile);
$header["NumFilesText"] = NextBinaryInt($xzpfile);
$header["Unknown2"] = NextBinaryInt($xzpfile);
$header["Unknown3"] = NextBinaryInt($xzpfile);
$header["NumFileNames"] = NextBinaryInt($xzpfile);
$header["FileInfoOffset"] = NextBinaryInt($xzpfile);
$header["FATtableSize"] = NextBinaryInt($xzpfile);

if (
$header["ID"] == "") {
    die(
"Invalid XZP archive.");
}

for (
$x 0$x < ($header["NumFiles"] - 1); $x++) {
    
$id NextBinaryInt($xzpfile);
    
$fat[$id]["FileSize"] = NextBinaryInt($xzpfile);
    
$fat[$id]["Offset"] = NextBinaryInt($xzpfile);
}

fseek($xzpfile,$header["FileInfoOffset"]);
for (
$x 0$x < ($header["NumFiles"] - 1); $x++) {
    
$id NextBinaryInt($xzpfile);
    
$fat[$id]["FilenameOffset"] = NextBinaryInt($xzpfile);
    
$fat[$id]["CreationTime"] = NextBinaryInt($xzpfile);
}

foreach (
$fat as $key => $fileentry) {
    
fseek($xzpfile,$fileentry["FilenameOffset"]);
    
$fat[$key]["Filename"] = "";
    
$temp NextBinaryChar($xzpfile);
    while (
$temp != "\0") {
        
$fat[$key]["Filename"] .= $temp;
        
$temp NextBinaryChar($xzpfile);
    }
}

$end["FileSize"] = NextBinaryInt($xzpfile);
$end["EOF"] = NextBinaryInt($xzpfile);

// Extract files
print "Extracting files...\r\n";
foreach (
$fat as $key => $fileentry) {
    
$dirarray explode("\\",$fileentry["Filename"]);
    
array_pop($dirarray);
    
$n 0;
    foreach (
$dirarray as $part) {
        if (
$n == 0) { $tmp $part; }
        else { 
$tmp $tmp."/".$part; }
        
$tomake $outdir.$tmp;
        if (!
is_dir($tomake)) {
            @
mkdir($tomake);
        }
        
$n++;
    }

    print 
" - Extracting ".$fileentry["Filename"]."... ";
    
fseek($xzpfile,$fileentry["Offset"]);
    
$data fread($xzpfile,$fileentry["FileSize"]);
    print 
"(".$fileentry["FileSize"]." bytes) ";
    
file_put_contents($outdir.$fileentry["Filename"],$data);
    
touch($outdir.$fileentry["Filename"],$fileentry["CreationTime"],$fileentry["CreationTime"]);
    print 
"done.\r\n";
}

?>