Add missing newlines escape. Should fix build failures on lucid-linux-x86
[gnash.git] / cygnal / libamf / sol.cpp
blob357823f8df2da14de2cd4ba77db79b8a85e8788d
1 //
2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
3 // Foundation, Inc
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 #include "GnashSystemNetHeaders.h"
20 #include "GnashFileUtilities.h"
21 #include "element.h"
22 #include "amf.h"
23 #include "buffer.h"
24 #include "sol.h"
25 #include "log.h"
26 #include "GnashException.h"
28 #include <boost/scoped_array.hpp>
29 #include <boost/cstdint.hpp>
30 #include <boost/shared_ptr.hpp>
31 #include <cerrno>
32 #include <string>
33 #include <vector>
34 #include <iostream>
35 #include <fstream>
36 #include <cassert>
38 using std::vector;
39 using gnash::ParserException;
40 using gnash::log_debug;
41 using gnash::log_error;
43 // It comprises of a magic number, followed by the file length, a
44 // filetype, which appears to always be "TCSO", and what appears to be
45 // a marker at the end of the header block.
46 // After the SOL header, the rest is all AMF objects.
48 // Magic Number - 2 bytes (always 0x00bf)
49 // Length - 4 bytes (the length of the file including the Marker bytes)
50 // Marker - 10 bytes (always "TCSO0x000400000000")
51 // Object Name - variable (the name of the object as an AMF encoded string)
52 // Padding - 4 bytes
53 // After this is a series of AMF objects
54 const short SOL_MAGIC = 0x00bf; // is in big-endian format, this is the first
55 // two bytes. of the .sol file.
56 //char *SOL_FILETYPE = "TCSO";
57 const short SOL_BLOCK_MARK = 0x0004;
59 /// \define ENSUREBYTES
60 ///
61 /// @param from The base address to check.
62 ///
63 /// @param tooFar The ending address that is one byte too many.
64 ///
65 /// @param size The number of bytes to check for: from to tooFar.
66 ///
67 /// @remarks May throw an Exception
68 #define ENSUREBYTES(from, toofar, size) { \
69 if ( from+size >= toofar ) \
70 throw ParserException("Premature end of AMF stream"); \
73 /// \namespace cygnal
74 ///
75 /// This namespace is for all the AMF specific classes in libamf.
76 namespace cygnal
79 SOL::SOL()
80 : _filesize(0)
82 // GNASH_REPORT_FUNCTION;
85 SOL::~SOL()
87 // GNASH_REPORT_FUNCTION;
90 /// \brief Extract the header from the file.
91 ///
92 /// @param filespec The name and path of the .sol file to parse.
93 ///
94 /// @return true if this succeeded. false if it doesn't.
95 bool
96 SOL::extractHeader(const std::string & /*filespec*/)
98 // GNASH_REPORT_FUNCTION;
99 return false;
102 /// \brief Extract the header from the file.
104 /// @param data a reference to a vector of bytes that contains the
105 /// .sol file data.
107 /// @return true if this succeeded. false if it doesn't.
108 bool
109 SOL::extractHeader(const vector<unsigned char> & /*data*/)
111 // GNASH_REPORT_FUNCTION;
112 return false;
115 /// \brief Add the AMF objects that are the data of the file
117 /// @param el A smart pointer to the Element to add to the .sol file.
119 /// @return nothing.
120 void
121 SOL::addObj(boost::shared_ptr<cygnal::Element> el)
123 // GNASH_REPORT_FUNCTION;
124 _amfobjs.push_back(el);
125 // _filesize += el->getName().size() + el->getDataSize() + 5;
128 /// \brief Create the file header.
130 /// @param data a reference to a vector of bytes that contains the
131 /// .sol file data.
133 /// @return true if this succeeded. false if it doesn't.
134 bool
135 SOL::formatHeader(const vector<unsigned char> & /*data*/)
137 // GNASH_REPORT_FUNCTION;
138 return false;
141 /// \brief Create the file header.
143 /// @param name The name of the SharedObject for this file.
145 /// @return true if this succeeded. false if it doesn't.
146 bool
147 SOL::formatHeader(const std::string &name)
149 return formatHeader(name, _filesize);
152 /// \brief Create the file header.
154 /// @param name The name of the SharedObject for this file.
156 /// @param filesize The size of the file.
158 /// @return true if this succeeded. false if it doesn't.
159 bool
160 SOL::formatHeader(const std::string &name, int filesize)
162 // GNASH_REPORT_FUNCTION;
163 boost::uint32_t i;
165 // First we add the magic number. All SOL data is in big-endian format,
166 // so we swap it first.
167 boost::uint16_t swapped = SOL_MAGIC;
168 swapped = htons(swapped);
169 boost::uint8_t *ptr = reinterpret_cast<boost::uint8_t *>(&swapped);
170 for (i=0; i<sizeof(boost::uint16_t); i++) {
171 _header.push_back(ptr[i]);
174 // Next is the file size to be created. We adjust it as the filesize
175 // includes the padding in the header, the mystery bytes, and the
176 // padding, plus the length of the name itself.
177 filesize += name.size() + 16;
178 boost::uint32_t len = filesize;
179 len = htonl(len);
180 ptr = reinterpret_cast<boost::uint8_t *>(&len);
181 for (i=0; i<sizeof(boost::uint32_t); i++) {
182 _header.push_back(ptr[i]);
185 // Then the mystery block, but as the value never seems to every change,
186 // we just built it the same way it always is.
187 // first is the TCSO, we have no idea what this stands for.
188 // ptr = reinterpret_cast<uint8_t *>(const_cast<uint8_t *>("TCSO");
189 ptr = (boost::uint8_t *)"TCSO";
190 for (i=0; i<sizeof(boost::uint32_t); i++) {
191 _header.push_back(ptr[i]);
193 // then the 0x0004 bytes, also a mystery
194 swapped = SOL_BLOCK_MARK;
195 swapped = htons(swapped);
196 ptr = reinterpret_cast<boost::uint8_t *>(&swapped);
197 for (i=0; i<sizeof(boost::uint16_t); i++) {
198 _header.push_back(ptr[i]);
200 // finally a bunch of zeros to pad things for this field
201 for (i=0; i<sizeof(boost::uint32_t); i++) {
202 _header.push_back('\0');
205 // Encode the name. This is not a string object, which has a type field
206 // one byte field precedding the length as a file type of AMF::STRING.
207 // First the length in two bytes
208 swapped = name.size();
209 swapped = htons(swapped);
210 ptr = reinterpret_cast<boost::uint8_t *>(&swapped);
211 for (i=0; i<sizeof(boost::uint16_t); i++) {
212 _header.push_back(ptr[i]);
214 // then the string itself
215 ptr = (boost::uint8_t *)name.c_str();
216 for (i=0; i<name.size(); i++) {
217 _header.push_back(ptr[i]);
220 // finally a bunch of zeros to pad things at the end of the header
221 for (i=0; i<sizeof(boost::uint32_t); i++) {
222 _header.push_back('\0');
225 #if 0
226 unsigned char *hexint;
227 hexint = new unsigned char[(_header.size() + 3) *3];
229 hexify(hexint, (unsigned char *)_header, _header.size(), true);
230 log_debug (_("%s: SOL file header is: \n%s"), __FUNCTION__, (char *)hexint);
231 delete hexint;
232 #endif
234 return true;
237 /// \brief Write the data to disk as a .sol file
239 /// @param filespec The name and path of the .sol file to parse.
241 /// @param name The name of the SharedObject for this file.
243 /// @return true if this succeeded. false if it doesn't.
244 bool
245 SOL::writeFile(const std::string &filespec, const std::string &name)
247 // GNASH_REPORT_FUNCTION;
248 std::ofstream ofs(filespec.c_str(), std::ios::binary);
249 if ( ! ofs ) {
250 log_error("Failed opening file '%s' in binary mode", filespec.c_str());
251 return false;
254 vector<boost::uint8_t>::iterator it;
255 vector<boost::shared_ptr<cygnal::Element> >::iterator ita;
256 AMF amf_obj;
257 char *ptr;
258 int size = 0;
260 if (filespec.empty()) {
261 return false;
264 for (ita = _amfobjs.begin(); ita != _amfobjs.end(); ita++) {
265 boost::shared_ptr<cygnal::Element> el = (*(ita));
266 size += el->getNameSize() + el->getDataSize() + 7;
268 _filesize = size;
270 boost::scoped_array<char> body ( new char[size + 20] );
271 std::fill_n(body.get(), size, 0);
272 ptr = body.get();
273 char* endPtr = ptr+size+20; // that's the amount we allocated..
275 for (ita = _amfobjs.begin(); ita != _amfobjs.end(); ita++) {
276 boost::shared_ptr<Element> el = (*(ita));
277 boost::shared_ptr<cygnal::Buffer> var = amf_obj.encodeProperty(el);
278 // boost::uint8_t *var = amf_obj.encodeProperty(el, outsize);
279 if (!var) {
280 continue;
282 size_t outsize = 0;
283 switch (el->getType()) {
284 case Element::BOOLEAN_AMF0:
285 outsize = el->getNameSize() + 4;
286 memcpy(ptr, var->reference(), outsize);
287 ptr += outsize;
288 break;
289 case Element::OBJECT_AMF0:
290 outsize = el->getNameSize() + 5;
291 assert(ptr+outsize < endPtr);
292 // outsize = el->getNameSize() + 5;
293 memcpy(ptr, var->reference(), outsize);
294 ptr += outsize;
295 *ptr++ = Element::OBJECT_END_AMF0;
296 // *ptr++ = 0; // objects are terminated too!
297 break;
298 case Element::NUMBER_AMF0:
299 outsize = el->getNameSize() + AMF0_NUMBER_SIZE + 3;
300 assert(ptr+outsize < endPtr);
301 memcpy(ptr, var->reference(), outsize);
302 ptr += outsize;
303 // *ptr++ = 0; // doubles are terminated too!
304 // *ptr++ = 0; // doubles are terminated too!
305 break;
306 case Element::STRING_AMF0:
307 if (el->getDataSize() == 0) {
308 assert(ptr+outsize+1 < endPtr);
309 memcpy(ptr, var->reference(), outsize+1);
310 ptr += outsize+1;
311 } else { // null terminate the string
312 assert(ptr+outsize < endPtr);
313 memcpy(ptr, var->reference(), outsize);
314 ptr += outsize;
315 *ptr++ = 0;
317 break;
318 default:
319 assert(ptr+outsize < endPtr);
320 memcpy(ptr, var->reference(), outsize);
321 ptr += outsize;
325 _filesize = ptr - body.get();
326 int len = name.size() + sizeof(boost::uint16_t) + 16;
327 boost::scoped_array<char> head ( new char[len + 4] );
328 memset(head.get(), 0, len);
329 ptr = head.get();
330 formatHeader(name);
331 for (it = _header.begin(); it != _header.end(); it++) {
332 *ptr++ = (*(it));
335 if ( ofs.write(head.get(), _header.size()).fail() )
337 log_error("Error writing %d bytes of header to output file %s",
338 _header.size(), filespec);
339 return false;
342 if ( ofs.write(body.get(), _filesize).fail() )
344 log_error("Error writing %d bytes of body to output file %s",
345 _filesize, filespec);
346 return false;
348 ofs.close();
350 return true;
353 /// \brief Read a .sol file from disk
355 /// @param filespec The name and path of the .sol file to parse.
357 /// @return true if this succeeded. false if it doesn't.
358 bool
359 SOL::readFile(const std::string &filespec)
361 // GNASH_REPORT_FUNCTION;
362 struct stat st;
363 boost::uint16_t size;
364 size_t bodysize;
366 // Make sure it's an SOL file
367 if (stat(filespec.c_str(), &st) != 0) return false;
369 try {
371 boost::uint8_t *ptr = 0;
373 std::ifstream ifs(filespec.c_str(), std::ios::binary);
375 _filesize = st.st_size;
376 boost::scoped_array<boost::uint8_t> buf(
377 new boost::uint8_t[_filesize + sizeof(int)]);
379 ptr = buf.get();
380 boost::uint8_t* tooFar = buf.get() + _filesize;
382 bodysize = st.st_size - 6;
383 _filespec = filespec;
384 ifs.read(reinterpret_cast<char *>(ptr), _filesize);
386 #ifndef GNASH_TRUST_AMF
387 ENSUREBYTES(ptr, tooFar, 2+4+10); // magic number, file size, file marker
388 #endif
390 // skip the magic number (will check later)
391 ptr += 2;
393 // extract the file size
394 boost::uint32_t length = *(reinterpret_cast<boost::uint32_t *>(ptr));
395 length = ntohl(length);
396 ptr += 4;
398 // skip the file marker field
399 ptr += 10;
401 // consistency check
402 if ((buf[0] == 0) && (buf[1] == 0xbf)) {
403 if (bodysize == length) {
404 log_debug("%s is an SOL file", filespec);
406 else {
407 log_error("%s looks like an SOL file, but the length is wrong. "
408 "Should be %d, got %d",
409 filespec, (_filesize - 6), length);
412 else {
413 log_error("%s isn't an SOL file", filespec);
416 #ifndef GNASH_TRUST_AMF
417 ENSUREBYTES(ptr, tooFar, 2);
418 #endif
420 // 2 bytes for the length of the object name, but it's also
421 // null terminated
422 size = *(reinterpret_cast<boost::uint16_t *>(ptr));
423 size = ntohs(size);
424 ptr += 2;
426 #ifndef GNASH_TRUST_AMF
427 ENSUREBYTES(ptr, tooFar, size+4); // 4 is the padding below
428 #endif
430 // TODO: make sure there's the null-termination, or
431 // force if if it's there by definition
432 _objname = reinterpret_cast<const char *>(ptr);
433 ptr += size;
435 // Go past the padding
436 ptr += 4;
438 AMF amf_obj;
439 boost::shared_ptr<cygnal::Element> el;
440 while ( ptr < tooFar) {
441 if (ptr) {
442 el = amf_obj.extractProperty(ptr, tooFar);
443 if (el != 0) {
444 ptr += amf_obj.totalsize() + 1;
445 _amfobjs.push_back(el);
447 else break;
448 } else break;
451 ifs.close();
452 return true;
454 catch (std::exception& e) {
455 log_error("Reading SharedObject %s: %s", filespec, e.what());
456 return false;
461 bool
462 SOL::updateSO(boost::shared_ptr<cygnal::Element> &newel)
464 // GNASH_REPORT_FUNCTION;
465 vector<boost::shared_ptr<cygnal::Element> >::iterator ita;
466 for (ita = _amfobjs.begin(); ita != _amfobjs.end(); ita++) {
467 boost::shared_ptr<cygnal::Element> oldel = (*(ita));
468 if (oldel == newel) {
469 oldel = newel;
472 return true;
475 bool
476 SOL::updateSO(int index, boost::shared_ptr<cygnal::Element> &el)
478 // GNASH_REPORT_FUNCTION;
479 _amfobjs[index] = el;
480 return true;
483 /// \brief Dump the internal data of this class in a human readable form.
485 /// @remarks This should only be used for debugging purposes.
486 void
487 SOL::dump()
489 using namespace std;
490 vector<boost::shared_ptr<cygnal::Element> >::iterator it;
492 cerr << "Dumping SOL file" << endl;
493 cerr << "The file name is: " << _filespec << endl;
494 cerr << "The size of the file is: " << _filesize << endl;
495 cerr << "The name of the object is: " << _objname << endl;
496 for (it = _amfobjs.begin(); it != _amfobjs.end(); it++) {
497 boost::shared_ptr<cygnal::Element> el = (*(it));
498 cerr << el->getName() << ": ";
499 if (el->getType() == Element::STRING_AMF0) {
500 if (el->getDataSize() != 0) {
501 cerr << el->to_string();
502 } else {
503 cerr << "null";
506 if (el->getType() == Element::NUMBER_AMF0) {
507 double ddd = el->to_number();
508 swapBytes(&ddd, sizeof(double));
509 cerr << ddd << endl;
510 // cerr << "( " << hexify(el->to_(), 8, false) << ")";
512 if (el->getType() == Element::BOOLEAN_AMF0) {
513 if (el->to_bool() == true) {
514 cerr << "true";
516 if (el->to_bool() == false) {
517 cerr << "false";
520 if (el->getType() == Element::OBJECT_AMF0) {
521 cerr << "is an object";
523 cerr << endl;
528 } // end of amf namespace
530 // local Variables:
531 // mode: C++
532 // indent-tabs-mode: t
533 // End: