275 lines
12 KiB
C++
275 lines
12 KiB
C++
/* 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 <eric.c.boston@gmail.com>
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
#include "include/grefirmware.h"
|
|
|
|
#include <QFileDialog>
|
|
|
|
/* 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<char *>(&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<int>(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;
|
|
}
|