Difference between pages "DBPF Source Code" and "People:Praiodan"

From SC4D Encyclopaedia
(Difference between pages)
Jump to navigation Jump to search
m (1 revision imported)
 
sc4e>CasperVg
(stub instead of expand)
 
Line 1: Line 1:
{{MTS2}}
+
{{stub}}
  
This page contains a sample PHP class to read [[DBPF]] files and extract information about them.  See also [[DBPF Compression]] for more information about compressed parts of DBPF files.
+
{{team|BSC}}
 
 
'''Use this code at your own risk.''' Any comments please use the [[Talk:DBPF Source Code]] page.
 
 
 
==Source Code==
 
<pre>
 
<?
 
 
 
class DBPFfile
 
{
 
 
 
        var $majorVersion;
 
        var $minorVersion;
 
 
 
        var $reserved;
 
 
 
        var $dateCreated;
 
        var $dateModified;
 
 
 
        var $indexMajorVersion;
 
        var $indexMinorVersion;
 
 
 
        var $indexCount;
 
        var $indexOffset;
 
        var $indexSize;
 
 
 
        var $holesCount;
 
        var $holesOffset;
 
        var $holesSize;
 
 
 
        var $reserved2;
 
 
 
        var $indexData;
 
 
 
 
 
        // ** Internally used I/O functions
 
 
 
        // Reads a 4 byte unsigned integer
 
        /*
 
                Used internally by the class to read a C/C++
 
                "unsigned long" (a 4 byte unsigned integer)
 
                from an open file
 
 
 
                $fh - the file handle from which to read
 
                returns - returns the value read; has no error return
 
        */
 
        function read_UL4($fh)
 
        {
 
                $d = fread($fh, 4);
 
                $a = unpack("Vn", $d);
 
                return $a["n"];
 
        }
 
 
 
        // Reads a 2 byte unsigned integer
 
        /*
 
                Used internally by the class to read a C/C++
 
                "unsigned short" (a 2 byte unsigned integer)
 
                from an open file
 
 
 
                $fh - the file handle from which to read
 
                returns - returns the value read; has no error return
 
        */
 
        function read_UL2($fh)
 
        {
 
                $d = fread($fh, 2);
 
                $a = unpack("vn", $d);
 
                return $a["n"];
 
        }
 
 
 
        // Reads a 1 byte unsigned integer
 
        /*
 
                Used internally by the class to read a C/C++
 
                "unsigned char" (a 1 byte unsigned integer)
 
                from an open file
 
 
 
                $fh - the file handle from which to read
 
                returns - returns the value read; has no error return
 
        */
 
        function read_UL1($fh)
 
        {
 
                $d = fread($fh, 1);
 
                $a = unpack("Cn", $d);
 
                return $a["n"];
 
        }
 
 
 
        // Reads a Unicode string of specified length
 
        /*
 
                Used internally by the class to read a C/C++
 
                array of "wchar_t" (a 2 byte character)
 
                from an open file
 
 
 
                $fh - the file handle from which to read
 
                $len - the number of characters to read
 
                returns - returns the value read; has no error return
 
        */
 
        function read_unistr($fh, $len)
 
        {
 
                $s = "";
 
                for($i = 0; $i < $len; ++$i)
 
                {
 
                        $c = fread($fh, 1);
 
                        if(ord($c) != 0) $s .= $c;
 
                        fseek($fh, 1, SEEK_CUR);
 
                }
 
                return $s;
 
        }
 
 
 
        function read_nullstring($fh)
 
        {
 
                $s = "";
 
                $str = fread($fh, 1);
 
                while (ord($str) != 0)
 
                {
 
                        $s .= $str;
 
                        $str = fread($fh, 1);
 
                }
 
 
 
                return $s;
 
 
 
        }
 
 
 
        // Reads a string length and then the string
 
        /*
 
                Used internally by the class to read a 4 byte unsigned integer
 
                and then read a string that many characters in length
 
 
 
                $fh - the file handle from which to read
 
                returns - returns the string read (not the length read); has no error return
 
        */
 
        function read_lenstr($fh)
 
        {
 
                $str_length = $this->read_UL4($fh);
 
                $s = fread($fh, $str_length);
 
                return $s;
 
        }
 
 
 
        // Reads a string length and then the Unicode string
 
        /*
 
                Used internally by the class to read a 4 byte unsigned integer
 
                and then read a Unicode string that many characters in length
 
 
 
                $fh - the file handle from which to read
 
                returns - returns the Unicode string read (not the length read); has no error return
 
        */
 
        function read_lenwstr($fh)
 
        {
 
                $str_length = $this->read_UL4($fh);
 
                $s = $this->read_unistr($fh, $str_length);
 
                return $s;
 
        }
 
 
 
        function strhex($string)
 
        {
 
                $hex="";
 
                for ($i=0;$i<strlen($string);$i++) $hex.=(strlen(dechex(ord($string[$i])))<2)? "0".dechex(ord($string[$i])): dechex(ord($string[$i]));
 
                return $hex;
 
        }
 
 
 
        function hexstr($hex)
 
        {
 
                $string="";
 
                for ($i=0;$i<strlen($hex)-1;$i+=2) $string.=chr(hexdec($hex[$i].$hex[$i+1]));
 
                return $string;
 
        }
 
 
 
        // Reads a string and reverses it, taking into account hex groupings:
 
        /*
 
                for example, 2397586c becomes 6c589723
 
        */
 
        function revhex($string)
 
        {
 
                for($i=0;$i<strlen($string)-1;$i+=2)
 
                {
 
                        $revstring = $string[$i].$string[$i+1].$revstring;
 
                }
 
                return $revstring;
 
        }
 
 
 
        // Decompression function used on string
 
        /*
 
                PHP DBPF decompression by Delphy
 
                Thanks to dmchess (http://hullabaloo.simshost.com/forum/viewtopic.php?t=6578&postdays=0&postorder=asc)
 
                for the Perl code that was used for this.
 
 
 
                $handle - file handle for reading
 
                $len - length of compressed string
 
 
 
        */
 
        function decompress($handle, $len) {
 
 
 
                $buf = '';
 
                $answer = "";
 
                $answerlen = 0;
 
                $numplain = "";
 
                $numcopy = "";
 
                $offset = "";
 
 
 
                for (;$len>0;) {
 
 
 
                        $cc = $this->read_UL1($handle);
 
                        $len -= 1;
 
 
 
                //      echo $cc."<br />\n";
 
 
 
                //      printf("      Control char is %02x, len remaining is %08x. \n",$cc,$len);
 
                        if ($cc >= 252): // 0xFC
 
                                $numplain = $cc & 0x03;
 
                                if ($numplain > $len) { $numplain = $len; }
 
                                //$numplain = $len if ($numplain > $len);
 
                                $numcopy = 0;
 
                                $offset = 0;
 
                        elseif ($cc >= 224): // 0xE0
 
                                $numplain = ($cc - 0xdf) << 2;
 
                                $numcopy = 0;
 
                                $offset = 0;
 
                        elseif ($cc >= 192): // 0xC0
 
                                $len -= 3;
 
 
 
                                /*
 
                                $buf = fread($handle, 1);
 
                                $byte1 = unpack("C", $buf);
 
                                $buf = fread($handle, 1);
 
                                $byte2 = unpack("C", $buf);
 
                                $buf = fread($handle, 1);
 
                                $byte3 = unpack("C", $buf);
 
                                */
 
 
 
                                $byte1 = $this->read_UL1($handle);
 
                                $byte2 = $this->read_UL1($handle);
 
                                $byte3 = $this->read_UL1($handle);
 
 
 
                                $numplain = $cc & 0x03;
 
                                $numcopy = (($cc & 0x0c) <<6) + 5 + $byte3;
 
                                $offset = (($cc & 0x10) << 12 ) + ($byte1 << 8) + $byte2;
 
                        elseif ($cc >= 128): // 0x80
 
                                $len -= 2;
 
 
 
                                /*
 
                                $buf = fread($handle, 1);
 
                                $byte1 = unpack("C", $buf);
 
                                $buf = fread($handle, 1);
 
                                $byte2 = unpack("C", $buf);
 
                                */
 
 
 
                                $byte1 = $this->read_UL1($handle);
 
                                $byte2 = $this->read_UL1($handle);
 
 
 
                                $numplain = ($byte1 & 0xc0) >> 6;
 
                                $numcopy = ($cc & 0x3f) + 4;
 
                                $offset = (($byte1 & 0x3f) << 8) + $byte2;
 
                        else:
 
                                $len -= 1;
 
 
 
                                $byte1 = $this->read_UL1($handle);
 
                                //$buf = fread($handle, 1);
 
                                //$byte1 = unpack("C", $buf);
 
 
 
                                $numplain = ($cc & 0x03);
 
                                $numcopy = (($cc & 0x1c) >> 2) + 3;
 
                                $offset = (($cc & 0x60) << 3) + $byte1;
 
                        endif;
 
 
 
                        #    printf "      plain, copy, offset: $numplain, $numcopy, $offset \n";
 
//                      echo "      plain, copy, offset: $numplain, $numcopy, $offset \n";
 
                        $len -= $numplain;
 
 
 
//                      echo $numplain." -- ".$len."<br />\n";
 
 
 
                        if ($numplain > 0) {
 
                                $buf = fread($handle, $numplain);
 
                                $answer = $answer.$buf;
 
                        }
 
 
 
                        $fromoffset = strlen($answer) - ($offset + 1);  # 0 == last char
 
                        for ($i=0;$i<$numcopy;$i++) {
 
                                $answer = $answer.substr($answer,$fromoffset+$i,1);
 
                        }
 
 
 
                        $answerlen += $numplain;
 
                        $answerlen += $numcopy;
 
                }
 
                //printf "      Answer length is %08x (%08x). \n",$answerlen,length($answer);
 
 
 
                return $answer;
 
        }
 
 
 
        // Loads a DBPF file
 
        /*
 
                $fileName - the name of a file to open
 
                returns - true on success, a string value on error (use $ret === true to test for success or failure)
 
        */
 
        function loadFile($handle, $offset = 0)
 
        {
 
//                $this->m_fileName = $fileName;
 
 
 
//                $handle = fopen($fileName, "rwb");
 
                if($handle)
 
                {
 
 
 
                      echo "Reading from offset: ".$offset."<br />";
 
 
 
                        $start = $offset;
 
if ($offset > 0) {
 
                        fseek($handle, $offset);
 
}
 
 
 
                        $test = fread($handle, 4);  // Should be DBPF
 
 
 
if ($test != "DBPF") { echo $test." - This is not a DBPF file!"; return; }
 
 
 
                        $this->majorVersion = $this->read_UL4($handle);
 
                        $this->minorVersion = $this->read_UL4($handle);
 
 
 
                        $this->reserved = fread($handle, 12);
 
 
 
                        $this->dateCreated = $this->read_UL4($handle);
 
                        $this->dateModified = $this->read_UL4($handle);
 
 
 
                        $this->indexMajorVersion = $this->read_UL4($handle);
 
                        $this->indexCount = $this->read_UL4($handle);
 
                        $this->indexOffset = $this->read_UL4($handle);
 
                        $this->indexSize = $this->read_UL4($handle);
 
 
 
                        $this->holesCount = $this->read_UL4($handle);
 
                        $this->holesOffset = $this->read_UL4($handle);
 
                        $this->holesSize = $this->read_UL4($handle);
 
 
 
                        $this->indexMinorVersion = $this->read_UL4($handle) - 1;
 
 
 
                        $this->reserved2 = fread($handle, 32);
 
 
 
                        $headerEnd = ftell($handle);
 
 
 
                        // Seek to index
 
                        fseek($handle, $offset + $this->indexOffset);
 
// echo ftell($handle)."\n";
 
 
 
                        for ($i=0;$i < $this->indexCount;$i++) {
 
                                $indexData[$i]['typeID'] = $this->revhex($this->strhex(fread($handle, 4)));
 
                                $indexData[$i]['groupID'] = $this->revhex($this->strhex(fread($handle, 4)));
 
                                $indexData[$i]['instanceID'] = $this->revhex($this->strhex(fread($handle, 4)));
 
 
 
if (($this->indexMajorVersion == "7") && ($this->indexMinorVersion == "1")) {
 
                                $indexData[$i]['instanceID2'] = $this->revhex($this->strhex(fread($handle, 4)));
 
}
 
                                $indexData[$i]['offset'] = $this->read_UL4($handle);
 
                                $indexData[$i]['filesize'] = $this->read_UL4($handle);
 
                                $indexData[$i]['compressed'] = false;
 
                                $indexData[$i]['truesize'] = 0;
 
                        }
 
 
 
//echo print_r($indexData, true)."\n";
 
 
 
                        // First check for a DIR resource
 
                        foreach($indexData as $value) {
 
                                if ($value['typeID'] == 'e86b1eef') {
 
                                        //echo "Getting DIR resource of size ".$value['filesize']."... <br />";
 
                                        fseek($handle, $offset + $value['offset']);
 
 
 
                                        // Each record in a Sims 2 file is 20 bytes long, so to get the total number of records we
 
                                        // divide $value['filsize'] by 20.
 
                                        if (($this->indexMajorVersion == "7") && ($this->indexMinorVersion == "1")) {
 
                                                $numRecords = ($value['filesize'] / 20);
 
                                        } else {
 
                                                $numRecords = ($value['filesize'] / 16);
 
                                        }
 
 
 
                                        for ($i=0;$i < $numRecords; $i++) {
 
                                                $typeID = $this->revhex($this->strhex(fread($handle, 4)));
 
                                                $groupID = $this->revhex($this->strhex(fread($handle, 4)));
 
                                                $instanceID = $this->revhex($this->strhex(fread($handle, 4)));
 
if (($this->indexMajorVersion == "7") && ($this->indexMinorVersion == "1")) {
 
                                                $instanceID2 = $this->revhex($this->strhex(fread($handle, 4)));
 
}
 
                                                $filesize_nc = $this->read_UL4($handle);
 
 
 
                                                for ($j=0; $j < $this->indexCount; $j++) {
 
                                                        if ($indexData[$j]['typeID'] == $typeID) {
 
                                                        if ($indexData[$j]['groupID'] == $groupID) {
 
                                                        if ($indexData[$j]['instanceID'] == $instanceID) {
 
if (($this->indexMajorVersion == "7") && ($this->indexMinorVersion == "1")) {
 
                                                        if ($indexData[$j]['instanceID2'] == $instanceID2) {
 
                                                                $indexData[$j]['compressed'] = true;
 
                                                                $indexData[$j]['truesize'] = $filesize_nc;
 
                                                        }
 
} else {
 
                                                        $indexData[$j]['compressed'] = true;
 
                                                                $indexData[$j]['truesize'] = $filesize_nc;
 
}
 
}
 
}
 
}
 
                                                }
 
 
 
                                        }
 
 
 
                                }
 
                        }
 
 
 
                        foreach($indexData as $value) {
 
 
 
                                /* Catalog Description - CTSS */
 
                                if ($value['typeID'] == '43545353') {
 
                                        //echo "Grabbing CTSS file at offset ".$value['offset']." (".($offset + $value['offset']).")...<br />";
 
                                        fseek($handle, $offset + $value['offset']);
 
 
 
                                        fread($handle, 64);
 
 
 
                                        fread($handle, 2); // FormatCode
 
                                        $numStrings = $this->read_UL2($handle);
 
                                        //echo "NumStrings: ".$numStrings."<br />";
 
 
 
                                        $numStrings = 2;
 
                                        for ($j = 0; $j < $numStrings; $j++)
 
                                        {
 
 
 
                                                $stringPairs = $this->read_UL1($handle);
 
                                                //echo "NumStringPairs: ".$stringPairs."<br />";
 
                                                //for ($i = 0; $i < $stringPairs; $i++)
 
                                                //{
 
                                                        $ret .= $this->read_nullstring ($handle);
 
                                                        if ($j == 0) { $ret .= "<br />"; }
 
                                                        echo "...".$ret." - ";
 
                                                        $ret2 = $this->read_nullstring($handle);
 
                                                        //echo $ret2."<br />";
 
                                                //}
 
                                        }
 
 
 
                                }
 
 
 
                                /* Object XML */
 
                                if ($value['typeID'] == 'cca8e925') {
 
                                        if ($value['compressed'] == true) {
 
                                                fseek($handle, $offset + $value['offset']);
 
                                                $dword = $this->read_UL4($handle);
 
                                                //$dword = fread($handle, 4);
 
                                                $data = fread($handle, 5);
 
 
 
                                                //echo $dword."<br /> \n ";
 
                                                //echo $data."<br /> \n ";
 
                                                //echo "decompressing file... <br />";
 
                                                $xmldata =  $this->decompress($handle, $dword - 9);
 
 
 
                                                $objXML = new xml2Array();
 
                                                $arrOutput2 = $objXML->parse($xmldata);
 
 
 
                                                foreach ($arrOutput2[CGZPROPERTYSETSTRING][ANYSTRING] as $value) {
 
                                                        if ($value[KEY] == 'description') {
 
                                                                $ret = $value[DATA];
 
                                                                echo "-- ".$value[DATA]."<br />";
 
                                                        }
 
                                                }
 
 
 
                                        }
 
                                }
 
 
 
 
 
                        }
 
 
 
                        if ($ret == '') {
 
                        foreach ($indexData as $value) {
 
 
 
                                /* String Text Lists - STR# */
 
                                if ($value['typeID'] == '53545223') {
 
                                        echo "Grabbing STR# file at offset ".$value['offset']." (".($offset + $value['offset']).")...\n";
 
                                        fseek($handle, $offset + $value['offset']);
 
 
 
                                        if ($value['compressed'] == true) {
 
                                                fseek($handle, $offset + $value['offset']);
 
                                                $dword = $this->read_UL4($handle);
 
                                                //$dword = fread($handle, 4);
 
                                                $data = fread($handle, 5);
 
 
 
                                                //echo $dword."<br /> \n ";
 
                                                //echo $data."<br /> \n ";
 
                                                //echo "decompressing file... <br />";
 
                                                $data =  $this->decompress($handle, $dword - 9);
 
 
 
                                                // $data now contains the decompressed data so we have to get the string pairs from it
 
 
 
                                                //echo strlen($data)."\n";
 
 
 
//if (strlen($data) > 66) {
 
                                                  // First, chop off the filename of 64 bytes
 
                                                  $data = substr($data, 64);
 
//}
 
 
 
                                                // Next, the FormatCode (2 bytes)
 
                                                $data = substr($data, 2);
 
 
 
//echo $data."\n";
 
 
 
                                                $numStrings = unpack("vn", substr($data, 0, 2));
 
                                                //echo "NumStrings: ".$numStrings."\n";
 
 
 
                                                $numStrings = 2;
 
                                                $soffset = 2;
 
                                                for ($j = 0; $j < $numStrings; $j++)
 
                                                {
 
 
 
if ($soffset < strlen($data)) {
 
 
 
                                                        $languageCode = unpack("Cn", substr($data, $soffset, 1));
 
                                                        $soffset++;
 
 
 
                                                        // Grab string pair
 
                                                        for ($l=0; $l < 2; $l++) {
 
                                                        $k = 0;
 
                                                        for (;$k < 1;) {
 
                                                                $tempstring = substr($data, $soffset, 1);
 
                                                                if (ord($tempstring) != 0) {
 
                                                                        $ret .= $tempstring;
 
                                                                } else {
 
                                                                        $k = 1;
 
                                                                }
 
                                                                $soffset++;
 
                                                        }
 
 
 
                                                        }
 
 
 
                                                        //$ret .= $this->read_nullstring ($handle);
 
                                                        if ($j == 0) { $ret .= "<br />"; }
 
                                                        echo "...".$ret." - ";
 
                                                        //$ret2 = $this->read_nullstring($handle);
 
                                                        //echo $ret2."<br />";
 
}
 
 
 
                                                }
 
                                        } else {
 
                                                fseek($handle, $offset + $value['offset']);
 
 
 
                                                fread($handle, 64);
 
 
 
                                                fread($handle, 2); // FormatCode
 
                                                $numStrings = $this->read_UL2($handle);
 
                                                //echo "NumStrings: ".$numStrings."<br />";
 
 
 
                                                $numStrings = 2;
 
                                                for ($j = 0; $j < $numStrings; $j++)
 
                                                {
 
 
 
                                                        $languageCode = $this->read_UL1($handle);
 
                                                        $ret .= $this->read_nullstring ($handle);
 
                                                        if ($j == 0) { $ret .= "<br />"; }
 
                                                        echo "...".$ret." - ";
 
                                                        $ret2 = $this->read_nullstring($handle);
 
                                                        //echo $ret2."<br />";
 
                                                }
 
 
 
                                        }
 
 
 
                                }
 
 
 
                        } }
 
 
 
 
 
                        return $ret;
 
                }
 
                else
 
                {
 
                        return "Could not open file '" . $fileName . "'";
 
                }
 
 
 
        }
 
 
 
 
 
}
 
 
 
?>
 
</pre>
 
 
 
 
 
[[Category:Modding]]
 
[[Category:MTS2]]
 

Revision as of 11:29, 16 September 2008