Created
April 24, 2024 17:18
-
-
Save kasuganosoras/e0e54c9b7a6f1e5e14e66d8237ccb990 to your computer and use it in GitHub Desktop.
A PHP lib for read & write GTAV GXT2 format files.
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 | |
/** | |
* GXT2 file reader and writer | |
* | |
* @package GXT2 | |
* @version 1.0.0 | |
* @author Akkariin | |
* @license MIT | |
* @link https://github.com/kasuganosoras/ | |
* @example | |
* require_once "gxt2.inc.php"; | |
* $gxt2 = new GXT2("global.gxt2"); | |
* $data = $gxt2->Load(); | |
* if ($data) { | |
* $data["ZERODREAM"] = "ZERODREAM233"; | |
* $newGxt2 = new GXT2("global2.gxt2"); | |
* $newGxt2->Save($data); | |
* } | |
*/ | |
class GXT2 | |
{ | |
private $file; | |
/** | |
* GXT2 constructor. | |
* @param $file | |
*/ | |
public function __construct($file) | |
{ | |
$this->file = fopen($file, "rw+"); | |
} | |
/** | |
* GXT2 destructor | |
*/ | |
public function __destruct() | |
{ | |
fclose($this->file); | |
} | |
/** | |
* Load GXT2 file | |
* @return array|void | |
* @throws Exception | |
*/ | |
public function Load() | |
{ | |
$head = $this->ReadUInt32(); | |
if ($head !== 1196971058) { | |
throw new Exception("Invalid GXT2 file"); | |
return; | |
} | |
$entryCount = unpack("V", fread($this->file, 4))[1]; | |
$textEntries = []; | |
for ($i = 0; $i < $entryCount; $i++) { | |
$hash = $this->ReadUInt32(); | |
$offset = $this->ReadUInt32(); | |
$textEntries[] = [ | |
"hash" => $hash, | |
"offset" => $offset | |
]; | |
} | |
$gxt2 = $this->ReadUInt32(); | |
if ($gxt2 !== 1196971058) { | |
throw new Exception("Invalid GXT2 file"); | |
return; | |
} | |
$endPos = $this->ReadUInt32(); | |
$textList = []; | |
foreach ($textEntries as $entry) { | |
fseek($this->file, $entry["offset"]); | |
$text = ""; | |
while (ftell($this->file) < $endPos) { | |
$char = fread($this->file, 1); | |
if ($char === "\0") { | |
break; | |
} | |
$text .= $char; | |
} | |
$textList[$entry["hash"]] = $text; | |
} | |
return $textList; | |
} | |
/** | |
* Save GXT2 file | |
* @param $textList | |
*/ | |
public function Save($textList) | |
{ | |
$newTextList = []; | |
foreach ($textList as $hash => $text) { | |
if (!is_int($hash)) { | |
$hash = $this->GetHashKey($hash); | |
} | |
$newTextList[$hash] = $text; | |
} | |
$textList = $newTextList; | |
$entryCount = count($textList); | |
$offset = 16 + ($entryCount * 8); | |
$datas = []; | |
fwrite($this->file, pack("V", 1196971058)); | |
fwrite($this->file, pack("V", $entryCount)); | |
foreach ($textList as $hash => $text) { | |
fwrite($this->file, pack("V", $hash)); | |
fwrite($this->file, pack("V", $offset)); | |
$datas[] = [ | |
"hash" => $hash, | |
"text" => $text . "\0" | |
]; | |
$offset += strlen($text) + 1; | |
} | |
fwrite($this->file, pack("V", 1196971058)); | |
fwrite($this->file, pack("V", $offset)); | |
foreach ($datas as $data) { | |
fwrite($this->file, $data["text"]); | |
} | |
} | |
/** | |
* Read UInt32 | |
* @return mixed | |
*/ | |
private function ReadUInt32() | |
{ | |
return unpack("V", fread($this->file, 4))[1]; | |
} | |
/** | |
* Get hash key | |
* @param $name | |
* @param bool $format | |
* @return int|string | |
*/ | |
private function GetHashKey($name, $format = false) | |
{ | |
$num = 0; | |
$byte = unpack('C*', mb_convert_encoding($name, 'UTF-8', 'auto')); | |
for ($i = 1; $i <= count($byte); $i++) { | |
$num += $byte[$i]; | |
$num += $num << 10; | |
$num &= 0xFFFFFFFF; | |
$num ^= $num >> 6; | |
} | |
$num += $num << 3; | |
$num &= 0xFFFFFFFF; | |
$num ^= $num >> 11; | |
$result = ($num + ($num << 15)); | |
$result &= 0xFFFFFFFF; | |
if ($result > 2147483647) { | |
$result -= 4294967296; | |
} | |
return $format ? sprintf("0x%08X", $result) : $result; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment