/* grefirmware.cpp - A simple GRE firmware container class This class does on-the-fly firmware trancoding. The omission of a save function is on purpose, due to copyright considerations. Copyright 2016 Eric A. Cottrell This file is part of GREFwTool. Source code is available at https://github.com/LinuxSheeple-E/GREFwTool GREFwTool 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 3 of the License, or (at your option) any later version. GREFwTool 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 GREFwTool. If not, see . */ #include "include/grefirmware.h" #include /* Transcode tables */ const int transcodeTableSize = 256; static quint8 ws1080Pro668Table[transcodeTableSize] = { 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x89, 0x48, 0x89, 0x48, 0x89, 0x48, 0x89, 0x48, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0xF7, 0xDF, 0xF7, 0xDF, 0xF7, 0xDF, 0xF7, 0xDF, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x18, 0xC0, 0x18, 0xC0, 0x18, 0xC0, 0x18, 0xC0, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x0A, 0xF1, 0x0A, 0xF1, 0x0A, 0xF1, 0x0A, 0xF1, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x18, 0xC0, 0x18, 0xC0, 0x18, 0xC0, 0x18, 0xC0, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x2E, 0x93, 0x2E, 0x93, 0x2E, 0x93, 0x2E, 0x93, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x18, 0xC0, 0x18, 0xC0, 0x18, 0xC0, 0x18, 0xC0, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x0A, 0xF1, 0x0A, 0xF1, 0x0A, 0xF1, 0x0A, 0xF1, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x18, 0xC0, 0x18, 0xC0, 0x18, 0xC0, 0x18, 0xC0, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0x99, 0xC8, 0xF7, 0xDF, 0xF7, 0xDF, 0xF7, 0xDF, 0xF7, 0xDF, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x89, 0x48, 0x89, 0x48, 0x89, 0x48, 0x89, 0x48, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x9B, 0x79, 0x9B, 0x79, 0x9B, 0x79, 0x9B, 0x79, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x89, 0x48, 0x89, 0x48, 0x89, 0x48, 0x89, 0x48, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0xBF, 0x1B, 0xBF, 0x1B, 0xBF, 0x1B, 0xBF, 0x1B, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x89, 0x48, 0x89, 0x48, 0x89, 0x48, 0x89, 0x48, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x9B, 0x79, 0x9B, 0x79, 0x9B, 0x79, 0x9B, 0x79, 0x08, 0x40, }; static quint8 ws1080PSR800Table[transcodeTableSize] = { 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x7E, 0x97, 0x7E, 0x97, 0x7E, 0x97, 0x7E, 0x97, 0x7E, 0x97, 0x7E, 0x97, 0x7E, 0x97, 0x7E, 0x97, 0x7E, 0x97, 0x7E, 0x97, 0x7E, 0x97, 0x7E, 0x97, 0x7E, 0x97, 0x7E, 0x97, 0x7E, 0x97, 0x7E, 0x97, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0x5A, 0xF5, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xA7, 0xDB, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xEF, 0x1F, 0xEF, 0x1F, 0xEF, 0x1F, 0xEF, 0x1F, 0xEF, 0x1F, 0xEF, 0x1F, 0xEF, 0x1F, 0xEF, 0x1F, 0xEF, 0x1F, 0xEF, 0x1F, 0xEF, 0x1F, 0xEF, 0x1F, 0xEF, 0x1F, 0xEF, 0x1F, 0xEF, 0x1F, 0xEF, 0x1F, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xCB, 0x7D, 0xA7, 0xDB, }; static quint8 ws1080Pro18Table[transcodeTableSize] = { 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0x6C, 0xA6, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xFD, 0x2E, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, 0xB5, 0xEA, }; static struct patchInfo { qint32 vOffset; quint8 vXor; quint8 vData; struct { qint32 offset; quint8 data; } patches[4]; } ws1080PatchTable[] = { { 4, 0x4e, 56, 0, 0, 0, 0 }, { 4, 0x4e, 57, 0x27d2f, 16, 0, 0xc9, 1, 0x58, 0, 0}, { 4, 0x4e, 64, 0x27d2f, 16, 0, 0xca, 1, 0x6b, 0, 0}, { 4, 0x4e, 65, 0x27d3b, 16, 0, 0xca, 1, 0x6b, 0, 0}, { 4, 0x4e, 67, 0x2bcc7, 16, 0, 0xb6, 1, 0xb8, 0, 0}, { 4, 0x4e, 68, 0x289d3, 16, 0, 0xa3, 1, 0x45, 0, 0}, { 4, 0x4e, 69, 0x28d31, 16, 0, 0x47, 1, 0xa7, 0, 0}, { 0, 0, 0, 0, 0 } }; static struct { quint8 platformOld; quint8 platformNew; quint8 *xorTable; struct patchInfo *patchTable; } transcodeTable[] = { { 0xE4, 0xE6, ws1080Pro668Table, nullptr }, { 0xE6, 0xE4, ws1080Pro668Table, ws1080PatchTable }, { 0xE6, 0xEC, ws1080Pro18Table, ws1080PatchTable }, { 0xE6, 0xEE, ws1080PSR800Table, ws1080PatchTable }, { 0xEC, 0xE6, ws1080Pro18Table, nullptr }, { 0xEE, 0xE6, ws1080PSR800Table, nullptr }, { 0, 0, nullptr, nullptr } }; /* Constructor */ GREFirmware::GREFirmware(QObject *parent) : QObject(parent) { } /* Destructor */ GREFirmware::~GREFirmware() { } /* openFile - Open a firmware file on disk and load it */ bool GREFirmware::openFile(QString path) { // Display dialog to get firmware binary file QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open Firmware Image"), path, tr("Firmware Files (*.BIN)")); if(!fileName.isEmpty()) { quint8 headerBytes[4]; QFileInfo fileInfo(fileName); if(fileInfo.isFile() && fileInfo.isReadable()) { QFile imageFile(fileName); if (!imageFile.open(QIODevice::ReadOnly)) return false; pathInfo = fileInfo.canonicalPath(); // Clear and read header info header.platform = 0; header.imageSize = 0; imageData.clear(); if(imageFile.read(reinterpret_cast(&headerBytes), 4) != 4) { imageFile.close(); return false; } header.platform = headerBytes[0]; header.imageSize = (headerBytes[1] << 16) | (headerBytes[2] << 8) | headerBytes[3]; // check if size in header matches file size if(header.imageSize != (imageFile.size() - 4)) { imageFile.close(); return false; } // size the container and read the file into it imageData.reserve(header.imageSize); imageData = imageFile.read(header.imageSize); imageFile.close(); // check if size in header matches size in container if(imageData.size() != header.imageSize) return false; return true; } } return false; } /* transcode - an experimental conversion of firmware between hardware platforms */ bool GREFirmware::transcode(quint8 newPlatform) { int i; QByteArray::iterator it; quint8 *pXor = nullptr; struct patchInfo *pPatch = nullptr; quint8 oldPlatform = header.platform; i = 0; // If the transcoding is supported then set a pointer to the right table for(i = 0; transcodeTable[i].xorTable != nullptr; i++ ) { if((transcodeTable[i].platformOld == oldPlatform) && (transcodeTable[i].platformNew == newPlatform)) { pXor = transcodeTable[i].xorTable; pPatch = transcodeTable[i].patchTable; break; } } // Return if trancoding is not supported if(pXor == nullptr) return false; // Do the actual work if(pPatch != nullptr) { unsigned char uc; bool okFlag = false; while((pPatch->vOffset != 0) && (okFlag == false) ) { uc = imageData[pPatch->vOffset] ^ pPatch->vXor; // Assume entry with no patch data is the last version not needing fixing if((uc <= pPatch->vData) && (pPatch->patches[0].offset == 0)) { okFlag = true; } else if(uc == pPatch->vData) { for(i = 0; i < 4; i++) { if((uc = pPatch->patches[i].data) == 0) break; imageData[pPatch->patches[i].offset] = imageData[pPatch->patches[i].offset] ^ uc; } okFlag = true; } pPatch++; } if(okFlag == false) return false; } i = 0; for(it = imageData.begin(); it != imageData.end(); it++ ) { *it = *it ^ *(pXor + i); i = (i + 1) % transcodeTableSize; } header.platform = newPlatform; return true; } /* getFirstPacket - return the firmware header packet in the firmware update format The first byte is the platform The second through seventh byte is the size of the firmware using ASCII hex */ QByteArray &GREFirmware::getFirstPacket() { QString size = QString("%1").arg(static_cast(header.imageSize),6, 16, QLatin1Char('0')).toUpper(); headerPacket.clear(); offset = 0; headerPacket.append(header.platform); headerPacket.append(size.toLocal8Bit()); return headerPacket; } /* getNextPacket - return the next firmware data packet in firmware update format The packet contains the next 50 bytes of binary data using ASCII hex (100 ASCII characters total) */ QByteArray &GREFirmware::getNextPacket() { QString data; dataPacket.clear(); if(offset < imageData.size()) { dataPacket = imageData.mid(offset, 50).toHex().toUpper(); offset += 50; } return dataPacket; }