├── CMakeLists.txt ├── LICENSE ├── README.md └── src ├── nozip.c ├── nozip.h ├── stb_inflate.h └── unzip.c /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.6) 2 | project(nozip VERSION 1.0.0 LANGUAGES C) 3 | 4 | set(CMAKE_C_STANDARD 99) 5 | 6 | if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") 7 | add_compile_options(-Wall -Wextra -Wno-unused-parameter) 8 | elseif (CMAKE_C_COMPILER_ID MATCHES "MSVC") 9 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS") 10 | endif() 11 | 12 | add_library(nozip OBJECT src/nozip.c) 13 | add_executable(myunzip src/unzip.c $) 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Ivan Vashchaev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nozip 2 | Tiny C library for reading ZIP files 3 | -------------------------------------------------------------------------------- /src/nozip.c: -------------------------------------------------------------------------------- 1 | #define NOZIP_IMPLEMENTATION 2 | #include "nozip.h" 3 | -------------------------------------------------------------------------------- /src/nozip.h: -------------------------------------------------------------------------------- 1 | #ifndef NOZIP_H 2 | #define NOZIP_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #if defined(NOZIP_STATIC) 9 | #define NOZIPDEF static 10 | #elif defined(__cplusplus) 11 | #define NOZIPDEF extern "C" 12 | #else 13 | #define NOZIPDEF extern 14 | #endif 15 | 16 | struct zip_entry { 17 | uint64_t uncompressed_size; 18 | uint64_t compressed_size; 19 | uint64_t local_header_offset; 20 | const char *filename; 21 | time_t mtime; 22 | }; 23 | 24 | NOZIPDEF size_t zip_read(struct zip_entry **ptr, FILE *stream); 25 | NOZIPDEF int zip_seek(FILE *stream, const struct zip_entry *entry); 26 | 27 | #endif // NOZIP_H 28 | 29 | #ifdef NOZIP_IMPLEMENTATION 30 | 31 | #include 32 | #include 33 | 34 | #if defined(__GNUC__) || defined(__clang__) 35 | #define PACK(x) x __attribute__((__packed__)) 36 | #elif defined(_MSC_VER) 37 | #define PACK(x) x __pragma(pack(push, 1)) x __pragma(pack(pop)) 38 | #else 39 | #error Unsupported compiler 40 | #endif 41 | 42 | PACK(struct local_file_header { 43 | uint32_t signature; 44 | uint16_t version_needed; 45 | uint16_t flags; 46 | uint16_t compression_method; 47 | uint16_t last_mod_file_time; 48 | uint16_t last_mod_file_date; 49 | uint32_t crc_32; 50 | uint32_t compressed_size; 51 | uint32_t uncompressed_size; 52 | uint16_t file_name_length; 53 | uint16_t extra_field_length; 54 | }); 55 | 56 | PACK(struct central_dir_header { 57 | uint32_t signature; 58 | uint16_t version; 59 | uint16_t version_needed; 60 | uint16_t flags; 61 | uint16_t compression_method; 62 | uint16_t last_mod_file_time; 63 | uint16_t last_mod_file_date; 64 | uint32_t crc_32; 65 | uint32_t compressed_size; 66 | uint32_t uncompressed_size; 67 | uint16_t file_name_length; 68 | uint16_t extra_field_length; 69 | uint16_t file_comment_length; 70 | uint16_t disk_number_start; 71 | uint16_t internal_file_attributes; 72 | uint32_t external_file_attributes; 73 | uint32_t local_header_offset; 74 | }); 75 | 76 | PACK(struct end_of_central_dir_record64 { 77 | uint32_t signature; 78 | uint64_t eocdr_size; 79 | uint16_t version; 80 | uint16_t version_needed; 81 | uint32_t disk_number; 82 | uint32_t cdr_disk_number; 83 | uint64_t disk_num_entries; 84 | uint64_t num_entries; 85 | uint64_t cdr_size; 86 | uint64_t cdr_offset; 87 | }); 88 | 89 | PACK(struct end_of_central_dir_locator64 { 90 | uint32_t signature; 91 | uint32_t eocdr_disk; 92 | uint64_t eocdr_offset; 93 | uint32_t num_disks; 94 | }); 95 | 96 | PACK(struct end_of_central_dir_record { 97 | uint32_t signature; 98 | uint16_t disk_number; 99 | uint16_t cdr_disk_number; 100 | uint16_t disk_num_entries; 101 | uint16_t num_entries; 102 | uint32_t cdr_size; 103 | uint32_t cdr_offset; 104 | uint16_t ZIP_file_comment_length; 105 | }); 106 | 107 | size_t zip_read(struct zip_entry **ptr, FILE *stream) { 108 | // find the end of central directory record 109 | uint32_t signature; 110 | off_t offset; 111 | for (offset = sizeof(struct end_of_central_dir_record);; ++offset) { 112 | if (offset > UINT16_MAX || fseeko(stream, -offset, SEEK_END) || !fread(&signature, sizeof(signature), 1, stream)) 113 | return 0; 114 | if (signature == 0x06054B50) 115 | break; 116 | } 117 | 118 | // read end of central directory record 119 | struct end_of_central_dir_record eocdr; 120 | if (!(fseeko(stream, -offset, SEEK_END) == 0 && 121 | fread(&eocdr, sizeof(eocdr), 1, stream) && 122 | eocdr.signature == 0x06054B50 && 123 | eocdr.disk_number == 0 && 124 | eocdr.cdr_disk_number == 0 && 125 | eocdr.disk_num_entries == eocdr.num_entries)) 126 | return 0; 127 | 128 | // check for zip64 129 | struct end_of_central_dir_record64 eocdr64; 130 | int zip64 = eocdr.num_entries == UINT16_MAX || eocdr.cdr_offset == UINT32_MAX || eocdr.cdr_size == UINT32_MAX; 131 | if (zip64) { 132 | // zip64 end of central directory locator 133 | struct end_of_central_dir_locator64 eocdl64; 134 | if (!(fseeko(stream, -offset - sizeof(eocdl64), SEEK_END) == 0 && 135 | fread(&eocdl64, sizeof(eocdl64), 1, stream) && 136 | eocdl64.signature == 0x07064B50 && 137 | eocdl64.eocdr_disk == 0 && 138 | eocdl64.num_disks == 1)) 139 | return 0; 140 | // zip64 end of central directory record 141 | if (!(fseeko(stream, eocdl64.eocdr_offset, SEEK_SET) == 0 && 142 | fread(&eocdr64, sizeof(eocdr64), 1, stream) && 143 | eocdr64.signature == 0x06064B50 && 144 | eocdr64.disk_number == 0 && 145 | eocdr64.cdr_disk_number == 0 && 146 | eocdr64.disk_num_entries == eocdr64.num_entries)) 147 | return 0; 148 | } 149 | 150 | // seek to central directory record 151 | if (fseeko(stream, zip64 ? eocdr64.cdr_offset : eocdr.cdr_offset, SEEK_SET)) 152 | return 0; 153 | 154 | // alloc buffer for entries array and filenames 155 | struct zip_entry *entries = (struct zip_entry *)malloc(zip64 ? eocdr64.cdr_size : eocdr.cdr_size); 156 | if (!entries) 157 | return 0; 158 | 159 | // store filenames after entries array 160 | char *strings = (char *)(entries + (zip64 ? eocdr64.num_entries : eocdr.num_entries)); 161 | 162 | for (size_t i = 0, i_end = zip64 ? eocdr64.num_entries : eocdr.num_entries; i < i_end; ++i) { 163 | // read central directory header, filename, extra field and skip comment 164 | struct central_dir_header cdh; 165 | if (!(fread(&cdh, sizeof(cdh), 1, stream) && 166 | cdh.signature == 0x02014B50 && 167 | fread(strings, cdh.file_name_length + cdh.extra_field_length, 1, stream) && 168 | fseeko(stream, cdh.file_comment_length, SEEK_CUR) == 0)) { 169 | free(entries); 170 | return 0; 171 | } 172 | 173 | struct zip_entry *entry = entries + i; 174 | entry->uncompressed_size = cdh.uncompressed_size; 175 | entry->compressed_size = cdh.compressed_size; 176 | entry->local_header_offset = cdh.local_header_offset; 177 | 178 | // find zip64 extended information extra field 179 | for (char *extra = strings + cdh.file_name_length; extra != strings + cdh.file_name_length + cdh.extra_field_length;) { 180 | uint16_t header_id; 181 | memcpy(&header_id, extra, sizeof(header_id)); 182 | extra += sizeof(header_id); 183 | 184 | uint16_t data_size; 185 | memcpy(&data_size, extra, sizeof(data_size)); 186 | extra += sizeof(data_size); 187 | 188 | switch (header_id) { 189 | case 0x0001: 190 | if (cdh.uncompressed_size == UINT32_MAX) { 191 | memcpy(&entry->uncompressed_size, extra, sizeof(entry->uncompressed_size)); 192 | extra += sizeof(entry->uncompressed_size); 193 | } 194 | if (cdh.compressed_size == UINT32_MAX) { 195 | memcpy(&entry->compressed_size, extra, sizeof(entry->compressed_size)); 196 | extra += sizeof(entry->compressed_size); 197 | } 198 | if (cdh.local_header_offset == UINT32_MAX) { 199 | memcpy(&entry->local_header_offset, extra, sizeof(entry->local_header_offset)); 200 | extra += sizeof(entry->local_header_offset); 201 | } 202 | if (cdh.disk_number_start == UINT16_MAX) { 203 | extra += sizeof(uint32_t); 204 | } 205 | break; 206 | default: 207 | extra += data_size; 208 | break; 209 | } 210 | } 211 | 212 | entry->filename = strings; 213 | strings += cdh.file_name_length; 214 | *strings++ = '\0'; 215 | entry->mtime = mktime(&(struct tm){ 216 | .tm_sec = (cdh.last_mod_file_time << 1) & 0x3F, 217 | .tm_min = (cdh.last_mod_file_time >> 5) & 0x3F, 218 | .tm_hour = (cdh.last_mod_file_time >> 11) & 0x1F, 219 | .tm_mday = cdh.last_mod_file_date & 0x1F, 220 | .tm_mon = ((cdh.last_mod_file_date >> 5) & 0xF) - 1, 221 | .tm_year = ((cdh.last_mod_file_date >> 9) & 0x7F) + 1980 - 1900, 222 | .tm_isdst = -1, 223 | }); 224 | } 225 | 226 | *ptr = entries; 227 | return zip64 ? eocdr64.num_entries : eocdr.num_entries; 228 | } 229 | 230 | int zip_seek(FILE *stream, const struct zip_entry *entry) { 231 | struct local_file_header lfh; 232 | return !(fseeko(stream, entry->local_header_offset, SEEK_SET) == 0 && 233 | fread(&lfh, sizeof(lfh), 1, stream) && 234 | lfh.signature == 0x04034B50 && 235 | fseeko(stream, lfh.file_name_length + lfh.extra_field_length, SEEK_CUR) == 0); 236 | } 237 | 238 | int zip_store(FILE *stream, const char *filename, const void *data, size_t size) { 239 | off_t offset = ftell(stream); 240 | if (offset == -1) 241 | return 1; 242 | 243 | time_t t = time(NULL); 244 | struct tm *tm = localtime(&t); 245 | 246 | struct local_file_header lfh = { 247 | .signature = 0x04034B50, 248 | .version_needed = 10, 249 | .flags = 0, 250 | .compression_method = 0, 251 | .last_mod_file_time = tm->tm_hour << 11 | tm->tm_min << 5 | tm->tm_sec >> 1, 252 | .last_mod_file_date = (tm->tm_year - 80) << 9 | (tm->tm_mon + 1) << 5 | tm->tm_mday, 253 | .crc_32 = 0, 254 | .compressed_size = size, 255 | .uncompressed_size = size, 256 | .file_name_length = strlen(filename), 257 | .extra_field_length = 0, 258 | }; 259 | 260 | fwrite(&lfh, sizeof(lfh), 1, stream); 261 | fwrite(filename, lfh.file_name_length, 1, stream); 262 | fwrite(data, size, 1, stream); 263 | 264 | return 0; 265 | } 266 | 267 | int zip_finalize(FILE *stream) { 268 | off_t offset = 0; 269 | struct local_file_header lfh; 270 | char filename[1024]; 271 | 272 | struct end_of_central_dir_record eocdr = { 273 | .signature = 0x06054B50, 274 | .disk_number = 0, 275 | .cdr_disk_number = 0, 276 | .disk_num_entries = 0, 277 | .num_entries = 0, 278 | .cdr_size = 0, 279 | .cdr_offset = ftello(stream), 280 | .ZIP_file_comment_length = 0, 281 | }; 282 | 283 | while (fseeko(stream, offset, SEEK_SET) == 0 && 284 | fread(&lfh, sizeof(lfh), 1, stream) && 285 | lfh.signature == 0x04034B50 && 286 | lfh.file_name_length < sizeof(filename) && 287 | fread(filename, lfh.file_name_length, 1, stream)) { 288 | 289 | printf("F %.*s\n", lfh.file_name_length, filename); 290 | struct central_dir_header cdh = { 291 | .signature = 0x02014B50, 292 | .version = 10, 293 | .version_needed = lfh.version_needed, 294 | .flags = lfh.flags, 295 | .compression_method = lfh.compression_method, 296 | .last_mod_file_time = lfh.last_mod_file_time, 297 | .last_mod_file_date = lfh.last_mod_file_date, 298 | .crc_32 = lfh.crc_32, 299 | .compressed_size = lfh.compressed_size, 300 | .uncompressed_size = lfh.uncompressed_size, 301 | .file_name_length = lfh.file_name_length, 302 | .extra_field_length = 0, 303 | .file_comment_length = 0, 304 | .disk_number_start = 0, 305 | .internal_file_attributes = 0, 306 | .external_file_attributes = 0, 307 | .local_header_offset = offset, 308 | }; 309 | fseeko(stream, 0, SEEK_END); 310 | fwrite(&cdh, sizeof(cdh), 1, stream); 311 | fwrite(filename, lfh.file_name_length, 1, stream); 312 | 313 | ++eocdr.num_entries; 314 | ++eocdr.disk_num_entries; 315 | 316 | offset += sizeof(lfh) + lfh.file_name_length + lfh.compressed_size; 317 | } 318 | 319 | fseeko(stream, 0, SEEK_END); 320 | eocdr.cdr_size = ftello(stream) - eocdr.cdr_offset; 321 | fwrite(&eocdr, sizeof(eocdr), 1, stream); 322 | 323 | return 0; 324 | } 325 | 326 | #endif // NOZIP_IMPLEMENTATION 327 | -------------------------------------------------------------------------------- /src/stb_inflate.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifndef NDEBUG 5 | #include 6 | #define STBI_ZERROR(x) fprintf(stderr, "%s:%d: error: %s\n", __FILE__, __LINE__, x), 0 7 | #else 8 | #define STBI_ZERROR(x) 0 9 | #endif 10 | 11 | // fast-way is faster to check than jpeg huffman, but slow way is slower 12 | #define STBI__ZFAST_BITS 9 // accelerate all cases in default tables 13 | #define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) 14 | 15 | // zlib-style huffman encoding 16 | // (jpegs packs from left, zlib from right, so can't share code) 17 | struct stbi__zhuffman { 18 | uint16_t fast[1 << STBI__ZFAST_BITS]; 19 | uint16_t firstcode[16]; 20 | int maxcode[17]; 21 | uint16_t firstsymbol[16]; 22 | uint8_t size[288]; 23 | uint16_t value[288]; 24 | }; 25 | 26 | static inline int stbi__bitreverse16(int n) 27 | { 28 | n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); 29 | n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); 30 | n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); 31 | n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); 32 | return n; 33 | } 34 | 35 | static inline int stbi__bit_reverse(int v, int bits) 36 | { 37 | // to bit reverse n bits, reverse 16 and shift 38 | // e.g. 11 bits, bit reverse and shift away 5 39 | return stbi__bitreverse16(v) >> (16-bits); 40 | } 41 | 42 | static int stbi__zbuild_huffman(struct stbi__zhuffman *z, uint8_t *sizelist, int num) 43 | { 44 | int i,k=0; 45 | int code, next_code[16], sizes[17]; 46 | 47 | // DEFLATE spec for generating codes 48 | memset(sizes, 0, sizeof(sizes)); 49 | memset(z->fast, 0, sizeof(z->fast)); 50 | for (i=0; i < num; ++i) 51 | ++sizes[sizelist[i]]; 52 | sizes[0] = 0; 53 | for (i=1; i < 16; ++i) 54 | if (sizes[i] > (1 << i)) 55 | return STBI_ZERROR("bad sizes"); 56 | code = 0; 57 | for (i=1; i < 16; ++i) { 58 | next_code[i] = code; 59 | z->firstcode[i] = (uint16_t) code; 60 | z->firstsymbol[i] = (uint16_t) k; 61 | code = (code + sizes[i]); 62 | if (sizes[i]) 63 | if (code-1 >= (1 << i)) return STBI_ZERROR("bad codelengths"); 64 | z->maxcode[i] = code << (16-i); // preshift for inner loop 65 | code <<= 1; 66 | k += sizes[i]; 67 | } 68 | z->maxcode[16] = 0x10000; // sentinel 69 | for (i=0; i < num; ++i) { 70 | int s = sizelist[i]; 71 | if (s) { 72 | int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; 73 | uint16_t fastv = (uint16_t) ((s << 9) | i); 74 | z->size [c] = (uint8_t ) s; 75 | z->value[c] = (uint16_t) i; 76 | if (s <= STBI__ZFAST_BITS) { 77 | int j = stbi__bit_reverse(next_code[s],s); 78 | while (j < (1 << STBI__ZFAST_BITS)) { 79 | z->fast[j] = fastv; 80 | j += (1 << s); 81 | } 82 | } 83 | ++next_code[s]; 84 | } 85 | } 86 | return 1; 87 | } 88 | 89 | // zlib-from-memory implementation for PNG reading 90 | // because PNG allows splitting the zlib stream arbitrarily, 91 | // and it's annoying structurally to have PNG call ZLIB call PNG, 92 | // we require PNG read all the IDATs and combine them into a single 93 | // memory buffer 94 | 95 | typedef struct stbi__stream 96 | { 97 | const uint8_t *start_in; 98 | const uint8_t *next_in; 99 | const uint8_t *end_in; 100 | 101 | uint8_t *start_out; 102 | uint8_t *next_out; 103 | uint8_t *end_out; 104 | 105 | void *cookie_in; 106 | void *cookie_out; 107 | 108 | size_t total_in; 109 | size_t total_out; 110 | 111 | int (*refill)(struct stbi__stream *); 112 | int (*flush)(struct stbi__stream *); 113 | 114 | int num_bits; 115 | uint32_t code_buffer; 116 | 117 | struct stbi__zhuffman z_length, z_distance; 118 | } stbi__zbuf; 119 | 120 | int refill_zeros(struct stbi__stream *stream) { 121 | static const uint8_t zeros[64] = {0}; 122 | stream->start_in = stream->next_in = zeros; 123 | stream->end_in = zeros + sizeof(zeros); 124 | return 0; 125 | } 126 | 127 | int refill_stdio(struct stbi__stream *stream) { 128 | size_t n = fread((void *)stream->start_in, 1, stream->end_in - stream->start_in, (FILE *)stream->cookie_in); 129 | if (n) { 130 | stream->next_in = stream->start_in; 131 | stream->end_in = stream->start_in + n; 132 | return 0; 133 | } 134 | return refill_zeros(stream); 135 | } 136 | 137 | int flush_stdio(struct stbi__stream *stream) { 138 | size_t n = fwrite(stream->start_out, 1, stream->next_out - stream->start_out, (FILE *)stream->cookie_out); 139 | if (n) { 140 | stream->next_out = stream->start_out; 141 | return 0; 142 | } 143 | return -1; 144 | } 145 | 146 | static inline uint8_t stbi__zget8(struct stbi__stream *stream) 147 | { 148 | if (stream->next_in == stream->end_in) 149 | stream->refill(stream); 150 | return *stream->next_in++; 151 | } 152 | 153 | static void stbi__fill_bits(stbi__zbuf *z) 154 | { 155 | do { 156 | z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; 157 | z->num_bits += 8; 158 | } while (z->num_bits <= 24); 159 | } 160 | 161 | static inline unsigned int stbi__zreceive(stbi__zbuf *z, int n) 162 | { 163 | unsigned int k; 164 | if (z->num_bits < n) stbi__fill_bits(z); 165 | k = z->code_buffer & ((1 << n) - 1); 166 | z->code_buffer >>= n; 167 | z->num_bits -= n; 168 | return k; 169 | } 170 | 171 | static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, struct stbi__zhuffman *z) 172 | { 173 | int b,s,k; 174 | // not resolved by fast table, so compute it the slow way 175 | // use jpeg approach, which requires MSbits at top 176 | k = stbi__bit_reverse(a->code_buffer, 16); 177 | for (s=STBI__ZFAST_BITS+1; ; ++s) 178 | if (k < z->maxcode[s]) 179 | break; 180 | if (s == 16) return -1; // invalid code! 181 | // code size is s, so: 182 | b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; 183 | a->code_buffer >>= s; 184 | a->num_bits -= s; 185 | return z->value[b]; 186 | } 187 | 188 | static inline int stbi__zhuffman_decode(stbi__zbuf *a, struct stbi__zhuffman *z) 189 | { 190 | int b,s; 191 | if (a->num_bits < 16) stbi__fill_bits(a); 192 | b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; 193 | if (b) { 194 | s = b >> 9; 195 | a->code_buffer >>= s; 196 | a->num_bits -= s; 197 | return b & 511; 198 | } 199 | return stbi__zhuffman_decode_slowpath(a, z); 200 | } 201 | 202 | static int stbi__zlength_base[31] = { 203 | 3,4,5,6,7,8,9,10,11,13, 204 | 15,17,19,23,27,31,35,43,51,59, 205 | 67,83,99,115,131,163,195,227,258,0,0 }; 206 | 207 | static int stbi__zlength_extra[31]= 208 | { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; 209 | 210 | static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 211 | 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; 212 | 213 | static int stbi__zdist_extra[32] = 214 | { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; 215 | 216 | static int stbi__parse_huffman_block(stbi__zbuf *a) 217 | { 218 | uint8_t *zout = a->next_out; 219 | for(;;) { 220 | int z = stbi__zhuffman_decode(a, &a->z_length); 221 | if (z < 256) { 222 | if (z < 0) return STBI_ZERROR("bad huffman code"); // error in huffman codes 223 | if (zout == a->end_out) { 224 | a->next_out = zout; 225 | if (a->flush(a)) 226 | return 0; 227 | zout = a->next_out; 228 | } 229 | *zout++ = (char) z; 230 | //a->window[a->total_out++ % (1 << 15)] = (char)z; 231 | //if (a->total_out % (1 << 15) == 0) 232 | // fwrite(a->window, sizeof(a->window), 1, stdout); 233 | } else { 234 | //uint8_t *p; 235 | int len,dist; 236 | if (z == 256) { 237 | a->next_out = zout; 238 | return 1; 239 | } 240 | z -= 257; 241 | len = stbi__zlength_base[z]; 242 | if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); 243 | z = stbi__zhuffman_decode(a, &a->z_distance); 244 | if (z < 0) return STBI_ZERROR("bad huffman code"); 245 | dist = stbi__zdist_base[z]; 246 | if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); 247 | //if (zout - a->zout_start < dist) return STBI_ZERROR("bad dist"); 248 | //if (zout + len > a->zout_end) { 249 | // if (!stbi__zexpand(a, zout, len)) return 0; 250 | // zout = a->zout; 251 | //} 252 | //p = (uint8_t *) (zout - dist); 253 | //if (dist == 1) { // run of one byte; common in images. 254 | // uint8_t v = *p; 255 | // if (len) { do *zout++ = v; while (--len); } 256 | //} else { 257 | // if (len) { do *zout++ = *p++; while (--len); } 258 | //} 259 | if (len) { 260 | uint8_t *src = zout - dist; 261 | if (src < a->start_out) 262 | src += a->end_out - a->start_out; 263 | do { 264 | if (src == a->end_out) 265 | src = a->start_out; 266 | if (zout == a->end_out){ 267 | a->next_out = zout; 268 | a->flush(a); 269 | zout = a->next_out; 270 | } 271 | *zout++ = *src++; 272 | } while (--len); 273 | 274 | ////zout += len; 275 | //size_t x = a->total_out - dist; 276 | //do { 277 | // a->window[a->total_out++ % (1 << 15)] = a->window[x++ % (1 << 15)]; 278 | // //a->window[a->total_out % (1 << 15)] = a->window[(a->total_out - dist) % (1 << 15)]; 279 | // if (a->total_out % (1 << 15) == 0) 280 | // fwrite(a->window, sizeof(a->window), 1, stdout); 281 | //} while (--len); 282 | } 283 | } 284 | } 285 | } 286 | 287 | static int stbi__compute_huffman_codes(stbi__zbuf *a) 288 | { 289 | static uint8_t length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; 290 | struct stbi__zhuffman z_codelength; 291 | uint8_t lencodes[286+32+137];//padding for maximum single op 292 | uint8_t codelength_sizes[19]; 293 | int i,n; 294 | 295 | int hlit = stbi__zreceive(a,5) + 257; 296 | int hdist = stbi__zreceive(a,5) + 1; 297 | int hclen = stbi__zreceive(a,4) + 4; 298 | 299 | memset(codelength_sizes, 0, sizeof(codelength_sizes)); 300 | for (i=0; i < hclen; ++i) { 301 | int s = stbi__zreceive(a,3); 302 | codelength_sizes[length_dezigzag[i]] = (uint8_t) s; 303 | } 304 | if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; 305 | 306 | n = 0; 307 | while (n < hlit + hdist) { 308 | int c = stbi__zhuffman_decode(a, &z_codelength); 309 | if (c < 0 || c >= 19) return STBI_ZERROR("bad codelengths"); 310 | if (c < 16) 311 | lencodes[n++] = (uint8_t) c; 312 | else if (c == 16) { 313 | c = stbi__zreceive(a,2)+3; 314 | memset(lencodes+n, lencodes[n-1], c); 315 | n += c; 316 | } else if (c == 17) { 317 | c = stbi__zreceive(a,3)+3; 318 | memset(lencodes+n, 0, c); 319 | n += c; 320 | } else { 321 | c = stbi__zreceive(a,7)+11; 322 | memset(lencodes+n, 0, c); 323 | n += c; 324 | } 325 | } 326 | if (n != hlit+hdist) return STBI_ZERROR("bad codelengths"); 327 | if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; 328 | if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; 329 | return 1; 330 | } 331 | 332 | static int stbi__parse_uncompressed_block(stbi__zbuf *a) 333 | { 334 | uint8_t header[4]; 335 | int len,nlen,k; 336 | if (a->num_bits & 7) 337 | stbi__zreceive(a, a->num_bits & 7); // discard 338 | // drain the bit-packed data into header 339 | k = 0; 340 | while (a->num_bits > 0) { 341 | header[k++] = (uint8_t) (a->code_buffer & 255); // suppress MSVC run-time check 342 | a->code_buffer >>= 8; 343 | a->num_bits -= 8; 344 | } 345 | // now fill header the normal way 346 | while (k < 4) 347 | header[k++] = stbi__zget8(a); 348 | len = header[1] * 256 + header[0]; 349 | nlen = header[3] * 256 + header[2]; 350 | if (nlen != (len ^ 0xffff)) return STBI_ZERROR("zlib corrupt"); 351 | #if 1 352 | do { 353 | size_t avail_out = a->end_out - a->next_out; 354 | while (avail_out-- && len--) 355 | *a->next_out++ = stbi__zget8(a); 356 | if (len > 0) 357 | a->flush(a); 358 | } while (len > 0); 359 | 360 | //while (len--) { 361 | // a->window[a->total_out++ % (1 << 15)] = stbi__zget8(a); 362 | // if (a->total_out % (1 << 15) == 0) 363 | // fwrite(a->window, sizeof(a->window), 1, stdout); 364 | //} 365 | #else 366 | memcpy(a->zout, a->zbuffer, len); 367 | a->zbuffer += len; 368 | a->zout += len; 369 | #endif 370 | return 1; 371 | } 372 | 373 | // @TODO: should statically initialize these for optimal thread safety 374 | static uint8_t stbi__zdefault_length[288], stbi__zdefault_distance[32]; 375 | static void stbi__init_zdefaults(void) 376 | { 377 | int i; // use <= to match clearly with spec 378 | for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; 379 | for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; 380 | for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; 381 | for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; 382 | 383 | for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; 384 | } 385 | 386 | int stb_inflate(struct stbi__stream *a) 387 | { 388 | int final, type; 389 | a->num_bits = 0; 390 | a->code_buffer = 0; 391 | a->total_out = 0; 392 | do { 393 | final = stbi__zreceive(a,1); 394 | type = stbi__zreceive(a,2); 395 | if (type == 0) { 396 | if (!stbi__parse_uncompressed_block(a)) return 0; 397 | } else if (type == 3) { 398 | return 0; 399 | } else { 400 | if (type == 1) { 401 | // use fixed code lengths 402 | if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); 403 | if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; 404 | if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; 405 | } else { 406 | if (!stbi__compute_huffman_codes(a)) return 0; 407 | } 408 | if (!stbi__parse_huffman_block(a)) return 0; 409 | } 410 | } while (!final); 411 | a->flush(a); 412 | return 1; 413 | } 414 | -------------------------------------------------------------------------------- /src/unzip.c: -------------------------------------------------------------------------------- 1 | #include "nozip.h" 2 | #include "stb_inflate.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int main(int argc, char **argv) { 13 | #if 0 14 | ZIP_GENERATE(ZIP_EXTRA_FIELD_HEADER_NEW); 15 | ZIP_GENERATE(ZIP64_EXTENDED_INFORMATION_EXTRA_FIELD_NEW); 16 | ZIP_GENERATE(ZIP_LOCAL_FILE_HEADER); 17 | ZIP_GENERATE(ZIP_CENTRAL_DIR_HEADER_NEW); 18 | ZIP_GENERATE(ZIP64_END_OF_CENTRAL_DIR_RECORD_NEW); 19 | ZIP_GENERATE(ZIP64_END_OF_CENTRAL_DIR_LOCATOR_NEW); 20 | ZIP_GENERATE(ZIP_END_OF_CENTRAL_DIR_RECORD_NEW); 21 | 22 | dump(u, ZIP_SIZEOF(ZIP_LOCAL_FILE_HEADER)); 23 | dump(u, ZIP_CENTRAL_DIR_HEADER(ZIP_POS_SIZE)); 24 | dump(u, ZIP_END_OF_CENTRAL_DIR_RECORD(ZIP_POS_SIZE)); 25 | dump(u, ZIP64_END_OF_CENTRAL_DIR_RECORD(ZIP_POS_SIZE)); 26 | dump(u, ZIP64_END_OF_CENTRAL_DIR_LOCATOR(ZIP_POS_SIZE)); 27 | #endif 28 | 29 | if (argc < 3) { 30 | fprintf(stderr, "usage: %s [-lvx] file [file ...]\n", argv[0]); 31 | return EXIT_FAILURE; 32 | } 33 | 34 | int mode; 35 | if (!strcmp("-l", argv[1])) 36 | mode = 'l'; 37 | else if (!strcmp("-v", argv[1])) 38 | mode = 'v'; 39 | else if (!strcmp("-x", argv[1])) 40 | mode = 'x'; 41 | else if (!strcmp("-z", argv[1])) 42 | mode = 'z'; 43 | else { 44 | fprintf(stderr, "%s: illegal option -- %s\n", argv[0], argv[1]); 45 | return EXIT_FAILURE; 46 | } 47 | 48 | FILE *fp = fopen(argv[2], "rb"); 49 | if (!fp) { 50 | perror(argv[2]); 51 | return EXIT_FAILURE; 52 | } 53 | 54 | struct zip_entry *entries = NULL; 55 | size_t num_entries = zip_read(&entries, fp); 56 | if (num_entries == 0 || entries == NULL) { 57 | perror(argv[2]); 58 | return EXIT_FAILURE; 59 | } 60 | 61 | switch (mode) { 62 | case 'l': 63 | for (size_t i = 0; i < num_entries; ++i) { 64 | printf("%s\n", entries[i].filename); 65 | } 66 | break; 67 | case 'v': 68 | for (size_t i = 0; i < num_entries; ++i) { 69 | struct zip_entry *e = entries + i; 70 | char buf[32]; 71 | strftime(buf, sizeof(buf), "%Y %b %d %H:%M", localtime(&e->mtime)); 72 | printf("%10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %s %s\n", 73 | e->local_header_offset, e->compressed_size, e->uncompressed_size, buf, e->filename); 74 | } 75 | break; 76 | case 'x': 77 | case 'z': 78 | for (int argi = 3; argi < argc; ++argi) { 79 | for (size_t i = 0; i < num_entries; ++i) { 80 | struct zip_entry *e = entries + i; 81 | if (!strcmp(argv[argi], e->filename)) { 82 | if (e->uncompressed_size == 0) 83 | continue; 84 | if (zip_seek(fp, e)) { 85 | perror(argv[argi]); 86 | return EXIT_FAILURE; 87 | } 88 | if (e->compressed_size == e->uncompressed_size) { 89 | void *buf = malloc(e->compressed_size); 90 | if (!buf) { 91 | perror(argv[argi]); 92 | return EXIT_FAILURE; 93 | } 94 | if (fread(buf, e->compressed_size, 1, fp) == 0) { 95 | perror(argv[argi]); 96 | return EXIT_FAILURE; 97 | } 98 | #if 1 99 | int zip_store(FILE *stream, const char *filename, const void *data, size_t size); 100 | int zip_finalize(FILE *stream); 101 | FILE *fp = fopen("test.zip", "w+b"); 102 | if (fp) { 103 | zip_store(fp, e->filename, buf, e->compressed_size); 104 | zip_store(fp, "foo", "hello world\n", 12); 105 | zip_finalize(fp); 106 | fclose(fp); 107 | } 108 | #endif 109 | 110 | if (fwrite(buf, e->compressed_size, 1, stdout) == 0) { 111 | perror(argv[argi]); 112 | return EXIT_FAILURE; 113 | } 114 | free(buf); 115 | } else { 116 | #if 0 117 | void *buf = malloc(e->compressed_size); 118 | void *out = malloc(e->uncompressed_size); 119 | if (fread(buf, e->compressed_size, 1, fp) == 0) { 120 | perror(argv[argi]); 121 | return EXIT_FAILURE; 122 | } 123 | 124 | if (mode == 'z') { 125 | z_stream stream = { 126 | .next_in = buf, 127 | .avail_in = e->compressed_size, 128 | .next_out = out, 129 | .avail_out = e->uncompressed_size, 130 | }; 131 | inflateInit2(&stream, -MAX_WBITS); 132 | while (stream.avail_out) { 133 | int ret = inflate(&stream, Z_NO_FLUSH); 134 | if (ret == Z_STREAM_END) 135 | break; 136 | if (ret != Z_OK) { 137 | fprintf(stderr, "error: uncompress: %d\n", ret); 138 | break; 139 | } 140 | } 141 | inflateEnd(&stream); 142 | } else { 143 | struct stbi__stream stream; 144 | memset(&stream, 0, sizeof(stream)); 145 | 146 | stream.start_in = stream.next_in = buf; 147 | stream.end_in = stream.start_in + e->compressed_size; 148 | 149 | if (!stb_inflate(&stream)) { 150 | perror(argv[argi]); 151 | return EXIT_FAILURE; 152 | } 153 | } 154 | 155 | if (mode == 'z' && fwrite(out, e->uncompressed_size, 1, stdout) == 0) { 156 | perror(argv[argi]); 157 | return EXIT_FAILURE; 158 | } 159 | free(buf); 160 | free(out); 161 | #else 162 | struct stbi__stream stream; 163 | memset(&stream, 0, sizeof(stream)); 164 | 165 | uint8_t buffer[BUFSIZ]; 166 | stream.start_in = buffer; 167 | stream.end_in = stream.next_in = buffer + sizeof(buffer); 168 | stream.cookie_in = fp; 169 | stream.refill = refill_stdio; 170 | 171 | uint8_t window[1 << 15]; 172 | stream.start_out = stream.next_out = window; 173 | stream.end_out = window + sizeof(window); 174 | stream.cookie_out = stdout; 175 | stream.flush = flush_stdio; 176 | 177 | if (!stb_inflate(&stream)) { 178 | perror(argv[argi]); 179 | return EXIT_FAILURE; 180 | } 181 | #endif 182 | } 183 | } 184 | } 185 | } 186 | break; 187 | } 188 | 189 | free(entries); 190 | fclose(fp); 191 | 192 | return 0; 193 | } 194 | --------------------------------------------------------------------------------