Also play model name on model changes (#5416)
[opentx.git] / companion / src / storage / firmwareinterface.cpp
blob369af65042cafa930db709efcc1eb4de44f5a4ab
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "hexinterface.h"
22 #include "splash.h"
23 #include "firmwareinterface.h"
24 #include "helpers.h"
25 #include "storage.h"
27 #include <QFile>
29 #define FW_MARK "FW"
30 #define VERS_MARK "VERS"
31 #define DATE_MARK "DATE"
32 #define TIME_MARK "TIME"
33 #define EEPR_MARK "EEPR"
34 #define FSIZE_MAX Boards::getFlashSize(Board::BOARD_UNKNOWN)
36 FirmwareInterface::FirmwareInterface(const QString & filename):
37 flash(FSIZE_MAX, 0),
38 flashSize(0),
39 versionId(0),
40 eepromVersion(0),
41 eepromVariant(0),
42 splashOffset(0),
43 splashSize(0),
44 splashWidth(0),
45 splashHeight(0),
46 isValidFlag(false)
48 if (!filename.isEmpty()) {
49 QFile file(filename);
50 if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { // reading HEX TEXT file
51 QTextStream inputStream(&file);
52 flashSize = HexInterface(inputStream).load((uint8_t *)flash.data(), FSIZE_MAX);
53 file.close();
54 if (flashSize == 0) {
55 file.open(QIODevice::ReadOnly);
56 flashSize = file.read((char *)flash.data(), FSIZE_MAX);
61 if (flashSize > 0) {
62 flavour = seekLabel(FW_MARK);
63 version = seekLabel(VERS_MARK);
64 if (version.startsWith("opentx-")) {
65 // old version format
66 int index = version.lastIndexOf('-');
67 flavour = version.mid(0, index);
68 version = version.mid(index+1);
70 date = seekLabel(DATE_MARK);
71 time = seekLabel(TIME_MARK);
72 eepromId = seekLabel(EEPR_MARK);
74 if (eepromId.contains('-')) {
75 QStringList list = eepromId.split('-');
76 eepromVersion = list[0].toInt();
77 eepromVariant = list[1].toInt();
79 else {
80 eepromVersion = eepromId.toInt();
83 versionId = version2index(version);
84 seekSplash();
85 isValidFlag = !version.isEmpty();
89 QString FirmwareInterface::seekString(const QString & string)
91 QString result = "";
93 int start = flash.indexOf(string);
94 if (start > 0) {
95 start += string.length();
96 int end = -1;
97 for (int i=start; i<start+50; i++) {
98 char c = flash.at(i);
99 if (c == '\0' || c == '\036') {
100 end = i;
101 break;
104 if (end > 0) {
105 result = flash.mid(start, (end - start)).trimmed();
109 return result;
112 QString FirmwareInterface::seekLabel(const QString & label)
114 QString result = seekString(label + "\037\033:");
115 if (result.isEmpty()) {
116 result = seekString(label + "\037\075:"); // This is for Horus
118 if (!result.isEmpty()) {
119 return result;
121 return seekString(label + ":");
124 QString FirmwareInterface::getFlavour() const
126 if (flavour == "opentx-taranis-x9e")
127 return "opentx-x9e";
128 else if (flavour == "opentx-x9dp" || flavour == "opentx-taranis-plus")
129 return "opentx-x9d+";
130 else if (flavour == "opentx-taranis")
131 return "opentx-x9d";
132 else if (flavour == "opentx-horus")
133 return "opentx-x12s";
134 else
135 return flavour;
138 bool FirmwareInterface::isHardwareCompatible(const FirmwareInterface &previousFirmware) const
140 QString newFlavour = getFlavour();
141 if (newFlavour.isEmpty()) {
142 return true;
144 QString previousFlavour = previousFirmware.getFlavour();
145 if (previousFlavour.isEmpty()) {
146 return true;
148 return (newFlavour == previousFlavour);
151 bool FirmwareInterface::seekSplash(QByteArray splash)
153 int start = flash.indexOf(splash);
154 if (start>0) {
155 splashOffset = start;
156 splashSize = splash.size();
157 return true;
159 else {
160 return false;
164 bool FirmwareInterface::seekSplash(QByteArray sps, QByteArray spe, int size)
166 int start = 0;
167 while (start>=0) {
168 start = flash.indexOf(sps, start+1);
169 if (start>0) {
170 int end = start + sps.size() + size;
171 if (end == flash.indexOf(spe, end)) {
172 splashOffset = start + sps.size();
173 splashSize = end - start - sps.size();
174 return true;
176 else {
177 qDebug() << flash.indexOf(spe, start) << end << sps.size() << spe;
181 return false;
184 #define OTX_SPS_9X "SPS\0\200\100"
185 #define OTX_SPS_TARANIS "SPS\0\324\100"
186 #define OTX_SPS_SIZE 6
187 #define OTX_SPE "SPE"
188 #define OTX_SPE_SIZE 4
190 void FirmwareInterface::seekSplash()
192 splashSize = 0;
193 splashOffset = 0;
194 splashWidth = SPLASH_WIDTH;
195 splashHeight = SPLASH_HEIGHT;
196 splash_format = QImage::Format_Mono;
198 if (seekSplash(QByteArray((const char *)gr9x_splash, sizeof(gr9x_splash))) || seekSplash(QByteArray((const char *)gr9xv4_splash, sizeof(gr9xv4_splash)))) {
199 return;
202 if (seekSplash(QByteArray((const char *)er9x_splash, sizeof(er9x_splash)))) {
203 return;
206 if (seekSplash(QByteArray((const char *)opentx_splash, sizeof(opentx_splash)))) {
207 return;
210 if (seekSplash(QByteArray((const char *)opentxtaranis_splash, sizeof(opentxtaranis_splash)))) {
211 splashWidth = SPLASHX9D_WIDTH;
212 splashHeight = SPLASHX9D_HEIGHT;
213 splash_format = QImage::Format_Indexed8;
214 return;
217 if (seekSplash(QByteArray((const char *)ersky9x_splash, sizeof(ersky9x_splash)))) {
218 return;
221 if (seekSplash(QByteArray(OTX_SPS_9X, OTX_SPS_SIZE), QByteArray(OTX_SPE, OTX_SPE_SIZE), 1024)) {
222 return;
225 if (seekSplash(QByteArray(OTX_SPS_TARANIS, OTX_SPS_SIZE), QByteArray(OTX_SPE, OTX_SPE_SIZE), 6784)) {
226 splashWidth = SPLASHX9D_WIDTH;
227 splashHeight = SPLASHX9D_HEIGHT;
228 splash_format = QImage::Format_Indexed8;
229 return;
232 if (seekSplash(QByteArray(ERSKY9X_SPS, sizeof(ERSKY9X_SPS)), QByteArray(ERSKY9X_SPE, sizeof(ERSKY9X_SPE)), 1030)) {
233 return;
236 if (seekSplash(QByteArray(ERSPLASH_MARKER, sizeof(ERSPLASH_MARKER)))) {
237 splashOffset += sizeof(ERSPLASH_MARKER);
238 splashSize = sizeof(er9x_splash);
242 bool FirmwareInterface::setSplash(const QImage & newsplash)
244 if (splashOffset == 0 || splashSize == 0) {
245 return false;
248 char b[SPLASH_SIZE_MAX] = {0};
249 QColor color;
250 QByteArray splash;
251 if (splash_format == QImage::Format_Indexed8) {
252 for (unsigned int y=0; y<splashHeight; y++) {
253 unsigned int idx = (y/2)*splashWidth;
254 for (unsigned int x=0; x<splashWidth; x++, idx++) {
255 QRgb gray = qGray(newsplash.pixel(x, y));
256 uint8_t z = ((255-gray)*15)/255;
257 if (y & 1) z <<= 4;
258 b[idx] |= z;
262 else {
263 QColor black = QColor(0,0,0);
264 QImage blackNwhite = newsplash.convertToFormat(QImage::Format_MonoLSB);
265 for (uint y=0; y<splashHeight; y++) {
266 for (uint x=0; x<splashWidth; x++) {
267 color = QColor(blackNwhite.pixel(x,y));
268 b[splashWidth*(y/8) + x] |= ((color==black ? 1: 0)<<(y % 8));
272 splash.clear();
273 splash.append(b, splashSize);
274 flash.replace(splashOffset, splashSize, splash);
275 return true;
278 int FirmwareInterface::getSplashWidth()
280 return splashWidth;
283 uint FirmwareInterface::getSplashHeight()
285 return splashHeight;
288 QImage::Format FirmwareInterface::getSplashFormat()
290 return splash_format;
293 QImage FirmwareInterface::getSplash()
295 if (splashOffset == 0 || splashSize == 0) {
296 return QImage(); // empty image
299 if (splash_format == QImage::Format_Indexed8) {
300 QImage image(splashWidth, splashHeight, QImage::Format_RGB888);
301 if (splashOffset > 0) {
302 for (unsigned int y=0; y<splashHeight; y++) {
303 unsigned int idx = (y/2)*splashWidth;
304 for (unsigned int x=0; x<splashWidth; x++, idx++) {
305 uint8_t byte = flash.at(splashOffset+idx);
306 unsigned int z = (y & 1) ? (byte >> 4) : (byte & 0x0F);
307 z = 255-(z*255)/15;
308 QRgb rgb = qRgb(z, z, z);
309 image.setPixel(x, y, rgb);
313 return image;
315 else {
316 QImage image(splashWidth, splashHeight, QImage::Format_Mono);
317 if (splashOffset > 0) {
318 for (unsigned int y=0; y<splashHeight; y++) {
319 for(unsigned int x=0; x<splashWidth; x++) {
320 image.setPixel(x, y, (flash.at(splashOffset+(splashWidth*(y/8)+x)) & (1<<(y % 8))) ? 0 : 1);
324 return image;
328 bool FirmwareInterface::hasSplash()
330 return (splashOffset > 0 ? true : false);
333 bool FirmwareInterface::isValid()
335 return isValidFlag;
338 unsigned int FirmwareInterface::save(const QString & filename)
340 uint8_t * binflash = (uint8_t*)malloc(FSIZE_MAX);
341 if (binflash == NULL) {
342 return -1;
344 memcpy(binflash, flash.constData(), flashSize);
345 QFile file(filename);
347 int fileType = getStorageType(filename);
349 if (fileType == STORAGE_TYPE_HEX) {
350 if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { //reading HEX TEXT file
351 free(binflash);
352 return -1;
354 QTextStream outputStream(&file);
355 HexInterface hex=HexInterface(outputStream);
356 hex.save(binflash, flashSize);
358 else {
359 if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) { //reading HEX TEXT file
360 free(binflash);
361 return -1;
363 file.write((char*)binflash, flashSize);
366 file.close();
368 free(binflash);
369 return flashSize;