A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 311 lines 10 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 <iostream> 20#include <iomanip> 21#include <ctime> 22#include <getpot/GetPot> 23#include <file.h> 24#include <updater.h> 25#include <utils.h> 26#include <firmware.h> 27 28 29static const char VERSION[] = "0.1"; 30 31void print_version() 32{ 33 std::cout 34 << "update_extract - Extracts a Creative firmware from an updater" 35 " executable." << std::endl 36 << "Version " << VERSION << std::endl 37 << "Copyright (c) 2007 Rasmus Ry" << std::endl; 38} 39 40void print_help() 41{ 42 print_version(); 43 std::cout << std::endl 44 << "Usage: update_extract [command] [options]" << std::endl 45 << std::endl 46 << " Commands:" << std::endl 47 << " -h,--help" << std::endl 48 << " prints this message." << std::endl 49 << " -u,--updater [file]" << std::endl 50 << " specifies the updater executable." << std::endl 51 << std::endl 52 << " Options:" << std::endl 53 << " -V,--verbose" << std::endl 54 << " prints verbose messages." << std::endl 55 << " -f,--firmware [file]" << std::endl 56 << " specifies the firmware arhive file name." << std::endl 57 << " -k,--key [key]" << std::endl 58 << " specifies the firmware archive key." << std::endl 59 << " -o,--offset [offset]" << std::endl 60 << " specifies the firmware archive offset in c-style" 61 " hexadecimal." << std::endl 62 << std::endl 63 ; 64} 65 66std::string options_name(const std::string& name) 67{ 68 return shared::replace_extension(name, ".opt"); 69} 70 71std::string default_firmware_name(const std::string& name) 72{ 73 return shared::replace_extension(name, "_rk.bin"); 74} 75 76int process_arguments(int argc, char* argv[]) 77{ 78 //-------------------------------------------------------------------- 79 // Parse input variables. 80 //-------------------------------------------------------------------- 81 82 GetPot cl(argc, argv); 83 if (cl.size() == 1 || cl.search(2, "-h", "--help")) 84 { 85 print_help(); 86 return 1; 87 } 88 89 std::string updatername; 90 if (cl.search("-u") || cl.search("--updater")) 91 updatername = cl.next(""); 92 if (updatername.empty()) 93 { 94 std::cerr << "Updater executable must be specified." << std::endl; 95 return 2; 96 } 97 98 std::string firmarename = default_firmware_name(updatername); 99 if (cl.search("-f") || cl.search("--firmware")) 100 firmarename = cl.next(firmarename.c_str()); 101 102 bool verbose = false; 103 if (cl.search("-V") || cl.search("--verbose")) 104 verbose = true; 105 106 // Get or find the firmware archive key. 107 std::string key; 108 if (cl.search("-k") || cl.search("--key")) 109 key = cl.next(""); 110 111 if (key.empty()) 112 { 113 if (verbose) 114 std::cout << "[*] Looking for firmware archive key..." 115 << std::endl; 116 shared::bytes buffer; 117 if (!shared::read_file(updatername, buffer)) 118 { 119 std::cerr << "Failed to read the firmware updater executable." 120 << std::endl; 121 return 3; 122 } 123 key = zen::find_firmware_key(&buffer[0], buffer.size()); 124 if (key.empty()) 125 { 126 std::cerr << "Failed to find the firmware archive key." 127 << std::endl; 128 return 4; 129 } 130 } 131 132 // Get or find the firmware archive offset. 133 std::string offset; 134 dword offset_pa = 0; 135 if (cl.search("-o") || cl.search("--ofset")) 136 offset = cl.next(""); 137 138 if (offset.empty()) 139 { 140 if (verbose) 141 std::cout << "[*] Looking for firmware archive offset..." 142 << std::endl; 143 144 dword offset_va = 0; 145 if (!zen::find_firmware_archive(updatername, offset_va, offset_pa)) 146 { 147 std::cerr << "Failed to find the firmware archive offset." 148 << std::endl; 149 return 5; 150 } 151 } 152 else 153 { 154 int offset_val; 155 if (!sscanf(offset.c_str(), "0x%x", &offset_val)) 156 { 157 if (!sscanf(offset.c_str(), "0x%X", &offset_val)) 158 { 159 std::cerr << "\'" << offset 160 << "\' is not a valid c-style hexadecimal value." 161 << std::endl; 162 return 6; 163 } 164 } 165 offset_pa = static_cast<dword>(offset_val); 166 } 167 168 // Read firmware archive size. 169 shared::bytes buffer; 170 if (!shared::read_file(updatername, buffer, offset_pa, sizeof(dword))) 171 { 172 std::cerr << "Failed to read the firmware archive size." << std::endl; 173 return 7; 174 } 175 dword archive_size = *(dword*)&buffer[0]; 176 177 if (verbose) 178 { 179 std::cout << "[*] Printing input variables..." << std::endl; 180 std::cout << " Updater executable: " << updatername << std::endl; 181 std::cout << " Firmware archive: " << firmarename << std::endl; 182 std::cout << " Key: " << key << std::endl; 183 std::cout << " Offset: " 184 << std::hex << std::showbase << std::setw(10) 185 << std::setfill('0') << std::internal 186 << offset_pa << std::endl; 187 std::cout << " Size: " 188 << std::hex << std::showbase << std::setw(10) 189 << std::setfill('0') << std::internal 190 << archive_size << std::endl; 191 } 192 193 194 //-------------------------------------------------------------------- 195 // Extract the firmware archive from the updater. 196 //-------------------------------------------------------------------- 197 198 if (verbose) 199 std::cout << "[*] Reading firmware archive..." << std::endl; 200 201 // Read the firmware archive. 202 offset_pa += sizeof(dword); 203 if (!shared::read_file(updatername, buffer, offset_pa, archive_size)) 204 { 205 std::cerr << "Failed to read the firmware archive." << std::endl; 206 return 8; 207 } 208 209 if (verbose) 210 std::cout << "[*] Decrypting firmware archive..." << std::endl; 211 212 // Decrypt the firmware archive. 213 if (!zen::crypt_firmware(key.c_str(), &buffer[0], buffer.size())) 214 { 215 std::cerr << "Failed to decrypt the firmware archive." << std::endl; 216 return 9; 217 } 218 219 if (verbose) 220 std::cout << "[*] Decompressing firmware archive..." << std::endl; 221 222 // Inflate the firmware archive to the output file. 223 if (!shared::inflate_to_file(buffer, firmarename.c_str())) 224 { 225 std::cerr << "Failed to decompress the firmware archive." << std::endl; 226 return 10; 227 } 228 229 if (verbose) 230 std::cout << "[*] Normalizing firmware archive..." << std::endl; 231 232 /* We only know the compressed size of the archive, not the uncompressed one. 233 * In some cases (like in some Zen X-Fi updater), the uncompressed archive 234 * has extraneous zero bytes at the end which will make the device reject 235 * the firmware. To normalize the archives, we simply read it and write it 236 * again, so that it will remove all useless extra bytes */ 237 zen::firmware_archive archive(false); 238 std::ifstream ifs; 239 ifs.open(firmarename.c_str(), std::ios::binary); 240 if (!ifs) 241 { 242 std::cerr << "Failed to open the firmware archive." << std::endl; 243 return 11; 244 } 245 246 if (!archive.read(ifs)) 247 { 248 std::cerr << "Failed to read the firmware archive." << std::endl; 249 return 12; 250 } 251 ifs.close(); 252 253 std::ofstream ofs; 254 ofs.open(firmarename.c_str(), std::ios::binary); 255 if (!archive.write(ofs)) 256 { 257 std::cerr << "Failed to write the firmware archive." << std::endl; 258 return 13; 259 } 260 ofs.close(); 261 262 //-------------------------------------------------------------------- 263 // Generate an options file for the extracted firmware archive. 264 //-------------------------------------------------------------------- 265 266 // Get options filename for the given input file. 267 std::string optionsname = options_name(updatername); 268 269 if (verbose) 270 std::cout << "[*] Producing options file..." << std::endl; 271 272 // Produce options file for the given input file. 273 ofs.open(optionsname.c_str(), std::ios::binary); 274 if (!ofs) 275 { 276 std::cerr << "Failed to create firmware archive options file." 277 << std::endl; 278 return 11; 279 } 280 281 time_t timeval = time(NULL); 282 ofs << "# Options file generated at: " << ctime(&timeval) 283 << "updater = \'" << shared::double_quote(updatername) << "\'" 284 << std::endl 285 << "firmware = \'" << shared::double_quote(firmarename) << "\'" 286 << std::endl 287 << "offset = " << (offset_pa - sizeof(dword)) << std::endl 288 << "size = " << archive_size << std::endl 289 << "key = \'" << key << "\'" << std::endl; 290 291 return 0; 292} 293 294int main(int argc, char* argv[]) 295{ 296 try 297 { 298 return process_arguments(argc, argv); 299 } 300 catch (const std::exception& xcpt) 301 { 302 std::cerr << "Exception caught: " << xcpt.what() << std::endl; 303 return -1; 304 } 305 catch (...) 306 { 307 std::cerr << "Unknown exception caught." << std::endl; 308 return -2; 309 } 310 return -3; 311}