I wrote this yesterday in the evening because I need it for a Project I am working on.
With this PHP Class you can extract official .res files straight from php.
If your .res file format differs from the official one, contact me if you want me to write a custom version for you.
Usage:
PHP Code:
$file = new ResFile("dataSub2.res");
$fileContents = $file->extractFile("propItem.txt.txt");
PHP Code:
<?php
class ResFile {
private $handle;
private $xorKey;
private $fileType;
public $fileCount;
public $files;
function __construct($fileName)
{
$this->handle = fopen($fileName, 'r');
$this->xorKey = $xorKey = $this->readByte($this->handle);
$this->fileType = $this->readByte($this->handle);
$headerSize = $this->readInt32($this->handle);
$headerBuffer = $this->readBytes($this->handle, $headerSize);
for ($i = 0; $i < $headerSize; $i++) {
//header[i] = (byte)((16 * (xorKey ^ (byte)~header[i])) | ((xorKey ^ (byte)~header[i]) >> 4));
$headerBuffer[$i] = unpack("C*", pack("L", ((16 * ($xorKey ^ unpack("C*", pack("L", ~$headerBuffer[$i]))[1])))))[1]
| (($xorKey ^ unpack("C*", pack("L", ~$headerBuffer[$i]))[1]) >> 4);
}
$this->filecount = $headerBuffer[7] + ($headerBuffer[8] << 8);
$currentPosition = 9;
$this->files = array();
for ($i = 0; $i < $this->filecount; $i++) {
$stringLength = $headerBuffer[$currentPosition++] + ($headerBuffer[$currentPosition++] << 8);
$name = implode(array_map("chr", array_slice($headerBuffer, $currentPosition, $stringLength)));
$currentPosition += $stringLength;
$fileSize = $headerBuffer[$currentPosition] +
($headerBuffer[$currentPosition + 1] << 8) +
($headerBuffer[$currentPosition + 2] << 16) +
($headerBuffer[$currentPosition + 3] << 24);
$currentPosition += 4;
$crc = $headerBuffer[$currentPosition] +
($headerBuffer[$currentPosition + 1] << 8) +
($headerBuffer[$currentPosition + 2] << 16) +
($headerBuffer[$currentPosition + 3] << 24);
$currentPosition += 4;
$offset = $headerBuffer[$currentPosition] +
($headerBuffer[$currentPosition + 1] << 8) +
($headerBuffer[$currentPosition + 2] << 16) +
($headerBuffer[$currentPosition + 3] << 24);
$currentPosition += 4;
$this->files[] = array(
"fileName" => $name,
"fileSize" => $fileSize,
"offset" => $offset,
"crc" => $crc
);
}
}
public function extractFile($fileName){
$currentFile = null;
foreach($this->files as $index => $file) {
if (strtolower($file["fileName"]) == strtolower($fileName)) {
$currentFile = $file;
break;
}
}
$returnStr = '';
fseek($this->handle, $currentFile["offset"]);
for($workedBytes = 0; $workedBytes < $currentFile["fileSize"]; $workedBytes+=min(1000, $currentFile["fileSize"] - $workedBytes)) {
$fileBuffer = $this->readBytes($this->handle, min(1000, $currentFile["fileSize"] - $workedBytes));
if ($this->fileType == 1) {
for ($i = 0; $i < min(1000, $currentFile["fileSize"] - $workedBytes); $i++) {
$fileBuffer[$i] = unpack("C*", pack("L", ((16 * ($this->xorKey ^ unpack("C*", pack("L", ~$fileBuffer[$i]))[1])))))[1]
| (($this->xorKey ^ unpack("C*", pack("L", ~$fileBuffer[$i]))[1]) >> 4);
}
}
$returnStr .= implode(array_map("chr", $fileBuffer));
}
return $returnStr;
}
function readBytes($handle, $length)
{
return array_values(unpack("C*", fread($handle, $length)));
}
function readByte($handle)
{
return unpack("C*", fread($handle, 1))[1];
}
function readInt32($handle)
{
$buffer = $this->readBytes($handle, 4);
return ($buffer[3] << 24) +
($buffer[2] << 16) +
($buffer[1] << 8) +
$buffer[0];
}
public function close(){
fclose($this->handle);
}
}






