LTEXReader::readData()   A
last analyzed

Complexity

Conditions 4
Paths 2

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 13
nc 2
nop 0
dl 0
loc 26
rs 9.8333
c 0
b 0
f 0
1
<?php
2
/**
3
 * Class LTEXReader
4
 *
5
 * @link         https://arctium.io/wiki/index.php?title=Locale_Lookup_Index_(.bin)
6
 *
7
 * @filesource   LTEXReader.php
8
 * @created      05.01.2019
9
 * @package      codemasher\WildstarDB
10
 * @author       smiley <[email protected]>
11
 * @copyright    2019 smiley
12
 * @license      MIT
13
 */
14
15
namespace codemasher\WildstarDB\Archive;
16
17
use codemasher\WildstarDB\WSDBException;
18
use function array_fill;
19
use function fread;
20
use function fseek;
21
use function ftell;
22
use function unpack;
23
24
/**
25
 * @property string $prettyname
26
 */
27
final class LTEXReader extends ReaderAbstract{
28
29
	// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c
30
	private const LCID = [
31
		0x0407 => 'de-DE', // 1031
32
		0x0409 => 'en-US', // 1033
33
		0x040C => 'fr-FR', // 1036
34
		0x0412 => 'ko-KR', // 1042
35
	];
36
37
	/**
38
	 * @var string
39
	 * @internal
40
	 */
41
	protected $FORMAT_HEADER = 'a4Signature/LVersion/LLanguage/LLCID/QTagNameStringLength/QTagNameStringPtr'.
42
	                           '/QShortNameStringLength/QShortNameStringPtr/QLongNameStringLength/QLongNameStringPtr'.
43
	                           '/QEntryCount/QEntryIndexPtr/QNameStoreLength/QNameStorePtr';
44
45
	/**
46
	 * @var string
47
	 */
48
	protected $prettyname;
49
50
	/**
51
	 * @param string $filename
52
	 *
53
	 * @return \codemasher\WildstarDB\Archive\ReaderInterface
54
	 * @throws \codemasher\WildstarDB\WSDBException
55
	 */
56
	public function read(string $filename):ReaderInterface{
57
		$this->loadFile($filename);
58
59
		if($this->header['Signature'] !== "\x58\x45\x54\x4c"){ // XETL
60
			throw new WSDBException('invalid LTEX');
61
		}
62
63
		fseek($this->fh, $this->headerSize + $this->header['LongNameStringPtr']);
64
65
		$this->prettyname = $this->decodeString(fread($this->fh, $this->header['LongNameStringLength'] * 2));
66
		$this->name       = 'LocalizedText_'.$this::LCID[$this->header['LCID']];
67
		$this->cols       = [
68
			['name' => 'ID',            'header' => ['DataType' =>   3]],
69
			['name' => 'LocalizedText', 'header' => ['DataType' => 130]],
70
		];
71
72
		$this->readData();
73
74
		$this->logger->info($this->prettyname.' ('.$this->header['LCID'].', '.$this::LCID[$this->header['LCID']].'), rows: '.$this->header['EntryCount']);
75
76
		return $this;
77
	}
78
79
	/**
80
	 * @return void
81
	 */
82
	private function readData():void{
83
		fseek($this->fh, $this->headerSize + $this->header['EntryIndexPtr']);
84
85
		$this->data = array_fill(0, $this->header['EntryCount'], null);
86
87
		foreach($this->data as $i => $_){
88
			// get the id and offset for the data block
89
			$c = unpack('Lid/Loffset', fread($this->fh, 8));
90
			// save the current position
91
			$p = ftell($this->fh);
92
93
			// seek forward to the data block
94
			fseek($this->fh, $this->headerSize + $this->header['NameStorePtr'] + $c['offset'] * 2);
95
96
			$v = '';
97
			// read until we hit a double nul or the void
98
			do{
99
				$s = fread($this->fh, 2);
100
				$v .= $s;
101
			}
102
			while($s !== "\x00\x00" && $s !== '');
103
104
			$this->data[$i] = ['ID' => $c['id'], 'LocalizedText' => $this->decodeString($v)];
105
106
			// restore the previous position
107
			fseek($this->fh, $p);
108
		}
109
110
	}
111
112
}
113