]> git.localhorst.tv Git - alttp.git/blob - app/Beat/Encoder.php
add emote stats command
[alttp.git] / app / Beat / Encoder.php
1 <?php
2
3 namespace App\Beat;
4
5 class Encoder {
6
7         public function __construct($source) {
8                 $this->source = $source;
9         }
10
11         public function createPatch($target, $metadata = '') {
12                 $this->target = $target;
13                 $this->patch = '';
14                 $this->sourceCursor = 0;
15                 $this->targetCursor = 0;
16
17                 $this->writeString('BPS1');
18                 $this->writeNumber(strlen($this->source));
19                 $this->writeNumber(strlen($this->target));
20                 $this->writeNumber(strlen($metadata));
21                 $this->writeString($metadata);
22
23                 $lastKnownChange = 0;
24                 $targetCopyPos = 0;
25                 while ($this->targetCursor < strlen($this->target)) {
26                         $numUnchanged = 0;
27                         while ($this->sourceLeft($numUnchanged) && $this->sourceEqual($numUnchanged, $numUnchanged, 1)) {
28                                 ++$numUnchanged;
29                         }
30                         if ($numUnchanged > 1 || $numUnchanged == (strlen($this->target) - $this->targetCursor)) {
31                                 $this->writeNumber(($numUnchanged - 1) << 2);
32                                 $this->sourceCursor += $numUnchanged;
33                                 $this->targetCursor += $numUnchanged;
34                         }
35
36                         $numChanged = 0;
37                         if ($lastKnownChange > $this->targetCursor) {
38                                 $numChanged = $lastKnownChange - $this->targetCursor;
39                         }
40                         while ((!$this->sourceLeft($numChanged) || !$this->sourceEqual($numChanged, $numChanged, 3))
41                                 && $this->targetLeft($numChanged)
42                         ) {
43                                 ++$numChanged;
44                                 if (!$this->sourceLeft($numChanged)) {
45                                         $numChanged = strlen($this->target) - $this->targetCursor;
46                                 }
47                         }
48                         $lastKnownChange = $this->targetCursor + $numChanged;
49                         if ($numChanged) {
50                                 $rle1Start = $this->targetCursor == 0 ? 1 : 0;
51                                 while (true) {
52                                         if ($this->targetEqual($rle1Start - 1, $rle1Start, 4)
53                                                 || $this->targetEqual($rle1Start - 2, $rle1Start, 5)
54                                         ) {
55                                                 $numChanged = $rle1Start;
56                                                 break;
57                                         }
58                                         if ($rle1Start + 3 > $numChanged) {
59                                                 break;
60                                         }
61                                         ++$rle1Start;
62                                 }
63                                 if ($numChanged) {
64                                         $this->writeNumber(($numChanged - 1) << 2 | 1);
65                                         $this->writeString(substr($this->target, $this->targetCursor, $numChanged));
66                                         $this->sourceCursor += $numChanged;
67                                         $this->targetCursor += $numChanged;
68                                 }
69                                 if ($this->targetEqual(-2, 0, 3)) {
70                                         $rleLen = 0;
71                                         while ($this->targetLeft($rleLen) && $this->targetEqual(0, $rleLen, 2)) {
72                                                 $rleLen += 2;
73                                         }
74                                         $this->writeNumber(($rleLen - 1) << 2 | 3);
75                                         $this->writeNumber(($this->targetCursor - $targetCopyPos - 2) << 1);
76                                         $this->sourceCursor += $rleLen;
77                                         $this->targetCursor += $rleLen;
78                                         $targetCopyPos = $this->targetCursor - 2;
79                                 } else if ($this->targetEqual(-1, 0, 2)) {
80                                         $rleLen = 0;
81                                         while ($this->targetLeft($rleLen) && $this->targetEqual(0, $rleLen, 1)) {
82                                                 $rleLen += 1;
83                                         }
84                                         $this->writeNumber(($rleLen - 1) << 2 | 3);
85                                         $this->writeNumber(($this->targetCursor - $targetCopyPos - 1) << 1);
86                                         $this->sourceCursor += $rleLen;
87                                         $this->targetCursor += $rleLen;
88                                         $targetCopyPos = $this->targetCursor - 1;
89                                 }
90                         }
91                 }
92
93                 $this->write32(crc32($this->source));
94                 $this->write32(crc32($this->target));
95                 $this->write32(crc32($this->patch));
96
97                 return $this->patch;
98         }
99
100
101         private function sourceChar($offset = 0) {
102                 return $this->source[$this->sourceCursor + $offset];
103         }
104
105         private function sourceEqual($aOff, $bOff, $len) {
106                 $aStr = substr($this->source, $this->sourceCursor + $aOff, $len);
107                 $bStr = substr($this->target, $this->targetCursor + $bOff, $len);
108                 return $aStr == $bStr;
109         }
110
111         private function sourceLeft($num) {
112                 return $this->sourceCursor + $num < strlen($this->source);
113         }
114
115         private function targetChar($offset = 0) {
116                 return $this->target[$this->targetCursor + $offset];
117         }
118
119         private function targetEqual($aOff, $bOff, $len) {
120                 $aStr = substr($this->target, $this->targetCursor + $aOff, $len);
121                 $bStr = substr($this->target, $this->targetCursor + $bOff, $len);
122                 return $aStr == $bStr;
123         }
124
125         private function targetLeft($num) {
126                 return $this->targetCursor + $num < strlen($this->target);
127         }
128
129
130         private function write32($val) {
131                 $this->writeByte($val & 0xFF);
132                 $this->writeByte(($val >> 8) & 0xFF);
133                 $this->writeByte(($val >> 16) & 0xFF);
134                 $this->writeByte(($val >> 24) & 0xFF);
135         }
136
137         private function writeByte($val) {
138                 $this->patch .= chr($val);
139         }
140
141         private function writeNumber($val) {
142                 $tmpval = $val;
143                 for ($i = 0; $i < 16; ++$i) {
144                         $tmpbyte = $tmpval & 0x7f;
145                         $tmpval >>= 7;
146                         if (!$tmpval) {
147                                 $this->writeByte($tmpbyte | 0x80);
148                                 break;
149                         }
150                         $this->writeByte($tmpbyte);
151                         --$tmpval;
152                 }
153         }
154
155         private function writeString($str) {
156                 $this->patch .= $str;
157         }
158
159
160         private $source;
161         private $target = '';
162         private $patch = '';
163
164         private $sourceCursor = 0;
165         private $targetCursor = 0;
166
167 }