6 use Services\ISimfileParser
;
7 use Services\IStatusReporter
;
9 class InvalidSmFileException
extends Exception
{}
11 class SimfileParser
implements ISimfileParser
14 private $_smFileLines;
15 private $_statusReporter;
17 public function __construct(IStatusReporter
$statusReporter)
19 $this->_statusReporter
= $statusReporter;
22 public function parse($simfileData)
24 $this->_smFileLines
= explode(";", $simfileData);
27 public function banner()
29 return $this->extractKey('BANNER') ?
: null
;
32 public function title()
34 $title = $this->extractKey('TITLE');
35 if(!$title) throw new InvalidSmFileException('Invalid SM file. TITLE missing');
37 //XXX: UTF8 encode to deal with unusual character that crop up in weeaboo shit.
38 return utf8_encode($title);
41 public function artist()
43 $artist = $this->extractKey('ARTIST');
44 //XXX: Artist can be null
45 if(!$artist)return null
;
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));
51 public function stops()
53 $stops = $this->extractKey('STOPS');
55 //XXX: SM files can be missing stops.
56 //if($stops === false) throw new Exception ('Invalid SM file. STOPS missing');
61 public function fgChanges()
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');
67 return (bool
)$fgChanges;
72 $displayBpm = $this->extractKey('DISPLAYBPM');
76 $bpmRange = explode(":",$displayBpm);
77 $bpmRange[1] = isset($bpmRange[1]) && is_numeric($bpmRange[1]) ?
: $bpmRange[0];
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]))
84 $bpms = $this->extractKey('BPMS');
85 $bpmRange = $this->parseBpms($bpms);
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]);
92 public function bgChanges()
94 $bgChanges = $this->extractKey('BGCHANGES');
96 //XXX: BGChanges can be missing
97 //if($bgChanges === false) throw new Exception ('Invalid SM file. BGCHANGES missing');
99 return (bool
)$bgChanges;
102 public function bpmChanges()
104 $bpms = $this->extractKey('BPMS');
106 //XXX: BPMS can be missing.
107 //if(!$bpms) throw new Exception ('Invalid SM file. BPMS missing');
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];
114 public function subtitle()
116 $subtitle = $this->extractKey('SUBTITLE');
117 if(!$subtitle) return null
;
124 if(empty($this->_smFileLines
)) throw new Exception('SM file data not set.');
127 foreach ($this->_smFileLines
as $line)
129 $pos = strpos($line, '#NOTES:');
132 $noteData = trim(substr($line, $pos +
9));
133 $steps = $this->stepchartFromNoteData($noteData);
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;
141 if(empty($allSteps)) throw new InvalidSmFileException('Invalid Sm file. NOTES missing');
145 private function stepchartFromNoteData($noteData)
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 $stepData[3] <= 18446744073709551615 ?
$stepData[3] : 9999999999999999999
157 private function extractKey($key)
159 //Throw regular exception here, this has nothing to do with the SM file.
160 if(empty($this->_smFileLines
)) throw new Exception('SM file data not set.');
162 foreach ($this->_smFileLines
as $line)
164 $pos = strpos($line, '#' . $key . ':');
165 if ($pos !== false
) return trim(substr($line, $pos +
strlen($key) +
2));
171 private function parseBpms($bpms)
173 $bpms = explode(",", $bpms);
174 $bpmRange = array('high' => null
, 'low' => null
);
176 foreach($bpms as $bpm)
178 $bpmMeasure = explode('=', $bpm);
179 $bpmValue = floatval($bpmMeasure[1]);
181 if(empty($bpmRange['low'])) $bpmRange['low'] = $bpmRange['high'] = $bpmValue;
183 //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
184 //fuck you if your simfile has bpms this high/low what's wrong with you.
185 if($bpmValue <= 9223372036854775807 && $bpmValue >= -9223372036854775808)
187 $bpmRange['high'] = ($bpmValue > $bpmRange['high']) ?
$bpmValue : $bpmRange['high'];
188 $bpmRange['low'] = ($bpmValue < $bpmRange['low']) ?
$bpmValue : $bpmRange['low'];
192 return array($bpmRange['low'], $bpmRange['high']);