/* zenutils - Utilities for working with creative firmwares. * Copyright 2007 (c) Rasmus Ry * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "firmware.h" #include #include zen::firmware_entry::firmware_entry(bool big_endian) : _big_endian(big_endian) { } zen::firmware_entry::firmware_entry(const firmware_entry& copy) { assign(copy); } zen::firmware_entry& zen::firmware_entry::operator=(const firmware_entry& right) { assign(right); return *this; } bool zen::firmware_entry::read(std::istream& is) { // Read the header. is.read((char*)&_header, sizeof(firmware_header_t)); if (!is.good()) return false; // If the firmware is big-endian, swap the header values to little-endian. if (_big_endian) { _header.tag = shared::swap(_header.tag); if (_header.tag != 'NULL') { _header.size = shared::swap(_header.size); } } // Resize the bytes buffer to the size specified in the header. _bytes.resize(_header.size); // Read the entry contents. is.read(reinterpret_cast(&_bytes[0]), _header.size); return is.good(); } bool zen::firmware_entry::write(std::ostream& os) const { // Form a header using the current size of the bytes buffer. firmware_header_t header = { _header.tag, static_cast(_bytes.size()) }; // If the firmware is big-endian, swap the header values back into big-endian. if (_big_endian) { if (header.tag != 'NULL') { header.size = shared::swap(header.size); } header.tag = shared::swap(header.tag); } // Write the header. os.write((const char*)&header, sizeof(firmware_header_t)); if (!os.good()) return false; // Write the entry contents. os.write(reinterpret_cast(&_bytes[0]), static_cast(_bytes.size())); return os.good(); } bool zen::firmware_entry::is_big_endian() const { return _big_endian; } const zen::firmware_header_t& zen::firmware_entry::get_header() const { return _header; } zen::firmware_header_t& zen::firmware_entry::get_header() { return _header; } const shared::bytes& zen::firmware_entry::get_bytes() const { return _bytes; } shared::bytes& zen::firmware_entry::get_bytes() { return _bytes; } std::string zen::firmware_entry::get_name() const { char name[5]; *(dword*)name = shared::swap(_header.tag); name[4] = '\0'; // Determine if all characters in the tag are printable. bool isprintable = true; for (int i = 0; i < 4; i++) { if (!isprint((byte)name[i])) { isprintable = false; break; } } // If they are, simply return the tag as a string. if (isprintable) { return std::string(name); } // Otherwise, encode the tag into a hexadecimal string. char buffer[11]; sprintf(buffer, "0x%08x", _header.tag); return std::string(buffer); } std::string zen::firmware_entry::get_content_name() const { std::string name = get_name(); if (name == "DATA") { name = ""; int nameoff = is_big_endian() ? 1 : 0; for (int i = 0; i < 16; i++) { char c = get_bytes()[i * 2 + nameoff]; if (!c) break; name += c; } } else if (name == "EXT0") { name = ""; int nameoff = is_big_endian() ? 1 : 0; for (int i = 0; i < 12; i++) { char c = get_bytes()[i * 2 + nameoff]; if (!c) break; name += c; } } return name; } size_t zen::firmware_entry::get_content_offset() const { std::string name = get_name(); if (name == "DATA") { return 32; } else if (name == "EXT0") { return 24; } return 0; } size_t zen::firmware_entry::calc_size() const { return _bytes.size() + sizeof(firmware_header_t); } void zen::firmware_entry::assign(const firmware_entry& copy) { _big_endian = copy._big_endian; _header.tag = copy._header.tag; _header.size = copy._header.size; _bytes.assign(copy._bytes.begin(), copy._bytes.end()); } zen::firmware_archive::firmware_archive(bool big_endian) : _big_endian(big_endian) { } zen::firmware_archive::firmware_archive(const firmware_archive& copy) { assign(copy); } zen::firmware_archive& zen::firmware_archive::operator=(const firmware_archive& right) { assign(right); return *this; } bool zen::firmware_archive::read(std::istream& is) { // Read the root entry's header. firmware_header_t root; is.read((char*)&root, sizeof(firmware_header_t)); if (!is.good()) return false; if ((root.tag != 'CIFF') && (root.tag != 'FFIC')) { throw std::runtime_error("Invalid firmware archive format!"); } _big_endian = root.tag == 'FFIC' ? true : false; if (_big_endian) { root.tag = shared::swap(root.tag); root.size = shared::swap(root.size); } // Save the current stream position. std::istream::pos_type endpos = is.tellg(); std::istream::pos_type curpos = endpos; endpos += std::istream::pos_type(root.size); // Read untill the end of the root entry contents. while (curpos < endpos) { firmware_entry entry(_big_endian); if (!entry.read(is)) return false; _children.push_back(entry); curpos = is.tellg(); } curpos = is.tellg(); is.seekg(0, std::ios::end); endpos = is.tellg(); is.seekg(curpos); // Read untill the end of the file. while (((size_t)curpos + sizeof(firmware_header_t)) < endpos) { firmware_entry entry(_big_endian); if (!entry.read(is)) return false; _neighbours.push_back(entry); curpos = is.tellg(); } return true; } bool zen::firmware_archive::write(std::ostream& os) const { // Read the root entry's header. firmware_header_t root = {'CIFF', 0}; // Calculate the total size of all the children entries. for (firmware_entries::const_iterator i = _children.begin(); i != _children.end(); ++i) { root.size += i->calc_size(); } // If the firmware is big-endian, swap the header values back into big-endian. if (_big_endian) { root.tag = shared::swap(root.tag); root.size = shared::swap(root.size); } // Write the header. os.write((const char*)&root, sizeof(firmware_header_t)); if (!os.good()) return false; // Write all the child entries. for (firmware_entries::const_iterator i = _children.begin(); i != _children.end(); ++i) { if (!i->write(os)) return false; } // Write all the neighbour entries. for (firmware_entries::const_iterator i = _neighbours.begin(); i != _neighbours.end(); ++i) { if (!i->write(os)) return false; } return true; } bool zen::firmware_archive::is_big_endian() const { return _big_endian; } const zen::firmware_entries& zen::firmware_archive::get_children() const { return _children; } zen::firmware_entries& zen::firmware_archive::get_children() { return _children; } const zen::firmware_entries& zen::firmware_archive::get_neighbours() const { return _neighbours; } zen::firmware_entries& zen::firmware_archive::get_neighbours() { return _neighbours; } bool zen::firmware_archive::is_signed() const { for (firmware_entries::const_iterator i = _neighbours.begin(); i != _neighbours.end(); i++) { if (i->get_name() == "NULL") return true; } return false; } size_t zen::firmware_archive::calc_size() const { size_t size = sizeof(firmware_header_t); for (firmware_entries::const_iterator i = _children.begin(); i != _children.end(); i++) { size += i->calc_size(); } for (firmware_entries::const_iterator i = _neighbours.begin(); i != _neighbours.end(); i++) { size += i->calc_size(); } return size; } void zen::firmware_archive::assign(const firmware_archive& copy) { _big_endian = copy._big_endian; _children.assign(copy._children.begin(), copy._children.end()); _neighbours.assign(copy._neighbours.begin(), copy._neighbours.end()); }