1 //  Copyright (c) 2014 Thomas Goyne <tgoyne@gmail.com>
2 //
3 //  Permission is hereby granted, free of charge, to any person obtaining a copy
4 //  of this software and associated documentation files (the "Software"), to deal
5 //  in the Software without restriction, including without limitation the rights
6 //  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 //  copies of the Software, and to permit persons to whom the Software is
8 //  furnished to do so, subject to the following conditions:
9 //
10 //  The above copyright notice and this permission notice shall be included in
11 //  all copies or substantial portions of the Software.
12 //
13 //  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 //  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 //  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 //  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 //  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 //  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 //  THE SOFTWARE.
20 
21 #include "zipfile.h"
22 
23 extern "C" {
24 #include <libavutil/avutil.h>
25 }
26 
27 #include "utils.h"
28 
ZipFile(const char * filename,const char * mode)29 ZipFile::ZipFile(const char *filename, const char *mode)
30     : file(filename, mode, FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ)
31     , is_file(true)
32     , state(Initial) {
33     buffer.resize(65536);
34     z = {};
35 }
36 
ZipFile()37 ZipFile::ZipFile()
38     : is_file(false)
39     , state(Initial) {
40     buffer.resize(65536);
41     z = {};
42 }
43 
ZipFile(const uint8_t * in_buffer,const size_t size)44 ZipFile::ZipFile(const uint8_t *in_buffer, const size_t size)
45     : index_buffer(in_buffer, in_buffer + size)
46     , is_file(false)
47     , state(Initial) {
48     z = {};
49 }
50 
~ZipFile()51 ZipFile::~ZipFile() {
52     if (state == Inflate)
53         inflateEnd(&z);
54     if (state == Deflate)
55         deflateEnd(&z);
56 }
57 
Read(void * data,size_t size)58 void ZipFile::Read(void *data, size_t size) {
59     if (state == Deflate) {
60         deflateEnd(&z);
61         state = Initial;
62     }
63     if (state != Inflate) {
64         if (inflateInit(&z) != Z_OK)
65             throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to initialize zlib");
66         state = Inflate;
67     }
68 
69     z.next_out = static_cast<unsigned char *>(data);
70     z.avail_out = size;
71     if (!z.avail_in && !is_file) {
72         z.next_in = reinterpret_cast<Bytef*>(&index_buffer[0]);
73         z.avail_in = index_buffer.size();
74     }
75     do {
76         if (!z.avail_in && is_file) {
77             z.next_in = reinterpret_cast<Bytef*>(&buffer[0]);
78             z.avail_in = file.Read(&buffer[0], buffer.size());
79         }
80         if (!z.avail_in) {
81             if (!is_file)
82                 throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: Buffer is empty");
83             else if (!file.Tell())
84                 throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: File is empty");
85         }
86 
87         switch (inflate(&z, Z_SYNC_FLUSH)) {
88         case Z_NEED_DICT:
89             throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: Dictionary error.");
90         case Z_DATA_ERROR:
91             throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: Data error.");
92         case Z_MEM_ERROR:
93             throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: Memory error.");
94         case Z_STREAM_END:
95             if (z.avail_out > 0)
96                 throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: Stream ended early");
97         }
98     } while (z.avail_out);
99 }
100 
Write(const void * data,size_t size)101 int ZipFile::Write(const void *data, size_t size) {
102     if (state == Inflate) {
103         inflateEnd(&z);
104         state = Initial;
105     }
106     if (state != Deflate) {
107         if (deflateInit(&z, 5) != Z_OK)
108             throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to initialize zlib");
109         state = Deflate;
110     }
111 
112     z.next_in = static_cast<unsigned char *>(const_cast<void *>(data));
113     z.avail_in = size;
114     int ret = 0;
115     do {
116         z.avail_out = buffer.size();
117         z.next_out = reinterpret_cast<Bytef*>(&buffer[0]);
118         ret = deflate(&z, size > 0 ? Z_NO_FLUSH : Z_FINISH);
119         uInt written = buffer.size() - z.avail_out;
120         if (written) {
121             if (is_file)
122                 file.Write(&buffer[0], written);
123             else
124                 index_buffer.insert(index_buffer.end(), &buffer[0], &buffer[written]);
125         }
126     } while (z.avail_out == 0);
127     return ret;
128 }
129 
Finish()130 void ZipFile::Finish() {
131     while (Write(nullptr, 0) != Z_STREAM_END);
132     deflateEnd(&z);
133     state = Initial;
134 }
135 
GetBuffer(size_t * size)136 uint8_t *ZipFile::GetBuffer(size_t *size) {
137     uint8_t *ret = (uint8_t *)av_malloc(index_buffer.size());
138     if (ret == nullptr)
139         throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED, "Failed to allocate index return buffer");
140 
141     memcpy(ret, reinterpret_cast<uint8_t *>(&index_buffer[0]), index_buffer.size());
142     *size = index_buffer.size();
143 
144     return ret;
145 }
146