Massive number horse shit.
[rock.divinelegy.git] / Services / SimfileParser.php
1 <?php
2
3 namespace Services;
4
5 use Exception;
6 use Services\ISimfileParser;
7 use Services\IStatusReporter;
8
9 class InvalidSmFileException extends Exception{}
10
11 class SimfileParser implements ISimfileParser
12 {
13
14 private $_smFileLines;
15 private $_statusReporter;
16
17 public function __construct(IStatusReporter $statusReporter)
18 {
19 $this->_statusReporter = $statusReporter;
20 }
21
22 public function parse($simfileData)
23 {
24 $this->_smFileLines = explode(";", $simfileData);
25 }
26
27 public function banner()
28 {
29 return $this->extractKey('BANNER') ?: null;
30 }
31
32 public function title()
33 {
34 $title = $this->extractKey('TITLE');
35 if(!$title) throw new InvalidSmFileException('Invalid SM file. TITLE missing');
36
37 //XXX: UTF8 encode to deal with unusual character that crop up in weeaboo shit.
38 return utf8_encode($title);
39 }
40
41 public function artist()
42 {
43 $artist = $this->extractKey('ARTIST');
44 //XXX: Artist can be null
45 if(!$artist)return null;
46
47 //XXX: UTF8 encode to deal with unusual character that crop up in weeaboo shit.
48 return new \Domain\VOs\StepMania\Artist(utf8_encode($artist));
49 }
50
51 public function stops()
52 {
53 $stops = $this->extractKey('STOPS');
54
55 //XXX: SM files can be missing stops.
56 //if($stops === false) throw new Exception ('Invalid SM file. STOPS missing');
57
58 return (bool)$stops;
59 }
60
61 public function fgChanges()
62 {
63 $fgChanges = $this->extractKey('FGCHANGES');
64 //XXX: Looks like fgChanges is allowed to be missing.
65 //if($fgChanges === false) throw new Exception ('Invalid SM file. FGCHANGES missing');
66
67 return (bool)$fgChanges;
68 }
69
70 public function bpm()
71 {
72 $displayBpm = $this->extractKey('DISPLAYBPM');
73
74 if($displayBpm)
75 {
76 $bpmRange = explode(":",$displayBpm);
77 $bpmRange[1] = isset($bpmRange[1]) && is_numeric($bpmRange[1]) ?: $bpmRange[0];
78 }
79
80 //XXX: Originally I had an else statement for this. But turns out some SM's have * as the display bpm
81 //so I just check if we don't have a displayBPM OR the displayBPM is not numeric.
82 if(!$displayBpm || !is_numeric($bpmRange[0]))
83 {
84 $bpms = $this->extractKey('BPMS');
85 $bpmRange = $this->parseBpms($bpms);
86 }
87
88 //XXX: I have nfi why I made the BPM VO high-low instead of low-high in the constructor but yolo
89 return new \Domain\VOs\StepMania\BPM($bpmRange[1], $bpmRange[0]);
90 }
91
92 public function bgChanges()
93 {
94 $bgChanges = $this->extractKey('BGCHANGES');
95
96 //XXX: BGChanges can be missing
97 //if($bgChanges === false) throw new Exception ('Invalid SM file. BGCHANGES missing');
98
99 return (bool)$bgChanges;
100 }
101
102 public function bpmChanges()
103 {
104 $bpms = $this->extractKey('BPMS');
105
106 //XXX: BPMS can be missing.
107 //if(!$bpms) throw new Exception ('Invalid SM file. BPMS missing');
108
109 $bpmRange = $this->parseBpms($bpms);
110 //XXX: We have bpm changes when the high and low bpms are different.
111 return $bpmRange[0] != $bpmRange[1];
112 }
113
114 public function subtitle()
115 {
116 $subtitle = $this->extractKey('SUBTITLE');
117 if(!$subtitle) return null;
118
119 return $subtitle;
120 }
121
122 function steps()
123 {
124 if(empty($this->_smFileLines)) throw new Exception('SM file data not set.');
125 $allSteps = array();
126
127 foreach ($this->_smFileLines as $line)
128 {
129 $pos = strpos($line, '#NOTES:');
130 if ($pos !== false)
131 {
132 $noteData = trim(substr($line, $pos + 9));
133 $steps = $this->stepchartFromNoteData($noteData);
134
135 //XXX: Sometimes we get a cabinet lights chart, those return false for getGame.
136 //We don't want to store cabinet lights, so just ignore it.
137 if($steps->getMode()->getGame()) $allSteps[] = $steps;
138 }
139 }
140
141 if(empty($allSteps)) throw new InvalidSmFileException('Invalid Sm file. NOTES missing');
142 return $allSteps;
143 }
144
145 private function stepchartFromNoteData($noteData)
146 {
147 $stepData = array_map('trim', explode(':', $noteData));
148 return new \Domain\VOs\StepMania\StepChart(
149 new \Domain\VOs\StepMania\DanceMode($stepData[0]),
150 new \Domain\VOs\StepMania\Difficulty($stepData[2]),
151 empty($stepData[1]) ? null : new \Domain\VOs\StepMania\StepArtist(utf8_encode($stepData[1])),
152 //XXX: Fuck you whoever made me do this. http://dev.mysql.com/doc/refman/5.5/en/integer-types.html
153 //XXX: Originally I was using MySQL unsigned bigint max value, but PHP does not have unsigned ints so
154 $stepData[3] <= 9223372036854775807 ? $stepData[3] : 9223372036854775807
155 );
156 }
157
158 private function extractKey($key)
159 {
160 //Throw regular exception here, this has nothing to do with the SM file.
161 if(empty($this->_smFileLines)) throw new Exception('SM file data not set.');
162
163 foreach ($this->_smFileLines as $line)
164 {
165 $pos = strpos($line, '#' . $key . ':');
166 if ($pos !== false) return trim(substr($line, $pos + strlen($key) + 2));
167 }
168
169 return false;
170 }
171
172 private function parseBpms($bpms)
173 {
174 $bpms = explode(",", $bpms);
175 $bpmRange = array('high' => null, 'low' => null);
176
177 foreach($bpms as $bpm)
178 {
179 $bpmMeasure = explode('=', $bpm);
180 $bpmValue = floatval($bpmMeasure[1]);
181
182 if(empty($bpmRange['low'])) $bpmRange['low'] = $bpmRange['high'] = $bpmValue;
183
184 //XXX: Anything bigger or smaller than this cannot be stored by MySQLs bigint type http://dev.mysql.com/doc/refman/5.5/en/integer-types.html
185 //fuck you if your simfile has bpms this high/low what's wrong with you.
186 if($bpmValue <= 9223372036854775807 && $bpmValue >= -9223372036854775808)
187 {
188 $bpmRange['high'] = ($bpmValue > $bpmRange['high']) ? $bpmValue : $bpmRange['high'];
189 $bpmRange['low'] = ($bpmValue < $bpmRange['low']) ? $bpmValue : $bpmRange['low'];
190 }
191 }
192
193 return array($bpmRange['low'], $bpmRange['high']);
194 }
195 }