GREFwTool/source/grefirmware.cpp

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;
}