Mercurial > repos > blastem
comparison zip.c @ 1534:c59adc305e46 nuklear_ui
Merge
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 24 Mar 2018 22:18:23 -0700 |
parents | 092675db4f37 |
children | 7121daaa48c2 |
comparison
equal
deleted
inserted
replaced
1528:855210dca5b9 | 1534:c59adc305e46 |
---|---|
1 #include <stdio.h> | |
2 #include <stdlib.h> | |
3 #include <stddef.h> | |
4 #include <string.h> | |
5 #include "util.h" | |
6 #include "zip.h" | |
7 #ifndef DISABLE_ZLIB | |
8 #include "zlib/zlib.h" | |
9 #endif | |
10 | |
11 static const char cdfd_magic[4] = {'P', 'K', 1, 2}; | |
12 static const char eocd_magic[4] = {'P', 'K', 5, 6}; | |
13 #define MIN_EOCD_SIZE 22 | |
14 #define MIN_CDFD_SIZE 46 | |
15 #define ZIP_MAX_EOCD_OFFSET (64*1024+MIN_EOCD_SIZE) | |
16 | |
17 enum { | |
18 ZIP_STORE = 0, | |
19 ZIP_DEFLATE = 8 | |
20 }; | |
21 | |
22 zip_file *zip_open(char *filename) | |
23 { | |
24 FILE *f = fopen(filename, "rb"); | |
25 if (!f) { | |
26 return NULL; | |
27 } | |
28 long fsize = file_size(f); | |
29 if (fsize < MIN_EOCD_SIZE) { | |
30 //too small to be a zip file | |
31 goto fail; | |
32 } | |
33 | |
34 long max_offset = fsize > ZIP_MAX_EOCD_OFFSET ? ZIP_MAX_EOCD_OFFSET : fsize; | |
35 fseek(f, -max_offset, SEEK_END); | |
36 uint8_t *buf = malloc(max_offset); | |
37 if (max_offset != fread(buf, 1, max_offset, f)) { | |
38 goto fail; | |
39 } | |
40 | |
41 long current_offset; | |
42 uint32_t cd_start, cd_size; | |
43 uint16_t cd_count; | |
44 for (current_offset = max_offset - MIN_EOCD_SIZE; current_offset >= 0; current_offset--) | |
45 { | |
46 if (memcmp(eocd_magic, buf + current_offset, sizeof(eocd_magic))) { | |
47 continue; | |
48 } | |
49 uint16_t comment_size = buf[current_offset + 20] | buf[current_offset + 21] << 8; | |
50 if (comment_size != (max_offset - current_offset - MIN_EOCD_SIZE)) { | |
51 continue; | |
52 } | |
53 cd_start = buf[current_offset + 16] | buf[current_offset + 17] << 8 | |
54 | buf[current_offset + 18] << 16 | buf[current_offset + 19] << 24; | |
55 if (cd_start > (fsize - (max_offset - current_offset))) { | |
56 continue; | |
57 } | |
58 cd_size = buf[current_offset + 12] | buf[current_offset + 13] << 8 | |
59 | buf[current_offset + 14] << 16 | buf[current_offset + 15] << 24; | |
60 if ((cd_start + cd_size) > (fsize - (max_offset - current_offset))) { | |
61 continue; | |
62 } | |
63 cd_count = buf[current_offset + 10] | buf[current_offset + 11] << 8; | |
64 break; | |
65 } | |
66 free(buf); | |
67 if (current_offset < 0) { | |
68 //failed to find EOCD | |
69 goto fail; | |
70 } | |
71 buf = malloc(cd_size); | |
72 fseek(f, cd_start, SEEK_SET); | |
73 if (cd_size != fread(buf, 1, cd_size, f)) { | |
74 goto fail_free; | |
75 } | |
76 zip_entry *entries = calloc(cd_count, sizeof(zip_entry)); | |
77 uint32_t cd_max_last = cd_size - MIN_CDFD_SIZE; | |
78 zip_entry *cur_entry = entries; | |
79 for (uint32_t off = 0; cd_count && off <= cd_max_last; cur_entry++, cd_count--) | |
80 { | |
81 if (memcmp(buf + off, cdfd_magic, sizeof(cdfd_magic))) { | |
82 goto fail_entries; | |
83 } | |
84 uint32_t name_length = buf[off + 28] | buf[off + 29] << 8; | |
85 uint32_t extra_length = buf[off + 30] | buf[off + 31] << 8; | |
86 //TODO: verify name length doesn't go past end of CD | |
87 | |
88 cur_entry->name = malloc(name_length + 1); | |
89 memcpy(cur_entry->name, buf + off + MIN_CDFD_SIZE, name_length); | |
90 cur_entry->name[name_length] = 0; | |
91 | |
92 cur_entry->compressed_size = buf[off + 20] | buf[off + 21] << 8 | |
93 | buf[off + 22] << 16 | buf[off + 23] << 24; | |
94 cur_entry->size = buf[off + 24] | buf[off + 25] << 8 | |
95 | buf[off + 26] << 16 | buf[off + 27] << 24; | |
96 | |
97 cur_entry->local_header_off = buf[off + 42] | buf[off + 43] << 8 | |
98 | buf[off + 44] << 16 | buf[off + 45] << 24; | |
99 | |
100 cur_entry->compression_method = buf[off + 10] | buf[off + 11] << 8; | |
101 | |
102 off += name_length + extra_length + MIN_CDFD_SIZE; | |
103 } | |
104 | |
105 zip_file *z = malloc(sizeof(zip_file)); | |
106 z->entries = entries; | |
107 z->file = f; | |
108 z->num_entries = cur_entry - entries; | |
109 return z; | |
110 | |
111 fail_entries: | |
112 for (cur_entry--; cur_entry >= entries; cur_entry--) | |
113 { | |
114 free(cur_entry->name); | |
115 } | |
116 free(entries); | |
117 fail_free: | |
118 free(buf); | |
119 fail: | |
120 fclose(f); | |
121 return NULL; | |
122 } | |
123 | |
124 uint8_t *zip_read(zip_file *f, uint32_t index, size_t *out_size) | |
125 { | |
126 | |
127 fseek(f->file, f->entries[index].local_header_off + 26, SEEK_SET); | |
128 uint8_t tmp[4]; | |
129 if (sizeof(tmp) != fread(tmp, 1, sizeof(tmp), f->file)) { | |
130 return NULL; | |
131 } | |
132 uint32_t local_variable = (tmp[0] | tmp[1] << 8) + (tmp[2] | tmp[3] << 8); | |
133 fseek(f->file, f->entries[index].local_header_off + local_variable + 30, SEEK_SET); | |
134 | |
135 size_t int_size; | |
136 if (!out_size) { | |
137 out_size = &int_size; | |
138 int_size = f->entries[index].size; | |
139 } | |
140 | |
141 uint8_t *buf = malloc(*out_size); | |
142 if (*out_size > f->entries[index].size) { | |
143 *out_size = f->entries[index].size; | |
144 } | |
145 switch(f->entries[index].compression_method) | |
146 { | |
147 case ZIP_STORE: | |
148 if (*out_size != fread(buf, 1, *out_size, f->file)) { | |
149 free(buf); | |
150 return NULL; | |
151 } | |
152 break; | |
153 #ifndef DISABLE_ZLIB | |
154 case ZIP_DEFLATE: { | |
155 //note in unzip.c in zlib/contrib suggests a dummy byte is needed, so we allocate an extra byte here | |
156 uint8_t *src_buf = malloc(f->entries[index].compressed_size + 1); | |
157 if (f->entries[index].compressed_size != fread(src_buf, 1, f->entries[index].compressed_size, f->file)) { | |
158 free(src_buf); | |
159 return NULL; | |
160 } | |
161 uLongf destLen = *out_size; | |
162 z_stream stream; | |
163 memset(&stream, 0, sizeof(stream)); | |
164 stream.avail_in = f->entries[index].compressed_size + 1; | |
165 stream.next_in = src_buf; | |
166 stream.next_out = buf; | |
167 stream.avail_out = *out_size; | |
168 if (Z_OK == inflateInit2(&stream, -15)) { | |
169 int result = inflate(&stream, Z_FINISH); | |
170 *out_size = stream.total_out; | |
171 free(src_buf); | |
172 inflateEnd(&stream); | |
173 if (result != Z_OK && result != Z_STREAM_END && result != Z_BUF_ERROR) { | |
174 free(buf); | |
175 return NULL; | |
176 } | |
177 } | |
178 break; | |
179 #endif | |
180 } | |
181 default: | |
182 free(buf); | |
183 return NULL; | |
184 } | |
185 | |
186 return buf; | |
187 } | |
188 | |
189 void zip_close(zip_file *f) | |
190 { | |
191 fclose(f->file); | |
192 for (uint32_t i = 0; i < f->num_entries; i++) | |
193 { | |
194 free(f->entries[i].name); | |
195 } | |
196 free(f->entries); | |
197 free(f); | |
198 } | |
199 | |
200 |