A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 387 lines 9.2 kB view raw
1/* zenutils - Utilities for working with creative firmwares. 2 * Copyright 2007 (c) Rasmus Ry <rasmus.ry{at}gmail.com> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 */ 18 19#include "firmware.h" 20#include <iostream> 21#include <stdexcept> 22 23 24zen::firmware_entry::firmware_entry(bool big_endian) 25 : _big_endian(big_endian) 26{ 27} 28 29zen::firmware_entry::firmware_entry(const firmware_entry& copy) 30{ 31 assign(copy); 32} 33 34zen::firmware_entry& zen::firmware_entry::operator=(const firmware_entry& right) 35{ 36 assign(right); 37 return *this; 38} 39 40 41bool zen::firmware_entry::read(std::istream& is) 42{ 43 // Read the header. 44 is.read((char*)&_header, sizeof(firmware_header_t)); 45 if (!is.good()) 46 return false; 47 48 // If the firmware is big-endian, swap the header values to little-endian. 49 if (_big_endian) 50 { 51 _header.tag = shared::swap(_header.tag); 52 if (_header.tag != 'NULL') 53 { 54 _header.size = shared::swap(_header.size); 55 } 56 } 57 58 // Resize the bytes buffer to the size specified in the header. 59 _bytes.resize(_header.size); 60 61 // Read the entry contents. 62 is.read(reinterpret_cast<char*>(&_bytes[0]), 63 _header.size); 64 65 return is.good(); 66} 67 68bool zen::firmware_entry::write(std::ostream& os) const 69{ 70 // Form a header using the current size of the bytes buffer. 71 firmware_header_t header = { 72 _header.tag, 73 static_cast<dword>(_bytes.size()) 74 }; 75 76 // If the firmware is big-endian, swap the header values back into big-endian. 77 if (_big_endian) 78 { 79 if (header.tag != 'NULL') 80 { 81 header.size = shared::swap(header.size); 82 } 83 header.tag = shared::swap(header.tag); 84 } 85 86 // Write the header. 87 os.write((const char*)&header, sizeof(firmware_header_t)); 88 if (!os.good()) 89 return false; 90 91 // Write the entry contents. 92 os.write(reinterpret_cast<const char*>(&_bytes[0]), 93 static_cast<std::streamsize>(_bytes.size())); 94 95 return os.good(); 96} 97 98 99bool zen::firmware_entry::is_big_endian() const 100{ 101 return _big_endian; 102} 103 104const zen::firmware_header_t& zen::firmware_entry::get_header() const 105{ 106 return _header; 107} 108zen::firmware_header_t& zen::firmware_entry::get_header() 109{ 110 return _header; 111} 112 113const shared::bytes& zen::firmware_entry::get_bytes() const 114{ 115 return _bytes; 116} 117shared::bytes& zen::firmware_entry::get_bytes() 118{ 119 return _bytes; 120} 121 122 123std::string zen::firmware_entry::get_name() const 124{ 125 char name[5]; 126 *(dword*)name = shared::swap(_header.tag); 127 name[4] = '\0'; 128 129 // Determine if all characters in the tag are printable. 130 bool isprintable = true; 131 for (int i = 0; i < 4; i++) 132 { 133 if (!isprint((byte)name[i])) 134 { 135 isprintable = false; 136 break; 137 } 138 } 139 140 // If they are, simply return the tag as a string. 141 if (isprintable) 142 { 143 return std::string(name); 144 } 145 146 // Otherwise, encode the tag into a hexadecimal string. 147 char buffer[11]; 148 sprintf(buffer, "0x%08x", _header.tag); 149 return std::string(buffer); 150} 151 152std::string zen::firmware_entry::get_content_name() const 153{ 154 std::string name = get_name(); 155 if (name == "DATA") 156 { 157 name = ""; 158 int nameoff = is_big_endian() ? 1 : 0; 159 for (int i = 0; i < 16; i++) 160 { 161 char c = get_bytes()[i * 2 + nameoff]; 162 if (!c) 163 break; 164 name += c; 165 } 166 } 167 else if (name == "EXT0") 168 { 169 name = ""; 170 int nameoff = is_big_endian() ? 1 : 0; 171 for (int i = 0; i < 12; i++) 172 { 173 char c = get_bytes()[i * 2 + nameoff]; 174 if (!c) 175 break; 176 name += c; 177 } 178 } 179 return name; 180} 181 182size_t zen::firmware_entry::get_content_offset() const 183{ 184 std::string name = get_name(); 185 if (name == "DATA") 186 { 187 return 32; 188 } 189 else if (name == "EXT0") 190 { 191 return 24; 192 } 193 return 0; 194} 195 196size_t zen::firmware_entry::calc_size() const 197{ 198 return _bytes.size() + sizeof(firmware_header_t); 199} 200 201 202void zen::firmware_entry::assign(const firmware_entry& copy) 203{ 204 _big_endian = copy._big_endian; 205 _header.tag = copy._header.tag; 206 _header.size = copy._header.size; 207 _bytes.assign(copy._bytes.begin(), copy._bytes.end()); 208} 209 210 211 212zen::firmware_archive::firmware_archive(bool big_endian) 213 : _big_endian(big_endian) 214{ 215} 216 217zen::firmware_archive::firmware_archive(const firmware_archive& copy) 218{ 219 assign(copy); 220} 221 222zen::firmware_archive& zen::firmware_archive::operator=(const firmware_archive& right) 223{ 224 assign(right); 225 return *this; 226} 227 228 229bool zen::firmware_archive::read(std::istream& is) 230{ 231 // Read the root entry's header. 232 firmware_header_t root; 233 is.read((char*)&root, sizeof(firmware_header_t)); 234 if (!is.good()) 235 return false; 236 237 if ((root.tag != 'CIFF') && (root.tag != 'FFIC')) 238 { 239 throw std::runtime_error("Invalid firmware archive format!"); 240 } 241 242 _big_endian = root.tag == 'FFIC' ? true : false; 243 if (_big_endian) 244 { 245 root.tag = shared::swap(root.tag); 246 root.size = shared::swap(root.size); 247 } 248 249 // Save the current stream position. 250 std::istream::pos_type endpos = is.tellg(); 251 std::istream::pos_type curpos = endpos; 252 endpos += std::istream::pos_type(root.size); 253 254 // Read untill the end of the root entry contents. 255 while (curpos < endpos) 256 { 257 firmware_entry entry(_big_endian); 258 if (!entry.read(is)) 259 return false; 260 261 _children.push_back(entry); 262 curpos = is.tellg(); 263 } 264 265 curpos = is.tellg(); 266 is.seekg(0, std::ios::end); 267 endpos = is.tellg(); 268 is.seekg(curpos); 269 270 // Read untill the end of the file. 271 while (((size_t)curpos + sizeof(firmware_header_t)) < endpos) 272 { 273 firmware_entry entry(_big_endian); 274 if (!entry.read(is)) 275 return false; 276 277 _neighbours.push_back(entry); 278 curpos = is.tellg(); 279 } 280 281 return true; 282} 283 284bool zen::firmware_archive::write(std::ostream& os) const 285{ 286 // Read the root entry's header. 287 firmware_header_t root = {'CIFF', 0}; 288 289 // Calculate the total size of all the children entries. 290 for (firmware_entries::const_iterator i = _children.begin(); 291 i != _children.end(); ++i) 292 { 293 root.size += i->calc_size(); 294 } 295 296 // If the firmware is big-endian, swap the header values back into big-endian. 297 if (_big_endian) 298 { 299 root.tag = shared::swap(root.tag); 300 root.size = shared::swap(root.size); 301 } 302 303 // Write the header. 304 os.write((const char*)&root, sizeof(firmware_header_t)); 305 if (!os.good()) 306 return false; 307 308 // Write all the child entries. 309 for (firmware_entries::const_iterator i = _children.begin(); 310 i != _children.end(); ++i) 311 { 312 if (!i->write(os)) 313 return false; 314 } 315 316 // Write all the neighbour entries. 317 for (firmware_entries::const_iterator i = _neighbours.begin(); 318 i != _neighbours.end(); ++i) 319 { 320 if (!i->write(os)) 321 return false; 322 } 323 324 return true; 325} 326 327 328bool zen::firmware_archive::is_big_endian() const 329{ 330 return _big_endian; 331} 332 333const zen::firmware_entries& zen::firmware_archive::get_children() const 334{ 335 return _children; 336} 337zen::firmware_entries& zen::firmware_archive::get_children() 338{ 339 return _children; 340} 341 342const zen::firmware_entries& zen::firmware_archive::get_neighbours() const 343{ 344 return _neighbours; 345} 346zen::firmware_entries& zen::firmware_archive::get_neighbours() 347{ 348 return _neighbours; 349} 350 351bool zen::firmware_archive::is_signed() const 352{ 353 for (firmware_entries::const_iterator i = _neighbours.begin(); 354 i != _neighbours.end(); i++) 355 { 356 if (i->get_name() == "NULL") 357 return true; 358 } 359 return false; 360} 361 362size_t zen::firmware_archive::calc_size() const 363{ 364 size_t size = sizeof(firmware_header_t); 365 366 for (firmware_entries::const_iterator i = _children.begin(); 367 i != _children.end(); i++) 368 { 369 size += i->calc_size(); 370 } 371 372 for (firmware_entries::const_iterator i = _neighbours.begin(); 373 i != _neighbours.end(); i++) 374 { 375 size += i->calc_size(); 376 } 377 378 return size; 379} 380 381 382void zen::firmware_archive::assign(const firmware_archive& copy) 383{ 384 _big_endian = copy._big_endian; 385 _children.assign(copy._children.begin(), copy._children.end()); 386 _neighbours.assign(copy._neighbours.begin(), copy._neighbours.end()); 387}