More biz to help simfiles make it. Better error reporting service.
[rock.divinelegy.git] / Services / ZipParser.php
1 <?php
2
3 namespace Services;
4
5 use Exception;
6 use Domain\Exception\InvalidDifficultyException;
7 use Domain\Exception\InvalidDanceModeException;
8 use ZipArchive;
9 use Services\ISimfileParser;
10 use Services\IBannerExtracter;
11 use Domain\Entities\IFile;
12 use Domain\Entities\StepMania\ISimfileStepByStepBuilder;
13 use Domain\Entities\StepMania\IPackStepByStepBuilder;
14 use Services\IZipParser;
15 use Services\IUserSession;
16 use Services\IStatusReporter;
17 use Services\InvalidSmFileException;
18
19 class ZipParser implements IZipParser
20 {
21 private $_za;
22 private $_smFiles = array();
23 private $_smParser;
24 private $_smBuilder;
25 private $_packBuilder;
26 private $_bannerExtracter;
27 private $_userSession;
28 private $_file;
29 private $_statusReporter;
30
31 public function __construct(
32 ISimfileParser $smParser,
33 ISimfileStepByStepBuilder $smBuilder,
34 IPackStepByStepBuilder $packBuilder,
35 IBannerExtracter $bannerExtracter,
36 IUserSession $userSession,
37 IStatusReporter $statusReporter
38 ) {
39 $this->_smParser = $smParser;
40 $this->_smBuilder = $smBuilder;
41 $this->_packBuilder = $packBuilder;
42 $this->_bannerExtracter = $bannerExtracter;
43 $this->_userSession = $userSession;
44 $this->_statusReporter = $statusReporter;
45 }
46
47 public function parse(IFile $file)
48 {
49 $this->_file = $file;
50 $this->_za = new ZipArchive();
51 //XXX: We assume all files are zips. Should be enforced by validation elsewhere.
52 $res = $this->_za->open('../files/StepMania/' . $file->getHash() . '.zip');
53
54 if($res !== true) throw new Exception ('Could not open zip for reading.');
55 $this->findSms();
56 }
57
58 public function pack()
59 {
60 if(count($this->_smFiles) > 1)
61 {
62 $packname = $this->packNameFromFiles();
63 $banner = $this->_bannerExtracter->extractPackBanner('../files/StepMania/' . $this->_file->getHash() . '.zip', $packname);
64
65 /* @var $builder \Domain\Entities\StepMania\PackStepByStepBuilder */
66 $builder = $this->_packBuilder;
67 return $builder->With_Title($packname)
68 ->With_Uploader($this->_userSession->getCurrentUser())
69 //->With_Simfiles($this->_smFiles)
70 ->With_Simfiles(array())
71 ->With_Banner($banner)
72 ->With_File($this->_file)
73 ->build();
74 }
75 }
76
77 public function simfiles()
78 {
79 return $this->_smFiles;
80 }
81
82 public function isPack()
83 {
84 return count($this->_smFiles) > 1;
85 }
86
87 public function isSingle()
88 {
89 return count($this->_smFiles) == 1;
90 }
91
92 private function findSms()
93 {
94 for($i=0; $i<$this->_za->numFiles; $i++)
95 {
96 $stat = $this->_za->statIndex($i);
97 if(pathinfo($stat['name'], PATHINFO_EXTENSION) == 'sm')
98 {
99 $path = realpath('../files/StepMania/' . $this->_file->getHash() . '.zip');
100 $smData = file_get_contents('zip://' . $path . '#' . $stat['name']);
101 $this->_smFiles[$stat['name']] = $smData;
102 }
103 }
104
105 //XXX: Hack. SmDataToSmClass needs to know whether we are dealing with a pack
106 //or single, but to do that we simply check the number of found sm files. To overcome this
107 //first populate the smFiles array with the raw sm data, then apply SmDataToSmClass on each
108 //array element. This way the check is accurate and the array gets populated as expected.
109 foreach($this->_smFiles as $index => $data)
110 {
111 try
112 {
113 $this->_smFiles[$index] = $this->SmDataToSmClass($data);
114 } catch(Exception $e) {
115 //Exceptions we care about at this stage
116 if(!$e instanceof InvalidSmFileException &&
117 !$e instanceof InvalidDanceModeException &&
118 !$e instanceof InvalidDifficultyException)
119 {
120 throw $e;
121 }
122
123 //Add to messages.
124 $this->_statusReporter->addMessage($e->getMessage() . ' in ' . $index);
125 unset($this->_smFiles[$index]);
126 }
127 }
128 //$this->_smFiles = array_map(array($this, 'SmDataToSmClass'), $this->_smFiles);
129 }
130
131 private function packNameFromFiles()
132 {
133 $packName = '';
134 $smpaths = array_keys($this->_smFiles);
135 foreach($smpaths as $path)
136 {
137 $pathComponents = explode('/', $path);
138
139 if(empty($packName)) $packName = $pathComponents[0];
140
141 if($packName != $pathComponents[0])
142 throw new Exception('Malformed zip. I found more than 1 sm file but the directory structure is not consistent with a pack.');
143 }
144
145 return utf8_encode($packName);
146 }
147
148 private function SmDataToSmClass($smData)
149 {
150 $parser = $this->_smParser;
151 $parser->parse($smData);
152 $banner = $this->_bannerExtracter->extractSongBanner(realpath('../files/StepMania/' . $this->_file->getHash() . '.zip'), $parser->banner());
153 $file = $this->isPack() ? null : $this->_file;
154
155 return $this->_smBuilder->With_Title($parser->title())
156 ->With_Artist($parser->artist())
157 ->With_Uploader($this->_userSession->getCurrentUser()) //obj
158 ->With_BPM($parser->bpm())
159 ->With_BpmChanges($parser->bpmChanges())
160 ->With_Stops($parser->stops())
161 ->With_FgChanges($parser->fgChanges())
162 ->With_BgChanges($parser->bgChanges())
163 ->With_Steps($parser->steps())
164 ->With_Simfile($file)
165 ->With_Banner($banner)
166 ->build();
167 }
168 }