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