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