Created
September 11, 2016 15:16
-
-
Save TorbenKoehn/6715a0c419815ffa1e2f0fde72955892 to your computer and use it in GitHub Desktop.
A class to quickly read Ragnarok Online's GRF Format and extract files from it. Supports version 0x200 and unencrypted files only.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class FastGrf | |
{ | |
private $files; | |
private $path; | |
private $handle; | |
public function __construct($path) | |
{ | |
$this->files = []; | |
$this->path = $path; | |
$this->handle = fopen($path, 'rb'); | |
//Jump to important info directly | |
fseek($this->handle, 30); | |
list($ftOffset, $seed, $fCount) = array_values(unpack('VftOffset/Vseed/VfCount', fread($this->handle, 12))); | |
//Jump to FT offset | |
fseek($this->handle, $ftOffset + 46); | |
//Unpack FT | |
$cSize = unpack('V', fread($this->handle, 4))[1]; | |
fseek($this->handle, 4, SEEK_CUR); | |
//Read the compressed file table | |
$cft = fread($this->handle, $cSize); | |
//Decompress table (Not using the second parameter, it leads to insufficient memory errors :() | |
$ft = @zlib_decode($cft); | |
$cft = null; | |
$fCount = $fCount - $seed - 7; | |
for ($i = 0, $o = 0; $i < $fCount; $i++) { | |
//read filename until 0x00 | |
$path = ''; | |
$null = chr(0x00); | |
while (($char = substr($ft, $o, 1)) !== $null) { | |
$path .= $char; | |
$o++; | |
} | |
$o++; | |
$path = mb_convert_encoding($path, 'UTF-8', 'EUC-KR'); | |
list($cSize, $aSize, $size, $flags, $offset) = array_values(unpack( | |
'VcSize/VaSize/Vsize/Cflags/Voffset', | |
substr($ft, $o, 17) | |
)); | |
$o += 17; | |
$file = [ | |
'cSize' => $cSize, | |
'aSize' => $aSize, | |
'size' => $size, | |
'flags' => $flags, | |
'offset' => $offset | |
]; | |
$file['crc32'] = sprintf('%u', crc32(implode(':', [$path, $file['size']]))); | |
$this->files[$path] = $file; | |
} | |
$ft = null; | |
} | |
public function close() | |
{ | |
if (is_resource($this->handle)) | |
fclose($this->handle); | |
} | |
public function getFiles() | |
{ | |
return $this->files; | |
} | |
/** | |
* @param $path | |
* @return bool | |
*/ | |
public function hasFile($path) | |
{ | |
return isset($this->files[$path]); | |
} | |
/** | |
* @param $path | |
* @return array | |
*/ | |
public function getFileInfo($path) | |
{ | |
return $this->files[$path]; | |
} | |
public function getFileHandle($path) | |
{ | |
$fInfo = $this->files[$path]; | |
if ($fInfo['flags'] !== 1) | |
throw new \RuntimeException( | |
"Encrypted GRF file $path. Unencrypt prior to using it." | |
); | |
$fp = fopen('php://memory', 'rb+'); | |
fseek($this->handle, $fInfo['offset'] + 46); | |
$cContent = fread($this->handle, $fInfo['aSize']); | |
$content = zlib_decode($cContent); | |
$cContent = null; | |
fwrite($fp, $content); | |
fseek($fp, 0, SEEK_SET); | |
$content = null; | |
return $fp; | |
} | |
public function getFileTextContent($path) | |
{ | |
$content = stream_get_contents($this->getFileHandle($path)); | |
return mb_convert_encoding($content, 'UTF-8', 'EUC-KR'); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment