-
Notifications
You must be signed in to change notification settings - Fork 0
/
Grammar.php
78 lines (69 loc) · 2.26 KB
/
Grammar.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?php
declare(strict_types=1);
namespace GingTeam;
/**
* @author ging-dev <[email protected]>
* @author ciricode <[email protected]>
*/
final class Grammar
{
/**
* @return \Generator<string>
*/
public static function extract(string $filename): \Generator
{
$data = fopen($filename, 'rb');
$num_bytes = fstat($data)['size'];
$beforeIDAT = '';
$png_chunk =
/**
* @param resource $data
*
* @return array{0:string,1:string,2:int}
*/
function ($data) {
$lenword = fread($data, 4);
$length = unpack('N', $lenword)[1];
$type = fread($data, 4);
$chunk_data = $length > 0 ? fread($data, $length) : '';
$crc = fread($data, 4);
$chunk = $lenword.$type.$chunk_data.$crc;
return [$type, $chunk, $length + 12];
};
for ($offset = 0; $offset < $num_bytes; ++$offset) {
fseek($data, $offset);
$four_bytes = fread($data, 4);
// work with .spr case
if ("\x50\x4c\x54\x45" === $four_bytes && '' === $beforeIDAT) {
$offset -= 4;
fseek($data, $offset);
$skip_bytes = 0;
do {
[$type, $chunk, $length] = $png_chunk($data);
$beforeIDAT .= $chunk;
$skip_bytes += $length;
} while ('tRNS' !== $type);
$offset += $skip_bytes;
}
if ("\x49\x48\x44\x52" === $four_bytes) {
$offset -= 4;
fseek($data, $offset);
$png_data = "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a";
$i = $skip_bytes = 0;
do {
[$type, $chunk, $length] = $png_chunk($data);
// Missing PLTE and tRNS
if (1 === $i && 'IDAT' === $type) {
$png_data .= $beforeIDAT;
}
$png_data .= $chunk;
$skip_bytes += $length;
++$i;
} while ('IEND' !== $type);
$offset += $skip_bytes;
yield $png_data;
}
}
fclose($data);
}
}