xref: /plugin/skillforge/classes/StoredZipWriter.php (revision 1729dd472fd5524951126802d13bd2e76b84b680) !
1<?php
2/**
3 * Minimal ZIP writer using the STORE method only.
4 * This avoids requiring ext-zip or PclZip. Suitable for generated Markdown packages.
5 */
6class SkillForge_StoredZipWriter {
7    private $entries = array();
8
9    public function addFileFromString($path, $data) {
10        $path = str_replace('\\', '/', trim($path, '/'));
11        if ($path === '') return;
12        $this->entries[] = array('path' => $path, 'data' => (string)$data);
13    }
14
15    public function addFile($pathInZip, $filePath) {
16        if (is_readable($filePath)) {
17            $this->addFileFromString($pathInZip, file_get_contents($filePath));
18        }
19    }
20
21    public function save($targetFile) {
22        $fh = fopen($targetFile, 'wb');
23        if (!$fh) return false;
24
25        $central = '';
26        $offset = 0;
27
28        foreach ($this->entries as $entry) {
29            $name = $entry['path'];
30            $data = $entry['data'];
31            $crc = crc32($data);
32            if ($crc < 0) $crc += 4294967296;
33            $size = strlen($data);
34            list($time, $date) = $this->dosDateTime();
35
36            $local = "PK\x03\x04";
37            $local .= pack('v', 20); // version needed
38            $local .= pack('v', 0);  // flags
39            $local .= pack('v', 0);  // compression: store
40            $local .= pack('v', $time);
41            $local .= pack('v', $date);
42            $local .= pack('V', $crc);
43            $local .= pack('V', $size);
44            $local .= pack('V', $size);
45            $local .= pack('v', strlen($name));
46            $local .= pack('v', 0);
47            $local .= $name;
48
49            fwrite($fh, $local);
50            fwrite($fh, $data);
51
52            $central .= "PK\x01\x02";
53            $central .= pack('v', 20); // version made by
54            $central .= pack('v', 20); // version needed
55            $central .= pack('v', 0);
56            $central .= pack('v', 0);
57            $central .= pack('v', $time);
58            $central .= pack('v', $date);
59            $central .= pack('V', $crc);
60            $central .= pack('V', $size);
61            $central .= pack('V', $size);
62            $central .= pack('v', strlen($name));
63            $central .= pack('v', 0); // extra length
64            $central .= pack('v', 0); // comment length
65            $central .= pack('v', 0); // disk start
66            $central .= pack('v', 0); // internal attrs
67            $central .= pack('V', 0); // external attrs
68            $central .= pack('V', $offset);
69            $central .= $name;
70
71            $offset += strlen($local) + $size;
72        }
73
74        $centralOffset = $offset;
75        fwrite($fh, $central);
76        $centralSize = strlen($central);
77
78        $end = "PK\x05\x06";
79        $end .= pack('v', 0);
80        $end .= pack('v', 0);
81        $end .= pack('v', count($this->entries));
82        $end .= pack('v', count($this->entries));
83        $end .= pack('V', $centralSize);
84        $end .= pack('V', $centralOffset);
85        $end .= pack('v', 0);
86        fwrite($fh, $end);
87        fclose($fh);
88        return true;
89    }
90
91    private function dosDateTime() {
92        $t = getdate();
93        $time = (($t['hours'] & 0x1f) << 11) | (($t['minutes'] & 0x3f) << 5) | (($t['seconds'] / 2) & 0x1f);
94        $date = ((($t['year'] - 1980) & 0x7f) << 9) | (($t['mon'] & 0x0f) << 5) | ($t['mday'] & 0x1f);
95        return array($time, $date);
96    }
97}
98