]> git.sesse.net Git - casparcg/blob - modules/flash/util/swf.cpp
3560321: Merged patch from Robert Nagy, but haven't been able to play any swf myself...
[casparcg] / modules / flash / util / swf.cpp
1 /*\r
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
3 *\r
4 * This file is part of CasparCG (www.casparcg.com).\r
5 *\r
6 * CasparCG is free software: you can redistribute it and/or modify\r
7 * it under the terms of the GNU General Public License as published by\r
8 * the Free Software Foundation, either version 3 of the License, or\r
9 * (at your option) any later version.\r
10 *\r
11 * CasparCG is distributed in the hope that it will be useful,\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 * GNU General Public License for more details.\r
15 *\r
16 * You should have received a copy of the GNU General Public License\r
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
18 *\r
19 * Author: Robert Nagy, ronag89@gmail.com\r
20 */\r
21 \r
22 #include "../StdAfx.h"\r
23 \r
24 #include "swf.h"\r
25 \r
26 #include <common/exception/exceptions.h>\r
27 \r
28 #include <zlib.h>\r
29 \r
30 #include <fstream>\r
31 #include <streambuf>\r
32 \r
33 namespace caspar { namespace flash {\r
34         \r
35 std::vector<char> decompress_one_file(const std::vector<char>& in_data, uLong buf_size = 5000000)\r
36 {\r
37         if(buf_size > 300*1000000)\r
38                 BOOST_THROW_EXCEPTION(file_read_error());\r
39 \r
40         std::vector<char> out_data(buf_size, 0);\r
41 \r
42         auto ret = uncompress((Bytef*)out_data.data(), &buf_size, (Bytef*)in_data.data(), in_data.size());\r
43 \r
44         if(ret == Z_BUF_ERROR)\r
45                 return decompress_one_file(in_data, buf_size*2);\r
46 \r
47         if(ret != Z_OK)\r
48                 BOOST_THROW_EXCEPTION(file_read_error());\r
49 \r
50         out_data.resize(buf_size);\r
51 \r
52         return out_data;\r
53 }\r
54 \r
55 std::string read_template_meta_info(const std::wstring& filename)\r
56 {\r
57         auto file = std::fstream(filename, std::ios::in | std::ios::binary);\r
58 \r
59         if(!file)\r
60                 BOOST_THROW_EXCEPTION(file_read_error());\r
61         \r
62         char head[4] = {};\r
63         file.read(head, 3);\r
64         \r
65         std::vector<char> data;\r
66         \r
67         file.seekg(0, std::ios::end);   \r
68         data.reserve(static_cast<size_t>(file.tellg()));\r
69         file.seekg(0, std::ios::beg);\r
70 \r
71         if(strcmp(head, "CWS") == 0)\r
72         {\r
73                 file.seekg(8, std::ios::beg);\r
74                 std::copy((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>(), std::back_inserter(data));\r
75                 data = decompress_one_file(data);\r
76         }\r
77         else\r
78         {\r
79                 file.seekg(0, std::ios::end);   \r
80                 data.reserve(static_cast<size_t>(file.tellg()));\r
81                 file.seekg(0, std::ios::beg);\r
82 \r
83                 std::copy((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>(), std::back_inserter(data));\r
84         }\r
85         \r
86         std::string beg_str = "<template version";\r
87         std::string end_str = "</template>";\r
88         auto beg_it = std::find_end(data.begin(), data.end(), beg_str.begin(), beg_str.end());\r
89         auto end_it = std::find_end(beg_it, data.end(), end_str.begin(), end_str.end());\r
90         \r
91         if(beg_it == data.end() || end_it == data.end())\r
92                 BOOST_THROW_EXCEPTION(file_read_error());\r
93                         \r
94         return std::string(beg_it, end_it+end_str.size());\r
95 }\r
96 \r
97 int extractDimensions(const unsigned char* _data);\r
98 \r
99 swf_t::header_t::header_t(const std::wstring& filename)\r
100         : valid(false)\r
101 {\r
102         auto stream = std::fstream();\r
103         stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);\r
104         stream.open(filename, std::ios::in | std::ios::binary);\r
105         stream.read(reinterpret_cast<char*>(this), 8);\r
106                 \r
107         const std::array<std::uint8_t, 3> s1 = {'F', 'W', 'S'};\r
108         const std::array<std::uint8_t, 3> s2 = {'C', 'W', 'S'};\r
109 \r
110         if(this->signature != s1 && this->signature != s2)\r
111                 return;\r
112         \r
113         _byteswap_ulong(this->file_length);\r
114                         \r
115         std::vector<char> file_data;\r
116         std::copy((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>(), std::back_inserter(file_data));\r
117 \r
118         std::array<char, 32> data;\r
119         uLongf file_size = 32;\r
120         auto ret = uncompress(reinterpret_cast<Bytef*>(data.data()), &file_size, reinterpret_cast<const Bytef*>(file_data.data()), static_cast<uLong>(file_data.size()));\r
121         \r
122         if(ret == Z_DATA_ERROR)\r
123                 BOOST_THROW_EXCEPTION(io_error());\r
124 \r
125         // http://thenobody.blog.matfyz.sk/p13084-how-to-get-dimensions-of-a-swf-file\r
126 \r
127         unsigned char nbits = reinterpret_cast<unsigned char*>(data.data())[0];\r
128 \r
129         unsigned int size = nbits >> 3;                                                                         // remove overlaping 3 bits\r
130 \r
131         unsigned long dims[4] = {};\r
132         unsigned long neg_root = 1 << (size-1);                                                         // numbers are signed, i.e. leftmost bit denotes -\r
133 \r
134         unsigned int bi_offset = (size % 8) ? (8-(size % 8)) : 0;                       // offset of bit numbers depending on specified size\r
135         unsigned int by_offset = (size + bi_offset) / 8;                                        // offest of bytes\r
136         unsigned int ioffset;                                                                                           // floating byte offset during iteration\r
137         unsigned long ibuf = (unsigned long) (nbits % 8);                                       // actual result - starts with last 3 bits of first byte\r
138 \r
139         for(auto i = 0; i < 4; ++i)\r
140         {\r
141                 ioffset = by_offset * i;\r
142 \r
143                 for(unsigned int j = 0; j < by_offset; ++j)\r
144                 {\r
145                         ibuf <<= 8;\r
146                         ibuf +=  reinterpret_cast<unsigned char*>(data.data())[1+ioffset+j];\r
147                 }\r
148 \r
149                 dims[i] = (ibuf >> (3 + bi_offset + (i * bi_offset))) / 20;             // coordinates in twips, so divide by 20 for pixels\r
150 \r
151                 if(dims[i] >= neg_root)                                                                                 // if the leftmost bit is 1 number is negative          \r
152                         dims[i] = (-1) * ( neg_root - (dims[i] - neg_root) );           // convert to negative number           \r
153 \r
154                 int expn = 3 + bi_offset + (i * bi_offset);                                             // bit divider for ...\r
155                 ibuf = ibuf % (1 << (expn-1));                                                                  // ... buffered number \r
156         }\r
157         \r
158         this->frame_width  = dims[1] - dims[0];                                                         // max - mix\r
159         this->frame_height = dims[3] - dims[2]; \r
160 \r
161         this->valid = true;\r
162 }\r
163 \r
164 swf_t::swf_t(const std::wstring& filename)\r
165         : header(filename)\r
166 {\r
167         auto stream = std::fstream();\r
168         stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);\r
169         stream.open(filename, std::ios::in | std::ios::binary);         \r
170         stream.seekg(8, std::fstream::beg);\r
171 \r
172         this->data.resize(this->header.file_length - 8);\r
173         \r
174         std::vector<char> file_data;\r
175         std::copy((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>(), std::back_inserter(file_data));\r
176 \r
177         uLongf file_size = this->header.file_length;\r
178         auto ret = uncompress(reinterpret_cast<Bytef*>(this->data.data()), &file_size, reinterpret_cast<const Bytef*>(file_data.data()), static_cast<uLong>(file_data.size()));\r
179 \r
180         if(ret != Z_OK)\r
181                 BOOST_THROW_EXCEPTION(io_error());\r
182 }\r
183 \r
184 }}