summaryrefslogtreecommitdiff
path: root/utils/rbutilqt/base
diff options
context:
space:
mode:
Diffstat (limited to 'utils/rbutilqt/base')
-rw-r--r--utils/rbutilqt/base/archiveutil.cpp30
-rw-r--r--utils/rbutilqt/base/archiveutil.h41
-rw-r--r--utils/rbutilqt/base/autodetection.cpp376
-rw-r--r--utils/rbutilqt/base/autodetection.h72
-rw-r--r--utils/rbutilqt/base/bootloaderinstallams.cpp201
-rw-r--r--utils/rbutilqt/base/bootloaderinstallams.h42
-rw-r--r--utils/rbutilqt/base/bootloaderinstallbase.cpp302
-rw-r--r--utils/rbutilqt/base/bootloaderinstallbase.h118
-rw-r--r--utils/rbutilqt/base/bootloaderinstallbspatch.cpp178
-rw-r--r--utils/rbutilqt/base/bootloaderinstallbspatch.h47
-rw-r--r--utils/rbutilqt/base/bootloaderinstallchinachip.cpp133
-rw-r--r--utils/rbutilqt/base/bootloaderinstallchinachip.h41
-rw-r--r--utils/rbutilqt/base/bootloaderinstallfile.cpp159
-rw-r--r--utils/rbutilqt/base/bootloaderinstallfile.h48
-rw-r--r--utils/rbutilqt/base/bootloaderinstallhelper.cpp140
-rw-r--r--utils/rbutilqt/base/bootloaderinstallhelper.h36
-rw-r--r--utils/rbutilqt/base/bootloaderinstallhex.cpp271
-rw-r--r--utils/rbutilqt/base/bootloaderinstallhex.h53
-rw-r--r--utils/rbutilqt/base/bootloaderinstallimx.cpp193
-rw-r--r--utils/rbutilqt/base/bootloaderinstallimx.h47
-rw-r--r--utils/rbutilqt/base/bootloaderinstallipod.cpp272
-rw-r--r--utils/rbutilqt/base/bootloaderinstallipod.h51
-rw-r--r--utils/rbutilqt/base/bootloaderinstallmi4.cpp162
-rw-r--r--utils/rbutilqt/base/bootloaderinstallmi4.h48
-rw-r--r--utils/rbutilqt/base/bootloaderinstallmpio.cpp143
-rw-r--r--utils/rbutilqt/base/bootloaderinstallmpio.h43
-rw-r--r--utils/rbutilqt/base/bootloaderinstalls5l.cpp437
-rw-r--r--utils/rbutilqt/base/bootloaderinstalls5l.h71
-rw-r--r--utils/rbutilqt/base/bootloaderinstallsansa.cpp286
-rw-r--r--utils/rbutilqt/base/bootloaderinstallsansa.h51
-rw-r--r--utils/rbutilqt/base/bootloaderinstalltcc.cpp165
-rw-r--r--utils/rbutilqt/base/bootloaderinstalltcc.h44
-rw-r--r--utils/rbutilqt/base/encoderbase.cpp86
-rw-r--r--utils/rbutilqt/base/encoderbase.h63
-rw-r--r--utils/rbutilqt/base/encoderexe.cpp78
-rw-r--r--utils/rbutilqt/base/encoderexe.h53
-rw-r--r--utils/rbutilqt/base/encoderlame.cpp312
-rw-r--r--utils/rbutilqt/base/encoderlame.h72
-rw-r--r--utils/rbutilqt/base/encoderrbspeex.cpp119
-rw-r--r--utils/rbutilqt/base/encoderrbspeex.h61
-rw-r--r--utils/rbutilqt/base/encttssettings.cpp70
-rw-r--r--utils/rbutilqt/base/encttssettings.h128
-rw-r--r--utils/rbutilqt/base/httpget.cpp256
-rw-r--r--utils/rbutilqt/base/httpget.h111
-rw-r--r--utils/rbutilqt/base/mspackutil.cpp164
-rw-r--r--utils/rbutilqt/base/mspackutil.h51
-rw-r--r--utils/rbutilqt/base/playerbuildinfo.cpp362
-rw-r--r--utils/rbutilqt/base/playerbuildinfo.h123
-rw-r--r--utils/rbutilqt/base/progressloggerinterface.h60
-rw-r--r--utils/rbutilqt/base/rbsettings.cpp207
-rw-r--r--utils/rbutilqt/base/rbsettings.h105
-rw-r--r--utils/rbutilqt/base/rockboxinfo.cpp83
-rw-r--r--utils/rbutilqt/base/rockboxinfo.h54
-rw-r--r--utils/rbutilqt/base/system.cpp519
-rw-r--r--utils/rbutilqt/base/system.h53
-rw-r--r--utils/rbutilqt/base/talkfile.cpp300
-rw-r--r--utils/rbutilqt/base/talkfile.h83
-rw-r--r--utils/rbutilqt/base/talkgenerator.cpp337
-rw-r--r--utils/rbutilqt/base/talkgenerator.h91
-rw-r--r--utils/rbutilqt/base/ttsbase.cpp122
-rw-r--r--utils/rbutilqt/base/ttsbase.h70
-rw-r--r--utils/rbutilqt/base/ttscarbon.cpp443
-rw-r--r--utils/rbutilqt/base/ttscarbon.h73
-rw-r--r--utils/rbutilqt/base/ttsespeak.h42
-rw-r--r--utils/rbutilqt/base/ttsespeakng.h41
-rw-r--r--utils/rbutilqt/base/ttsexes.cpp127
-rw-r--r--utils/rbutilqt/base/ttsexes.h61
-rw-r--r--utils/rbutilqt/base/ttsfestival.cpp412
-rw-r--r--utils/rbutilqt/base/ttsfestival.h72
-rw-r--r--utils/rbutilqt/base/ttsflite.h43
-rw-r--r--utils/rbutilqt/base/ttsmimic.h41
-rw-r--r--utils/rbutilqt/base/ttsmssp.h43
-rw-r--r--utils/rbutilqt/base/ttssapi.cpp274
-rw-r--r--utils/rbutilqt/base/ttssapi.h77
-rw-r--r--utils/rbutilqt/base/ttssapi4.h43
-rw-r--r--utils/rbutilqt/base/ttsswift.h40
-rw-r--r--utils/rbutilqt/base/uninstall.cpp126
-rw-r--r--utils/rbutilqt/base/uninstall.h63
-rw-r--r--utils/rbutilqt/base/utils.cpp1062
-rw-r--r--utils/rbutilqt/base/utils.h64
-rw-r--r--utils/rbutilqt/base/voicefile.cpp362
-rw-r--r--utils/rbutilqt/base/voicefile.h77
-rw-r--r--utils/rbutilqt/base/zipinstaller.cpp204
-rw-r--r--utils/rbutilqt/base/zipinstaller.h88
-rw-r--r--utils/rbutilqt/base/ziputil.cpp302
-rw-r--r--utils/rbutilqt/base/ziputil.h53
86 files changed, 12727 insertions, 0 deletions
diff --git a/utils/rbutilqt/base/archiveutil.cpp b/utils/rbutilqt/base/archiveutil.cpp
new file mode 100644
index 0000000000..d5f0a12471
--- /dev/null
+++ b/utils/rbutilqt/base/archiveutil.cpp
@@ -0,0 +1,30 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2013 Amaury Pouly
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include <QDebug>
21#include "archiveutil.h"
22
23ArchiveUtil::ArchiveUtil(QObject* parent)
24 :QObject(parent)
25{
26}
27
28ArchiveUtil::~ArchiveUtil()
29{
30}
diff --git a/utils/rbutilqt/base/archiveutil.h b/utils/rbutilqt/base/archiveutil.h
new file mode 100644
index 0000000000..76616728c3
--- /dev/null
+++ b/utils/rbutilqt/base/archiveutil.h
@@ -0,0 +1,41 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2013 Amaury Pouly
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef ARCHIVEUTIL_H
20#define ARCHIVEUTIL_H
21
22#include <QtCore>
23
24class ArchiveUtil : public QObject
25{
26 Q_OBJECT
27
28 public:
29 ArchiveUtil(QObject* parent);
30 ~ArchiveUtil();
31 virtual bool close(void) = 0;
32 virtual bool extractArchive(const QString& dest, QString file = "") = 0;
33 virtual QStringList files(void) = 0;
34
35 signals:
36 void logProgress(int, int);
37 void logItem(QString, int);
38};
39#endif
40
41
diff --git a/utils/rbutilqt/base/autodetection.cpp b/utils/rbutilqt/base/autodetection.cpp
new file mode 100644
index 0000000000..341f219c30
--- /dev/null
+++ b/utils/rbutilqt/base/autodetection.cpp
@@ -0,0 +1,376 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "autodetection.h"
21#include "rbsettings.h"
22#include "playerbuildinfo.h"
23
24#include "../ipodpatcher/ipodpatcher.h"
25#include "../sansapatcher/sansapatcher.h"
26
27
28#include "system.h"
29#include "utils.h"
30#include "rockboxinfo.h"
31#include "Logger.h"
32
33Autodetection::Autodetection(QObject* parent): QObject(parent)
34{
35}
36
37
38bool Autodetection::detect(void)
39{
40 QMap<PlayerStatus, QString> states;
41 states[PlayerOk] = "Ok";
42 states[PlayerAmbiguous] = "Ambiguous";
43 states[PlayerError] = "Error";
44 states[PlayerIncompatible] = "Incompatible";
45 states[PlayerMtpMode] = "MtpMode";
46
47 // clear detection state
48 m_detected.clear();
49
50 detectUsb();
51 mergeMounted();
52 mergePatcher();
53 // if any entry with usbdevices containing a value is left that entry
54 // hasn't been merged later. This indicates a problem during detection
55 // (ambiguous player but refining it failed). In this case create an entry
56 // for eacho of those so the user can select.
57 QList<struct Detected> detected;
58 for(int i = 0; i < m_detected.size(); ++i) {
59 int j = m_detected.at(i).usbdevices.size();
60 if(j > 0) {
61 struct Detected entry = m_detected.at(i);
62 while(j--) {
63 struct Detected d;
64 d.device = entry.usbdevices.at(j);
65 d.mountpoint = entry.mountpoint;
66 d.status = PlayerAmbiguous;
67 detected.append(d);
68 }
69 }
70 else {
71 detected.append(m_detected.at(i));
72 }
73 }
74 m_detected = detected;
75 for(int i = 0; i < m_detected.size(); ++i) {
76 LOG_INFO() << "Detected player:" << m_detected.at(i).device
77 << "at" << m_detected.at(i).mountpoint
78 << states[m_detected.at(i).status];
79 }
80
81 return m_detected.size() > 0;
82}
83
84
85/** @brief detect devices based on usb pid / vid.
86 */
87void Autodetection::detectUsb()
88{
89 // usb pid detection
90 QList<uint32_t> attached;
91 attached = System::listUsbIds();
92
93 int i = attached.size();
94 while(i--) {
95 QStringList a = PlayerBuildInfo::instance()->value(PlayerBuildInfo::UsbIdTargetList, attached.at(i)).toStringList();
96 if(a.size() > 0) {
97 struct Detected d;
98 d.status = PlayerOk;
99 d.usbdevices = a;
100 m_detected.append(d);
101 LOG_INFO() << "[USB] detected supported player" << d.usbdevices;
102 }
103 QStringList b = PlayerBuildInfo::instance()->value(PlayerBuildInfo::UsbIdErrorList, attached.at(i)).toStringList();
104 if(b.size() > 0) {
105 struct Detected d;
106 d.status = PlayerMtpMode;
107 d.usbdevices = b;
108 m_detected.append(d);
109 LOG_WARNING() << "[USB] detected problem with player" << d.device;
110 }
111 QString idstring = QString("%1").arg(attached.at(i), 8, 16, QChar('0'));
112 if(!PlayerBuildInfo::instance()->value(
113 PlayerBuildInfo::DisplayName, idstring).toString().isEmpty()) {
114 struct Detected d;
115 d.status = PlayerIncompatible;
116 d.device = idstring;
117 m_detected.append(d);
118 LOG_WARNING() << "[USB] detected incompatible player" << d.device;
119 }
120 }
121}
122
123
124// Merge players detected by checking mounted filesystems for known files:
125// - rockbox-info.txt / rbutil.log
126// - player specific files
127void Autodetection::mergeMounted(void)
128{
129 QStringList mounts = Utils::mountpoints(Utils::MountpointsSupported);
130 LOG_INFO() << "paths to check:" << mounts;
131
132 for(int i = 0; i < mounts.size(); i++)
133 {
134 // do the file checking
135 QDir dir(mounts.at(i));
136 if(dir.exists())
137 {
138 // check logfile first.
139 if(QFile(mounts.at(i) + "/.rockbox/rbutil.log").exists()) {
140 QSettings log(mounts.at(i) + "/.rockbox/rbutil.log",
141 QSettings::IniFormat, this);
142 if(!log.value("platform").toString().isEmpty()) {
143 struct Detected d;
144 d.device = log.value("platform").toString();
145 d.mountpoint = mounts.at(i);
146 d.status = PlayerOk;
147 updateDetectedDevice(d);
148 LOG_INFO() << "rbutil.log detected:"
149 << log.value("platform").toString() << mounts.at(i);
150 }
151 }
152
153 // check rockbox-info.txt afterwards.
154 RockboxInfo info(mounts.at(i));
155 if(info.success())
156 {
157 struct Detected d;
158 d.device = info.target();
159 d.mountpoint = mounts.at(i);
160 d.status = PlayerOk;
161 updateDetectedDevice(d);
162 LOG_INFO() << "rockbox-info.txt detected:"
163 << info.target() << mounts.at(i);
164 }
165
166 // check for some specific files in root folder
167 QDir root(mounts.at(i));
168 QStringList rootentries = root.entryList(QDir::Files);
169 if(rootentries.contains("archos.mod", Qt::CaseInsensitive))
170 {
171 // archos.mod in root folder -> Archos Player
172 struct Detected d;
173 d.device = "player";
174 d.mountpoint = mounts.at(i);
175 d.status = PlayerOk;
176 updateDetectedDevice(d);
177 }
178 if(rootentries.contains("ONDIOST.BIN", Qt::CaseInsensitive))
179 {
180 // ONDIOST.BIN in root -> Ondio FM
181 struct Detected d;
182 d.device = "ondiofm";
183 d.mountpoint = mounts.at(i);
184 d.status = PlayerOk;
185 updateDetectedDevice(d);
186 }
187 if(rootentries.contains("ONDIOSP.BIN", Qt::CaseInsensitive))
188 {
189 // ONDIOSP.BIN in root -> Ondio SP
190 struct Detected d;
191 d.device = "ondiosp";
192 d.mountpoint = mounts.at(i);
193 d.status = PlayerOk;
194 updateDetectedDevice(d);
195 }
196 if(rootentries.contains("ajbrec.ajz", Qt::CaseInsensitive))
197 {
198 LOG_INFO() << "ajbrec.ajz found. Trying detectAjbrec()";
199 struct Detected d;
200 d.device = detectAjbrec(mounts.at(i));
201 d.mountpoint = mounts.at(i);
202 d.status = PlayerOk;
203 if(!d.device.isEmpty()) {
204 LOG_INFO() << d.device;
205 updateDetectedDevice(d);
206 }
207 }
208 // detection based on player specific folders
209 QStringList rootfolders = root.entryList(QDir::Dirs
210 | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
211 if(rootfolders.contains("GBSYSTEM", Qt::CaseInsensitive))
212 {
213 // GBSYSTEM folder -> Gigabeat
214 struct Detected d;
215 d.device = "gigabeatf";
216 d.mountpoint = mounts.at(i);
217 updateDetectedDevice(d);
218 }
219 }
220 }
221#if 0
222 // Ipods have a folder "iPod_Control" in the root.
223 for(int i = 0; i < m_detected.size(); ++i) {
224 struct Detected entry = m_detected.at(i);
225 for(int j = 0; j < entry.usbdevices.size(); ++j) {
226 // limit this to Ipods only.
227 if(!entry.usbdevices.at(j).startsWith("ipod")
228 && !entry.device.startsWith("ipod")) {
229 continue;
230 }
231 // look for iPod_Control on all supported volumes.
232 for(int k = 0; k < mounts.size(); k++) {
233 QDir root(mounts.at(k));
234 QStringList rootfolders = root.entryList(QDir::Dirs
235 | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
236 if(rootfolders.contains("iPod_Control", Qt::CaseInsensitive)) {
237 entry.mountpoint = mounts.at(k);
238 m_detected.takeAt(i);
239 m_detected.append(entry);
240 }
241 }
242 }
243 }
244#endif
245
246}
247
248
249void Autodetection::mergePatcher(void)
250{
251 int n;
252 // try ipodpatcher
253 // initialize sector buffer. Needed.
254 struct ipod_t ipod;
255 ipod.sectorbuf = nullptr;
256 ipod_alloc_buffer(&ipod, BUFFER_SIZE);
257 n = ipod_scan(&ipod);
258 // FIXME: handle more than one Ipod connected in ipodpatcher.
259 if(n == 1) {
260 LOG_INFO() << "Ipod found:" << ipod.modelstr << "at" << ipod.diskname;
261 // since resolveMountPoint is doing exact matches we need to select
262 // the correct partition.
263 QString mp(ipod.diskname);
264#ifdef Q_OS_LINUX
265 mp.append("2");
266#endif
267#ifdef Q_OS_MACX
268 mp.append("s2");
269#endif
270 struct Detected d;
271 d.device = ipod.targetname;
272 d.mountpoint = Utils::resolveMountPoint(mp);
273 // if the found ipod is a macpod also notice it as device with problem.
274 if(ipod.macpod)
275 d.status = PlayerWrongFilesystem;
276 else
277 d.status = PlayerOk;
278 updateDetectedDevice(d);
279 }
280 else {
281 LOG_INFO() << "ipodpatcher: no Ipod found." << n;
282 }
283 ipod_dealloc_buffer(&ipod);
284
285 // try sansapatcher
286 // initialize sector buffer. Needed.
287 struct sansa_t sansa;
288 sansa_alloc_buffer(&sansa, BUFFER_SIZE);
289 n = sansa_scan(&sansa);
290 if(n == 1) {
291 LOG_INFO() << "Sansa found:"
292 << sansa.targetname << "at" << sansa.diskname;
293 QString mp(sansa.diskname);
294#ifdef Q_OS_LINUX
295 mp.append("1");
296#endif
297#ifdef Q_OS_MACX
298 mp.append("s1");
299#endif
300 struct Detected d;
301 d.device = QString("sansa%1").arg(sansa.targetname);
302 d.mountpoint = Utils::resolveMountPoint(mp);
303 d.status = PlayerOk;
304 updateDetectedDevice(d);
305 }
306 else {
307 LOG_INFO() << "sansapatcher: no Sansa found." << n;
308 }
309 sansa_dealloc_buffer(&sansa);
310}
311
312
313QString Autodetection::detectAjbrec(QString root)
314{
315 QFile f(root + "/ajbrec.ajz");
316 char header[24];
317 f.open(QIODevice::ReadOnly);
318 if(!f.read(header, 24)) return QString();
319 f.close();
320
321 // check the header of the file.
322 // recorder v1 had a 6 bytes sized header
323 // recorder v2, FM, Ondio SP and FM have a 24 bytes header.
324
325 // recorder v1 has the binary length in the first 4 bytes, so check
326 // for them first.
327 int len = (header[0]<<24) | (header[1]<<16) | (header[2]<<8) | header[3];
328 LOG_INFO() << "abjrec.ajz possible bin length:" << len
329 << "file len:" << f.size();
330 if((f.size() - 6) == len)
331 return "recorder";
332
333 // size didn't match, now we need to assume we have a headerlength of 24.
334 switch(header[11]) {
335 case 2:
336 return "recorderv2";
337 case 4:
338 return "fmrecorder";
339 case 8:
340 return "ondiofm";
341 case 16:
342 return "ondiosp";
343 default:
344 break;
345 }
346 return QString();
347}
348
349
350int Autodetection::findDetectedDevice(QString device)
351{
352 int i = m_detected.size();
353 while(i--) {
354 if(m_detected.at(i).usbdevices.contains(device))
355 return i;
356 }
357 i = m_detected.size();
358 while(i--) {
359 if(m_detected.at(i).device == device)
360 return i;
361 }
362 return -1;
363}
364
365
366void Autodetection::updateDetectedDevice(Detected& entry)
367{
368 int index = findDetectedDevice(entry.device);
369 if(index < 0) {
370 m_detected.append(entry);
371 }
372 else {
373 m_detected.takeAt(index);
374 m_detected.append(entry);
375 }
376}
diff --git a/utils/rbutilqt/base/autodetection.h b/utils/rbutilqt/base/autodetection.h
new file mode 100644
index 0000000000..cdbb94d303
--- /dev/null
+++ b/utils/rbutilqt/base/autodetection.h
@@ -0,0 +1,72 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21
22#ifndef AUTODETECTION_H_
23#define AUTODETECTION_H_
24
25#include <QObject>
26#include <QString>
27#include <QList>
28#include <QStringList>
29
30class Autodetection :public QObject
31{
32 Q_OBJECT
33
34public:
35 Autodetection(QObject* parent=nullptr);
36
37 enum PlayerStatus {
38 PlayerOk,
39 PlayerIncompatible,
40 PlayerMtpMode,
41 PlayerWrongFilesystem,
42 PlayerError,
43 PlayerAmbiguous,
44 };
45
46 struct Detected {
47 QString device;
48 QStringList usbdevices;
49 QString mountpoint;
50 enum PlayerStatus status;
51 };
52
53 bool detect();
54
55 QList<struct Detected> detected(void) { return m_detected; }
56
57private:
58 QString resolveMountPoint(QString);
59 void detectUsb(void);
60 void mergeMounted(void);
61 void mergePatcher(void);
62 QString detectAjbrec(QString);
63 int findDetectedDevice(QString device);
64 void updateDetectedDevice(struct Detected& entry);
65
66 QList<struct Detected> m_detected;
67 QList<int> m_usbconid;
68};
69
70
71#endif /*AUTODETECTION_H_*/
72
diff --git a/utils/rbutilqt/base/bootloaderinstallams.cpp b/utils/rbutilqt/base/bootloaderinstallams.cpp
new file mode 100644
index 0000000000..be43ed52db
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallams.cpp
@@ -0,0 +1,201 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "bootloaderinstallbase.h"
21#include "bootloaderinstallams.h"
22#include "Logger.h"
23
24#include "../mkamsboot/mkamsboot.h"
25
26BootloaderInstallAms::BootloaderInstallAms(QObject *parent)
27 : BootloaderInstallBase(parent)
28{
29}
30
31QString BootloaderInstallAms::ofHint()
32{
33 return tr("Bootloader installation requires you to provide "
34 "a copy of the original Sandisk firmware (bin file). "
35 "This firmware file will be patched and then installed to your "
36 "player along with the rockbox bootloader. "
37 "You need to download this file yourself due to legal "
38 "reasons. Please browse the "
39 "<a href='http://forums.sandisk.com/sansa/'>Sansa Forums</a> "
40 "or refer to the "
41 "<a href='http://www.rockbox.org/manual.shtml'>manual</a> and "
42 "the <a href='http://www.rockbox.org/wiki/SansaAMS'>SansaAMS</a> "
43 "wiki page on how to obtain this file.<br/>"
44 "<b>Note:</b> This file is not present on your player and will "
45 "disappear automatically after installing it.<br/><br/>"
46 "Press Ok to continue and browse your computer for the firmware "
47 "file.");
48}
49
50bool BootloaderInstallAms::install(void)
51{
52 if(m_offile.isEmpty())
53 return false;
54
55 LOG_INFO() << "installing bootloader";
56
57 // download firmware from server
58 emit logItem(tr("Downloading bootloader file"), LOGINFO);
59
60 connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallAms::installStage2);
61 downloadBlStart(m_blurl);
62
63 return true;
64}
65
66void BootloaderInstallAms::installStage2(void)
67{
68 LOG_INFO() << "installStage2";
69
70 unsigned char* buf;
71 unsigned char* of_packed;
72 int of_packedsize;
73 unsigned char* rb_packed;
74 int rb_packedsize;
75 off_t len;
76 struct md5sums sum;
77 char md5sum[33]; /* 32 hex digits, plus terminating zero */
78 int n;
79 int model;
80 int firmware_size;
81 int bootloader_size;
82 int patchable;
83 int totalsize;
84 char errstr[200];
85
86 sum.md5 = md5sum;
87
88 m_tempfile.open();
89 QString bootfile = m_tempfile.fileName();
90 m_tempfile.close();
91
92 /* Load bootloader file */
93 rb_packed = load_rockbox_file(bootfile.toLocal8Bit().data(), &model,
94 &bootloader_size,&rb_packedsize,
95 errstr,sizeof(errstr));
96 if (rb_packed == nullptr)
97 {
98 LOG_ERROR() << "could not load bootloader: " << bootfile;
99 emit logItem(errstr, LOGERROR);
100 emit logItem(tr("Could not load %1").arg(bootfile), LOGERROR);
101 emit done(true);
102 return;
103 }
104
105 /* Load original firmware file */
106 buf = load_of_file(m_offile.toLocal8Bit().data(), model, &len, &sum,
107 &firmware_size, &of_packed ,&of_packedsize,
108 errstr, sizeof(errstr));
109 if (buf == nullptr)
110 {
111 LOG_ERROR() << "could not load OF: " << m_offile;
112 emit logItem(errstr, LOGERROR);
113 emit logItem(tr("Could not load %1").arg(m_offile), LOGERROR);
114 free(rb_packed);
115 emit done(true);
116 return;
117 }
118
119 /* check total size */
120 patchable = check_sizes(sum.model, rb_packedsize, bootloader_size,
121 of_packedsize, firmware_size, &totalsize, errstr, sizeof(errstr));
122
123 if (!patchable)
124 {
125 LOG_ERROR() << "No room to insert bootloader";
126 emit logItem(errstr, LOGERROR);
127 emit logItem(tr("No room to insert bootloader, try another firmware version"),
128 LOGERROR);
129 free(buf);
130 free(of_packed);
131 free(rb_packed);
132 emit done(true);
133 return;
134 }
135
136 /* patch the firmware */
137 emit logItem(tr("Patching Firmware..."), LOGINFO);
138
139 patch_firmware(sum.model,firmware_revision(sum.model),firmware_size,buf,
140 len,of_packed,of_packedsize,rb_packed,rb_packedsize);
141
142 /* write out file */
143 QFile out(m_blfile);
144
145 if(!out.open(QIODevice::WriteOnly | QIODevice::Truncate))
146 {
147 LOG_ERROR() << "Could not open" << m_blfile << "for writing";
148 emit logItem(tr("Could not open %1 for writing").arg(m_blfile),LOGERROR);
149 free(buf);
150 free(of_packed);
151 free(rb_packed);
152 emit done(true);
153 return;
154 }
155
156 n = out.write((char*)buf, len);
157
158 if (n != len)
159 {
160 LOG_ERROR() << "Could not write firmware file";
161 emit logItem(tr("Could not write firmware file"),LOGERROR);
162 free(buf);
163 free(of_packed);
164 free(rb_packed);
165 emit done(true);
166 return;
167 }
168
169 out.close();
170
171 free(buf);
172 free(of_packed);
173 free(rb_packed);
174
175 //end of install
176 LOG_INFO() << "install successfull";
177 emit logItem(tr("Success: modified firmware file created"), LOGINFO);
178 logInstall(LogAdd);
179 emit done(false);
180 return;
181}
182
183bool BootloaderInstallAms::uninstall(void)
184{
185 emit logItem(tr("To uninstall, perform a normal upgrade with an unmodified "
186 "original firmware"), LOGINFO);
187 logInstall(LogRemove);
188 emit done(true);
189 return false;
190}
191
192BootloaderInstallBase::BootloaderType BootloaderInstallAms::installed(void)
193{
194 return BootloaderUnknown;
195}
196
197BootloaderInstallBase::Capabilities BootloaderInstallAms::capabilities(void)
198{
199 return (Install | NeedsOf);
200}
201
diff --git a/utils/rbutilqt/base/bootloaderinstallams.h b/utils/rbutilqt/base/bootloaderinstallams.h
new file mode 100644
index 0000000000..f3ce15ed24
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallams.h
@@ -0,0 +1,42 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18#ifndef BOOTLOADERINSTALLAMS_H
19#define BOOTLOADERINSTALLAMS_H
20
21#include <QtCore>
22#include "bootloaderinstallbase.h"
23
24//! bootloader installation derivate based on mkamsboot
25class BootloaderInstallAms : public BootloaderInstallBase
26{
27 Q_OBJECT
28 public:
29 BootloaderInstallAms(QObject *parent);
30 bool install(void);
31 bool uninstall(void);
32 BootloaderInstallBase::BootloaderType installed(void);
33 Capabilities capabilities(void);
34 QString ofHint();
35
36 private:
37
38 private slots:
39 void installStage2(void);
40};
41
42#endif
diff --git a/utils/rbutilqt/base/bootloaderinstallbase.cpp b/utils/rbutilqt/base/bootloaderinstallbase.cpp
new file mode 100644
index 0000000000..096c601b91
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallbase.cpp
@@ -0,0 +1,302 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19
20#include <QtCore>
21
22#include "bootloaderinstallbase.h"
23#include "utils.h"
24#include "ziputil.h"
25#include "mspackutil.h"
26#include "Logger.h"
27
28#if defined(Q_OS_MACX)
29#include <sys/param.h>
30#include <sys/ucred.h>
31#include <sys/mount.h>
32#endif
33
34
35BootloaderInstallBase::BootloaderType BootloaderInstallBase::installed(void)
36{
37 return BootloaderUnknown;
38}
39
40
41BootloaderInstallBase::Capabilities BootloaderInstallBase::capabilities(void)
42{
43 return Capabilities();
44}
45
46
47void BootloaderInstallBase::downloadBlStart(QUrl source)
48{
49 m_http.setFile(&m_tempfile);
50 m_http.setCache(true);
51 connect(&m_http, &HttpGet::done, this, &BootloaderInstallBase::downloadBlFinish);
52 // connect the http read signal to our logProgess *signal*
53 // to immediately emit it without any helper function.
54 connect(&m_http, &HttpGet::dataReadProgress,
55 this, &BootloaderInstallBase::logProgress);
56 m_http.getFile(source);
57}
58
59
60void BootloaderInstallBase::downloadReqFinished(int id, bool error)
61{
62 LOG_INFO() << "Download Request" << id
63 << "finished, error:" << m_http.errorString();
64
65 downloadBlFinish(error);
66}
67
68
69void BootloaderInstallBase::downloadBlFinish(bool error)
70{
71 LOG_INFO() << "Downloading bootloader finished, error:"
72 << error;
73
74 // update progress bar
75 emit logProgress(100, 100);
76
77 if(m_http.httpResponse() != 200) {
78 emit logItem(tr("Download error: received HTTP error %1.")
79 .arg(m_http.errorString()), LOGERROR);
80 emit done(true);
81 return;
82 }
83 if(error) {
84 emit logItem(tr("Download error: %1")
85 .arg(m_http.errorString()), LOGERROR);
86 emit done(true);
87 return;
88 }
89 else if(m_http.isCached())
90 emit logItem(tr("Download finished (cache used)."), LOGOK);
91 else
92 emit logItem(tr("Download finished."), LOGOK);
93
94 QCoreApplication::processEvents();
95 m_blversion = m_http.timestamp();
96 emit downloadDone();
97}
98
99
100void BootloaderInstallBase::installBlfile(void)
101{
102 LOG_INFO() << "installBlFile(void)";
103}
104
105
106void BootloaderInstallBase::progressAborted(void)
107{
108 LOG_INFO() << "progressAborted(void)";
109 emit installAborted();
110}
111
112
113//! @brief backup OF file.
114//! @param to folder to write backup file to. Folder will get created.
115//! @return true on success, false on error.
116bool BootloaderInstallBase::backup(QString to)
117{
118 LOG_INFO() << "Backing up bootloader file";
119 QDir targetDir(".");
120 emit logItem(tr("Creating backup of original firmware file."), LOGINFO);
121 if(!targetDir.mkpath(to)) {
122 emit logItem(tr("Creating backup folder failed"), LOGERROR);
123 return false;
124 }
125 QString tofile = to + "/" + QFileInfo(m_blfile).fileName();
126 LOG_INFO() << "trying to backup" << m_blfile << "to" << tofile;
127 if(!QFile::copy(Utils::resolvePathCase(m_blfile), tofile)) {
128 emit logItem(tr("Creating backup copy failed."), LOGERROR);
129 return false;
130 }
131 emit logItem(tr("Backup created."), LOGOK);
132 return true;
133}
134
135
136//! @brief log installation to logfile.
137//! @param mode action to perform. 0: add to log, 1: remove from log.
138//! @return 0 on success
139int BootloaderInstallBase::logInstall(LogMode mode)
140{
141 int result = 0;
142 QString section = m_blurl.path().section('/', -1);
143 QSettings s(m_logfile, QSettings::IniFormat, this);
144 emit logItem(tr("Creating installation log"), LOGINFO);
145
146 if(mode == LogAdd) {
147 s.setValue("Bootloader/" + section, m_blversion.toString(Qt::ISODate));
148 LOG_INFO() << "Writing log, version:"
149 << m_blversion.toString(Qt::ISODate);
150 }
151 else {
152 s.remove("Bootloader/" + section);
153 }
154 s.sync();
155
156 emit logItem(tr("Installation log created"), LOGOK);
157
158 return result;
159}
160
161
162#if defined(Q_OS_MACX)
163void BootloaderInstallBase::waitRemount()
164{
165 m_remountTries = 600;
166 emit logItem(tr("Waiting for system to remount player"), LOGINFO);
167
168 QTimer::singleShot(100, this, SLOT(checkRemount()));
169}
170#endif
171
172
173void BootloaderInstallBase::checkRemount()
174{
175#if defined(Q_OS_MACX)
176 if(m_remountTries--) {
177 int status = 0;
178 // check if device has been remounted
179 QCoreApplication::processEvents();
180 int num;
181 struct statfs *mntinf;
182
183 num = getmntinfo(&mntinf, MNT_WAIT);
184 while(num--) {
185 if(QString(mntinf->f_mntfromname).startsWith(m_remountDevice)
186 && QString(mntinf->f_fstypename).contains("msdos", Qt::CaseInsensitive))
187 status = 1;
188 mntinf++;
189 }
190 if(!status) {
191 // still not remounted, restart timer.
192 QTimer::singleShot(500, this, SLOT(checkRemount()));
193 LOG_INFO() << "Player not remounted yet" << m_remountDevice;
194 }
195 else {
196 emit logItem(tr("Player remounted"), LOGINFO);
197 emit remounted(true);
198 }
199 }
200 else {
201 emit logItem(tr("Timeout on remount"), LOGERROR);
202 emit remounted(false);
203 }
204#endif
205}
206
207
208//! @brief set list of possible bootloader files and pick the existing one.
209//! @param sl list of possible bootloader files.
210void BootloaderInstallBase::setBlFile(QStringList sl)
211{
212 // figue which of the possible bootloader filenames is correct.
213 for(int a = 0; a < sl.size(); a++) {
214 if(!Utils::resolvePathCase(sl.at(a)).isEmpty()) {
215 m_blfile = sl.at(a);
216 }
217 }
218 if(m_blfile.isEmpty()) {
219 m_blfile = sl.at(0);
220 }
221}
222
223
224bool BootloaderInstallBase::setOfFile(QString of, QStringList blfile)
225{
226 bool found = false;
227 ArchiveUtil *util = nullptr;
228
229 // check if we're actually looking for a zip file. If so we must avoid
230 // trying to unzip it.
231 bool wantZip = false;
232 for (int i = 0; i < blfile.size(); i++)
233 {
234 if (blfile.at(i).endsWith(".zip"))
235 wantZip = true;
236 }
237
238 // try ZIP first
239 ZipUtil *zu = new ZipUtil(this);
240 if(zu->open(of) && !wantZip)
241 {
242 emit logItem(tr("Zip file format detected"), LOGINFO);
243 util = zu;
244 }
245 else
246 delete zu;
247
248 // if ZIP failed, try CAB
249 if(util == nullptr)
250 {
251 MsPackUtil *msu = new MsPackUtil(this);
252 if(msu->open(of))
253 {
254 emit logItem(tr("CAB file format detected"), LOGINFO);
255 util = msu;
256 }
257 else
258 delete msu;
259 }
260
261 // check if the file set is in zip format
262 if(util) {
263 QStringList contents = util->files();
264 LOG_INFO() << "archive contains:" << contents;
265 for(int i = 0; i < blfile.size(); ++i) {
266 // strip any path, we don't know the structure in the zip
267 QString f = QFileInfo(blfile.at(i)).fileName();
268 LOG_INFO() << "searching archive for" << f;
269 // contents.indexOf() works case sensitive. Since the filename
270 // casing is unknown (and might change) do this manually.
271 // FIXME: support files in folders
272 for(int j = 0; j < contents.size(); ++j) {
273 if(contents.at(j).compare(f, Qt::CaseInsensitive) == 0) {
274 found = true;
275 emit logItem(tr("Extracting firmware %1 from archive")
276 .arg(f), LOGINFO);
277 // store in class temporary file
278 m_tempof.open();
279 m_offile = m_tempof.fileName();
280 m_tempof.close();
281 if(!util->extractArchive(m_offile, contents.at(j))) {
282 emit logItem(tr("Error extracting firmware from archive"), LOGERROR);
283 found = false;
284 break;
285 }
286 break;
287 }
288 }
289 }
290 if(!found) {
291 emit logItem(tr("Could not find firmware in archive"), LOGERROR);
292 }
293 delete util;
294 }
295 else {
296 m_offile = of;
297 found = true;
298 }
299
300 return found;
301}
302
diff --git a/utils/rbutilqt/base/bootloaderinstallbase.h b/utils/rbutilqt/base/bootloaderinstallbase.h
new file mode 100644
index 0000000000..23aac4f92f
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallbase.h
@@ -0,0 +1,118 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef BOOTLOADERINSTALLBASE_H
20#define BOOTLOADERINSTALLBASE_H
21
22#include <QtCore>
23#include "progressloggerinterface.h"
24#include "httpget.h"
25
26//! baseclass for all Bootloader installs
27class BootloaderInstallBase : public QObject
28{
29 Q_OBJECT
30 public:
31 enum Capability
32 { Install = 0x01, Uninstall = 0x02, Backup = 0x04,
33 IsFile = 0x08, IsRaw = 0x10, NeedsOf = 0x20,
34 CanCheckInstalled = 0x40, CanCheckVersion = 0x80 };
35 Q_DECLARE_FLAGS(Capabilities, Capability)
36
37 enum BootloaderType
38 { BootloaderNone, BootloaderRockbox, BootloaderOther, BootloaderUnknown };
39
40 BootloaderInstallBase(QObject *parent) : QObject(parent)
41 { }
42
43 //! install the bootloader, must be implemented
44 virtual bool install(void) = 0;
45 //! uninstall the bootloader, must be implemented
46 virtual bool uninstall(void) = 0;
47 //! returns the installed bootloader
48 virtual BootloaderType installed(void)=0;
49 //! returns the capabilities of the bootloader class
50 virtual Capabilities capabilities(void)=0;
51 //! returns a OF Firmware hint or empty if there is none
52 virtual QString ofHint() {return QString();}
53
54 //! backup a already installed bootloader
55 bool backup(QString to);
56
57 //! set the different filenames and paths
58 void setBlFile(QStringList f);
59 void setBlUrl(QUrl u)
60 { m_blurl = u; }
61 void setLogfile(QString f)
62 { m_logfile = f; }
63 bool setOfFile(QString of, QStringList blfile);
64
65 //! returns a port Install Hint or empty if there is none
66 //! static and in the base class, so the installer classes dont need to
67 // be modified for new targets
68 static QString postinstallHints(QString model);
69
70 //! returns the correct BootloaderInstaller object for the requested type
71 static BootloaderInstallBase* createBootloaderInstaller(QObject* parent,QString type);
72 protected slots:
73 void downloadReqFinished(int id, bool error);
74 void downloadBlFinish(bool error);
75 void installBlfile(void);
76 void progressAborted(void);
77
78 // NOTE: we need to keep this slot even on targets that don't need it
79 // -- using the preprocessor here confused moc.
80 void checkRemount(void);
81 protected:
82 enum LogMode
83 { LogAdd, LogRemove };
84
85 void downloadBlStart(QUrl source);
86 int logInstall(LogMode mode);
87
88 HttpGet m_http; //! http download object
89 QString m_blfile; //! bootloader filename on player
90 QString m_logfile; //! file for installation log
91 QUrl m_blurl; //! bootloader download URL
92 QTemporaryFile m_tempfile; //! temporary file for download
93 QTemporaryFile m_tempof; //! temporary file for OF extracted from archive
94 QDateTime m_blversion; //! download timestamp used for version information
95 QString m_offile; //! path to the offile
96#if defined(Q_OS_MACX)
97 void waitRemount(void);
98
99 int m_remountTries;
100 QString m_remountDevice;
101#endif
102
103 signals:
104 void downloadDone(void); //! internal signal sent when download finished.
105 void installAborted(void); //! internal signal sent on abort button click.
106 void done(bool);
107 void logItem(QString, int); //! set logger item
108 void logProgress(int, int); //! set progress bar.
109
110 // NOTE: we need to keep this signal even on targets that don't need it
111 // -- using the preprocessor here confused moc.
112 void remounted(bool);
113};
114
115Q_DECLARE_OPERATORS_FOR_FLAGS(BootloaderInstallBase::Capabilities)
116
117#endif
118
diff --git a/utils/rbutilqt/base/bootloaderinstallbspatch.cpp b/utils/rbutilqt/base/bootloaderinstallbspatch.cpp
new file mode 100644
index 0000000000..02291b0151
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallbspatch.cpp
@@ -0,0 +1,178 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2020 by Solomon Peachy
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include <QtDebug>
21#include "bootloaderinstallbase.h"
22#include "bootloaderinstallbspatch.h"
23#include "../bspatch/bspatch.h"
24#include "Logger.h"
25
26/* class for running bspatch() in a separate thread to keep the UI responsive. */
27class BootloaderThreadBSPatch : public QThread
28{
29 public:
30 void run(void);
31 void setInputFile(QString f)
32 { m_inputfile = f; }
33 void setOutputFile(QString f)
34 { m_outputfile = f; }
35 void setBootloaderFile(QString f)
36 { m_bootfile = f; }
37 int error(void)
38 { return m_error; }
39 private:
40 QString m_inputfile;
41 QString m_bootfile;
42 QString m_outputfile;
43 int m_error;
44};
45
46void BootloaderThreadBSPatch::run(void)
47{
48 LOG_INFO() << "Thread started.";
49
50 m_error = apply_bspatch(m_inputfile.toLocal8Bit().constData(),
51 m_outputfile.toLocal8Bit().constData(),
52 m_bootfile.toLocal8Bit().constData());
53
54 LOG_INFO() << "Thread finished, result:" << m_error;
55}
56
57BootloaderInstallBSPatch::BootloaderInstallBSPatch(QObject *parent)
58 : BootloaderInstallBase(parent)
59{
60 m_thread = nullptr;
61}
62
63QString BootloaderInstallBSPatch::ofHint()
64{
65 return tr("Bootloader installation requires you to provide "
66 "the correct verrsion of the original firmware file. "
67 "This file will be patched with the Rockbox bootloader and "
68 "installed to your player. You need to download this file "
69 "yourself due to legal reasons. Please refer to the "
70 "<a href='http://www.rockbox.org/wiki/'>rockbox wiki</a> "
71 "pages on how to obtain this file.<br/>"
72 "Press Ok to continue and browse your computer for the firmware "
73 "file.");
74}
75
76/** Start bootloader installation.
77 */
78bool BootloaderInstallBSPatch::install(void)
79{
80 if(!QFileInfo(m_offile).isReadable())
81 {
82 LOG_ERROR() << "could not read original firmware file"
83 << m_offile;
84 emit logItem(tr("Could not read original firmware file"), LOGERROR);
85 return false;
86 }
87
88 LOG_INFO() << "downloading bootloader";
89 // download bootloader from server
90 emit logItem(tr("Downloading bootloader file"), LOGINFO);
91 connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallBSPatch::installStage2);
92 downloadBlStart(m_blurl);
93 return true;
94}
95
96void BootloaderInstallBSPatch::installStage2(void)
97{
98 LOG_INFO() << "patching file...";
99 emit logItem(tr("Patching file..."), LOGINFO);
100 m_tempfile.open();
101
102 // we have not detailed progress on the patching so just show a busy
103 // indicator instead.
104 emit logProgress(0, 0);
105 m_patchedFile.open();
106 m_thread = new BootloaderThreadBSPatch();
107 m_thread->setInputFile(m_offile);
108 m_thread->setBootloaderFile(m_tempfile.fileName());
109 m_thread->setOutputFile(m_patchedFile.fileName());
110 m_tempfile.close();
111 m_patchedFile.close();
112 connect(m_thread, &QThread::finished, this, &BootloaderInstallBSPatch::installStage3);
113 m_thread->start();
114}
115
116void BootloaderInstallBSPatch::installStage3(void)
117{
118 int err = m_thread->error();
119 emit logProgress(1, 1);
120 // if the patch failed
121 if (err != 0)
122 {
123 LOG_ERROR() << "Could not patch the original firmware file";
124 emit logItem(tr("Patching the original firmware failed"), LOGERROR);
125 emit done(true);
126 return;
127 }
128
129 LOG_INFO() << "Original Firmware succesfully patched";
130 emit logItem(tr("Succesfully patched firmware file"), LOGINFO);
131
132 // if a bootloader is already present delete it.
133 QString fwfile(m_blfile);
134 if(QFileInfo(fwfile).isFile())
135 {
136 LOG_INFO() << "deleting old target file";
137 QFile::remove(fwfile);
138 }
139
140 // place (new) bootloader. Copy, since the temporary file will be removed
141 // automatically.
142 LOG_INFO() << "moving patched bootloader to" << fwfile;
143 if(m_patchedFile.copy(fwfile))
144 {
145 emit logItem(tr("Bootloader successful installed"), LOGOK);
146 logInstall(LogAdd);
147 emit done(false);
148 }
149 else
150 {
151 emit logItem(tr("Patched bootloader could not be installed"), LOGERROR);
152 emit done(true);
153 }
154 // clean up thread object.
155 delete m_thread;
156 return;
157}
158
159bool BootloaderInstallBSPatch::uninstall(void)
160{
161 emit logItem(tr("To uninstall, perform a normal upgrade with an unmodified "
162 "original firmware."), LOGINFO);
163 logInstall(LogRemove);
164 emit done(true);
165 return false;
166}
167
168
169BootloaderInstallBase::BootloaderType BootloaderInstallBSPatch::installed(void)
170{
171 return BootloaderUnknown;
172}
173
174
175BootloaderInstallBase::Capabilities BootloaderInstallBSPatch::capabilities(void)
176{
177 return (Install | NeedsOf);
178}
diff --git a/utils/rbutilqt/base/bootloaderinstallbspatch.h b/utils/rbutilqt/base/bootloaderinstallbspatch.h
new file mode 100644
index 0000000000..7108820fda
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallbspatch.h
@@ -0,0 +1,47 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2020 Solomon Peachy
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18#ifndef BOOTLOADERINSTALLBSPATCH_H
19#define BOOTLOADERINSTALLBSPATCH_H
20
21#include <QtCore>
22#include "bootloaderinstallbase.h"
23
24class BootloaderThreadBSPatch;
25
26//! bootloader installation class for devices handled by mkimxboot.
27class BootloaderInstallBSPatch : public BootloaderInstallBase
28{
29 Q_OBJECT
30 public:
31 BootloaderInstallBSPatch(QObject *parent);
32 bool install(void);
33 bool uninstall(void);
34 BootloaderInstallBase::BootloaderType installed(void);
35 Capabilities capabilities(void);
36 QString ofHint();
37
38 private slots:
39 void installStage2(void);
40 void installStage3(void);
41
42 private:
43 BootloaderThreadBSPatch *m_thread;
44 QTemporaryFile m_patchedFile;
45};
46
47#endif
diff --git a/utils/rbutilqt/base/bootloaderinstallchinachip.cpp b/utils/rbutilqt/base/bootloaderinstallchinachip.cpp
new file mode 100644
index 0000000000..f6af952b9a
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallchinachip.cpp
@@ -0,0 +1,133 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2009 by Maurus Cuelenaere
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "bootloaderinstallbase.h"
21#include "bootloaderinstallchinachip.h"
22
23#include "../chinachippatcher/chinachip.h"
24
25BootloaderInstallChinaChip::BootloaderInstallChinaChip(QObject *parent)
26 : BootloaderInstallBase(parent)
27{
28 (void)parent;
29}
30
31QString BootloaderInstallChinaChip::ofHint()
32{
33 return tr("Bootloader installation requires you to provide "
34 "a firmware file of the original firmware (HXF file). "
35 "You need to download this file yourself due to legal "
36 "reasons. Please refer to the "
37 "<a href='http://www.rockbox.org/manual.shtml'>manual</a> and the "
38 "<a href='http://www.rockbox.org/wiki/OndaVX747"
39 "#Download_and_extract_a_recent_ve'>OndaVX747</a> wiki page on "
40 "how to obtain this file.<br/>"
41 "Press Ok to continue and browse your computer for the firmware "
42 "file.");
43}
44
45bool BootloaderInstallChinaChip::install()
46{
47 if(m_offile.isEmpty())
48 return false;
49
50 emit logItem(tr("Downloading bootloader file"), LOGINFO);
51
52 connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallChinaChip::installStage2);
53 downloadBlStart(m_blurl);
54
55 return true;
56}
57
58void BootloaderInstallChinaChip::installStage2()
59{
60 enum cc_error result;
61 bool error = true;
62 m_tempfile.open();
63 QString blfile = m_tempfile.fileName();
64 m_tempfile.close();
65
66 QString backupfile = QFileInfo(m_blfile).absoluteDir().absoluteFilePath("ccpmp.bin");
67
68 result = chinachip_patch(m_offile.toLocal8Bit(), blfile.toLocal8Bit(),
69 m_blfile.toLocal8Bit(), backupfile.toLocal8Bit());
70 switch(result) {
71 case E_OK:
72 error = false;
73 break;
74 case E_OPEN_FIRMWARE:
75 emit logItem(tr("Could not open firmware file"), LOGERROR);
76 break;
77 case E_OPEN_BOOTLOADER:
78 emit logItem(tr("Could not open bootloader file"), LOGERROR);
79 break;
80 case E_MEMALLOC:
81 emit logItem(tr("Could not allocate memory"), LOGERROR);
82 break;
83 case E_LOAD_FIRMWARE:
84 emit logItem(tr("Could not load firmware file"), LOGERROR);
85 break;
86 case E_INVALID_FILE:
87 emit logItem(tr("File is not a valid ChinaChip firmware"), LOGERROR);
88 break;
89 case E_NO_CCPMP:
90 emit logItem(tr("Could not find ccpmp.bin in input file"), LOGERROR);
91 break;
92 case E_OPEN_BACKUP:
93 emit logItem(tr("Could not open backup file for ccpmp.bin"), LOGERROR);
94 break;
95 case E_WRITE_BACKUP:
96 emit logItem(tr("Could not write backup file for ccpmp.bin"), LOGERROR);
97 break;
98 case E_LOAD_BOOTLOADER:
99 emit logItem(tr("Could not load bootloader file"), LOGERROR);
100 break;
101 case E_GET_TIME:
102 emit logItem(tr("Could not get current time"), LOGERROR);
103 break;
104 case E_OPEN_OUTFILE:
105 emit logItem(tr("Could not open output file"), LOGERROR);
106 break;
107 case E_WRITE_OUTFILE:
108 emit logItem(tr("Could not write output file"), LOGERROR);
109 break;
110 default:
111 emit logItem(tr("Unexpected error from chinachippatcher"), LOGERROR);
112 break;
113 }
114
115 emit done(error);
116}
117
118bool BootloaderInstallChinaChip::uninstall()
119{
120 /* TODO: only way is to restore the OF */
121 return false;
122}
123
124BootloaderInstallBase::BootloaderType BootloaderInstallChinaChip::installed()
125{
126 /* TODO: find a way to figure this out */
127 return BootloaderUnknown;
128}
129
130BootloaderInstallBase::Capabilities BootloaderInstallChinaChip::capabilities()
131{
132 return (Install | IsFile | NeedsOf);
133}
diff --git a/utils/rbutilqt/base/bootloaderinstallchinachip.h b/utils/rbutilqt/base/bootloaderinstallchinachip.h
new file mode 100644
index 0000000000..e92f292283
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallchinachip.h
@@ -0,0 +1,41 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2009 by Maurus Cuelenaere
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef BOOTLOADERINSTALLCCPMP_H
20#define BOOTLOADERINSTALLCCPMP_H
21
22#include <QtCore>
23#include "bootloaderinstallbase.h"
24
25class BootloaderInstallChinaChip : public BootloaderInstallBase
26{
27 Q_OBJECT
28
29 public:
30 BootloaderInstallChinaChip(QObject *parent = nullptr);
31 bool install(void);
32 bool uninstall(void);
33 BootloaderInstallBase::BootloaderType installed(void);
34 Capabilities capabilities(void);
35 QString ofHint();
36
37 private slots:
38 void installStage2(void);
39};
40
41#endif // BOOTLOADERINSTALLCCPMP_H
diff --git a/utils/rbutilqt/base/bootloaderinstallfile.cpp b/utils/rbutilqt/base/bootloaderinstallfile.cpp
new file mode 100644
index 0000000000..bf3d5e449d
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallfile.cpp
@@ -0,0 +1,159 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include <QtDebug>
21#include "bootloaderinstallfile.h"
22#include "utils.h"
23#include "Logger.h"
24
25
26BootloaderInstallFile::BootloaderInstallFile(QObject *parent)
27 : BootloaderInstallBase(parent)
28{
29}
30
31
32bool BootloaderInstallFile::install(void)
33{
34 emit logItem(tr("Downloading bootloader"), LOGINFO);
35 LOG_INFO() << "installing bootloader";
36 downloadBlStart(m_blurl);
37 connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallFile::installStage2);
38 return true;
39}
40
41void BootloaderInstallFile::installStage2(void)
42{
43 emit logItem(tr("Installing Rockbox bootloader"), LOGINFO);
44 QCoreApplication::processEvents();
45
46 // if an old bootloader is present (Gigabeat) move it out of the way.
47 QString fwfile(Utils::resolvePathCase(m_blfile));
48 if(!fwfile.isEmpty()) {
49 QString moved = Utils::resolvePathCase(m_blfile) + ".ORIG";
50 LOG_INFO() << "renaming" << fwfile << "to" << moved;
51 QFile::rename(fwfile, moved);
52 }
53
54 // if no old file found resolve path without basename
55 QFileInfo fi(m_blfile);
56 QString absPath = Utils::resolvePathCase(fi.absolutePath());
57
58 // if it's not possible to locate the base path try to create it
59 if(absPath.isEmpty()) {
60 QStringList pathElements = m_blfile.split("/");
61 // remove filename from list and save last path element
62 pathElements.removeLast();
63 QString lastElement = pathElements.last();
64 // remove last path element for base
65 pathElements.removeLast();
66 QString basePath = pathElements.join("/");
67
68 // check for base and bail out if not found. Otherwise create folder.
69 absPath = Utils::resolvePathCase(basePath);
70 QDir d(absPath);
71 d.mkpath(lastElement);
72 absPath = Utils::resolvePathCase(fi.absolutePath());
73
74 if(absPath.isEmpty()) {
75 emit logItem(tr("Error accessing output folder"), LOGERROR);
76 emit done(true);
77 return;
78 }
79 }
80 fwfile = absPath + "/" + fi.fileName();
81
82 // place (new) bootloader
83 m_tempfile.open();
84 LOG_INFO() << "renaming" << m_tempfile.fileName()
85 << "to" << fwfile;
86 m_tempfile.close();
87
88 if(!Utils::resolvePathCase(fwfile).isEmpty()) {
89 emit logItem(tr("A firmware file is already present on player"), LOGERROR);
90 emit done(true);
91 return;
92 }
93 if(m_tempfile.copy(fwfile)) {
94 emit logItem(tr("Bootloader successful installed"), LOGOK);
95 }
96 else {
97 emit logItem(tr("Copying modified firmware file failed"), LOGERROR);
98 emit done(true);
99 return;
100 }
101
102 logInstall(LogAdd);
103
104 emit done(false);
105}
106
107
108bool BootloaderInstallFile::uninstall(void)
109{
110 LOG_INFO() << "Uninstalling bootloader";
111 emit logItem(tr("Removing Rockbox bootloader"), LOGINFO);
112 // check if a .ORIG file is present, and allow moving it back.
113 QString origbl = Utils::resolvePathCase(m_blfile + ".ORIG");
114 if(origbl.isEmpty()) {
115 emit logItem(tr("No original firmware file found."), LOGERROR);
116 emit done(true);
117 return false;
118 }
119 QString fwfile = Utils::resolvePathCase(m_blfile);
120 if(!QFile::remove(fwfile)) {
121 emit logItem(tr("Can't remove Rockbox bootloader file."), LOGERROR);
122 emit done(true);
123 return false;
124 }
125 if(!QFile::rename(origbl, fwfile)) {
126 emit logItem(tr("Can't restore bootloader file."), LOGERROR);
127 emit done(true);
128 return false;
129 }
130 emit logItem(tr("Original bootloader restored successfully."), LOGOK);
131 logInstall(LogRemove);
132 emit logProgress(1, 1);
133 emit done(false);
134
135 return true;
136}
137
138
139//! @brief check if bootloader is installed.
140//! @return BootloaderRockbox, BootloaderOther or BootloaderUnknown.
141BootloaderInstallBase::BootloaderType BootloaderInstallFile::installed(void)
142{
143 LOG_INFO() << "checking installed bootloader";
144 if(!Utils::resolvePathCase(m_blfile).isEmpty()
145 && !Utils::resolvePathCase(m_blfile + ".ORIG").isEmpty())
146 return BootloaderRockbox;
147 else if(!Utils::resolvePathCase(m_blfile).isEmpty())
148 return BootloaderOther;
149 else
150 return BootloaderUnknown;
151}
152
153
154BootloaderInstallBase::Capabilities BootloaderInstallFile::capabilities(void)
155{
156 LOG_INFO() << "getting capabilities";
157 return Install | Uninstall | IsFile | CanCheckInstalled | Backup;
158}
159
diff --git a/utils/rbutilqt/base/bootloaderinstallfile.h b/utils/rbutilqt/base/bootloaderinstallfile.h
new file mode 100644
index 0000000000..9f9586279c
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallfile.h
@@ -0,0 +1,48 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef BOOTLOADERINSTALLFILE_H
20#define BOOTLOADERINSTALLFILE_H
21
22#include <QtCore>
23#include "progressloggerinterface.h"
24#include "bootloaderinstallbase.h"
25
26//! install a bootloader by putting a single file on the player.
27// This installation method is used by Iaudio (firmware is flashed
28// automatically) and Gigabeat (Firmware is a file, OF needs to get
29// renamed).
30class BootloaderInstallFile : public BootloaderInstallBase
31{
32 Q_OBJECT
33
34 public:
35 BootloaderInstallFile(QObject *parent);
36 bool install(void);
37 bool uninstall(void);
38 BootloaderInstallBase::BootloaderType installed(void);
39 Capabilities capabilities(void);
40
41 private slots:
42 void installStage2(void);
43
44 private:
45};
46
47#endif
48
diff --git a/utils/rbutilqt/base/bootloaderinstallhelper.cpp b/utils/rbutilqt/base/bootloaderinstallhelper.cpp
new file mode 100644
index 0000000000..c94ed29140
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallhelper.cpp
@@ -0,0 +1,140 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2012 Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 * This file is a modified version of the AMS installer by Dominik Wenger
18 *
19 ****************************************************************************/
20
21#include <QtCore>
22
23#include "bootloaderinstallhelper.h"
24#include "bootloaderinstallmi4.h"
25#include "bootloaderinstallhex.h"
26#include "bootloaderinstallipod.h"
27#include "bootloaderinstallsansa.h"
28#include "bootloaderinstallfile.h"
29#include "bootloaderinstallchinachip.h"
30#include "bootloaderinstallams.h"
31#include "bootloaderinstalltcc.h"
32#include "bootloaderinstallmpio.h"
33#include "bootloaderinstallimx.h"
34#include "bootloaderinstalls5l.h"
35#include "bootloaderinstallbspatch.h"
36
37BootloaderInstallBase* BootloaderInstallHelper::createBootloaderInstaller(QObject* parent, QString type)
38{
39 if(type == "mi4") {
40 return new BootloaderInstallMi4(parent);
41 }
42 else if(type == "hex") {
43 return new BootloaderInstallHex(parent);
44 }
45 else if(type == "sansa") {
46 return new BootloaderInstallSansa(parent);
47 }
48 else if(type == "ipod") {
49 return new BootloaderInstallIpod(parent);
50 }
51 else if(type == "file") {
52 return new BootloaderInstallFile(parent);
53 }
54 else if(type == "chinachip") {
55 return new BootloaderInstallChinaChip(parent);
56 }
57 else if(type == "ams") {
58 return new BootloaderInstallAms(parent);
59 }
60 else if(type == "tcc") {
61 return new BootloaderInstallTcc(parent);
62 }
63 else if(type == "mpio") {
64 return new BootloaderInstallMpio(parent);
65 }
66 else if(type == "imx") {
67 return new BootloaderInstallImx(parent);
68 }
69 else if(type == "s5l") {
70 return new BootloaderInstallS5l(parent);
71 }
72 else if(type == "bspatch") {
73 return new BootloaderInstallBSPatch(parent);
74 }
75 else {
76 return nullptr;
77 }
78}
79
80
81//! @brief Return post install hints string.
82//! @param model model string
83//! @return hints.
84QString BootloaderInstallHelper::postinstallHints(QString model)
85{
86 bool hint = false;
87 QString msg = QObject::tr("Bootloader installation is almost complete. "
88 "Installation <b>requires</b> you to perform the "
89 "following steps manually:");
90
91 msg += "<ol>";
92 if(model != "sansafuzeplus") {
93 msg += QObject::tr("<li>Safely remove your player.</li>");
94 }
95 if(model == "iriverh100" || model == "iriverh120" || model == "iriverh300"
96 || model == "ondavx747" || model == "agptekrocker"
97 || model == "xduoox3" || model == "xduoox3ii" || model == "xduoox20") {
98 hint = true;
99 msg += QObject::tr("<li>Reboot your player into the original firmware.</li>"
100 "<li>Perform a firmware upgrade using the update functionality "
101 "of the original firmware. Please refer to your player's manual "
102 "on details.<br/><b>Important:</b> updating the firmware is a "
103 "critical process that must not be interrupted. <b>Make sure the "
104 "player is charged before starting the firmware update "
105 "process.</b></li>"
106 "<li>After the firmware has been updated reboot your player.</li>");
107 }
108 if(model == "sansafuzeplus") {
109 hint = true;
110 msg += QObject::tr("<li>Remove any previously inserted microSD card</li>");
111 msg += QObject::tr("<li>Disconnect your player. The player will reboot and "
112 "perform an update of the original firmware. "
113 "Please refer to your players manual on details.<br/>"
114 "<b>Important:</b> updating the firmware is a "
115 "critical process that must not be interrupted. <b>Make sure the "
116 "player is charged before disconnecting the player.</b></li>"
117 "<li>After the firmware has been updated reboot your player.</li>");
118 }
119 if(model == "iaudiox5" || model == "iaudiom5"
120 || model == "iaudiox5v" || model == "iaudiom3" || model == "mpioh200") {
121 hint = true;
122 msg += QObject::tr("<li>Turn the player off</li>"
123 "<li>Insert the charger</li>");
124 }
125 if(model == "gigabeatf") {
126 hint = true;
127 msg += QObject::tr("<li>Unplug USB and power adaptors</li>"
128 "<li>Hold <i>Power</i> to turn the player off</li>"
129 "<li>Toggle the battery switch on the player</li>"
130 "<li>Hold <i>Power</i> to boot into Rockbox</li>");
131 }
132 msg += "</ol>";
133 msg += QObject::tr("<p><b>Note:</b> You can safely install other parts first, but "
134 "the above steps are <b>required</b> to finish the installation!</p>");
135
136 if(hint)
137 return msg;
138 else
139 return QString();
140}
diff --git a/utils/rbutilqt/base/bootloaderinstallhelper.h b/utils/rbutilqt/base/bootloaderinstallhelper.h
new file mode 100644
index 0000000000..c89444d7a7
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallhelper.h
@@ -0,0 +1,36 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2012 Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 * This file is a modified version of the AMS installer by Dominik Wenger
18 *
19 ****************************************************************************/
20
21#ifndef BOOTLOADERINSTALLHELPER_H
22#define BOOTLOADERINSTALLHELPER_H
23
24#include <QtCore>
25#include "bootloaderinstallbase.h"
26
27class BootloaderInstallHelper : public QObject
28{
29 Q_OBJECT
30 public:
31 static BootloaderInstallBase* createBootloaderInstaller(QObject* parent, QString type);
32 static QString postinstallHints(QString model);
33};
34
35#endif
36
diff --git a/utils/rbutilqt/base/bootloaderinstallhex.cpp b/utils/rbutilqt/base/bootloaderinstallhex.cpp
new file mode 100644
index 0000000000..b3dde0bbfa
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallhex.cpp
@@ -0,0 +1,271 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "bootloaderinstallbase.h"
21#include "bootloaderinstallhex.h"
22#include "utils.h"
23#include "Logger.h"
24
25#include "../../tools/iriver.h"
26#include "../../tools/mkboot.h"
27
28struct md5s {
29 const char* orig;
30 const char* patched;
31};
32
33struct md5s md5sums[] = {
34#include "irivertools/h100sums.h"
35 { nullptr, nullptr },
36#include "irivertools/h120sums.h"
37 { nullptr, nullptr },
38#include "irivertools/h300sums.h"
39 { nullptr, nullptr }
40};
41
42
43BootloaderInstallHex::BootloaderInstallHex(QObject *parent)
44 : BootloaderInstallBase(parent)
45{
46}
47
48QString BootloaderInstallHex::ofHint()
49{
50 return tr("Bootloader installation requires you to provide "
51 "a firmware file of the original firmware (hex file). "
52 "You need to download this file yourself due to legal "
53 "reasons. Please refer to the "
54 "<a href='http://www.rockbox.org/manual.shtml'>manual</a> and the "
55 "<a href='http://www.rockbox.org/wiki/IriverBoot"
56 "#Download_and_extract_a_recent_ve'>IriverBoot</a> wiki page on "
57 "how to obtain this file.<br/>"
58 "Press Ok to continue and browse your computer for the firmware "
59 "file.");
60}
61
62bool BootloaderInstallHex::install(void)
63{
64 if(m_offile.isEmpty())
65 return false;
66 m_hashindex = -1;
67
68 // md5sum hex file
69 emit logItem(tr("checking MD5 hash of input file ..."), LOGINFO);
70 QByteArray filedata;
71 // read hex file into QByteArray
72 QFile file(m_offile);
73 file.open(QIODevice::ReadOnly);
74 filedata = file.readAll();
75 file.close();
76 QString hash = QCryptographicHash::hash(filedata,
77 QCryptographicHash::Md5).toHex();
78 LOG_INFO() << "hexfile hash:" << hash;
79 if(file.error() != QFile::NoError) {
80 emit logItem(tr("Could not verify original firmware file"), LOGERROR);
81 emit done(true);
82 return false;
83 }
84 // check hash and figure model from md5sum
85 int i = sizeof(md5sums) / sizeof(struct md5s);
86 m_model = 4;
87 // 3: h300, 2: h120, 1: h100, 0:invalid
88 while(i--) {
89 if(md5sums[i].orig == nullptr)
90 m_model--;
91 if(!qstrcmp(md5sums[i].orig, hash.toLatin1()))
92 break;
93 }
94 if(i < 0) {
95 emit logItem(tr("Firmware file not recognized."), LOGERROR);
96 return false;
97 }
98 else {
99 emit logItem(tr("MD5 hash ok"), LOGOK);
100 m_hashindex = i;
101 }
102
103 // check model agains download link.
104 QString match[] = {"", "h100", "h120", "h300"};
105 if(!m_blurl.path().contains(match[m_model])) {
106 emit logItem(tr("Firmware file doesn't match selected player."),
107 LOGERROR);
108 return false;
109 }
110
111 emit logItem(tr("Descrambling file"), LOGINFO);
112 m_descrambled.open();
113 int result;
114 result = iriver_decode(m_offile.toLatin1().data(),
115 m_descrambled.fileName().toLatin1().data(), FALSE, STRIP_NONE);
116 LOG_INFO() << "iriver_decode():" << result;
117
118 if(result < 0) {
119 emit logItem(tr("Error in descramble: %1").arg(scrambleError(result)), LOGERROR);
120 return false;
121 }
122
123 // download firmware from server
124 emit logItem(tr("Downloading bootloader file"), LOGINFO);
125 connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallHex::installStage2);
126
127 downloadBlStart(m_blurl);
128 return true;
129}
130
131
132void BootloaderInstallHex::installStage2(void)
133{
134 emit logItem(tr("Adding bootloader to firmware file"), LOGINFO);
135 QCoreApplication::processEvents();
136
137 // local temp file
138 QTemporaryFile tempbin;
139 tempbin.open();
140 QString tempbinName = tempbin.fileName();
141 tempbin.close();
142 // get temporary files filenames -- external tools need this.
143 m_descrambled.open();
144 QString descrambledName = m_descrambled.fileName();
145 m_descrambled.close();
146 m_tempfile.open();
147 QString tempfileName = m_tempfile.fileName();
148 m_tempfile.close();
149
150 int origin = 0;
151 switch(m_model) {
152 case 3:
153 origin = 0x3f0000;
154 break;
155 case 2:
156 case 1:
157 origin = 0x1f0000;
158 break;
159 default:
160 origin = 0;
161 break;
162 }
163
164 // iriver decode already done in stage 1
165 int result;
166 if((result = mkboot_iriver(descrambledName.toLocal8Bit().constData(),
167 tempfileName.toLocal8Bit().constData(),
168 tempbinName.toLocal8Bit().constData(), origin)) < 0)
169 {
170 QString error;
171 switch(result) {
172 case -1: error = tr("could not open input file"); break;
173 case -2: error = tr("reading header failed"); break;
174 case -3: error = tr("reading firmware failed"); break;
175 case -4: error = tr("can't open bootloader file"); break;
176 case -5: error = tr("reading bootloader file failed"); break;
177 case -6: error = tr("can't open output file"); break;
178 case -7: error = tr("writing output file failed"); break;
179 }
180 emit logItem(tr("Error in patching: %1").arg(error), LOGERROR);
181
182 emit done(true);
183 return;
184 }
185 QTemporaryFile targethex;
186 targethex.open();
187 QString targethexName = targethex.fileName();
188 if((result = iriver_encode(tempbinName.toLocal8Bit().constData(),
189 targethexName.toLocal8Bit().constData(), FALSE)) < 0)
190 {
191 emit logItem(tr("Error in scramble: %1").arg(scrambleError(result)), LOGERROR);
192 targethex.close();
193
194 emit done(true);
195 return;
196 }
197
198 // finally check the md5sum of the created file
199 QByteArray filedata;
200 filedata = targethex.readAll();
201 targethex.close();
202 QString hash = QCryptographicHash::hash(filedata,
203 QCryptographicHash::Md5).toHex();
204 LOG_INFO() << "created hexfile hash:" << hash;
205
206 emit logItem(tr("Checking modified firmware file"), LOGINFO);
207 if(hash != QString(md5sums[m_hashindex].patched)) {
208 emit logItem(tr("Error: modified file checksum wrong"), LOGERROR);
209 targethex.remove();
210 emit done(true);
211 return;
212 }
213 // finally copy file to player
214 if(!Utils::resolvePathCase(m_blfile).isEmpty()) {
215 emit logItem(tr("A firmware file is already present on player"), LOGERROR);
216 emit done(true);
217 return;
218 }
219 if(targethex.copy(m_blfile)) {
220 emit logItem(tr("Success: modified firmware file created"), LOGINFO);
221 }
222 else {
223 emit logItem(tr("Copying modified firmware file failed"), LOGERROR);
224 emit done(true);
225 return;
226 }
227
228 logInstall(LogAdd);
229 emit done(false);
230
231 return;
232}
233
234
235bool BootloaderInstallHex::uninstall(void)
236{
237 emit logItem(tr("Uninstallation not possible, only installation info removed"), LOGINFO);
238 logInstall(LogRemove);
239 emit done(true);
240 return false;
241}
242
243
244BootloaderInstallBase::BootloaderType BootloaderInstallHex::installed(void)
245{
246 return BootloaderUnknown;
247}
248
249
250BootloaderInstallBase::Capabilities BootloaderInstallHex::capabilities(void)
251{
252 return (Install | NeedsOf);
253}
254
255QString BootloaderInstallHex::scrambleError(int err)
256{
257 QString error;
258 switch(err) {
259 case -1: error = tr("Can't open input file"); break;
260 case -2: error = tr("Can't open output file"); break;
261 case -3: error = tr("invalid file: header length wrong"); break;
262 case -4: error = tr("invalid file: unrecognized header"); break;
263 case -5: error = tr("invalid file: \"length\" field wrong"); break;
264 case -6: error = tr("invalid file: \"length2\" field wrong"); break;
265 case -7: error = tr("invalid file: internal checksum error"); break;
266 case -8: error = tr("invalid file: \"length3\" field wrong"); break;
267 default: error = tr("unknown"); break;
268 }
269 return error;
270}
271
diff --git a/utils/rbutilqt/base/bootloaderinstallhex.h b/utils/rbutilqt/base/bootloaderinstallhex.h
new file mode 100644
index 0000000000..700d77447a
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallhex.h
@@ -0,0 +1,53 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef BOOTLOADERINSTALLHEX_H
20#define BOOTLOADERINSTALLHEX_H
21
22#include <QtCore>
23#include "bootloaderinstallbase.h"
24
25
26// bootloader installation derivate based on fwpatcher
27// This will patch a given hex file using (de)scramble / mkboot
28// and put it on the player.
29class BootloaderInstallHex : public BootloaderInstallBase
30{
31 Q_OBJECT
32
33 public:
34 BootloaderInstallHex(QObject *parent = nullptr);
35 bool install(void);
36 bool uninstall(void);
37 BootloaderInstallBase::BootloaderType installed(void);
38 Capabilities capabilities(void);
39 QString ofHint();
40
41 private:
42 int m_hashindex;
43 int m_model;
44 QTemporaryFile m_descrambled;
45 QString scrambleError(int);
46
47 private slots:
48 void installStage2(void);
49};
50
51
52#endif
53
diff --git a/utils/rbutilqt/base/bootloaderinstallimx.cpp b/utils/rbutilqt/base/bootloaderinstallimx.cpp
new file mode 100644
index 0000000000..7428cf10c8
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallimx.cpp
@@ -0,0 +1,193 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2011 by Jean-Louis Biasini
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include <QtDebug>
21#include "bootloaderinstallbase.h"
22#include "bootloaderinstallimx.h"
23#include "../mkimxboot/mkimxboot.h"
24#include "Logger.h"
25
26// class for running mkimxboot() in a separate thread to keep the UI responsive.
27class BootloaderThreadImx : public QThread
28{
29 public:
30 void run(void);
31 void setInputFile(QString f)
32 { m_inputfile = f; }
33 void setOutputFile(QString f)
34 { m_outputfile = f; }
35 void setBootloaderFile(QString f)
36 { m_bootfile = f; }
37 enum imx_error_t error(void)
38 { return m_error; }
39 private:
40 QString m_inputfile;
41 QString m_bootfile;
42 QString m_outputfile;
43 enum imx_error_t m_error;
44};
45
46
47void BootloaderThreadImx::run(void)
48{
49 LOG_INFO() << "Thread started.";
50 struct imx_option_t opt;
51 memset(&opt, 0, sizeof(opt));
52 opt.debug = false;
53 opt.output = IMX_DUALBOOT;
54 opt.fw_variant = VARIANT_DEFAULT;
55
56 m_error = mkimxboot(m_inputfile.toLocal8Bit().constData(),
57 m_bootfile.toLocal8Bit().constData(),
58 m_outputfile.toLocal8Bit().constData(), opt);
59 LOG_INFO() << "Thread finished, result:" << m_error;
60}
61
62
63BootloaderInstallImx::BootloaderInstallImx(QObject *parent)
64 : BootloaderInstallBase(parent)
65{
66 m_thread = nullptr;
67}
68
69
70QString BootloaderInstallImx::ofHint()
71{
72 return tr("Bootloader installation requires you to provide "
73 "a copy of the original Sandisk firmware (firmware.sb file). "
74 "This file will be patched with the Rockbox bootloader and "
75 "installed to your player. You need to download this file "
76 "yourself due to legal reasons. Please browse the "
77 "<a href='http://forums.sandisk.com/sansa/'>Sansa Forums</a> "
78 "or refer to the "
79 "<a href= 'http://www.rockbox.org/wiki/SansaFuzePlus'>SansaFuzePlus</a> "
80 "wiki page on how to obtain this file.<br/>"
81 "Press Ok to continue and browse your computer for the firmware "
82 "file.");
83}
84
85
86/** Start bootloader installation.
87 */
88bool BootloaderInstallImx::install(void)
89{
90 if(!QFileInfo(m_offile).isReadable())
91 {
92 LOG_ERROR() << "could not read original firmware file"
93 << m_offile;
94 emit logItem(tr("Could not read original firmware file"), LOGERROR);
95 return false;
96 }
97
98 LOG_INFO() << "downloading bootloader";
99 // download bootloader from server
100 emit logItem(tr("Downloading bootloader file"), LOGINFO);
101 connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallImx::installStage2);
102 downloadBlStart(m_blurl);
103 return true;
104}
105
106
107void BootloaderInstallImx::installStage2(void)
108{
109 LOG_INFO() << "patching file...";
110 emit logItem(tr("Patching file..."), LOGINFO);
111 m_tempfile.open();
112
113 // we have not detailed progress on the patching so just show a busy
114 // indicator instead.
115 emit logProgress(0, 0);
116 m_patchedFile.open();
117 m_thread = new BootloaderThreadImx();
118 m_thread->setInputFile(m_offile);
119 m_thread->setBootloaderFile(m_tempfile.fileName());
120 m_thread->setOutputFile(m_patchedFile.fileName());
121 m_tempfile.close();
122 m_patchedFile.close();
123 connect(m_thread, &QThread::finished, this, &BootloaderInstallImx::installStage3);
124 connect(m_thread, SIGNAL(terminated()), this, SLOT(installStage3()));
125 m_thread->start();
126}
127
128
129void BootloaderInstallImx::installStage3(void)
130{
131 enum imx_error_t err = m_thread->error();
132 emit logProgress(1, 1);
133 // if the patch failed
134 if (err != IMX_SUCCESS)
135 {
136 LOG_ERROR() << "Could not patch the original firmware file";
137 emit logItem(tr("Patching the original firmware failed"), LOGERROR);
138 emit done(true);
139 return;
140 }
141
142 LOG_INFO() << "Original Firmware succesfully patched";
143 emit logItem(tr("Succesfully patched firmware file"), LOGINFO);
144
145 // if a bootloader is already present delete it.
146 QString fwfile(m_blfile);
147 if(QFileInfo(fwfile).isFile())
148 {
149 LOG_INFO() << "deleting old target file";
150 QFile::remove(fwfile);
151 }
152
153 // place (new) bootloader. Copy, since the temporary file will be removed
154 // automatically.
155 LOG_INFO() << "moving patched bootloader to" << fwfile;
156 if(m_patchedFile.copy(fwfile))
157 {
158 emit logItem(tr("Bootloader successful installed"), LOGOK);
159 logInstall(LogAdd);
160 emit done(false);
161 }
162 else
163 {
164 emit logItem(tr("Patched bootloader could not be installed"), LOGERROR);
165 emit done(true);
166 }
167 // clean up thread object.
168 delete m_thread;
169 return;
170}
171
172
173bool BootloaderInstallImx::uninstall(void)
174{
175 emit logItem(tr("To uninstall, perform a normal upgrade with an unmodified "
176 "original firmware."), LOGINFO);
177 logInstall(LogRemove);
178 emit done(true);
179 return false;
180}
181
182
183BootloaderInstallBase::BootloaderType BootloaderInstallImx::installed(void)
184{
185 return BootloaderUnknown;
186}
187
188
189BootloaderInstallBase::Capabilities BootloaderInstallImx::capabilities(void)
190{
191 return (Install | NeedsOf);
192}
193
diff --git a/utils/rbutilqt/base/bootloaderinstallimx.h b/utils/rbutilqt/base/bootloaderinstallimx.h
new file mode 100644
index 0000000000..1d780998ef
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallimx.h
@@ -0,0 +1,47 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2011 by Jean-Louis Biasini
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18#ifndef BOOTLOADERINSTALLIMX_H
19#define BOOTLOADERINSTALLIMX_H
20
21#include <QtCore>
22#include "bootloaderinstallbase.h"
23
24class BootloaderThreadImx;
25
26//! bootloader installation class for devices handled by mkimxboot.
27class BootloaderInstallImx : public BootloaderInstallBase
28{
29 Q_OBJECT
30 public:
31 BootloaderInstallImx(QObject *parent);
32 bool install(void);
33 bool uninstall(void);
34 BootloaderInstallBase::BootloaderType installed(void);
35 Capabilities capabilities(void);
36 QString ofHint();
37
38 private slots:
39 void installStage2(void);
40 void installStage3(void);
41
42 private:
43 BootloaderThreadImx *m_thread;
44 QTemporaryFile m_patchedFile;
45};
46
47#endif
diff --git a/utils/rbutilqt/base/bootloaderinstallipod.cpp b/utils/rbutilqt/base/bootloaderinstallipod.cpp
new file mode 100644
index 0000000000..807c6e870b
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallipod.cpp
@@ -0,0 +1,272 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "bootloaderinstallbase.h"
21#include "bootloaderinstallipod.h"
22
23#include "../ipodpatcher/ipodpatcher.h"
24#include "utils.h"
25#include "Logger.h"
26
27
28BootloaderInstallIpod::BootloaderInstallIpod(QObject *parent)
29 : BootloaderInstallBase(parent)
30{
31 (void)parent;
32 // initialize sector buffer. The sector buffer is part of the ipod_t
33 // structure, so a second instance of this class will have its own buffer.
34 ipod_alloc_buffer(&ipod, BUFFER_SIZE);
35}
36
37
38BootloaderInstallIpod::~BootloaderInstallIpod()
39{
40 if(ipod.sectorbuf) {
41 ipod_dealloc_buffer(&ipod);
42 }
43}
44
45
46bool BootloaderInstallIpod::install(void)
47{
48 if(ipod.sectorbuf == nullptr) {
49 emit logItem(tr("Error: can't allocate buffer memory!"), LOGERROR);
50 emit done(true);
51 return false;
52 }
53 // save buffer pointer before cleaning up ipod_t structure
54 unsigned char* sb = ipod.sectorbuf;
55 memset(&ipod, 0, sizeof(struct ipod_t));
56 ipod.sectorbuf = sb;
57
58 if(!ipodInitialize(&ipod)) {
59 emit done(true);
60 return false;
61 }
62
63 if(ipod.nimages <= 0) {
64 emit logItem(tr("Failed to read firmware directory"), LOGERROR);
65 emit done(true);
66 return false;
67 }
68 if(getmodel(&ipod,(ipod.ipod_directory[ipod.ososimage].vers>>8)) < 0) {
69 emit logItem(tr("Unknown version number in firmware (%1)").arg(
70 ipod.ipod_directory[ipod.ososimage].vers), LOGERROR);
71 emit done(true);
72 return false;
73 }
74 if(ipod.macpod) {
75 emit logItem(tr("Warning: This is a MacPod, Rockbox only runs on WinPods. \n"
76 "See http://www.rockbox.org/wiki/IpodConversionToFAT32"), LOGERROR);
77 emit done(true);
78 return false;
79 }
80 emit logItem(tr("Downloading bootloader file"), LOGINFO);
81
82 downloadBlStart(m_blurl);
83 connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallIpod::installStage2);
84 return true;
85}
86
87
88void BootloaderInstallIpod::installStage2(void)
89{
90 emit logItem(tr("Installing Rockbox bootloader"), LOGINFO);
91 QCoreApplication::processEvents();
92
93 if(ipod_reopen_rw(&ipod) < 0) {
94 emit logItem(tr("Could not open Ipod in R/W mode"), LOGERROR);
95 emit done(true);
96 return;
97 }
98 QCoreApplication::processEvents();
99
100 m_tempfile.open();
101 QString blfile = m_tempfile.fileName();
102 m_tempfile.close();
103 if(add_bootloader(&ipod, blfile.toLatin1().data(), FILETYPE_DOT_IPOD) == 0) {
104 emit logItem(tr("Successfull added bootloader"), LOGOK);
105 ipod_close(&ipod);
106#if defined(Q_OS_MACX)
107 m_remountDevice = ipod.diskname;
108 connect(this, SIGNAL(remounted(bool)), this, SLOT(installStage3(bool)));
109 waitRemount();
110#else
111 installStage3(true);
112#endif
113 }
114 else {
115 emit logItem(tr("Failed to add bootloader"), LOGERROR);
116 ipod_close(&ipod);
117 emit done(true);
118 return;
119 }
120}
121
122
123void BootloaderInstallIpod::installStage3(bool mounted)
124{
125 if(mounted) {
126 logInstall(LogAdd);
127 emit logItem(tr("Bootloader Installation complete."), LOGINFO);
128 emit done(false);
129 return;
130 }
131 else {
132 emit logItem(tr("Writing log aborted"), LOGERROR);
133 emit done(true);
134 }
135 LOG_INFO() << "version installed:"
136 << m_blversion.toString(Qt::ISODate);
137}
138
139
140bool BootloaderInstallIpod::uninstall(void)
141{
142 emit logItem(tr("Uninstalling bootloader"), LOGINFO);
143 QCoreApplication::processEvents();
144
145 if(!ipodInitialize(&ipod)) {
146 emit done(true);
147 return false;
148 }
149
150 if (ipod.nimages <= 0) {
151 emit logItem(tr("Failed to read firmware directory"),LOGERROR);
152 emit done(true);
153 return false;
154 }
155 if (getmodel(&ipod,(ipod.ipod_directory[ipod.ososimage].vers>>8)) < 0) {
156 emit logItem(tr("Unknown version number in firmware (%1)").arg(
157 ipod.ipod_directory[ipod.ososimage].vers), LOGERROR);
158 emit done(true);
159 return false;
160 }
161
162 if (ipod_reopen_rw(&ipod) < 0) {
163 emit logItem(tr("Could not open Ipod in R/W mode"), LOGERROR);
164 emit done(true);
165 return false;
166 }
167
168 if (ipod_has_bootloader(&ipod) == 0) {
169 emit logItem(tr("No bootloader detected."), LOGERROR);
170 emit done(true);
171 return false;
172 }
173
174 if (delete_bootloader(&ipod)==0) {
175 emit logItem(tr("Successfully removed bootloader"), LOGOK);
176 logInstall(LogRemove);
177 emit logProgress(1, 1);
178 emit done(false);
179 ipod_close(&ipod);
180 return true;
181 }
182 else {
183 emit logItem(tr("Removing bootloader failed."), LOGERROR);
184 emit done(true);
185 ipod_close(&ipod);
186 return false;
187 }
188}
189
190
191BootloaderInstallBase::BootloaderType BootloaderInstallIpod::installed(void)
192{
193 BootloaderInstallBase::BootloaderType result = BootloaderRockbox;
194
195 if(!ipodInitialize(&ipod)) {
196 LOG_INFO() << "installed: BootloaderUnknown";
197 result = BootloaderUnknown;
198 }
199 else {
200 read_directory(&ipod);
201 getmodel(&ipod,(ipod.ipod_directory[ipod.ososimage].vers>>8));
202 if(!ipod_has_bootloader(&ipod)) {
203 result = BootloaderOther;
204 }
205 else {
206 LOG_INFO() << "installed: BootloaderRockbox";
207 }
208 }
209 ipod_close(&ipod);
210
211 return result;
212}
213
214
215BootloaderInstallBase::Capabilities BootloaderInstallIpod::capabilities(void)
216{
217 return (Install | Uninstall | IsRaw);
218}
219
220
221/** @initialize Ipod by opening its file handle and checking if its an ipod.
222 * Note: the caller has to make sure the file handle gets closed!
223 */
224bool BootloaderInstallIpod::ipodInitialize(struct ipod_t *ipod)
225{
226 if(!m_blfile.isEmpty()) {
227 QString devicename = Utils::resolveDevicename(m_blfile);
228 if(devicename.isEmpty()) {
229 emit logItem(tr("Error: could not retrieve device name"), LOGERROR);
230 return false;
231 }
232#if defined(Q_OS_WIN32)
233 sprintf(ipod->diskname, "\\\\.\\PhysicalDrive%i", devicename.toInt());
234#elif defined(Q_OS_MACX)
235 sprintf(ipod->diskname, "%s",
236 qPrintable(devicename.remove(QRegExp("s[0-9]+$"))));
237#else
238 sprintf(ipod->diskname, "%s",
239 qPrintable(devicename.remove(QRegExp("[0-9]+$"))));
240#endif
241 LOG_INFO() << "ipodpatcher: overriding scan, using"
242 << ipod->diskname;
243 }
244 else {
245 emit logItem(tr("Error: no mountpoint specified!"), LOGERROR);
246 LOG_ERROR() << "no mountpoint specified!";
247 }
248 int result = ipod_open(ipod, 1);
249 if(result == -2) {
250 emit logItem(tr("Could not open Ipod: permission denied"), LOGERROR);
251 return false;
252 }
253 else if(result < 0) {
254 emit logItem(tr("Could not open Ipod"), LOGERROR);
255 return false;
256 }
257
258 if(read_partinfo(ipod, 1) < 0) {
259 emit logItem(tr("Error reading partition table - possibly not an Ipod"), LOGERROR);
260 ipod_close(ipod);
261 return false;
262 }
263
264 if(ipod->pinfo[0].start == 0) {
265 emit logItem(tr("No firmware partition on disk"), LOGERROR);
266 ipod_close(ipod);
267 return false;
268 }
269 read_directory(ipod);
270 return true;
271}
272
diff --git a/utils/rbutilqt/base/bootloaderinstallipod.h b/utils/rbutilqt/base/bootloaderinstallipod.h
new file mode 100644
index 0000000000..ac69c608d6
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallipod.h
@@ -0,0 +1,51 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef BOOTLOADERINSTALLIPOD_H
20#define BOOTLOADERINSTALLIPOD_H
21
22#include <QtCore>
23#include "bootloaderinstallbase.h"
24#include "../ipodpatcher/ipodpatcher.h"
25
26// installer class derivate for Ipod installation
27// based on ipodpatcher.
28class BootloaderInstallIpod : public BootloaderInstallBase
29{
30 Q_OBJECT
31
32 public:
33 BootloaderInstallIpod(QObject *parent);
34 ~BootloaderInstallIpod();
35 bool install(void);
36 bool uninstall(void);
37 BootloaderInstallBase::BootloaderType installed(void);
38 Capabilities capabilities(void);
39
40 private slots:
41 void installStage2(void);
42 void installStage3(bool mounted);
43
44 private:
45 bool ipodInitialize(struct ipod_t *);
46 struct ipod_t ipod;
47};
48
49
50#endif
51
diff --git a/utils/rbutilqt/base/bootloaderinstallmi4.cpp b/utils/rbutilqt/base/bootloaderinstallmi4.cpp
new file mode 100644
index 0000000000..2657347b87
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallmi4.cpp
@@ -0,0 +1,162 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include <QtDebug>
21#include <QtDebug>
22#include "bootloaderinstallmi4.h"
23#include "utils.h"
24#include "Logger.h"
25
26BootloaderInstallMi4::BootloaderInstallMi4(QObject *parent)
27 : BootloaderInstallBase(parent)
28{
29}
30
31
32bool BootloaderInstallMi4::install(void)
33{
34 emit logItem(tr("Downloading bootloader"), LOGINFO);
35 LOG_INFO() << "installing bootloader";
36 downloadBlStart(m_blurl);
37 connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallMi4::installStage2);
38 return true;
39}
40
41void BootloaderInstallMi4::installStage2(void)
42{
43 emit logItem(tr("Installing Rockbox bootloader"), LOGINFO);
44 QCoreApplication::processEvents();
45
46 // move old bootloader out of the way
47 QString fwfile(Utils::resolvePathCase(m_blfile));
48 QFile oldbl(fwfile);
49 QString moved = QFileInfo(Utils::resolvePathCase(m_blfile)).absolutePath()
50 + "/OF.mi4";
51 if(!QFileInfo::exists(moved)) {
52 LOG_INFO() << "renaming" << fwfile << "to" << moved;
53 oldbl.rename(moved);
54 }
55 else {
56 LOG_INFO() << "OF.mi4 already present, not renaming again.";
57 oldbl.remove();
58 }
59
60 // place new bootloader
61 m_tempfile.open();
62 LOG_INFO() << "renaming" << m_tempfile.fileName()
63 << "to" << fwfile;
64 m_tempfile.close();
65 if(!Utils::resolvePathCase(fwfile).isEmpty()) {
66 emit logItem(tr("A firmware file is already present on player"), LOGERROR);
67 emit done(true);
68 return;
69 }
70 if(m_tempfile.copy(fwfile)) {
71 emit logItem(tr("Bootloader successful installed"), LOGOK);
72 }
73 else {
74 emit logItem(tr("Copying modified firmware file failed"), LOGERROR);
75 emit done(true);
76 return;
77 }
78
79 emit logItem(tr("Bootloader successful installed"), LOGOK);
80 logInstall(LogAdd);
81
82 emit done(false);
83}
84
85
86bool BootloaderInstallMi4::uninstall(void)
87{
88 LOG_INFO() << "Uninstalling bootloader";
89
90 // check if it's actually a Rockbox bootloader
91 emit logItem(tr("Checking for Rockbox bootloader"), LOGINFO);
92 if(installed() != BootloaderRockbox) {
93 emit logItem(tr("No Rockbox bootloader found"), LOGERROR);
94 emit done(true);
95 return false;
96 }
97
98 // check if OF file present
99 emit logItem(tr("Checking for original firmware file"), LOGINFO);
100 QString original = QFileInfo(Utils::resolvePathCase(m_blfile)).absolutePath()
101 + "/OF.mi4";
102
103 if(Utils::resolvePathCase(original).isEmpty()) {
104 emit logItem(tr("Error finding original firmware file"), LOGERROR);
105 emit done(true);
106 return false;
107 }
108
109 // finally remove RB bootloader
110 QString resolved = Utils::resolvePathCase(m_blfile);
111 QFile blfile(resolved);
112 blfile.remove();
113
114 QFile::rename(Utils::resolvePathCase(original), resolved);
115 emit logItem(tr("Rockbox bootloader successful removed"), LOGINFO);
116 logInstall(LogRemove);
117 emit logProgress(1, 1);
118 emit done(false);
119
120 return true;
121}
122
123
124//! check if a bootloader is installed and return its state.
125BootloaderInstallBase::BootloaderType BootloaderInstallMi4::installed(void)
126{
127 // for MI4 files we can check if we actually have a RB bootloader
128 // installed.
129 // RB bootloader has "RBBL" at 0x1f8 in the mi4 file.
130
131 // make sure to resolve case to prevent case issues
132 QString resolved;
133 resolved = Utils::resolvePathCase(m_blfile);
134 if(resolved.isEmpty()) {
135 LOG_INFO() << "installed: BootloaderNone";
136 return BootloaderNone;
137 }
138
139 QFile f(resolved);
140 f.open(QIODevice::ReadOnly);
141 f.seek(0x1f8);
142 char magic[4];
143 f.read(magic, 4);
144 f.close();
145
146 if(!memcmp(magic, "RBBL", 4)) {
147 LOG_INFO() << "installed: BootloaderRockbox";
148 return BootloaderRockbox;
149 }
150 else {
151 LOG_INFO() << "installed: BootloaderOther";
152 return BootloaderOther;
153 }
154}
155
156
157BootloaderInstallBase::Capabilities BootloaderInstallMi4::capabilities(void)
158{
159 LOG_INFO() << "getting capabilities";
160 return Install | Uninstall | Backup | IsFile | CanCheckInstalled | CanCheckVersion;
161}
162
diff --git a/utils/rbutilqt/base/bootloaderinstallmi4.h b/utils/rbutilqt/base/bootloaderinstallmi4.h
new file mode 100644
index 0000000000..c56669b99f
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallmi4.h
@@ -0,0 +1,48 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef BOOTLOADERINSTALLMI4_H
20#define BOOTLOADERINSTALLMI4_H
21
22#include <QtCore>
23#include "progressloggerinterface.h"
24#include "bootloaderinstallbase.h"
25
26
27// mi4 bootloader file based installation.
28// Puts the bootloader file to the correct location and
29// renames the OF to OF.mi4.
30class BootloaderInstallMi4 : public BootloaderInstallBase
31{
32 Q_OBJECT
33
34 public:
35 BootloaderInstallMi4(QObject *parent);
36 bool install(void);
37 bool uninstall(void);
38 BootloaderInstallBase::BootloaderType installed(void);
39 Capabilities capabilities(void);
40
41 private slots:
42 void installStage2(void);
43
44 private:
45};
46
47#endif
48
diff --git a/utils/rbutilqt/base/bootloaderinstallmpio.cpp b/utils/rbutilqt/base/bootloaderinstallmpio.cpp
new file mode 100644
index 0000000000..863418e591
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallmpio.cpp
@@ -0,0 +1,143 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Wenger
10 * $Id: bootloaderinstallams.cpp 24778 2010-02-19 23:45:29Z funman $
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include <QtCore>
21#include "bootloaderinstallbase.h"
22#include "bootloaderinstallmpio.h"
23#include "Logger.h"
24
25#include "../mkmpioboot/mkmpioboot.h"
26
27BootloaderInstallMpio::BootloaderInstallMpio(QObject *parent)
28 : BootloaderInstallBase(parent)
29{
30}
31
32QString BootloaderInstallMpio::ofHint()
33{
34 return tr("Bootloader installation requires you to provide "
35 "a firmware file of the original firmware (bin file). "
36 "You need to download this file yourself due to legal "
37 "reasons. Please refer to the "
38 "<a href='http://www.rockbox.org/manual.shtml'>manual</a> and "
39 "the <a href='http://www.rockbox.org/wiki/MPIOHD200Port'>MPIOHD200Port</a> "
40 "wiki page on how to obtain this file.<br/>"
41 "Press Ok to continue and browse your computer for the firmware "
42 "file.");
43}
44
45bool BootloaderInstallMpio::install(void)
46{
47 if(m_offile.isEmpty())
48 return false;
49
50 LOG_INFO() << "installing bootloader";
51
52 // download firmware from server
53 emit logItem(tr("Downloading bootloader file"), LOGINFO);
54
55 connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallMpio::installStage2);
56 downloadBlStart(m_blurl);
57
58 return true;
59}
60
61void BootloaderInstallMpio::installStage2(void)
62{
63 LOG_INFO() << "installStage2";
64
65 int origin = 0xe0000; /* MPIO HD200 bootloader address */
66
67 m_tempfile.open();
68 QString bootfile = m_tempfile.fileName();
69 m_tempfile.close();
70
71 int ret = mkmpioboot(m_offile.toLocal8Bit().data(),
72 bootfile.toLocal8Bit().data(), m_blfile.toLocal8Bit().data(), origin);
73
74 if(ret != 0)
75 {
76 QString error;
77 switch(ret)
78 {
79 case -1:
80 error = tr("Could not open the original firmware.");
81 break;
82 case -2:
83 error = tr("Could not read the original firmware.");
84 break;
85 case -3:
86 error = tr("Loaded firmware file does not look like MPIO original firmware file.");
87 break;
88 case -4:
89 error = tr("Could not open downloaded bootloader.");
90 break;
91 case -5:
92 error = tr("Place for bootloader in OF file not empty.");
93 break;
94 case -6:
95 error = tr("Could not read the downloaded bootloader.");
96 break;
97 case -7:
98 error = tr("Bootloader checksum error.");
99 break;
100 case -8:
101 error = tr("Could not open output file.");
102 break;
103 case -9:
104 error = tr("Could not write output file.");
105 break;
106 default:
107 error = tr("Unknown error number: %1").arg(ret);
108 break;
109 }
110
111 LOG_ERROR() << "Patching original firmware failed:" << error;
112 emit logItem(tr("Patching original firmware failed: %1").arg(error), LOGERROR);
113 emit done(true);
114 return;
115 }
116
117 //end of install
118 LOG_INFO() << "install successful";
119 emit logItem(tr("Success: modified firmware file created"), LOGINFO);
120 logInstall(LogAdd);
121 emit done(false);
122 return;
123}
124
125bool BootloaderInstallMpio::uninstall(void)
126{
127 emit logItem(tr("To uninstall, perform a normal upgrade with an unmodified "
128 "original firmware"), LOGINFO);
129 logInstall(LogRemove);
130 emit done(true);
131 return false;
132}
133
134BootloaderInstallBase::BootloaderType BootloaderInstallMpio::installed(void)
135{
136 return BootloaderUnknown;
137}
138
139BootloaderInstallBase::Capabilities BootloaderInstallMpio::capabilities(void)
140{
141 return (Install | NeedsOf);
142}
143
diff --git a/utils/rbutilqt/base/bootloaderinstallmpio.h b/utils/rbutilqt/base/bootloaderinstallmpio.h
new file mode 100644
index 0000000000..8e6c65affe
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallmpio.h
@@ -0,0 +1,43 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Wenger
10 * $Id: bootloaderinstallams.h 22317 2009-08-15 13:04:21Z Domonoky $
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#ifndef BOOTLOADERINSTALLMPIO_H
20#define BOOTLOADERINSTALLMPIO_H
21
22#include <QtCore>
23#include "bootloaderinstallbase.h"
24
25//! bootloader installation derivate based on mkmpioboot
26class BootloaderInstallMpio : public BootloaderInstallBase
27{
28 Q_OBJECT
29 public:
30 BootloaderInstallMpio(QObject *parent);
31 bool install(void);
32 bool uninstall(void);
33 BootloaderInstallBase::BootloaderType installed(void);
34 Capabilities capabilities(void);
35 QString ofHint();
36
37 private:
38
39 private slots:
40 void installStage2(void);
41};
42
43#endif
diff --git a/utils/rbutilqt/base/bootloaderinstalls5l.cpp b/utils/rbutilqt/base/bootloaderinstalls5l.cpp
new file mode 100644
index 0000000000..63a30ff2b0
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstalls5l.cpp
@@ -0,0 +1,437 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "bootloaderinstallbase.h"
21#include "bootloaderinstalls5l.h"
22#include "Logger.h"
23#include "utils.h"
24#include "system.h"
25#include "rbsettings.h"
26#include "playerbuildinfo.h"
27
28#include "../mks5lboot/mks5lboot.h"
29
30
31BootloaderInstallS5l::BootloaderInstallS5l(QObject *parent)
32 : BootloaderInstallBase(parent)
33{
34}
35
36
37bool BootloaderInstallS5l::install(void)
38{
39 LOG_INFO() << "installing bootloader";
40 doInstall = true;
41 return installStage1();
42}
43
44
45bool BootloaderInstallS5l::uninstall(void)
46{
47 LOG_INFO() << "uninstalling bootloader";
48 doInstall = false;
49 return installStage1();
50}
51
52
53bool BootloaderInstallS5l::installStage1(void)
54{
55 LOG_INFO() << "installStage1";
56
57 mntpoint = RbSettings::value(RbSettings::Mountpoint).toString();
58
59 if (!Utils::mountpoints(Utils::MountpointsSupported).contains(mntpoint)) {
60 LOG_ERROR() << "iPod not mounted:" << mntpoint;
61 emit logItem(tr("Could not find mounted iPod."), LOGERROR);
62 emit done(true);
63 return false;
64 }
65
66 if (doInstall) {
67 // download firmware from server
68 emit logItem(tr("Downloading bootloader file..."), LOGINFO);
69 connect(this, &BootloaderInstallBase::downloadDone,
70 this, &BootloaderInstallS5l::installStageMkdfu);
71 downloadBlStart(m_blurl);
72 }
73 else {
74 installStageMkdfu();
75 }
76
77 return true;
78}
79
80
81void BootloaderInstallS5l::installStageMkdfu(void)
82{
83 int dfu_type;
84 QString dfu_arg;
85 char errstr[200];
86
87 LOG_INFO() << "installStageMkdfu";
88
89 setProgress(0);
90 aborted = false;
91 connect(this, &BootloaderInstallBase::installAborted,
92 this, &BootloaderInstallS5l::abortInstall);
93 connect(this, &BootloaderInstallBase::done,
94 this, &BootloaderInstallS5l::installDone);
95
96 if (doInstall) {
97 dfu_type = DFU_INST;
98 m_tempfile.open();
99 dfu_arg = m_tempfile.fileName();
100 m_tempfile.close();
101 }
102 else {
103 dfu_type = DFU_UNINST;
104 dfu_arg = RbSettings::value(RbSettings::Platform).toString();
105 }
106
107 // build DFU image
108 dfu_buf = mkdfu(dfu_type, dfu_arg.toLocal8Bit().data(),
109 &dfu_size, errstr, sizeof(errstr));
110 if (!dfu_buf) {
111 LOG_ERROR() << "mkdfu() failed:" << errstr;
112 emit logItem(errstr, LOGERROR);
113 emit logItem(tr("Could not make DFU image."), LOGERROR);
114 emit done(true);
115 return;
116 }
117
118 LOG_INFO() << "preparing installStageWaitForEject";
119 emit logItem(tr("Ejecting iPod..."), LOGINFO);
120 setProgress(10);
121 scanTimer.invalidate();
122 installStageWaitForEject();
123}
124
125
126void BootloaderInstallS5l::installStageWaitForEject(void)
127{
128 if (!updateProgress())
129 return; /* aborted */
130
131 if (!scanTimer.isValid() || (scanTimer.elapsed() > 3000)) {
132 scanSuccess = Utils::ejectDevice(mntpoint);
133 if (!scanSuccess) {
134 scanSuccess = !Utils::mountpoints(
135 Utils::MountpointsSupported).contains(mntpoint);
136 }
137 scanTimer.start();
138 }
139 if (!scanSuccess) {
140 if (!actionShown) {
141 emit logItem(tr("Action required:\n\n"
142 "Please make sure no programs are accessing "
143 "files on the device. If ejecting still fails "
144 "please use your computers eject functionality."),
145 LOGWARNING);
146 actionShown = true;
147 }
148 QTimer::singleShot(250, this, &BootloaderInstallS5l::installStageWaitForEject);
149 return;
150 }
151 emit logItem(tr("Device successfully ejected."), LOGINFO);
152
153 LOG_INFO() << "preparing installStageWaitForProcs";
154 setProgress(40, 18);
155 scanTimer.invalidate();
156 installStageWaitForProcs();
157}
158
159
160void BootloaderInstallS5l::installStageWaitForProcs(void)
161{
162 if (!updateProgress())
163 return; /* aborted */
164
165 if (!scanTimer.isValid() || (scanTimer.elapsed() > 1000)) {
166 scanSuccess = Utils::findRunningProcess(QStringList("iTunes")).isEmpty();
167 scanTimer.start();
168 }
169 if (!scanSuccess) {
170 if (!actionShown) {
171 emit logItem(tr("Action required:\n\n"
172 "Quit iTunes application."), LOGWARNING);
173 actionShown = true;
174 }
175 QTimer::singleShot(250, this, &BootloaderInstallS5l::installStageWaitForProcs);
176 return;
177 }
178 if (actionShown) {
179 emit logItem(tr("iTunes closed."), LOGINFO);
180 if (!updateProgress())
181 return; /* aborted */
182 }
183
184 QList<int> helperPids = Utils::findRunningProcess(
185#if defined(Q_OS_WIN32)
186 QStringList("iTunesHelper"))["iTunesHelper.exe"];
187#else
188 QStringList("iTunesHelper"))["iTunesHelper"];
189#endif
190 suspendedPids = Utils::suspendProcess(helperPids, true);
191 if (suspendedPids.size() != helperPids.size()) {
192 emit logItem(tr("Could not suspend iTunesHelper. Stop it "
193 "using the Task Manager, and try again."), LOGERROR);
194 emit done(true);
195 return;
196 }
197
198 LOG_INFO() << "preparing installStageWaitForSpindown";
199 // for Windows: skip waiting if the HDD was ejected a time ago
200 if (progressTimer.elapsed() < progressTimeout)
201 emit logItem(tr("Waiting for HDD spin-down..."), LOGINFO);
202 installStageWaitForSpindown();
203}
204
205
206void BootloaderInstallS5l::installStageWaitForSpindown(void)
207{
208 if (!updateProgress())
209 return; /* aborted */
210
211 if (progressTimer.elapsed() < progressTimeout) {
212 QTimer::singleShot(250, this, &BootloaderInstallS5l::installStageWaitForSpindown);
213 return;
214 }
215
216 LOG_INFO() << "preparing installStageWaitForDfu";
217 emit logItem(tr("Waiting for DFU mode..."), LOGINFO);
218 emit logItem(tr("Action required:\n\n"
219 "Press and hold SELECT+MENU buttons, after "
220 "about 12 seconds a new action will require "
221 "you to release the buttons, DO IT QUICKLY, "
222 "otherwise the process could fail."), LOGWARNING);
223 scanTimer.invalidate();
224 installStageWaitForDfu();
225}
226
227
228void BootloaderInstallS5l::installStageWaitForDfu(void)
229{
230 if (!updateProgress())
231 return; /* aborted */
232
233 if (!scanTimer.isValid() || (scanTimer.elapsed() > 2000)) {
234 scanSuccess = System::listUsbIds().contains(0x05ac1223);
235 scanTimer.start();
236 }
237 if (!scanSuccess) {
238 QTimer::singleShot(250, this, &BootloaderInstallS5l::installStageWaitForDfu);
239 return;
240 }
241 emit logItem(tr("DFU mode detected."), LOGINFO);
242
243 emit logItem(tr("Action required:\n\n"
244 "Release SELECT+MENU buttons and wait..."), LOGWARNING);
245
246 // Once the iPod enters DFU mode, the device will reset again if
247 // SELECT+MENU remains pressed for another 8 seconds. To avoid a
248 // reset while the NOR is being written, we wait ~10 seconds
249 // before sending the DFU image.
250 LOG_INFO() << "preparing installStageSendDfu";
251 setProgress(60, 10);
252 installStageSendDfu();
253}
254
255
256void BootloaderInstallS5l::installStageSendDfu(void)
257{
258 if (!updateProgress())
259 return; /* aborted */
260
261 if (progressTimer.elapsed() < progressTimeout) {
262 QTimer::singleShot(250, this, &BootloaderInstallS5l::installStageSendDfu);
263 return;
264 }
265
266 if (!System::listUsbIds().contains(0x05ac1223)) {
267 LOG_ERROR() << "device not in DFU mode";
268 emit logItem(tr("Device is not in DFU mode. It seems that "
269 "the previous required action failed, please "
270 "try again."), LOGERROR);
271 emit done(true);
272 return;
273 }
274
275 emit logItem(tr("Transfering DFU image..."), LOGINFO);
276 if (!updateProgress())
277 return; /* aborted */
278
279 char errstr[200];
280 if (!ipoddfu_send(0x1223, dfu_buf, dfu_size, errstr, sizeof(errstr))) {
281 LOG_ERROR() << "ipoddfu_send() failed:" << errstr;
282#if defined(Q_OS_WIN32)
283 if (strstr(errstr, "DFU device not found"))
284 {
285 emit logItem(tr("No valid DFU USB driver found.\n\n"
286 "Install iTunes (or the Apple Device Driver) "
287 "and try again."),
288 LOGERROR);
289 }
290 else
291#endif
292 {
293 emit logItem(errstr, LOGERROR);
294 emit logItem(tr("Could not transfer DFU image."), LOGERROR);
295 }
296 emit done(true);
297 return;
298 }
299 emit logItem(tr("DFU transfer completed."), LOGINFO);
300
301 LOG_INFO() << "preparing installStageWaitForRemount";
302 emit logItem(tr("Restarting iPod, waiting for remount..."), LOGINFO);
303 setProgress(99, 45);
304 scanTimer.invalidate();
305 installStageWaitForRemount();
306}
307
308
309void BootloaderInstallS5l::installStageWaitForRemount(void)
310{
311 if (!updateProgress())
312 return; /* aborted */
313
314 if (!scanTimer.isValid() || (scanTimer.elapsed() > 5000)) {
315 scanSuccess = Utils::mountpoints(
316 Utils::MountpointsSupported).contains(mntpoint);
317 scanTimer.start();
318 }
319 if (!scanSuccess) {
320 if (!actionShown && (progressTimer.elapsed() > progressTimeout)) {
321 emit logItem(tr("Action required:\n\n"
322 "Could not remount the device, try to do it "
323 "manually. If the iPod didn't restart, force "
324 "a reset by pressing SELECT+MENU buttons "
325 "for about 5 seconds. If the problem could "
326 "not be solved then click 'Abort' to cancel."),
327 LOGWARNING);
328 actionShown = true;
329 }
330 QTimer::singleShot(250, this, &BootloaderInstallS5l::installStageWaitForRemount);
331 return;
332 }
333 emit logItem(tr("Device remounted."), LOGINFO);
334
335 if (doInstall)
336 emit logItem(tr("Bootloader successfully installed."), LOGOK);
337 else
338 emit logItem(tr("Bootloader successfully uninstalled."), LOGOK);
339
340 logInstall(doInstall ? LogAdd : LogRemove);
341 emit logProgress(1, 1);
342 emit done(false);
343}
344
345
346void BootloaderInstallS5l::installDone(bool status)
347{
348 LOG_INFO() << "installDone, status:" << status;
349 if (Utils::suspendProcess(suspendedPids, false).size() != suspendedPids.size())
350 emit logItem(tr("Could not resume iTunesHelper."), LOGWARNING);
351}
352
353
354void BootloaderInstallS5l::abortInstall(void)
355{
356 LOG_INFO() << "abortInstall";
357 aborted = true;
358 disconnect(this, &BootloaderInstallBase::installAborted,
359 this, &BootloaderInstallS5l::abortInstall);
360}
361
362
363bool BootloaderInstallS5l::abortDetected(void)
364{
365 if (aborted) {
366 LOG_ERROR() << "abortDetected";
367 if (doInstall)
368 emit logItem(tr("Install aborted by user."), LOGERROR);
369 else
370 emit logItem(tr("Uninstall aborted by user."), LOGERROR);
371 emit done(true);
372 return true;
373 }
374 return false;
375}
376
377
378void BootloaderInstallS5l::setProgress(int progress, int secondsTimeout)
379{
380 progressTimer.start();
381 progressTimeout = secondsTimeout * 1000;
382 progOrigin = progTarget;
383 progTarget = progress;
384 actionShown = false;
385}
386
387
388bool BootloaderInstallS5l::updateProgress(void)
389{
390 if (progressTimeout) {
391 progCurrent = qMin(progTarget, progOrigin +
392 static_cast<int>(progressTimer.elapsed())
393 * (progTarget - progOrigin) / progressTimeout);
394 }
395 else {
396 progCurrent = progTarget;
397 }
398 emit logProgress(progCurrent, 100);
399 QCoreApplication::sendPostedEvents();
400 QCoreApplication::processEvents();
401 return !abortDetected();
402}
403
404
405BootloaderInstallBase::BootloaderType BootloaderInstallS5l::installed(void)
406{
407 bool rbblInstalled;
408
409 QString device = Utils::resolveDevicename(m_blfile);
410 if (device.isEmpty()) {
411 LOG_INFO() << "installed: BootloaderUnknown";
412 return BootloaderUnknown;
413 }
414
415 // rely on logfile
416 QString logfile = RbSettings::value(RbSettings::Mountpoint).toString()
417 + "/.rockbox/rbutil.log";
418 QSettings s(logfile, QSettings::IniFormat, this);
419 QString section = PlayerBuildInfo::instance()->value(
420 PlayerBuildInfo::BootloaderName).toString().section('/', -1);
421 rbblInstalled = s.contains("Bootloader/" + section);
422
423 if (rbblInstalled) {
424 LOG_INFO() << "installed: BootloaderRockbox";
425 return BootloaderRockbox;
426 }
427 else {
428 LOG_INFO() << "installed: BootloaderOther";
429 return BootloaderOther;
430 }
431}
432
433
434BootloaderInstallBase::Capabilities BootloaderInstallS5l::capabilities(void)
435{
436 return (Install | Uninstall);
437}
diff --git a/utils/rbutilqt/base/bootloaderinstalls5l.h b/utils/rbutilqt/base/bootloaderinstalls5l.h
new file mode 100644
index 0000000000..e2a44a031f
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstalls5l.h
@@ -0,0 +1,71 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef BOOTLOADERINSTALLS5L_H
20#define BOOTLOADERINSTALLS5L_H
21
22#include <QtCore>
23#include "bootloaderinstallbase.h"
24
25
26//! bootloader installation derivate based on mks5lboot
27class BootloaderInstallS5l : public BootloaderInstallBase
28{
29 Q_OBJECT
30
31 public:
32 BootloaderInstallS5l(QObject *parent);
33 bool install(void);
34 bool uninstall(void);
35 BootloaderInstallBase::BootloaderType installed(void);
36 Capabilities capabilities(void);
37
38 private slots:
39 bool installStage1(void);
40 void installStageMkdfu(void);
41 void installStageWaitForEject(void);
42 void installStageWaitForSpindown(void);
43 void installStageWaitForProcs(void);
44 void installStageWaitForDfu(void);
45 void installStageSendDfu(void);
46 void installStageWaitForRemount(void);
47 void abortInstall(void);
48 void installDone(bool);
49
50 private:
51 bool doInstall;
52 QString mntpoint;
53 unsigned char* dfu_buf;
54 int dfu_size;
55 QList<int> suspendedPids;
56 bool aborted;
57 bool abortDetected(void);
58 QElapsedTimer scanTimer;
59 bool scanSuccess;
60 // progress
61 QElapsedTimer progressTimer;
62 int progressTimeout;
63 int progCurrent;
64 int progOrigin;
65 int progTarget;
66 bool actionShown;
67 void setProgress(int, int=0);
68 bool updateProgress(void);
69};
70
71#endif
diff --git a/utils/rbutilqt/base/bootloaderinstallsansa.cpp b/utils/rbutilqt/base/bootloaderinstallsansa.cpp
new file mode 100644
index 0000000000..b1f0167e42
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallsansa.cpp
@@ -0,0 +1,286 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "bootloaderinstallbase.h"
21#include "bootloaderinstallsansa.h"
22#include "Logger.h"
23
24#include "../sansapatcher/sansapatcher.h"
25#include "utils.h"
26
27BootloaderInstallSansa::BootloaderInstallSansa(QObject *parent)
28 : BootloaderInstallBase(parent)
29{
30 (void)parent;
31 // initialize sector buffer. The sector buffer is part of the sansa_t
32 // structure, so a second instance of this class will have its own buffer.
33 sansa_alloc_buffer(&sansa, BUFFER_SIZE);
34}
35
36
37BootloaderInstallSansa::~BootloaderInstallSansa()
38{
39 if(sansa.sectorbuf) {
40 sansa_dealloc_buffer(&sansa);
41 }
42}
43
44
45/** Start bootloader installation.
46 */
47bool BootloaderInstallSansa::install(void)
48{
49 if(sansa.sectorbuf == nullptr) {
50 emit logItem(tr("Error: can't allocate buffer memory!"), LOGERROR);
51 return false;
52 emit done(true);
53 }
54
55 emit logItem(tr("Searching for Sansa"), LOGINFO);
56
57 int n = sansa_scan(&sansa);
58 if(n == -1) {
59 emit logItem(tr("Permission for disc access denied!\n"
60 "This is required to install the bootloader"),
61 LOGERROR);
62 emit done(true);
63 return false;
64 }
65 if(n == 0) {
66 emit logItem(tr("No Sansa detected!"), LOGERROR);
67 emit done(true);
68 return false;
69 }
70 if(sansa.hasoldbootloader) {
71 emit logItem(tr("OLD ROCKBOX INSTALLATION DETECTED, ABORTING.\n"
72 "You must reinstall the original Sansa firmware before running\n"
73 "sansapatcher for the first time.\n"
74 "See http://www.rockbox.org/wiki/SansaE200Install\n"),
75 LOGERROR);
76 emit done(true);
77 return false;
78 }
79 emit logItem(tr("Downloading bootloader file"), LOGINFO);
80
81 downloadBlStart(m_blurl);
82 connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallSansa::installStage2);
83 return true;
84}
85
86
87/** Finish bootloader installation.
88 */
89void BootloaderInstallSansa::installStage2(void)
90{
91 unsigned char* buf = nullptr;
92 unsigned int len;
93
94 emit logItem(tr("Installing Rockbox bootloader"), LOGINFO);
95 QCoreApplication::processEvents();
96 if(!sansaInitialize(&sansa)) {
97 emit done(true);
98 return;
99 }
100
101 if(sansa_reopen_rw(&sansa) < 0) {
102 emit logItem(tr("Could not open Sansa in R/W mode"), LOGERROR);
103 emit done(true);
104 return;
105 }
106
107 // check model -- if sansapatcher reports a c200 don't install an e200
108 // bootloader and vice versa.
109 // The model is available in the mi4 file at offset 0x1fc and matches
110 // the targetname set by sansapatcher.
111 emit logItem(tr("Checking downloaded bootloader"), LOGINFO);
112 m_tempfile.open();
113 QString blfile = m_tempfile.fileName();
114 char magic[4];
115 m_tempfile.seek(0x1fc);
116 m_tempfile.read(magic, 4);
117 m_tempfile.close();
118 if(memcmp(sansa.targetname, magic, 4) != 0) {
119 emit logItem(tr("Bootloader mismatch! Aborting."), LOGERROR);
120 LOG_INFO("Targetname: %s, mi4 magic: %c%c%c%c",
121 sansa.targetname, magic[0], magic[1], magic[2], magic[3]);
122 emit done(true);
123 sansa_close(&sansa);
124 return;
125 }
126
127 len = sansa_read_bootloader(&sansa, blfile.toLatin1().data(), &buf);
128 if(sansa_add_bootloader(&sansa, buf, len) == 0) {
129 emit logItem(tr("Successfully installed bootloader"), LOGOK);
130 sansa_close(&sansa);
131#if defined(Q_OS_MACX)
132 m_remountDevice = sansa.diskname;
133 connect(this, SIGNAL(remounted(bool)), this, SLOT(installStage3(bool)));
134 waitRemount();
135#else
136 installStage3(true);
137#endif
138 }
139 else {
140 emit logItem(tr("Failed to install bootloader"), LOGERROR);
141 sansa_close(&sansa);
142 emit done(true);
143 return;
144 }
145
146}
147
148
149void BootloaderInstallSansa::installStage3(bool mounted)
150{
151 if(mounted) {
152 logInstall(LogAdd);
153 emit logItem(tr("Bootloader Installation complete."), LOGINFO);
154 emit done(false);
155 return;
156 }
157 else {
158 emit logItem(tr("Writing log aborted"), LOGERROR);
159 emit done(true);
160 }
161 LOG_INFO() << "version installed:"
162 << m_blversion.toString(Qt::ISODate);
163}
164
165
166/** Uninstall the bootloader.
167 */
168bool BootloaderInstallSansa::uninstall(void)
169{
170 emit logItem(tr("Uninstalling bootloader"), LOGINFO);
171 QCoreApplication::processEvents();
172
173 if(!sansaInitialize(&sansa)) {
174 emit done(true);
175 return false;
176 }
177
178 if (sansa.hasoldbootloader) {
179 emit logItem(tr("OLD ROCKBOX INSTALLATION DETECTED, ABORTING.\n"
180 "You must reinstall the original Sansa firmware before running\n"
181 "sansapatcher for the first time.\n"
182 "See http://www.rockbox.org/wiki/SansaE200Install\n"),
183 LOGERROR);
184 emit done(true);
185 return false;
186 }
187
188 if (sansa_reopen_rw(&sansa) < 0) {
189 emit logItem(tr("Could not open Sansa in R/W mode"), LOGERROR);
190 emit done(true);
191 return false;
192 }
193
194 if (sansa_delete_bootloader(&sansa)==0) {
195 emit logItem(tr("Successfully removed bootloader"), LOGOK);
196 logInstall(LogRemove);
197 emit logProgress(1, 1);
198 emit done(false);
199 sansa_close(&sansa);
200 return true;
201 }
202 else {
203 emit logItem(tr("Removing bootloader failed."),LOGERROR);
204 emit done(true);
205 sansa_close(&sansa);
206 return false;
207 }
208
209 return false;
210}
211
212
213/** Check if bootloader is already installed
214 */
215BootloaderInstallBase::BootloaderType BootloaderInstallSansa::installed(void)
216{
217 int num;
218
219 if(!sansaInitialize(&sansa)) {
220 return BootloaderUnknown;
221 }
222 if((num = sansa_list_images(&sansa)) == 2) {
223 sansa_close(&sansa);
224 return BootloaderRockbox;
225 }
226 else if(num == 1) {
227 sansa_close(&sansa);
228 return BootloaderOther;
229 }
230 return BootloaderUnknown;
231
232}
233
234bool BootloaderInstallSansa::sansaInitialize(struct sansa_t *sansa)
235{
236 if(!m_blfile.isEmpty()) {
237 QString devicename = Utils::resolveDevicename(m_blfile);
238 if(devicename.isEmpty()) {
239 emit logItem(tr("Error: could not retrieve device name"), LOGERROR);
240 return false;
241 }
242#if defined(Q_OS_WIN32)
243 sprintf(sansa->diskname, "\\\\.\\PhysicalDrive%i", devicename.toInt());
244#elif defined(Q_OS_MACX)
245 sprintf(sansa->diskname,
246 "%s", qPrintable(devicename.remove(QRegExp("s[0-9]+$"))));
247#else
248 sprintf(sansa->diskname,
249 "%s", qPrintable(devicename.remove(QRegExp("[0-9]+$"))));
250#endif
251 LOG_INFO() << "sansapatcher: overriding scan, using"
252 << sansa->diskname;
253 }
254 else if(sansa_scan(sansa) != 1) {
255 emit logItem(tr("Can't find Sansa"), LOGERROR);
256 return false;
257 }
258
259 if (sansa_open(sansa, 0) < 0) {
260 emit logItem(tr("Could not open Sansa"), LOGERROR);
261 return false;
262 }
263
264 if (sansa_read_partinfo(sansa,0) < 0) {
265 emit logItem(tr("Could not read partition table"), LOGERROR);
266 sansa_close(sansa);
267 return false;
268 }
269
270 int i = is_sansa(sansa);
271 if(i < 0) {
272 emit logItem(tr("Disk is not a Sansa (Error %1), aborting.").arg(i), LOGERROR);
273 sansa_close(sansa);
274 return false;
275 }
276 return true;
277}
278
279
280/** Get capabilities of subclass installer.
281 */
282BootloaderInstallBase::Capabilities BootloaderInstallSansa::capabilities(void)
283{
284 return (Install | Uninstall | IsRaw | CanCheckInstalled);
285}
286
diff --git a/utils/rbutilqt/base/bootloaderinstallsansa.h b/utils/rbutilqt/base/bootloaderinstallsansa.h
new file mode 100644
index 0000000000..45837f0ac3
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstallsansa.h
@@ -0,0 +1,51 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2008 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef BOOTLOADERINSTALLSANSA_H
20#define BOOTLOADERINSTALLSANSA_H
21
22#include <QtCore>
23#include "bootloaderinstallbase.h"
24#include "sansapatcher.h"
25
26
27// bootloader installation class for devices handled by sansapatcher.
28class BootloaderInstallSansa : public BootloaderInstallBase
29{
30 Q_OBJECT
31
32 public:
33 BootloaderInstallSansa(QObject *parent = nullptr);
34 ~BootloaderInstallSansa();
35 bool install(void);
36 bool uninstall(void);
37 BootloaderInstallBase::BootloaderType installed(void);
38 Capabilities capabilities(void);
39
40 private:
41 bool sansaInitialize(struct sansa_t *);
42 struct sansa_t sansa;
43
44 private slots:
45 void installStage2(void);
46 void installStage3(bool);
47};
48
49
50#endif
51
diff --git a/utils/rbutilqt/base/bootloaderinstalltcc.cpp b/utils/rbutilqt/base/bootloaderinstalltcc.cpp
new file mode 100644
index 0000000000..ffc8555733
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstalltcc.cpp
@@ -0,0 +1,165 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2009 by Tomer Shalev
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 * This file is a modified version of the AMS installer by Dominik Wenger
18 *
19 ****************************************************************************/
20
21#include <QtCore>
22#include "bootloaderinstallbase.h"
23#include "bootloaderinstalltcc.h"
24#include "../mktccboot/mktccboot.h"
25
26BootloaderInstallTcc::BootloaderInstallTcc(QObject *parent)
27 : BootloaderInstallBase(parent)
28{
29}
30
31QString BootloaderInstallTcc::ofHint()
32{
33 return tr("Bootloader installation requires you to provide "
34 "a firmware file of the original firmware (bin file). "
35 "You need to download this file yourself due to legal "
36 "reasons. Please refer to the "
37 "<a href='http://www.rockbox.org/manual.shtml'>manual</a> and the "
38 "<a href='http://www.rockbox.org/wiki/CowonD2Info'>CowonD2Info</a> "
39 "wiki page on how to obtain the file.<br/>"
40 "Press Ok to continue and browse your computer for the firmware "
41 "file.");
42}
43
44bool BootloaderInstallTcc::install(void)
45{
46 if(m_offile.isEmpty())
47 return false;
48
49 // Download firmware from server
50 emit logItem(tr("Downloading bootloader file"), LOGINFO);
51
52 connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallTcc::installStage2);
53 downloadBlStart(m_blurl);
54
55 return true;
56}
57
58void BootloaderInstallTcc::installStage2(void)
59{
60 unsigned char *of_buf, *boot_buf = nullptr, *patched_buf = nullptr;
61 int n, of_size, boot_size, patched_size;
62 char errstr[200];
63 bool ret = false;
64
65 m_tempfile.open();
66 QString bootfile = m_tempfile.fileName();
67 m_tempfile.close();
68
69 /* Construct path for write out.
70 * Combine path of m_blfile with filename from OF */
71 QString outfilename = QFileInfo(m_blfile).absolutePath() + "/" +
72 QFileInfo(m_offile).fileName();
73
74 /* Write out file */
75 QFile out(outfilename);
76
77 /* Load original firmware file */
78 of_buf = file_read(m_offile.toLocal8Bit().data(), &of_size);
79 if (of_buf == nullptr)
80 {
81 emit logItem(errstr, LOGERROR);
82 emit logItem(tr("Could not load %1").arg(m_offile), LOGERROR);
83 goto exit;
84 }
85
86 /* A CRC test in order to reject non OF file */
87 if (test_firmware_tcc(of_buf, of_size))
88 {
89 emit logItem(errstr, LOGERROR);
90 emit logItem(tr("Unknown OF file used: %1").arg(m_offile), LOGERROR);
91 goto exit;
92 }
93
94 /* Load bootloader file */
95 boot_buf = file_read(bootfile.toLocal8Bit().data(), &boot_size);
96 if (boot_buf == nullptr)
97 {
98 emit logItem(errstr, LOGERROR);
99 emit logItem(tr("Could not load %1").arg(bootfile), LOGERROR);
100 goto exit;
101 }
102
103 /* Patch the firmware */
104 emit logItem(tr("Patching Firmware..."), LOGINFO);
105
106 patched_buf = patch_firmware_tcc(of_buf, of_size, boot_buf, boot_size,
107 &patched_size);
108 if (patched_buf == nullptr)
109 {
110 emit logItem(errstr, LOGERROR);
111 emit logItem(tr("Could not patch firmware"), LOGERROR);
112 goto exit;
113 }
114
115 if(!out.open(QIODevice::WriteOnly | QIODevice::Truncate))
116 {
117 emit logItem(tr("Could not open %1 for writing").arg(m_blfile),
118 LOGERROR);
119 goto exit;
120 }
121
122 n = out.write((char*)patched_buf, patched_size);
123 out.close();
124 if (n != patched_size)
125 {
126 emit logItem(tr("Could not write firmware file"), LOGERROR);
127 goto exit;
128 }
129
130 /* End of install */
131 emit logItem(tr("Success: modified firmware file created"), LOGINFO);
132 logInstall(LogAdd);
133
134 ret = true;
135
136exit:
137 if (of_buf)
138 free(of_buf);
139
140 if (boot_buf)
141 free(boot_buf);
142
143 if (patched_buf)
144 free(patched_buf);
145
146 emit done(ret);
147}
148
149bool BootloaderInstallTcc::uninstall(void)
150{
151 emit logItem(tr("To uninstall, perform a normal upgrade with an unmodified original firmware"), LOGINFO);
152 logInstall(LogRemove);
153 emit done(true);
154 return false;
155}
156
157BootloaderInstallBase::BootloaderType BootloaderInstallTcc::installed(void)
158{
159 return BootloaderUnknown;
160}
161
162BootloaderInstallBase::Capabilities BootloaderInstallTcc::capabilities(void)
163{
164 return (Install | NeedsOf);
165}
diff --git a/utils/rbutilqt/base/bootloaderinstalltcc.h b/utils/rbutilqt/base/bootloaderinstalltcc.h
new file mode 100644
index 0000000000..cc1fd45802
--- /dev/null
+++ b/utils/rbutilqt/base/bootloaderinstalltcc.h
@@ -0,0 +1,44 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2009 by Tomer Shalev
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 * This file is a modified version of the AMS installer by Dominik Wenger
18 *
19 ****************************************************************************/
20#ifndef BOOTLOADERINSTALLTCC_H
21#define BOOTLOADERINSTALLTCC_H
22
23#include <QtCore>
24#include "bootloaderinstallbase.h"
25
26//! bootloader installation derivate based on mktccboot
27class BootloaderInstallTcc : public BootloaderInstallBase
28{
29 Q_OBJECT
30 public:
31 BootloaderInstallTcc(QObject *parent);
32 bool install(void);
33 bool uninstall(void);
34 BootloaderInstallBase::BootloaderType installed(void);
35 Capabilities capabilities(void);
36 QString ofHint();
37
38 private:
39
40 private slots:
41 void installStage2(void);
42};
43
44#endif
diff --git a/utils/rbutilqt/base/encoderbase.cpp b/utils/rbutilqt/base/encoderbase.cpp
new file mode 100644
index 0000000000..fe45eee49b
--- /dev/null
+++ b/utils/rbutilqt/base/encoderbase.cpp
@@ -0,0 +1,86 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include "encoderbase.h"
20#include "utils.h"
21#include "rbsettings.h"
22#include "encoderrbspeex.h"
23#include "encoderlame.h"
24#include "encoderexe.h"
25
26#include "Logger.h"
27
28/*********************************************************************
29* Encoder Base
30**********************************************************************/
31QMap<QString,QString> EncoderBase::encoderList;
32
33EncoderBase::EncoderBase(QObject *parent): EncTtsSettingInterface(parent)
34{
35
36}
37
38// initialize list of encoders
39void EncoderBase::initEncodernamesList()
40{
41 encoderList["rbspeex"] = "Rockbox Speex Encoder";
42 encoderList["lame"] = "Lame Mp3 Encoder";
43}
44
45
46// get nice name for a specific encoder
47QString EncoderBase::getEncoderName(QString encoder)
48{
49 if(encoderList.isEmpty())
50 initEncodernamesList();
51 return encoderList.value(encoder);
52}
53
54
55// get a specific encoder object
56EncoderBase* EncoderBase::getEncoder(QObject* parent,QString encoder)
57{
58 EncoderBase* enc;
59 if(encoder == "lame")
60 {
61 enc = new EncoderLame(parent);
62 if (!enc->configOk())
63 {
64 LOG_WARNING() << "Could not load lame dll, falling back to command "
65 "line lame. This is notably slower.";
66 delete enc;
67 enc = new EncoderExe(encoder, parent);
68
69 }
70 return enc;
71 }
72 else // rbspeex is default
73 {
74 enc = new EncoderRbSpeex(parent);
75 return enc;
76 }
77}
78
79
80QStringList EncoderBase::getEncoderList()
81{
82 if(encoderList.isEmpty())
83 initEncodernamesList();
84 return encoderList.keys();
85}
86
diff --git a/utils/rbutilqt/base/encoderbase.h b/utils/rbutilqt/base/encoderbase.h
new file mode 100644
index 0000000000..810b38aa9e
--- /dev/null
+++ b/utils/rbutilqt/base/encoderbase.h
@@ -0,0 +1,63 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#ifndef ENCODERS_H
22#define ENCODERS_H
23
24#include <QtCore>
25
26#include "encttssettings.h"
27
28
29class EncoderBase : public EncTtsSettingInterface
30{
31 Q_OBJECT
32 public:
33 EncoderBase(QObject *parent );
34
35 //! Child class should encode a wav file
36 virtual bool encode(QString input,QString output) =0;
37 //! Child class should do startup
38 virtual bool start()=0;
39 //! Child class should stop
40 virtual bool stop()=0;
41
42 // settings
43 //! Child class should return true when configuration is ok
44 virtual bool configOk()=0;
45 //! Child class should fill in settingsList
46 virtual void generateSettings() = 0;
47 //! Child class should commit from SettingsList to permanent storage
48 virtual void saveSettings() = 0;
49
50 // static functions
51 static QString getEncoderName(QString name);
52 static EncoderBase* getEncoder(QObject* parent,QString name);
53 static QStringList getEncoderList(void);
54
55 private:
56 static void initEncodernamesList(void);
57
58 protected:
59 static QMap<QString,QString> encoderList;
60};
61
62#endif
63
diff --git a/utils/rbutilqt/base/encoderexe.cpp b/utils/rbutilqt/base/encoderexe.cpp
new file mode 100644
index 0000000000..331b5fb2a4
--- /dev/null
+++ b/utils/rbutilqt/base/encoderexe.cpp
@@ -0,0 +1,78 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "encoderexe.h"
21#include "rbsettings.h"
22#include "utils.h"
23#include "Logger.h"
24
25EncoderExe::EncoderExe(QString name, QObject *parent) : EncoderBase(parent),
26 m_name(name)
27{
28}
29
30
31void EncoderExe::generateSettings()
32{
33 QString exepath = RbSettings::subValue(m_name,RbSettings::EncoderPath).toString();
34 if(exepath.isEmpty()) exepath = Utils::findExecutable(m_name);
35
36 insertSetting(eEXEPATH, new EncTtsSetting(this, EncTtsSetting::eSTRING,
37 tr("Path to Encoder:"), exepath, EncTtsSetting::eBROWSEBTN));
38 insertSetting(eEXEOPTIONS, new EncTtsSetting(this, EncTtsSetting::eSTRING,
39 tr("Encoder options:"), RbSettings::subValue(m_name, RbSettings::EncoderOptions)));
40}
41
42void EncoderExe::saveSettings()
43{
44 RbSettings::setSubValue(m_name, RbSettings::EncoderPath, getSetting(eEXEPATH)->current().toString());
45 RbSettings::setSubValue(m_name, RbSettings::EncoderOptions, getSetting(eEXEOPTIONS)->current().toString());
46 RbSettings::sync();
47}
48
49bool EncoderExe::start()
50{
51 m_EncExec = RbSettings::subValue(m_name, RbSettings::EncoderPath).toString();
52 m_EncOpts = RbSettings::subValue(m_name, RbSettings::EncoderOptions).toString();
53
54 QFileInfo enc(m_EncExec);
55 return enc.exists();
56}
57
58bool EncoderExe::encode(QString input,QString output)
59{
60 if (!configOk())
61 return false;
62
63 QStringList args;
64 args << m_EncOpts;
65 args << input;
66 args << output;
67 int result = QProcess::execute(m_EncExec, args);
68 return result == 0;
69}
70
71
72bool EncoderExe::configOk()
73{
74 QString path = RbSettings::subValue(m_name, RbSettings::EncoderPath).toString();
75
76 return QFileInfo::exists(path);
77}
78
diff --git a/utils/rbutilqt/base/encoderexe.h b/utils/rbutilqt/base/encoderexe.h
new file mode 100644
index 0000000000..4008689167
--- /dev/null
+++ b/utils/rbutilqt/base/encoderexe.h
@@ -0,0 +1,53 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#ifndef ENCODEREXES_H
22#define ENCODEREXES_H
23
24#include <QtCore>
25#include "encoderbase.h"
26
27class EncoderExe : public EncoderBase
28{
29 enum ESettings
30 {
31 eEXEPATH,
32 eEXEOPTIONS
33 };
34
35 Q_OBJECT
36 public:
37 EncoderExe(QString name,QObject *parent = nullptr);
38 bool encode(QString input,QString output);
39 bool start();
40 bool stop() {return true;}
41
42 // setting
43 bool configOk();
44 void generateSettings();
45 void saveSettings();
46
47 private:
48 QString m_name;
49 QString m_EncExec;
50 QString m_EncOpts;
51};
52#endif
53
diff --git a/utils/rbutilqt/base/encoderlame.cpp b/utils/rbutilqt/base/encoderlame.cpp
new file mode 100644
index 0000000000..1658a7092d
--- /dev/null
+++ b/utils/rbutilqt/base/encoderlame.cpp
@@ -0,0 +1,312 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2012 Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "encoderlame.h"
21#include "rbsettings.h"
22#include "lame/lame.h"
23#include "Logger.h"
24
25/** Resolve a symbol from loaded library.
26 */
27#define SYMBOLRESOLVE(symbol, type) \
28 do { m_##symbol = (type)lib.resolve(#symbol); \
29 if(!m_##symbol) return; \
30 LOG_INFO() << "Resolved symbol " #symbol; } \
31 while(0)
32
33EncoderLame::EncoderLame(QObject *parent) : EncoderBase(parent),
34 lib("libmp3lame", this), m_symbolsResolved(false)
35{
36 lib.load();
37 if (!lib.isLoaded()) {
38 LOG_WARNING() << "Loading mp3lame lib failed:" << lib.errorString();
39 return;
40 }
41
42 SYMBOLRESOLVE(get_lame_short_version, const char* (*)());
43 SYMBOLRESOLVE(lame_set_out_samplerate, int (*)(lame_global_flags*, int));
44 SYMBOLRESOLVE(lame_set_in_samplerate, int (*)(lame_global_flags*, int));
45 SYMBOLRESOLVE(lame_set_num_channels, int (*)(lame_global_flags*, int));
46 SYMBOLRESOLVE(lame_set_scale, int (*)(lame_global_flags*, float));
47 SYMBOLRESOLVE(lame_set_mode, int (*)(lame_global_flags*, MPEG_mode));
48 SYMBOLRESOLVE(lame_set_VBR, int (*)(lame_global_flags*, vbr_mode));
49 SYMBOLRESOLVE(lame_set_VBR_quality, int (*)(lame_global_flags*, float));
50 SYMBOLRESOLVE(lame_set_VBR_max_bitrate_kbps, int (*)(lame_global_flags*, int));
51 SYMBOLRESOLVE(lame_set_bWriteVbrTag, int (*)(lame_global_flags*, int));
52 SYMBOLRESOLVE(lame_init, lame_global_flags* (*)());
53 SYMBOLRESOLVE(lame_init_params, int (*)(lame_global_flags*));
54 SYMBOLRESOLVE(lame_encode_buffer, int (*)(lame_global_flags*, short int*, short int*, int, unsigned char*, int));
55 SYMBOLRESOLVE(lame_encode_flush, int (*)(lame_global_flags*, unsigned char*, int));
56 SYMBOLRESOLVE(lame_close, int (*)(lame_global_flags*));
57
58 m_encoderVolume = RbSettings::subValue("lame", RbSettings::EncoderVolume).toDouble();
59 m_encoderQuality = RbSettings::subValue("lame", RbSettings::EncoderQuality).toDouble();
60 m_symbolsResolved = true;
61}
62
63void EncoderLame::generateSettings()
64{
65 // no settings for now.
66 // show lame version.
67 if(m_symbolsResolved) {
68 double quality = RbSettings::subValue("lame",
69 RbSettings::EncoderQuality).toDouble();
70 // default quality is 0.999.
71 if(quality < 0) {
72 quality = 0.99;
73 }
74 insertSetting(LAMEVERSION, new EncTtsSetting(this, EncTtsSetting::eREADONLYSTRING,
75 tr("LAME"), QString(m_get_lame_short_version())));
76 insertSetting(VOLUME, new EncTtsSetting(this, EncTtsSetting::eDOUBLE,
77 tr("Volume"),
78 RbSettings::subValue("lame", RbSettings::EncoderVolume).toDouble(),
79 0.0, 2.0));
80 insertSetting(QUALITY, new EncTtsSetting(this, EncTtsSetting::eDOUBLE,
81 tr("Quality"), quality, 0.0, 1.0));
82 }
83 else {
84 insertSetting(LAMEVERSION, new EncTtsSetting(this, EncTtsSetting::eREADONLYSTRING,
85 tr("LAME"), tr("Could not find libmp3lame!")));
86 }
87}
88
89void EncoderLame::saveSettings()
90{
91 if(m_symbolsResolved) {
92 RbSettings::setSubValue("lame", RbSettings::EncoderVolume,
93 getSetting(VOLUME)->current().toDouble());
94 RbSettings::setSubValue("lame", RbSettings::EncoderQuality,
95 getSetting(QUALITY)->current().toDouble());
96 m_encoderVolume =
97 RbSettings::subValue("lame", RbSettings::EncoderVolume).toDouble();
98 m_encoderQuality =
99 RbSettings::subValue("lame", RbSettings::EncoderQuality).toDouble();
100 }
101}
102
103bool EncoderLame::start()
104{
105 if(!m_symbolsResolved) {
106 return false;
107 }
108 // try to get config from settings
109 return true;
110}
111
112bool EncoderLame::encode(QString input,QString output)
113{
114 LOG_INFO() << "Encoding" << QDir::cleanPath(input);
115 if(!m_symbolsResolved) {
116 LOG_ERROR() << "Symbols not successfully resolved, cannot run!";
117 return false;
118 }
119
120 QFile fin(input);
121 QFile fout(output);
122 // initialize encoder
123 lame_global_flags *gfp;
124 unsigned char header[12];
125 unsigned char chunkheader[8];
126 unsigned int datalength = 0;
127 unsigned int channels = 0;
128 unsigned int samplerate = 0;
129 unsigned int samplesize = 0;
130 int num_samples = 0;
131 int ret;
132 unsigned char* mp3buf;
133 int mp3buflen;
134 short int* wavbuf;
135 int wavbuflen;
136
137
138 gfp = m_lame_init();
139 m_lame_set_out_samplerate(gfp, 12000); // resample to 12kHz
140 // scale input volume
141 m_lame_set_scale(gfp, m_encoderVolume);
142 m_lame_set_mode(gfp, MONO); // mono output mode
143 m_lame_set_VBR(gfp, vbr_default); // enable default VBR mode
144 // VBR quality
145 m_lame_set_VBR_quality(gfp, m_encoderQuality);
146 m_lame_set_VBR_max_bitrate_kbps(gfp, 64); // maximum bitrate 64kbps
147 m_lame_set_bWriteVbrTag(gfp, 0); // disable LAME tag.
148
149 if(!fin.open(QIODevice::ReadOnly)) {
150 LOG_ERROR() << "Could not open input file" << input;
151 return false;
152 }
153
154 // read RIFF header
155 fin.read((char*)header, 12);
156 if(memcmp("RIFF", header, 4) != 0) {
157 LOG_ERROR() << "RIFF header not found!"
158 << header[0] << header[1] << header[2] << header[3];
159 fin.close();
160 return false;
161 }
162 if(memcmp("WAVE", &header[8], 4) != 0) {
163 LOG_ERROR() << "WAVE FOURCC not found!"
164 << header[8] << header[9] << header[10] << header[11];
165 fin.close();
166 return false;
167 }
168
169 // search for fmt chunk
170 do {
171 // read fmt
172 fin.read((char*)chunkheader, 8);
173 int chunkdatalen = chunkheader[4] | chunkheader[5]<<8
174 | chunkheader[6]<<16 | chunkheader[7]<<24;
175 if(memcmp("fmt ", chunkheader, 4) == 0) {
176 // fmt found, read rest of chunk.
177 // NOTE: This code ignores the format tag value.
178 // Ideally this should be checked as well. However, rbspeex doesn't
179 // check the format tag either when reading wave files, so if
180 // problems arise we should notice pretty soon. Furthermore, the
181 // input format used should be known. In case some TTS uses a
182 // different wave encoding some time this needs to get adjusted.
183 if(chunkdatalen < 16) {
184 LOG_ERROR() << "fmt chunk too small!";
185 }
186 else {
187 unsigned char *buf = new unsigned char[chunkdatalen];
188 fin.read((char*)buf, chunkdatalen);
189 channels = buf[2] | buf[3]<<8;
190 samplerate = buf[4] | buf[5]<<8 | buf[6]<<16 | buf[7]<<24;
191 samplesize = buf[14] | buf[15]<<8;
192 delete[] buf;
193 }
194 }
195 // read data
196 else if(memcmp("data", chunkheader, 4) == 0) {
197 datalength = chunkdatalen;
198 break;
199 }
200 else {
201 // unknown chunk, just skip its data.
202 LOG_WARNING() << "unknown chunk, skipping."
203 << chunkheader[0] << chunkheader[1]
204 << chunkheader[2] << chunkheader[3];
205 fin.seek(fin.pos() + chunkdatalen);
206 }
207 } while(!fin.atEnd());
208
209 // check format
210 if(channels == 0 || samplerate == 0 || samplesize == 0 || datalength == 0) {
211 LOG_ERROR() << "invalid format. Channels:" << channels
212 << "Samplerate:" << samplerate << "Samplesize:" << samplesize
213 << "Data chunk length:" << datalength;
214 fin.close();
215 return false;
216 }
217 num_samples = (datalength / channels / (samplesize/8));
218
219 // set input format values
220 m_lame_set_in_samplerate(gfp, samplerate);
221 m_lame_set_num_channels(gfp, channels);
222
223 // initialize encoder.
224 ret = m_lame_init_params(gfp);
225 if(ret != 0) {
226 LOG_ERROR() << "lame_init_params() failed with" << ret;
227 fin.close();
228 return false;
229 }
230
231 // we're dealing with rather small files here (100kB-ish), so don't care
232 // about the possible output size and simply allocate the same number of
233 // bytes the input file has. This wastes space but should be ok.
234 // Put an upper limit of 8MiB.
235 if(datalength > 8*1024*1024) {
236 LOG_ERROR() << "Input file too large:" << datalength;
237 fin.close();
238 return false;
239 }
240 mp3buflen = datalength;
241 wavbuflen = datalength;
242 mp3buf = new unsigned char[mp3buflen];
243 wavbuf = new short int[wavbuflen];
244#if defined(Q_OS_MACX)
245 // handle byte order -- the host might not be LE.
246 if(samplesize == 8) {
247 // no need to convert.
248 fin.read((char*)wavbuf, wavbuflen);
249 }
250 else if(samplesize == 16) {
251 // read LE 16bit words. Since the input format is either mono or
252 // interleaved there's no need to care for that.
253 unsigned int pos = 0;
254 char word[2];
255 while(pos < datalength) {
256 fin.read(word, 2);
257 wavbuf[pos++] = (word[0]&0xff) | ((word[1]<<8)&0xff00);
258 }
259 }
260 else {
261 LOG_ERROR() << "Unknown samplesize:" << samplesize;
262 fin.close();
263 delete[] mp3buf;
264 delete[] wavbuf;
265 return false;
266 }
267#else
268 // all systems but OS X are considered LE.
269 fin.read((char*)wavbuf, wavbuflen);
270#endif
271 fin.close();
272 // encode data.
273 fout.open(QIODevice::ReadWrite);
274 ret = m_lame_encode_buffer(gfp, wavbuf, wavbuf, num_samples, mp3buf, mp3buflen);
275 if(ret < 0) {
276 LOG_ERROR() << "Error during encoding:" << ret;
277 }
278 if(fout.write((char*)mp3buf, ret) != (unsigned int)ret) {
279 LOG_ERROR() << "Writing mp3 data failed!" << ret;
280 fout.close();
281 delete[] mp3buf;
282 delete[] wavbuf;
283 return false;
284 }
285 // flush remaining data
286 ret = m_lame_encode_flush(gfp, mp3buf, mp3buflen);
287 if(fout.write((char*)mp3buf, ret) != (unsigned int)ret) {
288 LOG_ERROR() << "Writing final mp3 data failed!";
289 fout.close();
290 delete[] mp3buf;
291 delete[] wavbuf;
292 return false;
293 }
294 // shut down encoder and clean up.
295 m_lame_close(gfp);
296 fout.close();
297 delete[] mp3buf;
298 delete[] wavbuf;
299
300 return true;
301}
302
303/** Check if the current configuration is usable.
304 * Since we're loading a library dynamically in the constructor test if that
305 * succeeded. Otherwise the "configuration" is not usable, even though the
306 * problem is not necessarily related to configuration values set by the user.
307 */
308bool EncoderLame::configOk()
309{
310 return m_symbolsResolved;
311}
312
diff --git a/utils/rbutilqt/base/encoderlame.h b/utils/rbutilqt/base/encoderlame.h
new file mode 100644
index 0000000000..a5f1b2d3f4
--- /dev/null
+++ b/utils/rbutilqt/base/encoderlame.h
@@ -0,0 +1,72 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2012 Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef ENCODERLAME_H
20#define ENCODERLAME_H
21
22#include <QtCore>
23#include "encoderbase.h"
24#include "lame/lame.h"
25
26class EncoderLame : public EncoderBase
27{
28 enum ESettings
29 {
30 LAMEVERSION,
31 VOLUME,
32 QUALITY,
33 };
34
35 Q_OBJECT
36 public:
37 EncoderLame(QObject *parent = nullptr);
38 bool encode(QString input,QString output);
39 bool start();
40 bool stop() {return true;}
41
42 // for settings view
43 bool configOk();
44 void generateSettings();
45 void saveSettings();
46
47 private:
48 QLibrary lib;
49 const char*(*m_get_lame_short_version)(void);
50 int (*m_lame_set_out_samplerate)(lame_global_flags*, int);
51 int (*m_lame_set_in_samplerate)(lame_global_flags*, int);
52 int (*m_lame_set_num_channels)(lame_global_flags*, int);
53 int (*m_lame_set_scale)(lame_global_flags*, float);
54 int (*m_lame_set_mode)(lame_global_flags*, MPEG_mode);
55 int (*m_lame_set_VBR)(lame_global_flags*, vbr_mode);
56 int (*m_lame_set_VBR_quality)(lame_global_flags*, float);
57 int (*m_lame_set_VBR_max_bitrate_kbps)(lame_global_flags*, int);
58 int (*m_lame_set_bWriteVbrTag)(lame_global_flags*, int);
59 lame_global_flags*(*m_lame_init)(void);
60 int (*m_lame_init_params)(lame_global_flags*);
61 int (*m_lame_encode_buffer)(lame_global_flags*, short int[], short
62 int[], int, unsigned char*, int);
63 int (*m_lame_encode_flush)(lame_global_flags*, unsigned char*, int);
64 int (*m_lame_close)(lame_global_flags*);
65
66 bool m_symbolsResolved;
67 double m_encoderVolume;
68 double m_encoderQuality;
69};
70
71#endif
72
diff --git a/utils/rbutilqt/base/encoderrbspeex.cpp b/utils/rbutilqt/base/encoderrbspeex.cpp
new file mode 100644
index 0000000000..2bee66028a
--- /dev/null
+++ b/utils/rbutilqt/base/encoderrbspeex.cpp
@@ -0,0 +1,119 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "encoderrbspeex.h"
21#include "rbsettings.h"
22#include "rbspeex.h"
23#include "Logger.h"
24
25EncoderRbSpeex::EncoderRbSpeex(QObject *parent) : EncoderBase(parent)
26{
27
28}
29
30void EncoderRbSpeex::generateSettings()
31{
32 loadSettings();
33 insertSetting(eVOLUME, new EncTtsSetting(this, EncTtsSetting::eDOUBLE,
34 tr("Volume:"), volume, 0.0, 2.0));
35 insertSetting(eQUALITY, new EncTtsSetting(this, EncTtsSetting::eDOUBLE,
36 tr("Quality:"), quality, 0, 10.0));
37 insertSetting(eCOMPLEXITY, new EncTtsSetting(this, EncTtsSetting::eINT,
38 tr("Complexity:"), complexity, 0, 10));
39 insertSetting(eNARROWBAND,new EncTtsSetting(this, EncTtsSetting::eBOOL,
40 tr("Use Narrowband:"), narrowband));
41}
42
43void EncoderRbSpeex::saveSettings()
44{
45 //save settings in user config
46 RbSettings::setSubValue("rbspeex",RbSettings::EncoderVolume,
47 getSetting(eVOLUME)->current().toDouble());
48 RbSettings::setSubValue("rbspeex",RbSettings::EncoderQuality,
49 getSetting(eQUALITY)->current().toDouble());
50 RbSettings::setSubValue("rbspeex",RbSettings::EncoderComplexity,
51 getSetting(eCOMPLEXITY)->current().toInt());
52 RbSettings::setSubValue("rbspeex",RbSettings::EncoderNarrowBand,
53 getSetting(eNARROWBAND)->current().toBool());
54
55 RbSettings::sync();
56}
57
58
59void EncoderRbSpeex::loadSettings(void)
60{
61 // try to get config from settings
62 quality = RbSettings::subValue("rbspeex", RbSettings::EncoderQuality).toDouble();
63 if(quality < 0) {
64 quality = 8.0;
65 }
66 complexity = RbSettings::subValue("rbspeex", RbSettings::EncoderComplexity).toInt();
67 volume = RbSettings::subValue("rbspeex", RbSettings::EncoderVolume).toDouble();
68 narrowband = RbSettings::subValue("rbspeex", RbSettings::EncoderNarrowBand).toBool();
69}
70
71
72bool EncoderRbSpeex::start()
73{
74
75 // make sure configuration parameters are set.
76 loadSettings();
77 return true;
78}
79
80bool EncoderRbSpeex::encode(QString input,QString output)
81{
82 LOG_INFO() << "Encoding " << input << " to "<< output;
83 char errstr[512];
84
85 FILE *fin,*fout;
86 if ((fin = fopen(input.toLocal8Bit(), "rb")) == nullptr) {
87 LOG_ERROR() << "Error: could not open input file\n";
88 return false;
89 }
90 if ((fout = fopen(output.toLocal8Bit(), "wb")) == nullptr) {
91 LOG_ERROR() << "Error: could not open output file\n";
92 fclose(fin);
93 return false;
94 }
95
96 int ret = encode_file(fin, fout, quality, complexity, narrowband, volume,
97 errstr, sizeof(errstr));
98 fclose(fout);
99 fclose(fin);
100
101 if (!ret) {
102 /* Attempt to delete unfinished output */
103 LOG_ERROR() << "Error:" << errstr;
104 QFile(output).remove();
105 return false;
106 }
107 return true;
108}
109
110bool EncoderRbSpeex::configOk()
111{
112 // check config. Make sure current settings are loaded.
113 loadSettings();
114 if(volume <= 0 || quality <= 0 || complexity <= 0)
115 return false;
116 else
117 return true;
118}
119
diff --git a/utils/rbutilqt/base/encoderrbspeex.h b/utils/rbutilqt/base/encoderrbspeex.h
new file mode 100644
index 0000000000..6ad41c38a3
--- /dev/null
+++ b/utils/rbutilqt/base/encoderrbspeex.h
@@ -0,0 +1,61 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef ENCODERRBSPEEX_H
20#define ENCODERRBSPEEX_H
21
22#include <QtCore>
23#include "encoderbase.h"
24
25class EncoderRbSpeex : public EncoderBase
26{
27 enum ESettings
28 {
29 eVOLUME,
30 eQUALITY,
31 eCOMPLEXITY,
32 eNARROWBAND
33 };
34
35 Q_OBJECT
36 public:
37 EncoderRbSpeex(QObject *parent = nullptr);
38 bool encode(QString input,QString output);
39 bool start();
40 bool stop() {return true;}
41
42 // for settings view
43 bool configOk();
44 void generateSettings();
45 void saveSettings();
46
47 private:
48 void loadSettings(void);
49 float quality;
50 float volume;
51 int complexity;
52 bool narrowband;
53
54 float defaultQuality;
55 float defaultVolume;
56 int defaultComplexity;
57 bool defaultBand;
58};
59
60#endif
61
diff --git a/utils/rbutilqt/base/encttssettings.cpp b/utils/rbutilqt/base/encttssettings.cpp
new file mode 100644
index 0000000000..9c8c2e57fd
--- /dev/null
+++ b/utils/rbutilqt/base/encttssettings.cpp
@@ -0,0 +1,70 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 * $Id: encoders.h 17902 2008-06-30 22:09:45Z bluebrother $
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "encttssettings.h"
23
24
25EncTtsSetting::EncTtsSetting(QObject* parent,ESettingType type,QString name,QVariant current, EButton btn) : QObject(parent)
26{
27 m_btn = btn;
28 m_name =name;
29 m_type =type;
30 m_currentValue = current;
31}
32
33EncTtsSetting::EncTtsSetting(QObject* parent,ESettingType type,QString name,QVariant current,QStringList list,EButton btn) : QObject(parent)
34{
35 m_btn = btn;
36 m_name =name;
37 m_type =type;
38 m_currentValue = current;
39 m_list = list;
40}
41
42EncTtsSetting::EncTtsSetting(QObject* parent,ESettingType type,QString name,QVariant current,QVariant min,QVariant max, EButton btn) : QObject(parent)
43{
44 m_btn = btn;
45 m_name =name;
46 m_type =type;
47 m_currentValue = current;
48 m_minValue = min;
49 m_maxValue = max;
50}
51
52void EncTtsSetting::setCurrent(QVariant current,bool noticeGui)
53{
54 m_currentValue = current;
55 emit dataChanged();
56
57 if(noticeGui) emit updateGui();
58}
59
60//! insert a setting
61void EncTtsSettingInterface::insertSetting(int id,EncTtsSetting* setting)
62{
63 settingsList.insert(id,setting);
64}
65
66//! retrieve a specific setting
67EncTtsSetting* EncTtsSettingInterface::getSetting(int id)
68{
69 return settingsList.at(id);
70}
diff --git a/utils/rbutilqt/base/encttssettings.h b/utils/rbutilqt/base/encttssettings.h
new file mode 100644
index 0000000000..1258d81d57
--- /dev/null
+++ b/utils/rbutilqt/base/encttssettings.h
@@ -0,0 +1,128 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#ifndef ENCTTSSETTINGS_H
22#define ENCTTSSETTINGS_H
23
24#include <QtCore>
25
26//! \brief This class stores everything needed to display a Setting.
27//!
28class EncTtsSetting : public QObject
29{
30 Q_OBJECT
31public:
32 enum ESettingType
33 {
34 eBASE,
35 eBOOL,
36 eDOUBLE,
37 eINT,
38 eSTRING,
39 eREADONLYSTRING,
40 eSTRINGLIST,
41 };
42 enum EButton
43 {
44 eNOBTN,
45 eBROWSEBTN,
46 eREFRESHBTN
47 };
48
49 //! constructor for a String or Bool setting
50 EncTtsSetting(QObject* parent,ESettingType type,QString name,QVariant current,EButton btn = eNOBTN);
51 //! contructor for a Stringlist setting, ie a enumeration
52 EncTtsSetting(QObject* parent,ESettingType type,QString name,QVariant current,QStringList list,EButton btn = eNOBTN);
53 //! constructor for a setting with a min-max range
54 EncTtsSetting(QObject* parent,ESettingType type,QString name,QVariant current,QVariant min,QVariant max,EButton = eNOBTN);
55
56 //! get currentValue
57 QVariant current() {return m_currentValue;}
58 //! set currentValue
59 void setCurrent(QVariant current,bool noticeGui=true);
60
61 //! get name of the Setting
62 QString name() {return m_name;}
63 //! get the type of the setting
64 ESettingType type() {return m_type;}
65 //! get what type of button this setting needs
66 EButton button() {return m_btn;}
67 //! get the minValue (only valid for a range setting, ie eDOUBLE or eINT)
68 QVariant min() {return m_minValue; }
69 //! get the maxValue (only valid for a range setting, ie eDOUBLE or eINT)
70 QVariant max() {return m_maxValue; }
71 //! get the enumerationlist (only valid for eSTRINGLIST settings)
72 QStringList list() {return m_list;}
73 //! set the enumeration list
74 void setList(QStringList list){m_list = list;}
75
76signals:
77 //! connect to this signal if you want to get noticed when the data changes
78 void dataChanged();
79 //! connect to this if you want to react on refresh button
80 void refresh();
81 //! will be emited when the gui should update this setting
82 void updateGui();
83
84private:
85 ESettingType m_type;
86 EButton m_btn;
87 QString m_name;
88 QVariant m_currentValue;
89 QVariant m_minValue;
90 QVariant m_maxValue;
91 QStringList m_list;
92};
93
94
95//! \brief this class is the Interface for Encoder and TTS engines, to display settings
96//! It wraps nearly everything needed, only updateModel() and commitModel() needs to be reimplemented
97//!
98class EncTtsSettingInterface : public QObject
99{
100 Q_OBJECT
101public:
102 EncTtsSettingInterface(QObject* parent) : QObject(parent) {}
103
104 //! get the Settings list
105 QList<EncTtsSetting*> getSettings() {generateSettings(); return settingsList;}
106
107 //! Chlid class should commit the from SettingsList to permanent storage
108 virtual void saveSettings() = 0;
109
110signals:
111 void busy(); // emit this if a operation takes time
112 void busyEnd(); // emit this at the end of a busy section
113
114protected:
115 //! Child class should fill in the setttingsList
116 virtual void generateSettings() = 0;
117
118 //! insert a setting
119 void insertSetting(int id,EncTtsSetting* setting);
120 //! retrieve a specific setting
121 EncTtsSetting* getSetting(int id);
122
123private:
124 //! The setting storage.
125 QList<EncTtsSetting*> settingsList;
126
127};
128#endif
diff --git a/utils/rbutilqt/base/httpget.cpp b/utils/rbutilqt/base/httpget.cpp
new file mode 100644
index 0000000000..2df9501fd6
--- /dev/null
+++ b/utils/rbutilqt/base/httpget.cpp
@@ -0,0 +1,256 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2013 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtNetwork>
20
21#include <QNetworkAccessManager>
22#include <QNetworkRequest>
23
24#include "httpget.h"
25#include "Logger.h"
26
27QString HttpGet::m_globalUserAgent; //< globally set user agent for requests
28QDir HttpGet::m_globalCache; //< global cach path value for new objects
29QNetworkProxy HttpGet::m_globalProxy;
30
31HttpGet::HttpGet(QObject *parent)
32 : QObject(parent),
33 m_mgr(this),
34 m_reply(nullptr),
35 m_cache(nullptr),
36 m_cachedir(m_globalCache),
37 m_outputFile(nullptr),
38 m_proxy(QNetworkProxy::NoProxy)
39{
40 setCache(true);
41 connect(&m_mgr, &QNetworkAccessManager::finished, this,
42 static_cast<void (HttpGet::*)(QNetworkReply*)>(&HttpGet::requestFinished));
43 m_lastServerTimestamp = QDateTime();
44}
45
46
47/** @brief set cache path
48 * @param d new directory to use as cache path
49 */
50void HttpGet::setCache(const QDir& d)
51{
52 if(m_cache && m_cachedir == d.absolutePath())
53 return;
54 m_cachedir.setPath(d.absolutePath());
55 setCache(true);
56}
57
58
59/** @brief enable / disable cache useage
60 * @param c set cache usage
61 */
62void HttpGet::setCache(bool c)
63{
64 // don't change cache if it's already (un)set.
65 if(c && m_cache) return;
66 if(!c && !m_cache) return;
67 // don't delete the old cache directly, it might still be in use. Just
68 // instruct it to delete itself later.
69 if(m_cache) m_cache->deleteLater();
70 m_cache = nullptr;
71
72 QString path = m_cachedir.absolutePath();
73
74 if(!c || m_cachedir.absolutePath().isEmpty()) {
75 LOG_INFO() << "disabling download cache";
76 }
77 else {
78 // append the cache path to make it unique in case the path points to
79 // the system temporary path. In that case using it directly might
80 // cause problems. Extra path also used in configure dialog.
81 path += "/rbutil-cache";
82 LOG_INFO() << "setting cache folder to" << path;
83 m_cache = new QNetworkDiskCache(this);
84 m_cache->setCacheDirectory(path);
85 }
86 m_mgr.setCache(m_cache);
87}
88
89
90/** @brief read all downloaded data into a buffer
91 * @return data
92 */
93QByteArray HttpGet::readAll()
94{
95 return m_data;
96}
97
98
99/** @brief Set and enable Proxy to use.
100 * @param proxy Proxy URL.
101 */
102void HttpGet::setProxy(const QUrl &proxy)
103{
104 LOG_INFO() << "Proxy set to" << proxy;
105 m_proxy.setType(QNetworkProxy::HttpProxy);
106 m_proxy.setHostName(proxy.host());
107 m_proxy.setPort(proxy.port());
108 m_proxy.setUser(proxy.userName());
109 m_proxy.setPassword(proxy.password());
110 m_mgr.setProxy(m_proxy);
111}
112
113
114/** @brief Enable or disable use of previously set proxy.
115 * @param enable Enable proxy.
116 */
117void HttpGet::setProxy(bool enable)
118{
119 if(enable) m_mgr.setProxy(m_proxy);
120 else m_mgr.setProxy(QNetworkProxy::NoProxy);
121}
122
123
124/** @brief Set output file.
125 *
126 * Set filename for storing the downloaded file to. If no file is set the
127 * downloaded file will not be stored to disk but kept in memory. The result
128 * can then be retrieved using readAll().
129 *
130 * @param file Output file.
131 */
132void HttpGet::setFile(QFile *file)
133{
134 m_outputFile = file;
135}
136
137
138void HttpGet::abort()
139{
140 if(m_reply) m_reply->abort();
141}
142
143
144void HttpGet::requestFinished(QNetworkReply* reply)
145{
146 m_lastStatusCode
147 = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
148 LOG_INFO() << "Request finished, status code:" << m_lastStatusCode;
149 m_lastServerTimestamp
150 = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().toLocalTime();
151 LOG_INFO() << "Data from cache:"
152 << reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool();
153 m_lastRequestCached =
154 reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool();
155 if(reply->attribute(QNetworkRequest::RedirectionTargetAttribute).isValid()) {
156 // handle relative URLs using QUrl::resolved()
157 QUrl org = reply->request().url();
158 QUrl url = QUrl(org).resolved(
159 reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl());
160 // reconstruct query
161#if QT_VERSION < 0x050000
162 QList<QPair<QByteArray, QByteArray> > qitms = org.encodedQueryItems();
163 for(int i = 0; i < qitms.size(); ++i)
164 url.addEncodedQueryItem(qitms.at(i).first, qitms.at(i).second);
165#else
166 url.setQuery(org.query());
167#endif
168 LOG_INFO() << "Redirected to" << url;
169 startRequest(url);
170 return;
171 }
172 else if(m_lastStatusCode == 200 ||
173 (reply->url().scheme() == "file" && reply->error() == 0)) {
174 // callers might not be aware if the request is file:// so fake 200.
175 m_lastStatusCode = 200;
176 m_data = reply->readAll();
177 if(m_outputFile && m_outputFile->open(QIODevice::WriteOnly)) {
178 m_outputFile->write(m_data);
179 m_outputFile->close();
180 }
181 emit done(false);
182 }
183 else {
184 m_data.clear();
185 emit done(true);
186 }
187 reply->deleteLater();
188 m_reply = nullptr;
189}
190
191
192void HttpGet::downloadProgress(qint64 received, qint64 total)
193{
194 emit dataReadProgress((int)received, (int)total);
195}
196
197
198void HttpGet::startRequest(QUrl url)
199{
200 LOG_INFO() << "Request started";
201 QNetworkRequest req(url);
202 if(!m_globalUserAgent.isEmpty())
203 req.setRawHeader("User-Agent", m_globalUserAgent.toLatin1());
204
205 m_reply = m_mgr.get(req);
206#if QT_VERSION < 0x050f00
207 connect(m_reply,
208 static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),
209 this, &HttpGet::networkError);
210#else
211 connect(m_reply, &QNetworkReply::errorOccurred, this, &HttpGet::networkError);
212#endif
213 connect(m_reply, &QNetworkReply::downloadProgress, this, &HttpGet::downloadProgress);
214}
215
216
217void HttpGet::networkError(QNetworkReply::NetworkError error)
218{
219 LOG_ERROR() << "NetworkError occured:" << error << m_reply->errorString();
220 m_lastErrorString = m_reply->errorString();
221}
222
223
224/** @brief Retrieve the file pointed to by url.
225 *
226 * Note: This also handles file:// URLs. Be aware that QUrl requires file://
227 * URLs to be absolute, i.e. file://filename.txt doesn't work. Use
228 * QDir::absoluteFilePath() to convert to an absolute path first.
229 *
230 * @param url URL to download.
231 */
232void HttpGet::getFile(const QUrl &url)
233{
234 LOG_INFO() << "Get URI" << url.toString();
235 m_data.clear();
236 startRequest(url);
237}
238
239
240/** @brief Retrieve string representation for most recent error.
241 * @return Error string.
242 */
243QString HttpGet::errorString(void)
244{
245 return m_lastErrorString;
246}
247
248
249/** @brief Return last HTTP response code.
250 * @return Response code.
251 */
252int HttpGet::httpResponse(void)
253{
254 return m_lastStatusCode;
255}
256
diff --git a/utils/rbutilqt/base/httpget.h b/utils/rbutilqt/base/httpget.h
new file mode 100644
index 0000000000..dfd7b87c89
--- /dev/null
+++ b/utils/rbutilqt/base/httpget.h
@@ -0,0 +1,111 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Riebeling
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21
22#ifndef HTTPGET_H
23#define HTTPGET_H
24
25#include <QtCore>
26#include <QtNetwork>
27#include <QNetworkAccessManager>
28#include "Logger.h"
29
30class HttpGet : public QObject
31{
32 Q_OBJECT
33
34 public:
35 HttpGet(QObject *parent = nullptr);
36
37 void getFile(const QUrl &url);
38 void setProxy(const QUrl &url);
39 void setProxy(bool);
40 QString errorString(void);
41 void setFile(QFile*);
42 void setCache(const QDir&);
43 void setCache(bool);
44 int httpResponse(void);
45 QByteArray readAll(void);
46 bool isCached()
47 { return m_lastRequestCached; }
48 QDateTime timestamp(void)
49 { return m_lastServerTimestamp; }
50 //< set global cache path
51 static void setGlobalCache(const QDir& d)
52 {
53 LOG_INFO() << "Global cache set to" << d.absolutePath();
54 m_globalCache = d;
55 }
56 //< set global proxy value
57 static void setGlobalProxy(const QUrl& p)
58 {
59 LOG_INFO() << "setting global proxy" << p;
60 if(!p.isValid() || p.isEmpty()) {
61 HttpGet::m_globalProxy.setType(QNetworkProxy::NoProxy);
62 }
63 else {
64 HttpGet::m_globalProxy.setType(QNetworkProxy::HttpProxy);
65 HttpGet::m_globalProxy.setHostName(p.host());
66 HttpGet::m_globalProxy.setPort(p.port());
67 HttpGet::m_globalProxy.setUser(p.userName());
68 HttpGet::m_globalProxy.setPassword(p.password());
69 }
70 QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy);
71 QNetworkProxy::setApplicationProxy(HttpGet::m_globalProxy);
72 }
73 //< set global user agent string
74 static void setGlobalUserAgent(const QString& u)
75 { m_globalUserAgent = u; }
76
77 public slots:
78 void abort(void);
79
80 signals:
81 void done(bool);
82 void dataReadProgress(int, int);
83 void requestFinished(int, bool);
84 void headerFinished(void);
85
86 private slots:
87 void requestFinished(QNetworkReply* reply);
88 void startRequest(QUrl url);
89 void downloadProgress(qint64 received, qint64 total);
90 void networkError(QNetworkReply::NetworkError error);
91
92 private:
93 static QString m_globalUserAgent;
94 static QNetworkProxy m_globalProxy;
95 QNetworkAccessManager m_mgr;
96 QNetworkReply *m_reply;
97 QNetworkDiskCache *m_cache;
98 QDir m_cachedir;
99 static QDir m_globalCache; //< global cache path value
100 QByteArray m_data;
101 QFile *m_outputFile;
102 int m_lastStatusCode;
103 QString m_lastErrorString;
104 QDateTime m_lastServerTimestamp;
105 bool m_lastRequestCached;
106 QNetworkProxy m_proxy;
107};
108
109
110#endif
111
diff --git a/utils/rbutilqt/base/mspackutil.cpp b/utils/rbutilqt/base/mspackutil.cpp
new file mode 100644
index 0000000000..b794272199
--- /dev/null
+++ b/utils/rbutilqt/base/mspackutil.cpp
@@ -0,0 +1,164 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2013 Amaury Pouly
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "Logger.h"
21#include "mspackutil.h"
22#include "progressloggerinterface.h"
23
24MsPackUtil::MsPackUtil(QObject* parent)
25 :ArchiveUtil(parent)
26{
27 m_cabd = mspack_create_cab_decompressor(nullptr);
28 m_cabinet = nullptr;
29 if(!m_cabd)
30 LOG_ERROR() << "CAB decompressor creation failed!";
31}
32
33MsPackUtil::~MsPackUtil()
34{
35 close();
36 if(m_cabd)
37 mspack_destroy_cab_decompressor(m_cabd);
38}
39
40bool MsPackUtil::open(QString& mspackfile)
41{
42 close();
43
44 if(m_cabd == nullptr)
45 {
46 LOG_ERROR() << "No CAB decompressor available: cannot open file!";
47 return false;
48 }
49 m_cabinet = m_cabd->search(m_cabd, QFile::encodeName(mspackfile).constData());
50 return m_cabinet != nullptr;
51}
52
53bool MsPackUtil::close(void)
54{
55 if(m_cabd && m_cabinet)
56 m_cabd->close(m_cabd, m_cabinet);
57 m_cabinet = nullptr;
58 return true;
59}
60
61bool MsPackUtil::extractArchive(const QString& dest, QString file)
62{
63 LOG_INFO() << "extractArchive" << dest << file;
64 if(!m_cabinet)
65 {
66 LOG_ERROR() << "CAB file not open!";
67 return false;
68 }
69
70 // construct the filename when extracting a single file from an archive.
71 // if the given destination is a full path use it as output name,
72 // otherwise use it as path to place the file as named in the archive.
73 QString singleoutfile;
74 if(!file.isEmpty() && QFileInfo(dest).isDir())
75 singleoutfile = dest + "/" + file;
76 else if(!file.isEmpty())
77 singleoutfile = dest;
78 struct mscabd_file *f = m_cabinet->files;
79 if(f == nullptr)
80 {
81 LOG_WARNING() << "CAB doesn't contain file" << file;
82 return true;
83 }
84 bool found = false;
85 while(f)
86 {
87 QString name = QFile::decodeName(f->filename);
88 name.replace("\\", "/");
89 if(name.at(0) == '/')
90 name.remove(0, 1);
91 if(name == file || file.isEmpty())
92 {
93 QString path;
94 if(!singleoutfile.isEmpty())
95 path = singleoutfile;
96 else
97 path = dest + "/" + name;
98 // make sure the output path exists
99 if(!QDir().mkpath(QFileInfo(path).absolutePath()))
100 {
101 emit logItem(tr("Creating output path failed"), LOGERROR);
102 LOG_ERROR() << "creating output path failed for:" << path;
103 emit logProgress(1, 1);
104 return false;
105 }
106 int ret = m_cabd->extract(m_cabd, f, QFile::encodeName(path).constData());
107 if(ret != MSPACK_ERR_OK)
108 {
109 emit logItem(tr("Error during CAB operation"), LOGERROR);
110 LOG_ERROR() << "mspack error: " << ret
111 << "(" << errorStringMsPack(ret) << ")";
112 emit logProgress(1, 1);
113 return false;
114 }
115 found = true;
116 }
117 f = f->next;
118 }
119 emit logProgress(1, 1);
120
121 return found;
122}
123
124QStringList MsPackUtil::files(void)
125{
126 QStringList list;
127 if(!m_cabinet)
128 {
129 LOG_WARNING() << "CAB file not open!";
130 return list;
131 }
132 struct mscabd_file *file = m_cabinet->files;
133 while(file)
134 {
135 QString name = QFile::decodeName(file->filename);
136 name.replace("\\", "/");
137 if(name.at(0) == '/')
138 name.remove(0, 1);
139 list.append(name);
140 file = file->next;
141 }
142
143 return list;
144}
145
146QString MsPackUtil::errorStringMsPack(int error) const
147{
148 switch(error)
149 {
150 case MSPACK_ERR_OK: return "Ok";
151 case MSPACK_ERR_ARGS: return "Bad arguments";
152 case MSPACK_ERR_OPEN: return "Open error";
153 case MSPACK_ERR_READ: return "Read error";
154 case MSPACK_ERR_WRITE: return "Write error";
155 case MSPACK_ERR_SEEK: return "Seek error";
156 case MSPACK_ERR_NOMEMORY: return "Out of memory";
157 case MSPACK_ERR_SIGNATURE: return "Bad signature";
158 case MSPACK_ERR_DATAFORMAT: return "Bad data format";
159 case MSPACK_ERR_CHECKSUM: return "Checksum error";
160 case MSPACK_ERR_CRUNCH: return "Compression error";
161 case MSPACK_ERR_DECRUNCH: return "Decompression error";
162 default: return "Unknown";
163 }
164}
diff --git a/utils/rbutilqt/base/mspackutil.h b/utils/rbutilqt/base/mspackutil.h
new file mode 100644
index 0000000000..1cb4350b13
--- /dev/null
+++ b/utils/rbutilqt/base/mspackutil.h
@@ -0,0 +1,51 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2013 Amaury Pouly
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef MSPACKUTIL_H
20#define MSPACKUTIL_H
21
22#include <QtCore>
23#include "archiveutil.h"
24#include "mspack/mspack.h"
25
26class MsPackUtil : public ArchiveUtil
27{
28 Q_OBJECT
29
30 public:
31 // archive types can be ORed
32 MsPackUtil(QObject* parent);
33 ~MsPackUtil();
34 bool open(QString& mspackfile);
35 virtual bool close(void);
36 virtual bool extractArchive(const QString& dest, QString file = "");
37 virtual QStringList files(void);
38
39 signals:
40 void logProgress(int, int);
41 void logItem(QString, int);
42
43 private:
44 QString errorStringMsPack(int error) const;
45 struct mscab_decompressor* m_cabd;
46 struct mscabd_cabinet *m_cabinet;
47
48};
49#endif
50
51
diff --git a/utils/rbutilqt/base/playerbuildinfo.cpp b/utils/rbutilqt/base/playerbuildinfo.cpp
new file mode 100644
index 0000000000..f118a9fd7a
--- /dev/null
+++ b/utils/rbutilqt/base/playerbuildinfo.cpp
@@ -0,0 +1,362 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2020 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include "playerbuildinfo.h"
20#include "rbsettings.h"
21#include "Logger.h"
22
23PlayerBuildInfo* PlayerBuildInfo::infoInstance = nullptr;
24
25PlayerBuildInfo* PlayerBuildInfo::instance()
26{
27 if (infoInstance == nullptr) {
28 infoInstance = new PlayerBuildInfo();
29 }
30 return infoInstance;
31}
32
33// server infos
34const static struct {
35 PlayerBuildInfo::BuildInfo item;
36 const char* name;
37} ServerInfoList[] = {
38 { PlayerBuildInfo::BuildVoiceLangs, "voices/:version:" },
39 { PlayerBuildInfo::BuildVersion, ":build:/:target:" },
40 { PlayerBuildInfo::BuildUrl, ":build:/build_url" },
41 { PlayerBuildInfo::BuildVoiceUrl, ":build:/voice_url" },
42 { PlayerBuildInfo::BuildManualUrl, ":build:/manual_url" },
43 { PlayerBuildInfo::BuildSourceUrl, ":build:/source_url" },
44 { PlayerBuildInfo::BuildFontUrl, ":build:/font_url" },
45
46 // other URLs -- those are not directly related to the build, but handled here.
47 { PlayerBuildInfo::DoomUrl, "other/doom_url" },
48 { PlayerBuildInfo::Duke3DUrl, "other/duke3d_url" },
49 { PlayerBuildInfo::PuzzFontsUrl, "other/puzzfonts_url" },
50 { PlayerBuildInfo::QuakeUrl, "other/quake_url" },
51 { PlayerBuildInfo::Wolf3DUrl, "other/wolf3d_url" },
52 { PlayerBuildInfo::XWorldUrl, "other/xworld_url" },
53 { PlayerBuildInfo::MidiPatchsetUrl, "other/patcheset_url" },
54};
55
56const static struct {
57 PlayerBuildInfo::DeviceInfo item;
58 const char* name;
59} PlayerInfoList[] = {
60 { PlayerBuildInfo::BuildStatus, "status/:target:" },
61 { PlayerBuildInfo::DisplayName, ":target:/name" },
62 { PlayerBuildInfo::BootloaderMethod, ":target:/bootloadermethod" },
63 { PlayerBuildInfo::BootloaderName, ":target:/bootloadername" },
64 { PlayerBuildInfo::BootloaderFile, ":target:/bootloaderfile" },
65 { PlayerBuildInfo::BootloaderFilter, ":target:/bootloaderfilter" },
66 { PlayerBuildInfo::Encoder, ":target:/encoder" },
67 { PlayerBuildInfo::Brand, ":target:/brand" },
68 { PlayerBuildInfo::PlayerPicture, ":target:/playerpic" },
69 { PlayerBuildInfo::TargetNamesAll, "_targets/all" },
70 { PlayerBuildInfo::TargetNamesEnabled, "_targets/enabled" },
71 { PlayerBuildInfo::LanguageInfo, "languages/:target:" },
72 { PlayerBuildInfo::LanguageList, "_languages/list" },
73 { PlayerBuildInfo::UsbIdErrorList, "_usb/error" },
74 { PlayerBuildInfo::UsbIdTargetList, "_usb/target" },
75};
76
77const static struct {
78 PlayerBuildInfo::SystemUrl item;
79 const char* name;
80} PlayerSystemUrls[] = {
81 { PlayerBuildInfo::BootloaderUrl, "bootloader/download_url" },
82 { PlayerBuildInfo::BuildInfoUrl, "build_info_url" },
83 { PlayerBuildInfo::GenlangUrl, "genlang_url" },
84 { PlayerBuildInfo::ThemesUrl, "themes_url" },
85 { PlayerBuildInfo::ThemesInfoUrl, "themes_info_url" },
86 { PlayerBuildInfo::RbutilUrl, "rbutil_url" },
87};
88
89PlayerBuildInfo::PlayerBuildInfo() :
90 serverInfo(nullptr),
91 playerInfo(":/ini/rbutil.ini", QSettings::IniFormat)
92{
93
94}
95
96void PlayerBuildInfo::setBuildInfo(QString file)
97{
98 if (serverInfo)
99 delete serverInfo;
100 LOG_INFO() << "updated:" << file;
101 serverInfo = new QSettings(file, QSettings::IniFormat);
102}
103
104QVariant PlayerBuildInfo::value(BuildInfo item, BuildType type)
105{
106 // locate setting item in server info file
107 int i = 0;
108 while(ServerInfoList[i].item != item)
109 i++;
110
111 // split of variant for target.
112 // we can have an optional variant part in the target string.
113 // For build info we don't use that.
114 QString target = RbSettings::value(RbSettings::CurrentPlatform).toString().split('.').at(0);
115
116 QString s = ServerInfoList[i].name;
117 s.replace(":target:", target);
118 QString v;
119 switch(type) {
120 case TypeRelease:
121 v = "release";
122 break;
123 case TypeCandidate:
124 v = "release-candidate";
125 break;
126 case TypeDaily:
127 v = "daily";
128 break;
129 case TypeDevel:
130 v = "development";
131 break;
132 }
133
134 QVariant result = QString();
135 if (!serverInfo)
136 return result;
137 QStringList version = serverInfo->value(v + "/" + target, "").toStringList();
138 s.replace(":build:", v);
139 s.replace(":version:", version.at(0));
140
141 // get value from server build-info
142 // we need to get a version string, otherwise the data is invalid.
143 // For invalid data return an empty string.
144 if(version.at(0).isEmpty()) {
145 LOG_INFO() << s << "(version invalid)";
146 return result;
147 }
148 if(!s.isEmpty())
149 result = serverInfo->value(s);
150
151 // depending on the actual value we need more replacements.
152 switch(item) {
153 case BuildVersion:
154 result = result.toStringList().at(0);
155 break;
156
157 case BuildUrl:
158 if(version.size() > 1) {
159 // version info has an URL appended. Takes precendence.
160 result = version.at(1);
161 }
162 break;
163
164 case BuildVoiceLangs:
165 if (type == TypeDaily)
166 s = "voices/daily";
167 result = serverInfo->value(s);
168 break;
169
170 case BuildManualUrl:
171 {
172 // special case: if playerInfo has a non-empty manualname entry for the
173 // target, use that as target for the manual name.
174 QString manualtarget = playerInfo.value(target + "/manualname", "").toString();
175 if(!manualtarget.isEmpty())
176 target = manualtarget;
177 break;
178 }
179
180 default:
181 break;
182 }
183 // if the value is a string we can replace some patterns.
184 // if we cannot convert it (f.e. for a QStringList) we leave as-is, since
185 // the conversion would return an empty type.
186 if (result.canConvert(QMetaType::QString))
187 result = result.toString()
188 .replace("%TARGET%", target)
189 .replace("%VERSION%", version.at(0));
190
191 LOG_INFO() << "B:" << s << result;
192 return result;
193}
194
195QVariant PlayerBuildInfo::value(DeviceInfo item, QString target)
196{
197 // locate setting item in server info file
198 int i = 0;
199 while(PlayerInfoList[i].item != item)
200 i++;
201
202 // split of variant for target.
203 // we can have an optional variant part in the target string.
204 // For device info we use this.
205 if (target.isEmpty())
206 target = RbSettings::value(RbSettings::CurrentPlatform).toString();
207
208 QVariant result = QString();
209
210 QString s = PlayerInfoList[i].name;
211 s.replace(":target:", target);
212
213 switch(item) {
214 case BuildStatus:
215 {
216 // build status is the only value that doesn't depend on the version
217 // but the selected target instead.
218 bool ok = false;
219 if (serverInfo)
220 result = serverInfo->value(s).toInt(&ok);
221 if (!ok)
222 result = -1;
223 break;
224 }
225 case TargetNamesAll:
226 // list of all internal target names. Doesn't depend on the passed target.
227 result = targetNames(true);
228 break;
229 case TargetNamesEnabled:
230 // list of all non-disabled target names. Doesn't depend on the passed target.
231 result = targetNames(false);
232 break;
233
234 case LanguageList:
235 // Return a map (language, display string).
236 {
237 // need to use (QString, QVariant) here, so we can put the map into
238 // a QVariant by itself.
239 QMap<QString, QVariant> m;
240
241 playerInfo.beginGroup("languages");
242 QStringList a = playerInfo.childKeys();
243
244 for(int i = 0; i < a.size(); i++) {
245 QStringList v = playerInfo.value(a.at(i)).toStringList();
246 m[v.at(0)] = v.at(1);
247 }
248 playerInfo.endGroup();
249 result = m;
250 }
251 break;
252
253 default:
254 result = playerInfo.value(s);
255 break;
256 }
257
258 LOG_INFO() << "T:" << s << result;
259 return result;
260}
261
262QVariant PlayerBuildInfo::value(DeviceInfo item, unsigned int match)
263{
264 QStringList result;
265 int i = 0;
266 while(PlayerInfoList[i].item != item)
267 i++;
268 QString s = PlayerInfoList[i].name;
269
270 switch(item) {
271 case UsbIdErrorList:
272 {
273 // go through all targets and find the one indicated by the usb id "target".
274 // return list of matching players (since it could be more than one)
275 QStringList targets = targetNames(true);
276 for(int i = 0; i < targets.size(); i++) {
277 QStringList usbids = playerInfo.value(targets.at(i) + "/usberror").toStringList();
278 for(int j = 0; j < usbids.size(); j++) {
279 if(usbids.at(j).toUInt(nullptr, 0) == match) {
280 result << targets.at(i);
281 }
282 }
283 }
284 break;
285 }
286
287 case UsbIdTargetList:
288 {
289 QStringList targets = targetNames(true);
290 for(int i = 0; i < targets.size(); i++) {
291 QStringList usbids = playerInfo.value(targets.at(i) + "/usbid").toStringList();
292 for(int j = 0; j < usbids.size(); j++) {
293 if(usbids.at(j).toUInt(nullptr, 0) == match) {
294 result << targets.at(i);
295 }
296 }
297 }
298 break;
299 }
300
301 default:
302 break;
303 }
304 LOG_INFO() << "T:" << s << result;
305 return result;
306}
307
308QVariant PlayerBuildInfo::value(SystemUrl item)
309{
310 // locate setting item in server info file
311 int i = 0;
312 while(PlayerSystemUrls[i].item != item)
313 i++;
314
315 QVariant result = playerInfo.value(PlayerSystemUrls[i].name);
316 LOG_INFO() << "U:" << PlayerSystemUrls[i].name << result;
317 return result;
318}
319
320
321QString PlayerBuildInfo::statusAsString(QString platform)
322{
323 QString result;
324 switch(value(BuildStatus, platform).toInt())
325 {
326 case STATUS_RETIRED:
327 result = tr("Stable (Retired)");
328 break;
329 case STATUS_UNUSABLE:
330 result = tr("Unusable");
331 break;
332 case STATUS_UNSTABLE:
333 result = tr("Unstable");
334 break;
335 case STATUS_STABLE:
336 result = tr("Stable");
337 break;
338 default:
339 result = tr("Unknown");
340 break;
341 }
342
343 return result;
344}
345
346
347QStringList PlayerBuildInfo::targetNames(bool all)
348{
349 QStringList result;
350 playerInfo.beginGroup("platforms");
351 QStringList a = playerInfo.childKeys();
352 playerInfo.endGroup();
353 for(int i = 0; i < a.size(); i++)
354 {
355 QString target = playerInfo.value("platforms/" + a.at(i), "null").toString();
356 if(playerInfo.value(target + "/status").toString() != "disabled" || all) {
357 result.append(target);
358 }
359 }
360 return result;
361}
362
diff --git a/utils/rbutilqt/base/playerbuildinfo.h b/utils/rbutilqt/base/playerbuildinfo.h
new file mode 100644
index 0000000000..6a88f750ed
--- /dev/null
+++ b/utils/rbutilqt/base/playerbuildinfo.h
@@ -0,0 +1,123 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2020 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef PLAYERBUILDINFO_H
20#define PLAYERBUILDINFO_H
21
22#include <QSettings>
23
24#define STATUS_RETIRED 0
25#define STATUS_UNUSABLE 1
26#define STATUS_UNSTABLE 2
27#define STATUS_STABLE 3
28
29// Provide information about both player and builds.
30// For build info data retrieved from the build server has to be passed.
31class PlayerBuildInfo : public QObject
32{
33 Q_OBJECT
34
35public:
36
37 enum BuildType {
38 TypeRelease,
39 TypeCandidate,
40 TypeDaily,
41 TypeDevel,
42 };
43 enum BuildInfo {
44 BuildUrl,
45 BuildVersion,
46 BuildManualUrl,
47 BuildVoiceUrl,
48 BuildVoiceLangs,
49 BuildSourceUrl,
50 BuildFontUrl,
51
52 DoomUrl,
53 Duke3DUrl,
54 PuzzFontsUrl,
55 QuakeUrl,
56 Wolf3DUrl,
57 XWorldUrl,
58 MidiPatchsetUrl,
59 };
60 enum DeviceInfo {
61 BuildStatus,
62
63 DisplayName,
64 BootloaderMethod,
65 BootloaderName,
66 BootloaderFile,
67 BootloaderFilter,
68 Encoder,
69 Brand,
70 PlayerPicture,
71
72 TargetNamesAll,
73 TargetNamesEnabled,
74 LanguageInfo,
75 LanguageList,
76 UsbIdErrorList,
77 UsbIdTargetList,
78 };
79
80 enum SystemUrl {
81 BootloaderUrl,
82 BuildInfoUrl,
83 GenlangUrl,
84 ThemesUrl,
85 ThemesInfoUrl,
86 RbutilUrl,
87 };
88
89 static PlayerBuildInfo* instance();
90
91 //! Update with build information from server
92 void setBuildInfo(QString file);
93
94 // Get information about a device. This data does not depend on the build type.
95 QVariant value(DeviceInfo item, QString target = "");
96
97 // Get information about a device. Make a numeric match
98 // (only sensible implementation for USB IDs)
99 QVariant value(DeviceInfo item, unsigned int match);
100
101 // Get build information for currently selected player.
102 QVariant value(BuildInfo item, BuildType type);
103
104 // Get fixed download URL information
105 QVariant value(SystemUrl item);
106
107 QString statusAsString(QString target = "");
108
109protected:
110 explicit PlayerBuildInfo();
111
112private:
113 //! Return a list with all target names (as used internally).
114 //! @all false filter out all targets with status = disabled.
115 QStringList targetNames(bool all);
116
117 static PlayerBuildInfo* infoInstance;
118 QSettings* serverInfo;
119 QSettings playerInfo;
120
121};
122
123#endif
diff --git a/utils/rbutilqt/base/progressloggerinterface.h b/utils/rbutilqt/base/progressloggerinterface.h
new file mode 100644
index 0000000000..ae4270ae6b
--- /dev/null
+++ b/utils/rbutilqt/base/progressloggerinterface.h
@@ -0,0 +1,60 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#ifndef PROGRESSLOGGERINTERFACE_H
22#define PROGRESSLOGGERINTERFACE_H
23
24#include <QtCore>
25
26enum {
27 LOGNOICON, LOGOK, LOGINFO, LOGWARNING, LOGERROR
28};
29
30
31
32class ProgressloggerInterface : public QObject
33{
34 Q_OBJECT
35
36public:
37 ProgressloggerInterface(QObject* parent) : QObject(parent) {}
38
39 virtual void setProgressValue(int value)=0;
40 virtual void setProgressMax(int max)=0;
41 virtual int getProgressMax()=0;
42
43signals:
44 void aborted();
45
46
47public slots:
48 virtual void addItem(const QString &text, int flag)=0; //! add a string to the list, with icon
49
50 virtual void close()=0;
51 virtual void show()=0;
52 virtual void setRunning()=0;
53 virtual void setFinished()=0;
54
55private:
56
57};
58
59#endif
60
diff --git a/utils/rbutilqt/base/rbsettings.cpp b/utils/rbutilqt/base/rbsettings.cpp
new file mode 100644
index 0000000000..8eaa01f7f6
--- /dev/null
+++ b/utils/rbutilqt/base/rbsettings.cpp
@@ -0,0 +1,207 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include "rbsettings.h"
20#include "playerbuildinfo.h"
21#include <QSettings>
22#include "Logger.h"
23
24#if defined(Q_OS_LINUX)
25#include <unistd.h>
26#endif
27
28// user settings
29const static struct {
30 RbSettings::UserSettings setting;
31 const char* name;
32 const char* def;
33} UserSettingsList[] = {
34 { RbSettings::RbutilVersion, "rbutil_version", "" },
35 { RbSettings::ShowChangelog, "show_changelog", "false" },
36 { RbSettings::CurrentPlatform, "platform", "" },
37 { RbSettings::Mountpoint, "mountpoint", "" },
38 { RbSettings::CachePath, "cachepath", "" },
39 { RbSettings::Build, "build", "" },
40 { RbSettings::ProxyType, "proxytype", "" },
41 { RbSettings::Proxy, "proxy", "" },
42 { RbSettings::OfPath, "ofpath", "" },
43 { RbSettings::Platform, "platform", "" },
44 { RbSettings::Language, "lang", "" },
45 { RbSettings::BackupPath, "backuppath", "" },
46 { RbSettings::InstallRockbox, "install_rockbox", "true" },
47 { RbSettings::InstallFonts, "install_fonts", "true" },
48 { RbSettings::InstallThemes, "install_themes", "false" },
49 { RbSettings::InstallPluginData, "install_plugin_data", "true" },
50 { RbSettings::InstallVoice, "install_voice", "false" },
51 { RbSettings::InstallManual, "install_manual", "false" },
52#if defined(Q_OS_WIN32)
53 { RbSettings::Tts, "tts", "sapi" },
54#elif defined(Q_OS_MACX)
55 { RbSettings::Tts, "tts", "carbon" },
56#else
57 { RbSettings::Tts, "tts", "espeak" },
58#endif
59 { RbSettings::UseTtsCorrections, "use_tts_corrections", "true" },
60 { RbSettings::TalkFolders, "talk_folders", "" },
61 { RbSettings::TalkProcessFiles, "talk_process_files", "true" },
62 { RbSettings::TalkProcessFolders, "talk_process_folders", "true" },
63 { RbSettings::TalkRecursive, "talk_recursive", "true" },
64 { RbSettings::TalkSkipExisting, "talk_skip_existing", "true" },
65 { RbSettings::TalkStripExtensions, "talk_strip_extensions","true" },
66 { RbSettings::TalkIgnoreFiles, "talk_ignore_files", "false" },
67 { RbSettings::TalkIgnoreWildcards, "talk_ignore_wildcards","" },
68 { RbSettings::VoiceLanguage, "voicelanguage", "" },
69 { RbSettings::TtsLanguage, ":tts:/language", "" },
70 { RbSettings::TtsOptions, ":tts:/options", "" },
71 { RbSettings::TtsPitch, ":tts:/pitch", "0" },
72 { RbSettings::TtsPath, ":tts:/path", "" },
73 { RbSettings::TtsVoice, ":tts:/voice", "" },
74 { RbSettings::EncoderPath, ":encoder:/encoderpath", "" },
75 { RbSettings::EncoderOptions, ":encoder:/encoderoptions", "" },
76 { RbSettings::CacheDisabled, "cachedisable", "false" },
77 { RbSettings::TtsUseSapi4, "sapi/useSapi4", "false" },
78 { RbSettings::EncoderNarrowBand, ":encoder:/narrowband", "false" },
79 { RbSettings::WavtrimThreshold, "wavtrimthreshold", "500"},
80 { RbSettings::TtsSpeed, ":tts:/speed", "175" },
81 { RbSettings::EncoderComplexity, ":encoder:/complexity", "10" },
82 { RbSettings::EncoderQuality, ":encoder:/quality", "-1.0" },
83 { RbSettings::EncoderVolume, ":encoder:/volume", "1.0" },
84};
85
86//! pointer to setting object to NULL
87QSettings* RbSettings::userSettings = nullptr;
88
89void RbSettings::ensureRbSettingsExists()
90{
91 if(userSettings == nullptr)
92 {
93 // portable installation:
94 // check for a configuration file in the program folder.
95 QFileInfo config;
96 config.setFile(QCoreApplication::instance()->applicationDirPath()
97 + "/RockboxUtility.ini");
98 if(config.isFile())
99 {
100 userSettings = new QSettings(QCoreApplication::instance()->applicationDirPath()
101 + "/RockboxUtility.ini", QSettings::IniFormat, nullptr);
102 LOG_INFO() << "configuration: portable";
103 }
104 else
105 {
106 userSettings = new QSettings(QSettings::IniFormat,
107 QSettings::UserScope, "rockbox.org", "RockboxUtility",nullptr);
108 LOG_INFO() << "configuration: system";
109 }
110 }
111}
112
113void RbSettings::sync()
114{
115 ensureRbSettingsExists();
116
117 userSettings->sync();
118#if defined(Q_OS_LINUX)
119 // when using sudo it runs rbutil with uid 0 but unfortunately without a
120 // proper login shell, thus the configuration file gets placed in the
121 // calling users $HOME. This in turn will cause issues if trying to
122 // run rbutil later as user. Try to detect this case via the environment
123 // variable SUDO_UID and SUDO_GID and if set chown the user config file.
124 if(getuid() == 0)
125 {
126 char* realuser = getenv("SUDO_UID");
127 char* realgroup = getenv("SUDO_GID");
128 if(realuser != nullptr && realgroup != nullptr)
129 {
130 int realuid = atoi(realuser);
131 int realgid = atoi(realgroup);
132 // chown is attribute warn_unused_result, but in case this fails
133 // we can't do anything useful about it. Notifying the user
134 // is somewhat pointless. Add hack to suppress compiler warning.
135 if(chown(qPrintable(userSettings->fileName()), realuid, realgid))
136 { }
137 }
138 }
139#endif
140}
141
142QString RbSettings::userSettingFilename()
143{
144 ensureRbSettingsExists();
145 return userSettings->fileName();
146}
147
148QVariant RbSettings::value(enum UserSettings setting)
149{
150 QString empty;
151 return subValue(empty, setting);
152}
153
154QVariant RbSettings::subValue(QString sub, enum UserSettings setting)
155{
156 ensureRbSettingsExists();
157
158 // locate setting item
159 int i = 0;
160 while(UserSettingsList[i].setting != setting)
161 i++;
162
163 QString s = constructSettingPath(UserSettingsList[i].name, sub);
164 LOG_INFO() << "GET U:" << s << userSettings->value(s).toString();
165 return userSettings->value(s, UserSettingsList[i].def);
166}
167
168void RbSettings::setValue(enum UserSettings setting , QVariant value)
169{
170 QString empty;
171 return setSubValue(empty, setting, value);
172}
173
174void RbSettings::setSubValue(QString sub, enum UserSettings setting, QVariant value)
175{
176 ensureRbSettingsExists();
177
178 // locate setting item
179 int i = 0;
180 while(UserSettingsList[i].setting != setting)
181 i++;
182
183 QString s = constructSettingPath(UserSettingsList[i].name, sub);
184 userSettings->setValue(s, value);
185 LOG_INFO() << "SET U:" << s << userSettings->value(s).toString();
186}
187
188QString RbSettings::constructSettingPath(QString path, QString substitute)
189{
190 // anything to substitute?
191 if(path.contains(':')) {
192 QString platform = userSettings->value("platform").toString();
193 if(!substitute.isEmpty()) {
194 path.replace(":tts:", substitute);
195 path.replace(":encoder:", substitute);
196 }
197 else {
198 path.replace(":tts:", userSettings->value("tts").toString());
199 path.replace(":encoder:", PlayerBuildInfo::instance()->value(
200 PlayerBuildInfo::Encoder, platform).toString());
201 }
202 path.replace(":platform:", platform);
203 }
204
205 return path;
206}
207
diff --git a/utils/rbutilqt/base/rbsettings.h b/utils/rbutilqt/base/rbsettings.h
new file mode 100644
index 0000000000..ef0c4eb134
--- /dev/null
+++ b/utils/rbutilqt/base/rbsettings.h
@@ -0,0 +1,105 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#ifndef RBSETTINGS_H
22#define RBSETTINGS_H
23
24#include <QtCore>
25
26class RbSettings : public QObject
27{
28 Q_OBJECT
29 public:
30
31 //! All user settings
32 enum UserSettings {
33 RbutilVersion,
34 ShowChangelog,
35 CurrentPlatform,
36 Mountpoint,
37 CachePath,
38 Build,
39 ProxyType,
40 Proxy,
41 OfPath,
42 Platform,
43 Language,
44 BackupPath,
45 InstallRockbox,
46 InstallFonts,
47 InstallThemes,
48 InstallPluginData,
49 InstallVoice,
50 InstallManual,
51 Tts,
52 UseTtsCorrections,
53 TalkFolders,
54 TalkProcessFiles,
55 TalkProcessFolders,
56 TalkRecursive,
57 TalkSkipExisting,
58 TalkStripExtensions,
59 TalkIgnoreFiles,
60 TalkIgnoreWildcards,
61 VoiceLanguage,
62 TtsLanguage,
63 TtsOptions,
64 TtsPath,
65 TtsVoice,
66 TtsPitch,
67 EncoderPath,
68 EncoderOptions,
69 WavtrimThreshold,
70 EncoderComplexity,
71 TtsSpeed,
72 CacheDisabled,
73 TtsUseSapi4,
74 EncoderNarrowBand,
75 EncoderQuality,
76 EncoderVolume,
77 };
78
79 //! call this to flush the user Settings
80 static void sync();
81 //! returns the filename of the usersettings file
82 static QString userSettingFilename();
83 //! get a value from user settings
84 static QVariant value(enum UserSettings setting);
85 //! set a user setting value
86 static void setValue(enum UserSettings setting , QVariant value);
87 //! get a user setting from a subvalue (ie for encoders and tts engines)
88 static QVariant subValue(QString sub, enum UserSettings setting);
89 //! set a user setting from a subvalue (ie for encoders and tts engines)
90 static void setSubValue(QString sub, enum UserSettings setting, QVariant value);
91
92 private:
93 //! you shouldnt call this, its a fully static calls
94 RbSettings() {}
95 //! create the setting objects if neccessary
96 static void ensureRbSettingsExists();
97 //! create a settings path, substitute platform, tts and encoder
98 static QString constructSettingPath(QString path, QString substitute = QString());
99
100 //! pointers to our setting object
101 static QSettings *userSettings;
102};
103
104#endif
105
diff --git a/utils/rbutilqt/base/rockboxinfo.cpp b/utils/rbutilqt/base/rockboxinfo.cpp
new file mode 100644
index 0000000000..f34adbfc2f
--- /dev/null
+++ b/utils/rbutilqt/base/rockboxinfo.cpp
@@ -0,0 +1,83 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include "rockboxinfo.h"
20
21#include <QtCore>
22#include <QDebug>
23#include "Logger.h"
24
25RockboxInfo::RockboxInfo(QString mountpoint, QString fname)
26{
27 LOG_INFO() << "Getting version info from rockbox-info.txt";
28 QFile file(mountpoint + "/" + fname);
29 m_success = false;
30 m_voicefmt = 400; // default value for compatibility
31 if(!file.exists())
32 return;
33
34 if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
35 return;
36
37 // read file contents
38 QRegExp hash("^Version:\\s+(r?)([0-9a-fM]+)");
39 QRegExp version("^Version:\\s+(\\S.*)");
40 QRegExp release("^Version:\\s+([0-9\\.]+)\\s*$");
41 QRegExp target("^Target:\\s+(\\S.*)");
42 QRegExp features("^Features:\\s+(\\S.*)");
43 QRegExp targetid("^Target id:\\s+(\\S.*)");
44 QRegExp memory("^Memory:\\s+(\\S.*)");
45 QRegExp voicefmt("^Voice format:\\s+(\\S.*)");
46 while (!file.atEnd())
47 {
48 QString line = file.readLine().trimmed();
49
50 if(version.indexIn(line) >= 0) {
51 m_version = version.cap(1);
52 }
53 if(release.indexIn(line) >= 0) {
54 m_release = release.cap(1);
55 }
56 if(hash.indexIn(line) >= 0) {
57 // git hashes are usually at least 7 characters.
58 // svn revisions are expected to be at least 4 digits.
59 if(hash.cap(2).size() > 3)
60 m_revision = hash.cap(2);
61 }
62 else if(target.indexIn(line) >= 0) {
63 m_target = target.cap(1);
64 }
65 else if(features.indexIn(line) >= 0) {
66 m_features = features.cap(1);
67 }
68 else if(targetid.indexIn(line) >= 0) {
69 m_targetid = targetid.cap(1);
70 }
71 else if(memory.indexIn(line) >= 0) {
72 m_ram = memory.cap(1).toInt();
73 }
74 else if(voicefmt.indexIn(line) >= 0) {
75 m_voicefmt = voicefmt.cap(1).toInt();
76 }
77 }
78
79 file.close();
80 m_success = true;
81 return;
82}
83
diff --git a/utils/rbutilqt/base/rockboxinfo.h b/utils/rbutilqt/base/rockboxinfo.h
new file mode 100644
index 0000000000..af5cf227f7
--- /dev/null
+++ b/utils/rbutilqt/base/rockboxinfo.h
@@ -0,0 +1,54 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21
22#ifndef ROCKBOXINFO_H
23#define ROCKBOXINFO_H
24
25#include <QString>
26
27class RockboxInfo
28{
29public:
30 RockboxInfo(QString mountpoint, QString fname = "/.rockbox/rockbox-info.txt");
31
32 QString version() {return m_version;}
33 QString features(){return m_features;}
34 QString targetID() {return m_targetid;}
35 QString target() {return m_target;}
36 int ram() { return m_ram; }
37 int voicefmt() { return m_voicefmt; }
38 bool success() { return m_success; }
39 QString revision(void) { return m_revision; }
40 QString release(void) { return m_release; }
41private:
42 QString m_version;
43 QString m_revision;
44 QString m_release;
45 QString m_features;
46 QString m_targetid;
47 QString m_target;
48 int m_ram;
49 int m_voicefmt;
50 bool m_success;
51};
52
53#endif
54
diff --git a/utils/rbutilqt/base/system.cpp b/utils/rbutilqt/base/system.cpp
new file mode 100644
index 0000000000..dafab971ec
--- /dev/null
+++ b/utils/rbutilqt/base/system.cpp
@@ -0,0 +1,519 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19
20#include "system.h"
21
22#include <QtCore>
23#include <QDebug>
24
25#include <cstdlib>
26#include <stdio.h>
27
28// Windows Includes
29#if defined(Q_OS_WIN32)
30#if defined(UNICODE)
31#define _UNICODE
32#endif
33#include <windows.h>
34#include <tchar.h>
35#include <lm.h>
36#include <windows.h>
37#include <setupapi.h>
38#endif
39
40// Linux and Mac includes
41#if defined(Q_OS_LINUX) || defined(Q_OS_MACX)
42#include <sys/utsname.h>
43#include <unistd.h>
44#include <pwd.h>
45#endif
46
47// Linux includes
48#if defined(Q_OS_LINUX)
49#include <libusb-1.0/libusb.h>
50#include <mntent.h>
51#endif
52
53// Mac includes
54#if defined(Q_OS_MACX)
55#include <sys/param.h>
56#include <sys/ucred.h>
57#include <sys/mount.h>
58
59#include <CoreFoundation/CoreFoundation.h>
60#include <SystemConfiguration/SystemConfiguration.h>
61#include <CoreServices/CoreServices.h>
62#include <IOKit/IOKitLib.h>
63#include <IOKit/usb/IOUSBLib.h>
64#endif
65
66#include "utils.h"
67#include "rbsettings.h"
68#include "Logger.h"
69
70/** @brief detect permission of user (only Windows at moment).
71 * @return enum userlevel.
72 */
73#if defined(Q_OS_WIN32)
74enum System::userlevel System::userPermissions(void)
75{
76 LPUSER_INFO_1 buf = NULL;
77 wchar_t userbuf[UNLEN];
78 DWORD usersize = UNLEN;
79 BOOL status;
80 enum userlevel result = ERR;
81
82 status = GetUserNameW(userbuf, &usersize);
83 if(!status)
84 return ERR;
85
86 if(NetUserGetInfo(NULL, userbuf, (DWORD)1, (LPBYTE*)&buf) == NERR_Success) {
87 switch(buf->usri1_priv) {
88 case USER_PRIV_GUEST:
89 result = GUEST;
90 break;
91 case USER_PRIV_USER:
92 result = USER;
93 break;
94 case USER_PRIV_ADMIN:
95 result = ADMIN;
96 break;
97 default:
98 result = ERR;
99 break;
100 }
101 }
102 if(buf != NULL)
103 NetApiBufferFree(buf);
104
105 return result;
106}
107
108/** @brief detects user permissions (only Windows at moment).
109 * @return a user readable string with the permission.
110 */
111QString System::userPermissionsString(void)
112{
113 QString result;
114 int perm = userPermissions();
115 switch(perm) {
116 case GUEST:
117 result = tr("Guest");
118 break;
119 case ADMIN:
120 result = tr("Admin");
121 break;
122 case USER:
123 result = tr("User");
124 break;
125 default:
126 result = tr("Error");
127 break;
128 }
129 return result;
130}
131#endif
132
133
134/** @brief detects current Username.
135 * @return string with Username.
136 */
137QString System::userName(void)
138{
139#if defined(Q_OS_WIN32)
140 wchar_t userbuf[UNLEN];
141 DWORD usersize = UNLEN;
142
143 if(GetUserNameW(userbuf, &usersize) == 0)
144 return QString();
145
146 return QString::fromWCharArray(userbuf);
147#endif
148#if defined(Q_OS_LINUX) || defined(Q_OS_MACX)
149 struct passwd *user;
150 user = getpwuid(geteuid());
151 return QString(user->pw_name);
152#endif
153}
154
155
156/** @brief detects the OS Version
157 * @return String with OS Version.
158 */
159QString System::osVersionString(void)
160{
161 QString result;
162#if defined(Q_OS_WIN32)
163 SYSTEM_INFO sysinfo;
164 OSVERSIONINFO osvi;
165 ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
166 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
167 GetVersionEx(&osvi);
168 GetSystemInfo(&sysinfo);
169
170 result = QString("Windows version %1.%2, ").arg(osvi.dwMajorVersion).arg(osvi.dwMinorVersion);
171 if(osvi.szCSDVersion)
172 result += QString("build %1 (%2)").arg(osvi.dwBuildNumber)
173 .arg(QString::fromWCharArray(osvi.szCSDVersion));
174 else
175 result += QString("build %1").arg(osvi.dwBuildNumber);
176 result += QString("<br/>CPU: %1, %2 processor(s)").arg(sysinfo.dwProcessorType)
177 .arg(sysinfo.dwNumberOfProcessors);
178#endif
179#if defined(Q_OS_LINUX) || defined(Q_OS_MACX)
180 struct utsname u;
181 int ret;
182 ret = uname(&u);
183
184#if defined(Q_OS_MACX)
185 SInt32 cores;
186 Gestalt(gestaltCountOfCPUs, &cores);
187#else
188 long cores = sysconf(_SC_NPROCESSORS_ONLN);
189#endif
190 if(ret != -1) {
191 result = QString("CPU: %1, %2 processor(s)").arg(u.machine).arg(cores);
192 result += QString("<br/>System: %2<br/>Release: %3<br/>Version: %4")
193 .arg(u.sysname).arg(u.release).arg(u.version);
194 }
195 else {
196 result = QString("(Error when retrieving system information)");
197 }
198#if defined(Q_OS_MACX)
199 SInt32 major;
200 SInt32 minor;
201 SInt32 bugfix;
202 Gestalt(gestaltSystemVersionMajor, &major);
203 Gestalt(gestaltSystemVersionMinor, &minor);
204 Gestalt(gestaltSystemVersionBugFix, &bugfix);
205
206 result += QString("<br/>OS X %1.%2.%3 ").arg(major).arg(minor).arg(bugfix);
207 // 1: 86k, 2: ppc, 10: i386
208 SInt32 arch;
209 Gestalt(gestaltSysArchitecture, &arch);
210 switch(arch) {
211 case 1:
212 result.append("(86k)");
213 break;
214 case 2:
215 result.append("(ppc)");
216 break;
217 case 10:
218 result.append("(x86)");
219 break;
220 default:
221 result.append("(unknown)");
222 break;
223 }
224#endif
225#endif
226 result += QString("<br/>Qt version %1").arg(qVersion());
227 return result;
228}
229
230QList<uint32_t> System::listUsbIds(void)
231{
232 return listUsbDevices().keys();
233}
234
235/** @brief detect devices based on usb pid / vid.
236 * @return list with usb VID / PID values.
237 */
238QMultiMap<uint32_t, QString> System::listUsbDevices(void)
239{
240 QMultiMap<uint32_t, QString> usbids;
241 // usb pid detection
242 LOG_INFO() << "Searching for USB devices";
243#if defined(Q_OS_LINUX)
244 libusb_device **devs;
245 if(libusb_init(nullptr) != 0) {
246 LOG_ERROR() << "Initializing libusb-1 failed.";
247 return usbids;
248 }
249
250 if(libusb_get_device_list(nullptr, &devs) < 1) {
251 LOG_ERROR() << "Error getting device list.";
252 return usbids;
253 }
254 libusb_device *dev;
255 int i = 0;
256 while((dev = devs[i++]) != nullptr) {
257 QString name;
258 unsigned char buf[256];
259 uint32_t id;
260 struct libusb_device_descriptor descriptor;
261 if(libusb_get_device_descriptor(dev, &descriptor) == 0) {
262 id = descriptor.idVendor << 16 | descriptor.idProduct;
263
264 libusb_device_handle *dh;
265 if(libusb_open(dev, &dh) == 0) {
266 libusb_get_string_descriptor_ascii(dh, descriptor.iManufacturer, buf, 256);
267 name += QString::fromLatin1((char*)buf) + " ";
268 libusb_get_string_descriptor_ascii(dh, descriptor.iProduct, buf, 256);
269 name += QString::fromLatin1((char*)buf);
270 libusb_close(dh);
271 }
272 if(name.isEmpty())
273 name = tr("(no description available)");
274 if(id) {
275 usbids.insert(id, name);
276 LOG_INFO("USB: 0x%08x, %s", id, name.toLocal8Bit().data());
277 }
278 }
279 }
280
281 libusb_free_device_list(devs, 1);
282 libusb_exit(nullptr);
283#endif
284
285#if defined(Q_OS_MACX)
286 kern_return_t result = KERN_FAILURE;
287 CFMutableDictionaryRef usb_matching_dictionary;
288 io_iterator_t usb_iterator = IO_OBJECT_NULL;
289 usb_matching_dictionary = IOServiceMatching(kIOUSBDeviceClassName);
290 result = IOServiceGetMatchingServices(kIOMasterPortDefault, usb_matching_dictionary,
291 &usb_iterator);
292 if(result) {
293 LOG_ERROR() << "USB: IOKit: Could not get matching services.";
294 return usbids;
295 }
296
297 io_object_t usbCurrentObj;
298 while((usbCurrentObj = IOIteratorNext(usb_iterator))) {
299 uint32_t id;
300 QString name;
301 /* get vendor ID */
302 CFTypeRef vidref = NULL;
303 int vid = 0;
304 vidref = IORegistryEntryCreateCFProperty(usbCurrentObj, CFSTR("idVendor"),
305 kCFAllocatorDefault, 0);
306 CFNumberGetValue((CFNumberRef)vidref, kCFNumberIntType, &vid);
307 CFRelease(vidref);
308
309 /* get product ID */
310 CFTypeRef pidref = NULL;
311 int pid = 0;
312 pidref = IORegistryEntryCreateCFProperty(usbCurrentObj, CFSTR("idProduct"),
313 kCFAllocatorDefault, 0);
314 CFNumberGetValue((CFNumberRef)pidref, kCFNumberIntType, &pid);
315 CFRelease(pidref);
316 id = vid << 16 | pid;
317
318 /* get product vendor */
319 char vendor_buf[256];
320 CFIndex vendor_buflen = 256;
321 CFTypeRef vendor_name_ref = NULL;
322
323 vendor_name_ref = IORegistryEntrySearchCFProperty(usbCurrentObj,
324 kIOServicePlane, CFSTR("USB Vendor Name"),
325 kCFAllocatorDefault, 0);
326 if(vendor_name_ref != NULL) {
327 CFStringGetCString((CFStringRef)vendor_name_ref, vendor_buf, vendor_buflen,
328 kCFStringEncodingUTF8);
329 name += QString::fromUtf8(vendor_buf) + " ";
330 CFRelease(vendor_name_ref);
331 }
332 else {
333 name += QObject::tr("(unknown vendor name) ");
334 }
335
336 /* get product name */
337 char product_buf[256];
338 CFIndex product_buflen = 256;
339 CFTypeRef product_name_ref = NULL;
340
341 product_name_ref = IORegistryEntrySearchCFProperty(usbCurrentObj,
342 kIOServicePlane, CFSTR("USB Product Name"),
343 kCFAllocatorDefault, 0);
344 if(product_name_ref != NULL) {
345 CFStringGetCString((CFStringRef)product_name_ref, product_buf, product_buflen,
346 kCFStringEncodingUTF8);
347 name += QString::fromUtf8(product_buf);
348 CFRelease(product_name_ref);
349 }
350 else {
351 name += QObject::tr("(unknown product name)");
352 }
353
354 if(id) {
355 usbids.insertMulti(id, name);
356 LOG_INFO() << "USB:" << QString("0x%1").arg(id, 8, 16) << name;
357 }
358
359 }
360 IOObjectRelease(usb_iterator);
361#endif
362
363#if defined(Q_OS_WIN32)
364 HDEVINFO deviceInfo;
365 SP_DEVINFO_DATA infoData;
366 DWORD i;
367
368 // Iterate over all devices
369 // by doing it this way it's unneccessary to use GUIDs which might be not
370 // present in current MinGW. It also seemed to be more reliably than using
371 // a GUID.
372 // See KB259695 for an example.
373 deviceInfo = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
374
375 infoData.cbSize = sizeof(SP_DEVINFO_DATA);
376
377 for(i = 0; SetupDiEnumDeviceInfo(deviceInfo, i, &infoData); i++) {
378 DWORD data;
379 LPTSTR buffer = NULL;
380 DWORD buffersize = 0;
381 QString description;
382
383 // get device descriptor first
384 // for some reason not doing so results in bad things (tm)
385 while(!SetupDiGetDeviceRegistryProperty(deviceInfo, &infoData,
386 SPDRP_DEVICEDESC, &data, (PBYTE)buffer, buffersize, &buffersize)) {
387 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
388 if(buffer) free(buffer);
389 // double buffer size to avoid problems as per KB888609
390 buffer = (LPTSTR)malloc(buffersize * 2);
391 }
392 else {
393 break;
394 }
395 }
396 if(!buffer) {
397 LOG_WARNING() << "Got no device description"
398 << "(SetupDiGetDeviceRegistryProperty), item" << i;
399 continue;
400 }
401 description = QString::fromWCharArray(buffer);
402
403 // now get the hardware id, which contains PID and VID.
404 while(!SetupDiGetDeviceRegistryProperty(deviceInfo, &infoData,
405 SPDRP_HARDWAREID, &data, (PBYTE)buffer, buffersize, &buffersize)) {
406 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
407 if(buffer) free(buffer);
408 // double buffer size to avoid problems as per KB888609
409 buffer = (LPTSTR)malloc(buffersize * 2);
410 }
411 else {
412 break;
413 }
414 }
415
416 if(buffer) {
417 // convert buffer text to upper case to avoid depending on the case of
418 // the keys (W7 uses different casing than XP at least), in addition
419 // XP may use "Vid_" and "Pid_".
420 QString data = QString::fromWCharArray(buffer).toUpper();
421 QRegExp rex("USB\\\\VID_([0-9A-F]{4})&PID_([0-9A-F]{4}).*");
422 if(rex.indexIn(data) >= 0) {
423 uint32_t id;
424 id = rex.cap(1).toUInt(0, 16) << 16 | rex.cap(2).toUInt(0, 16);
425 usbids.insert(id, description);
426 LOG_INFO() << "USB:" << QString("0x%1").arg(id, 8, 16);
427 }
428 free(buffer);
429 }
430 }
431 SetupDiDestroyDeviceInfoList(deviceInfo);
432
433#endif
434 return usbids;
435}
436
437
438/** @brief detects current system proxy
439 * @return QUrl with proxy or empty
440 */
441QUrl System::systemProxy(void)
442{
443#if defined(Q_OS_LINUX)
444 return QUrl(getenv("http_proxy"));
445#elif defined(Q_OS_WIN32)
446 HKEY hk;
447 wchar_t proxyval[80];
448 DWORD buflen = 80;
449 long ret;
450 DWORD enable;
451 DWORD enalen = sizeof(DWORD);
452
453 ret = RegOpenKeyEx(HKEY_CURRENT_USER,
454 _TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"),
455 0, KEY_QUERY_VALUE, &hk);
456 if(ret != ERROR_SUCCESS) return QUrl("");
457
458 ret = RegQueryValueEx(hk, _TEXT("ProxyServer"), NULL, NULL, (LPBYTE)proxyval, &buflen);
459 if(ret != ERROR_SUCCESS) return QUrl("");
460
461 ret = RegQueryValueEx(hk, _TEXT("ProxyEnable"), NULL, NULL, (LPBYTE)&enable, &enalen);
462 if(ret != ERROR_SUCCESS) return QUrl("");
463
464 RegCloseKey(hk);
465
466 //LOG_INFO() << QString::fromWCharArray(proxyval) << QString("%1").arg(enable);
467 if(enable != 0)
468 return QUrl("http://" + QString::fromWCharArray(proxyval));
469 else
470 return QUrl("");
471#elif defined(Q_OS_MACX)
472
473 CFDictionaryRef dictref;
474 CFStringRef stringref;
475 CFNumberRef numberref;
476 int enable = 0;
477 int port = 0;
478 unsigned int bufsize = 0;
479 char *buf;
480 QUrl proxy;
481
482 dictref = SCDynamicStoreCopyProxies(NULL);
483 if(dictref == NULL)
484 return proxy;
485 numberref = (CFNumberRef)CFDictionaryGetValue(dictref, kSCPropNetProxiesHTTPEnable);
486 if(numberref != NULL)
487 CFNumberGetValue(numberref, kCFNumberIntType, &enable);
488 if(enable == 1) {
489 // get proxy string
490 stringref = (CFStringRef)CFDictionaryGetValue(dictref, kSCPropNetProxiesHTTPProxy);
491 if(stringref != NULL) {
492 // get number of characters. CFStringGetLength uses UTF-16 code pairs
493 bufsize = CFStringGetLength(stringref) * 2 + 1;
494 buf = (char*)malloc(sizeof(char) * bufsize);
495 if(buf == NULL) {
496 LOG_ERROR() << "can't allocate memory for proxy string!";
497 CFRelease(dictref);
498 return QUrl("");
499 }
500 CFStringGetCString(stringref, buf, bufsize, kCFStringEncodingUTF16);
501 numberref = (CFNumberRef)CFDictionaryGetValue(dictref, kSCPropNetProxiesHTTPPort);
502 if(numberref != NULL)
503 CFNumberGetValue(numberref, kCFNumberIntType, &port);
504 proxy.setScheme("http");
505 proxy.setHost(QString::fromUtf16((unsigned short*)buf));
506 proxy.setPort(port);
507
508 free(buf);
509 }
510 }
511 CFRelease(dictref);
512
513 return proxy;
514#else
515 return QUrl("");
516#endif
517}
518
519
diff --git a/utils/rbutilqt/base/system.h b/utils/rbutilqt/base/system.h
new file mode 100644
index 0000000000..61f0a6f1a9
--- /dev/null
+++ b/utils/rbutilqt/base/system.h
@@ -0,0 +1,53 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21
22#ifndef SYSTEM_H
23#define SYSTEM_H
24
25#include <QtCore/QObject>
26
27#include <inttypes.h>
28
29#include <QMultiMap>
30#include <QString>
31#include <QUrl>
32
33class System : public QObject
34{
35public:
36 System() {}
37
38#if defined(Q_OS_WIN32)
39 enum userlevel { ERR, GUEST, USER, ADMIN };
40 static enum userlevel userPermissions(void);
41 static QString userPermissionsString(void);
42#endif
43
44 static QString userName(void);
45 static QString osVersionString(void);
46 static QList<uint32_t> listUsbIds(void);
47 static QMultiMap<uint32_t, QString> listUsbDevices(void);
48
49 static QUrl systemProxy(void);
50
51};
52#endif
53
diff --git a/utils/rbutilqt/base/talkfile.cpp b/utils/rbutilqt/base/talkfile.cpp
new file mode 100644
index 0000000000..cef53c852b
--- /dev/null
+++ b/utils/rbutilqt/base/talkfile.cpp
@@ -0,0 +1,300 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include "talkfile.h"
20#include "rbsettings.h"
21#include "Logger.h"
22
23TalkFileCreator::TalkFileCreator(QObject* parent): QObject(parent)
24{
25
26}
27
28//! \brief Creates Talkfiles.
29//!
30//! \param logger A pointer to a Loggerobject
31bool TalkFileCreator::createTalkFiles()
32{
33 m_abort = false;
34 QString errStr;
35
36 emit logItem(tr("Starting Talk file generation for folder %1")
37 .arg(m_dir), LOGINFO);
38 emit logProgress(0,0);
39 QCoreApplication::processEvents();
40
41 // read in Maps of paths - file/dirnames
42 emit logItem(tr("Reading Filelist..."),LOGINFO);
43 if(createTalkList(m_mountpoint + "/" + m_dir) == false)
44 {
45 emit logItem(tr("Talk file creation aborted"),LOGERROR);
46 doAbort();
47 return false;
48 }
49 QCoreApplication::processEvents();
50
51 // generate entries
52 TalkGenerator generator(this);
53 // no string corrections yet: do not set language for TalkGenerator.
54 connect(&generator, &TalkGenerator::done, this, &TalkFileCreator::done);
55 connect(&generator, &TalkGenerator::logItem, this, &TalkFileCreator::logItem);
56 connect(&generator, &TalkGenerator::logProgress, this, &TalkFileCreator::logProgress);
57 connect(this, &TalkFileCreator::aborted, &generator, &TalkGenerator::abort);
58
59 if(generator.process(&m_talkList) == TalkGenerator::eERROR)
60 {
61 doAbort();
62 return false;
63 }
64
65 // Copying talk files
66 emit logItem(tr("Copying Talkfiles..."),LOGINFO);
67 if(copyTalkFiles(&errStr) == false)
68 {
69 emit logItem(errStr,LOGERROR);
70 doAbort();
71 return false;
72 }
73
74 // Deleting left overs
75 if( !cleanup())
76 return false;
77
78 emit logItem(tr("Finished creating Talk files"),LOGOK);
79 emit logProgress(1,1);
80 emit done(false);
81
82 return true;
83}
84
85//! \brief Strips everything after and including the last dot in a string. If there is no dot, nothing is changed
86//!
87//! \param filename The filename from which to strip the Extension
88//! \returns the modified string
89QString TalkFileCreator::stripExtension(QString filename)
90{
91 // only strip extension if there is a dot in the filename and there are chars before the dot
92 if(filename.lastIndexOf(".") != -1 && filename.left(filename.lastIndexOf(".")) != "")
93 return filename.left(filename.lastIndexOf("."));
94 else
95 return filename;
96}
97
98//! \brief Does needed Tasks when we need to abort. Cleans up Files. Stops the Logger, Stops TTS and Encoder
99//!
100void TalkFileCreator::doAbort()
101{
102 cleanup();
103 emit logProgress(0,1);
104 emit done(true);
105}
106//! \brief creates a list of what to generate
107//!
108//! \param startDir The directory from which to start scanning
109bool TalkFileCreator::createTalkList(QDir startDir)
110{
111 LOG_INFO() << "generating list of files" << startDir;
112 m_talkList.clear();
113
114 // create Iterator
115 QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags;
116 if(m_recursive)
117 flags = QDirIterator::Subdirectories;
118
119 QDirIterator it(startDir,flags);
120
121 //create temp directory
122 QDir tempDir(QDir::tempPath()+ "/talkfiles/");
123 if(!tempDir.exists())
124 tempDir.mkpath(QDir::tempPath()+ "/talkfiles/");
125
126 // read in Maps of paths - file/dirnames
127 while (it.hasNext())
128 {
129 it.next();
130 if(m_abort)
131 {
132 return false;
133 }
134
135 QFileInfo fileInf = it.fileInfo();
136
137 // its a dir
138 if(fileInf.isDir())
139 {
140 QDir dir = fileInf.dir();
141
142 // insert into List
143 if(!dir.dirName().isEmpty() && m_talkFolders)
144 {
145 // check if we should ignore it
146 if(m_generateOnlyNew && QFileInfo::exists(dir.path() + "/_dirname.talk"))
147 {
148 continue;
149 }
150
151 //generate entry
152 TalkGenerator::TalkEntry entry;
153 entry.toSpeak = dir.dirName();
154 entry.wavfilename = QDir::tempPath() + "/talkfiles/"
155 + QCryptographicHash::hash(entry.toSpeak.toUtf8(),
156 QCryptographicHash::Md5).toHex() + ".wav";
157 entry.talkfilename = QDir::tempPath() + "/talkfiles/"
158 + QCryptographicHash::hash(entry.toSpeak.toUtf8(),
159 QCryptographicHash::Md5).toHex() + ".talk";
160 entry.target = dir.path() + "/_dirname.talk";
161 entry.voiced = false;
162 entry.encoded = false;
163 LOG_INFO() << "toSpeak:" << entry.toSpeak
164 << "target:" << entry.target
165 << "intermediates:" << entry.wavfilename << entry.talkfilename;
166 m_talkList.append(entry);
167 }
168 }
169 else // its a File
170 {
171 // insert into List
172 if( !fileInf.fileName().isEmpty() && !fileInf.fileName().endsWith(".talk") && m_talkFiles)
173 {
174 //test if we should ignore this file
175 bool match = false;
176 for(int i=0; i < m_ignoreFiles.size();i++)
177 {
178 QRegExp rx(m_ignoreFiles[i].trimmed());
179 rx.setPatternSyntax(QRegExp::Wildcard);
180 if(rx.exactMatch(fileInf.fileName()))
181 match = true;
182 }
183 if(match)
184 continue;
185
186 // check if we should ignore it
187 if(m_generateOnlyNew && QFileInfo::exists(fileInf.path() + "/" + fileInf.fileName() + ".talk"))
188 {
189 continue;
190 }
191
192 //generate entry
193 TalkGenerator::TalkEntry entry;
194 if(m_stripExtensions)
195 entry.toSpeak = stripExtension(fileInf.fileName());
196 else
197 entry.toSpeak = fileInf.fileName();
198 entry.wavfilename = QDir::tempPath() + "/talkfiles/"
199 + QCryptographicHash::hash(entry.toSpeak.toUtf8(),
200 QCryptographicHash::Md5).toHex() + ".wav";
201 entry.talkfilename = QDir::tempPath() + "/talkfiles/"
202 + QCryptographicHash::hash(entry.toSpeak.toUtf8(),
203 QCryptographicHash::Md5).toHex() + ".talk";
204 entry.target = fileInf.path() + "/" + fileInf.fileName() + ".talk";
205 entry.voiced = false;
206 entry.encoded = false;
207 LOG_INFO() << "toSpeak:" << entry.toSpeak
208 << "target:" << entry.target
209 << "intermediates:"
210 << entry.wavfilename << entry.talkfilename;
211 m_talkList.append(entry);
212 }
213 }
214 QCoreApplication::processEvents();
215 }
216 LOG_INFO() << "list created, entries:" << m_talkList.size();
217 return true;
218}
219
220
221//! \brief copys Talkfiles from the temp dir to the target. Progress and installlog is handled inside
222//!
223//! \param errString Pointer to a QString where the error cause is written.
224//! \returns true on success, false on error or user abort
225bool TalkFileCreator::copyTalkFiles(QString* errString)
226{
227 int progressMax = m_talkList.size();
228 int m_progress = 0;
229 emit logProgress(m_progress,progressMax);
230
231 QSettings installlog(m_mountpoint + "/.rockbox/rbutil.log", QSettings::IniFormat, nullptr);
232 installlog.beginGroup("talkfiles");
233
234 for(int i=0; i < m_talkList.size(); i++)
235 {
236 if(m_abort)
237 {
238 *errString = tr("File copy aborted");
239 return false;
240 }
241
242 // skip not encoded files
243 if(m_talkList[i].encoded == false)
244 {
245 emit logProgress(++m_progress,progressMax);
246 continue; // this file was skipped in one of the previous steps
247 }
248 // remove target if it exists, and if we should overwrite it
249 if(QFile::exists(m_talkList[i].target))
250 QFile::remove(m_talkList[i].target);
251
252 // copying
253 LOG_INFO() << "copying" << m_talkList[i].talkfilename
254 << "to" << m_talkList[i].target;
255 if(!QFile::copy(m_talkList[i].talkfilename,m_talkList[i].target))
256 {
257 *errString = tr("Copying of %1 to %2 failed").arg(m_talkList[i].talkfilename).arg(m_talkList[i].target);
258 return false;
259 }
260
261 // add to installlog
262 QString now = QDate::currentDate().toString("yyyyMMdd");
263 installlog.setValue(m_talkList[i].target.remove(0,m_mountpoint.length()),now);
264
265 emit logProgress(++m_progress,progressMax);
266 QCoreApplication::processEvents();
267 }
268 installlog.endGroup();
269 installlog.sync();
270 return true;
271}
272
273
274//! \brief Cleans up Files potentially left in the temp dir
275//!
276bool TalkFileCreator::cleanup()
277{
278 emit logItem(tr("Cleaning up..."),LOGINFO);
279
280 for(int i=0; i < m_talkList.size(); i++)
281 {
282 if(QFile::exists(m_talkList[i].wavfilename))
283 QFile::remove(m_talkList[i].wavfilename);
284 if(QFile::exists(m_talkList[i].talkfilename))
285 QFile::remove(m_talkList[i].talkfilename);
286
287 QCoreApplication::processEvents();
288 }
289 emit logItem(tr("Finished"),LOGINFO);
290 return true;
291}
292
293//! \brief slot, which is connected to the abort of the Logger. Sets a flag, so Creating Talkfiles ends at the next possible position
294//!
295void TalkFileCreator::abort()
296{
297 m_abort = true;
298 emit aborted();
299}
300
diff --git a/utils/rbutilqt/base/talkfile.h b/utils/rbutilqt/base/talkfile.h
new file mode 100644
index 0000000000..11dd95ca14
--- /dev/null
+++ b/utils/rbutilqt/base/talkfile.h
@@ -0,0 +1,83 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21
22#ifndef TALKFILE_H
23#define TALKFILE_H
24
25#include <QtCore>
26#include "progressloggerinterface.h"
27
28#include "talkgenerator.h"
29
30class TalkFileCreator :public QObject
31{
32 Q_OBJECT
33
34public:
35 TalkFileCreator(QObject* parent);
36
37 bool createTalkFiles();
38
39 void setDir(QString dir) {m_dir = dir;}
40 void setMountPoint(QString mountpoint) {m_mountpoint = mountpoint;}
41
42 void setGenerateOnlyNew(bool ov) {m_generateOnlyNew = ov;}
43 void setRecursive(bool ov) {m_recursive = ov;}
44 void setStripExtensions(bool ov) {m_stripExtensions = ov;}
45 void setTalkFolders(bool ov) {m_talkFolders = ov;}
46 void setTalkFiles(bool ov) {m_talkFiles = ov;}
47 void setIgnoreFiles(QStringList wildcards) {m_ignoreFiles = wildcards;}
48public slots:
49 void abort();
50
51signals:
52 void done(bool);
53 void aborted();
54 void logItem(QString, int); //! set logger item
55 void logProgress(int, int); //! set progress bar.
56
57private:
58 bool cleanup();
59 QString stripExtension(QString filename);
60 void doAbort();
61 void resetProgress(int max);
62 bool copyTalkFiles(QString* errString);
63
64 bool createTalkList(QDir startDir);
65
66 QString m_dir;
67 QString m_mountpoint;
68
69 bool m_generateOnlyNew;
70 bool m_recursive;
71 bool m_stripExtensions;
72 bool m_talkFolders;
73 bool m_talkFiles;
74 QStringList m_ignoreFiles;
75
76 bool m_abort;
77
78 QList<TalkGenerator::TalkEntry> m_talkList;
79};
80
81
82#endif
83
diff --git a/utils/rbutilqt/base/talkgenerator.cpp b/utils/rbutilqt/base/talkgenerator.cpp
new file mode 100644
index 0000000000..9139ceb274
--- /dev/null
+++ b/utils/rbutilqt/base/talkgenerator.cpp
@@ -0,0 +1,337 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include "talkgenerator.h"
20#include "rbsettings.h"
21#include "playerbuildinfo.h"
22#include "wavtrim.h"
23#include "Logger.h"
24
25TalkGenerator::TalkGenerator(QObject* parent): QObject(parent)
26{
27
28}
29
30//! \brief Creates Talkfiles.
31//!
32TalkGenerator::Status TalkGenerator::process(QList<TalkEntry>* list,int wavtrimth)
33{
34 m_abort = false;
35 QString errStr;
36 bool warnings = false;
37
38 //tts
39 emit logItem(tr("Starting TTS Engine"), LOGINFO);
40 m_tts = TTSBase::getTTS(this, RbSettings::value(RbSettings::Tts).toString());
41 if(!m_tts)
42 {
43 LOG_ERROR() << "getting the TTS object failed!";
44 emit logItem(tr("Init of TTS engine failed"), LOGERROR);
45 emit done(true);
46 return eERROR;
47 }
48 if(!m_tts->start(&errStr))
49 {
50 emit logItem(errStr.trimmed(),LOGERROR);
51 emit logItem(tr("Init of TTS engine failed"), LOGERROR);
52 emit done(true);
53 return eERROR;
54 }
55 QCoreApplication::processEvents();
56
57 // Encoder
58 emit logItem(tr("Starting Encoder Engine"),LOGINFO);
59 m_enc = EncoderBase::getEncoder(this, PlayerBuildInfo::instance()->value(
60 PlayerBuildInfo::Encoder).toString());
61 if(!m_enc->start())
62 {
63 emit logItem(tr("Init of Encoder engine failed"),LOGERROR);
64 emit done(true);
65 m_tts->stop();
66 return eERROR;
67 }
68 QCoreApplication::processEvents();
69
70 emit logProgress(0,0);
71
72 // Voice entries
73 emit logItem(tr("Voicing entries..."),LOGINFO);
74 Status voiceStatus= voiceList(list,wavtrimth);
75 if(voiceStatus == eERROR)
76 {
77 m_tts->stop();
78 m_enc->stop();
79 emit done(true);
80 return eERROR;
81 }
82 else if( voiceStatus == eWARNING)
83 warnings = true;
84
85 QCoreApplication::processEvents();
86
87 // Encoding Entries
88 emit logItem(tr("Encoding files..."),LOGINFO);
89 Status encoderStatus = encodeList(list);
90 if( encoderStatus == eERROR)
91 {
92 m_tts->stop();
93 m_enc->stop();
94 emit done(true);
95 return eERROR;
96 }
97 else if( voiceStatus == eWARNING)
98 warnings = true;
99
100 QCoreApplication::processEvents();
101
102 m_tts->stop();
103 m_enc->stop();
104 emit logProgress(1,1);
105
106 if(warnings)
107 return eWARNING;
108 return eOK;
109}
110
111//! \brief Voices a List of string
112//!
113TalkGenerator::Status TalkGenerator::voiceList(QList<TalkEntry>* list,int wavtrimth)
114{
115 int progressMax = list->size();
116 int m_progress = 0;
117 emit logProgress(m_progress,progressMax);
118
119 QStringList errors;
120 QStringList duplicates;
121
122 bool warnings = false;
123 for(int i=0; i < list->size(); i++)
124 {
125 if(m_abort)
126 {
127 emit logItem(tr("Voicing aborted"), LOGERROR);
128 return eERROR;
129 }
130
131 // skip duplicated wav entrys
132 if(!duplicates.contains(list->at(i).wavfilename))
133 duplicates.append(list->at(i).wavfilename);
134 else
135 {
136 LOG_INFO() << "duplicate skipped";
137 (*list)[i].voiced = true;
138 emit logProgress(++m_progress,progressMax);
139 continue;
140 }
141
142 // skip already voiced entrys
143 if(list->at(i).voiced == true)
144 {
145 emit logProgress(++m_progress,progressMax);
146 continue;
147 }
148 // skip entry whith empty text
149 if(list->at(i).toSpeak == "")
150 {
151 emit logProgress(++m_progress,progressMax);
152 continue;
153 }
154
155 // voice entry
156 QString error;
157 LOG_INFO() << "voicing: " << list->at(i).toSpeak
158 << "to" << list->at(i).wavfilename;
159 TTSStatus status = m_tts->voice(list->at(i).toSpeak,
160 list->at(i).wavfilename, &error);
161 if(status == Warning)
162 {
163 warnings = true;
164 emit logItem(tr("Voicing of %1 failed: %2").arg(list->at(i).toSpeak).arg(error),
165 LOGWARNING);
166 }
167 else if (status == FatalError)
168 {
169 emit logItem(tr("Voicing of %1 failed: %2").arg(list->at(i).toSpeak).arg(error),
170 LOGERROR);
171 return eERROR;
172 }
173 else
174 (*list)[i].voiced = true;
175
176 // wavtrim if needed
177 if(wavtrimth != -1)
178 {
179 char buffer[255];
180 if(wavtrim(list->at(i).wavfilename.toLocal8Bit().data(),
181 wavtrimth, buffer, 255))
182 {
183 LOG_ERROR() << "wavtrim returned error on"
184 << list->at(i).wavfilename;
185 return eERROR;
186 }
187 }
188
189 emit logProgress(++m_progress,progressMax);
190 QCoreApplication::processEvents();
191 }
192 if(warnings)
193 return eWARNING;
194 else
195 return eOK;
196}
197
198
199//! \brief Encodes a List of strings
200//!
201TalkGenerator::Status TalkGenerator::encodeList(QList<TalkEntry>* list)
202{
203 QStringList duplicates;
204
205 int progressMax = list->size();
206 int m_progress = 0;
207 emit logProgress(m_progress,progressMax);
208
209 for(int i=0; i < list->size(); i++)
210 {
211 if(m_abort)
212 {
213 emit logItem(tr("Encoding aborted"), LOGERROR);
214 return eERROR;
215 }
216
217 //skip non-voiced entrys
218 if(list->at(i).voiced == false)
219 {
220 LOG_WARNING() << "non voiced entry detected:"
221 << list->at(i).toSpeak;
222 emit logProgress(++m_progress,progressMax);
223 continue;
224 }
225 //skip duplicates
226 if(!duplicates.contains(list->at(i).talkfilename))
227 duplicates.append(list->at(i).talkfilename);
228 else
229 {
230 LOG_INFO() << "duplicate skipped";
231 (*list)[i].encoded = true;
232 emit logProgress(++m_progress,progressMax);
233 continue;
234 }
235
236 //encode entry
237 LOG_INFO() << "encoding " << list->at(i).wavfilename
238 << "to" << list->at(i).talkfilename;
239 if(!m_enc->encode(list->at(i).wavfilename,list->at(i).talkfilename))
240 {
241 emit logItem(tr("Encoding of %1 failed").arg(
242 QFileInfo(list->at(i).wavfilename).baseName()), LOGERROR);
243 return eERROR;
244 }
245 (*list)[i].encoded = true;
246 emit logProgress(++m_progress,progressMax);
247 QCoreApplication::processEvents();
248 }
249 return eOK;
250}
251
252//! \brief slot, which is connected to the abort of the Logger.
253//Sets a flag, so Creating Talkfiles ends at the next possible position
254//!
255void TalkGenerator::abort()
256{
257 m_abort = true;
258}
259
260QString TalkGenerator::correctString(QString s)
261{
262 QString corrected = s;
263 int i = 0;
264 int max = m_corrections.size();
265 while(i < max) {
266 corrected = corrected.replace(QRegExp(m_corrections.at(i).search,
267 m_corrections.at(i).modifier.contains("i")
268 ? Qt::CaseInsensitive : Qt::CaseSensitive),
269 m_corrections.at(i).replace);
270 i++;
271 }
272
273 if(corrected != s)
274 LOG_INFO() << "corrected string" << s << "to" << corrected;
275
276 return corrected;
277 m_abort = true;
278}
279
280void TalkGenerator::setLang(QString name)
281{
282 m_lang = name;
283
284 // re-initialize corrections list
285 m_corrections.clear();
286 QFile correctionsFile(":/builtin/voice-corrections.txt");
287 correctionsFile.open(QIODevice::ReadOnly);
288
289 QString engine = RbSettings::value(RbSettings::Tts).toString();
290 TTSBase* tts = TTSBase::getTTS(this,RbSettings::value(RbSettings::Tts).toString());
291 if(!tts)
292 {
293 LOG_ERROR() << "getting the TTS object failed!";
294 return;
295 }
296 QString vendor = tts->voiceVendor();
297 delete tts;
298
299 if(m_lang.isEmpty())
300 m_lang = "english";
301 LOG_INFO() << "building string corrections list for"
302 << m_lang << engine << vendor;
303 QTextStream stream(&correctionsFile);
304 while(!stream.atEnd()) {
305 QString line = stream.readLine();
306 if(line.startsWith(" ") || line.length() < 10)
307 continue;
308 // separator is first character
309 QString separator = line.at(0);
310 line.remove(0, 1);
311 QStringList items = line.split(separator);
312 // we need to have at least 6 separate entries.
313 if(items.size() < 6)
314 continue;
315
316 QRegExp re_lang(items.at(0));
317 QRegExp re_engine(items.at(1));
318 QRegExp re_vendor(items.at(2));
319 if(!re_lang.exactMatch(m_lang)) {
320 continue;
321 }
322 if(!re_vendor.exactMatch(vendor)) {
323 continue;
324 }
325 if(!re_engine.exactMatch(engine)) {
326 continue;
327 }
328 struct CorrectionItems co;
329 co.search = items.at(3);
330 co.replace = items.at(4);
331 // Qt uses backslash for back references, Perl uses dollar sign.
332 co.replace.replace(QRegExp("\\$(\\d+)"), "\\\\1");
333 co.modifier = items.at(5);
334 m_corrections.append(co);
335 }
336 correctionsFile.close();
337}
diff --git a/utils/rbutilqt/base/talkgenerator.h b/utils/rbutilqt/base/talkgenerator.h
new file mode 100644
index 0000000000..3e2f9394fb
--- /dev/null
+++ b/utils/rbutilqt/base/talkgenerator.h
@@ -0,0 +1,91 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21
22#ifndef TALKGENERATOR_H
23#define TALKGENERATOR_H
24
25#include <QtCore>
26#include "progressloggerinterface.h"
27
28#include "encoderbase.h"
29#include "ttsbase.h"
30
31//! \brief Talk generator, generates .wav and .talk files out of a list.
32class TalkGenerator :public QObject
33{
34 Q_OBJECT
35public:
36 enum Status
37 {
38 eOK,
39 eWARNING,
40 eERROR
41 };
42
43 struct TalkEntry
44 {
45 QString toSpeak;
46 QString wavfilename;
47 QString talkfilename;
48 QString target;
49 bool voiced;
50 bool encoded;
51 };
52
53 TalkGenerator(QObject* parent);
54
55 Status process(QList<TalkEntry>* list,int wavtrimth = -1);
56 QString correctString(QString s);
57
58public slots:
59 void abort();
60 void setLang(QString name);
61
62signals:
63 void done(bool);
64 void logItem(QString, int); //! set logger item
65 void logProgress(int, int); //! set progress bar.
66
67private:
68 Status voiceList(QList<TalkEntry>* list,int wavetrimth);
69 Status encodeList(QList<TalkEntry>* list);
70
71 TTSBase* m_tts;
72 EncoderBase* m_enc;
73
74 QString m_lang;
75
76 struct CorrectionItems
77 {
78 QString search;
79 QString replace;
80 QString modifier;
81 };
82 QList<struct CorrectionItems> m_corrections;
83
84 bool m_abort;
85
86
87};
88
89
90#endif
91
diff --git a/utils/rbutilqt/base/ttsbase.cpp b/utils/rbutilqt/base/ttsbase.cpp
new file mode 100644
index 0000000000..0102d215a6
--- /dev/null
+++ b/utils/rbutilqt/base/ttsbase.cpp
@@ -0,0 +1,122 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "ttsbase.h"
21
22#include "ttsfestival.h"
23#include "ttssapi.h"
24#include "ttssapi4.h"
25#include "ttsmssp.h"
26#include "ttsexes.h"
27#include "ttsespeak.h"
28#include "ttsespeakng.h"
29#include "ttsflite.h"
30#include "ttsmimic.h"
31#include "ttsswift.h"
32#if defined(Q_OS_MACX)
33#include "ttscarbon.h"
34#endif
35
36// list of tts names and identifiers
37QMap<QString,QString> TTSBase::ttsList;
38
39TTSBase::TTSBase(QObject* parent): EncTtsSettingInterface(parent)
40{
41}
42
43// static functions
44void TTSBase::initTTSList()
45{
46#if !defined(Q_OS_WIN)
47 ttsList["espeak"] = tr("Espeak TTS Engine");
48 ttsList["espeakng"] = tr("Espeak-ng TTS Engine");
49 ttsList["mimic"] = tr("Mimic TTS Engine");
50#endif
51 ttsList["flite"] = tr("Flite TTS Engine");
52 ttsList["swift"] = tr("Swift TTS Engine");
53#if defined(Q_OS_WIN)
54#if 0 /* SAPI4 has been disabled since long. Keep support for now. */
55 ttsList["sapi4"] = tr("SAPI4 TTS Engine");
56#endif
57 ttsList["sapi"] = tr("SAPI5 TTS Engine");
58 ttsList["mssp"] = tr("MS Speech Platform");
59#endif
60#if defined(Q_OS_LINUX)
61 ttsList["festival"] = tr("Festival TTS Engine");
62#endif
63#if defined(Q_OS_MACX)
64 ttsList["carbon"] = tr("OS X System Engine");
65#endif
66}
67
68// function to get a specific encoder
69TTSBase* TTSBase::getTTS(QObject* parent,QString ttsName)
70{
71
72 TTSBase* tts = nullptr;
73#if defined(Q_OS_WIN)
74 if(ttsName == "sapi")
75 tts = new TTSSapi(parent);
76 else if (ttsName == "sapi4")
77 tts = new TTSSapi4(parent);
78 else if (ttsName == "mssp")
79 tts = new TTSMssp(parent);
80 else
81#elif defined(Q_OS_LINUX)
82 if (ttsName == "festival")
83 tts = new TTSFestival(parent);
84 else
85#elif defined(Q_OS_MACX)
86 if(ttsName == "carbon")
87 tts = new TTSCarbon(parent);
88 else
89#endif
90 if(ttsName == "espeak")
91 tts = new TTSEspeak(parent);
92 else if(ttsName == "espeakng")
93 tts = new TTSEspeakNG(parent);
94 else if(ttsName == "mimic")
95 tts = new TTSMimic(parent);
96 else if(ttsName == "flite")
97 tts = new TTSFlite(parent);
98 else if(ttsName == "swift")
99 tts = new TTSSwift(parent);
100 else if(ttsName == "user")
101 tts = new TTSExes(parent);
102
103 return tts;
104}
105
106// get the list of encoders, nice names
107QStringList TTSBase::getTTSList()
108{
109 // init list if its empty
110 if(ttsList.count() == 0)
111 initTTSList();
112
113 return ttsList.keys();
114}
115
116// get nice name of a specific tts
117QString TTSBase::getTTSName(QString tts)
118{
119 if(ttsList.isEmpty())
120 initTTSList();
121 return ttsList.value(tts);
122}
diff --git a/utils/rbutilqt/base/ttsbase.h b/utils/rbutilqt/base/ttsbase.h
new file mode 100644
index 0000000000..125fe49ff4
--- /dev/null
+++ b/utils/rbutilqt/base/ttsbase.h
@@ -0,0 +1,70 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21
22#ifndef TTSBASE_H
23#define TTSBASE_H
24
25#include <QtCore>
26
27#include "encttssettings.h"
28
29enum TTSStatus{ FatalError, NoError, Warning };
30class TTSBase : public EncTtsSettingInterface
31{
32 Q_OBJECT
33 public:
34 enum Capability { None = 0, RunInParallel = 1, CanSpeak = 2 };
35 Q_DECLARE_FLAGS(Capabilities, Capability)
36
37 TTSBase(QObject *parent);
38 //! Child class should generate a clip
39 virtual TTSStatus voice(QString text,QString wavfile, QString* errStr) =0;
40 //! Child class should do startup
41 virtual bool start(QString *errStr) =0;
42 //! child class should stop
43 virtual bool stop() =0;
44
45 virtual QString voiceVendor(void) = 0;
46 // configuration
47 //! Child class should return true, when configuration is good
48 virtual bool configOk()=0;
49 //! Child class should generate and insertSetting(..) its settings
50 virtual void generateSettings() = 0;
51 //! Chlid class should commit the Settings to permanent storage
52 virtual void saveSettings() = 0;
53
54 virtual Capabilities capabilities() = 0;
55
56 // static functions
57 static TTSBase* getTTS(QObject* parent,QString ttsname);
58 static QStringList getTTSList();
59 static QString getTTSName(QString tts);
60
61 private:
62 //inits the tts List
63 static void initTTSList();
64
65 protected:
66 static QMap<QString,QString> ttsList;
67};
68Q_DECLARE_OPERATORS_FOR_FLAGS(TTSBase::Capabilities)
69
70#endif
diff --git a/utils/rbutilqt/base/ttscarbon.cpp b/utils/rbutilqt/base/ttscarbon.cpp
new file mode 100644
index 0000000000..2d9fa49dbe
--- /dev/null
+++ b/utils/rbutilqt/base/ttscarbon.cpp
@@ -0,0 +1,443 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2010 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "ttsbase.h"
21#include "ttscarbon.h"
22#include "encttssettings.h"
23#include "rbsettings.h"
24
25#include <CoreFoundation/CoreFoundation.h>
26#include <ApplicationServices/ApplicationServices.h>
27#include <Carbon/Carbon.h>
28#include <unistd.h>
29#include <sys/stat.h>
30#include <inttypes.h>
31#include "Logger.h"
32
33TTSCarbon::TTSCarbon(QObject* parent) : TTSBase(parent)
34{
35}
36
37TTSBase::Capabilities TTSCarbon::capabilities()
38{
39 return TTSBase::CanSpeak;
40}
41
42bool TTSCarbon::configOk()
43{
44 return true;
45}
46
47
48bool TTSCarbon::start(QString *errStr)
49{
50 (void)errStr;
51 VoiceSpec vspec;
52 VoiceSpec* vspecref = NULL;
53 VoiceDescription vdesc;
54 OSErr error;
55 QString selectedVoice
56 = RbSettings::subValue("carbon", RbSettings::TtsVoice).toString();
57 SInt16 numVoices;
58 SInt16 voiceIndex;
59 error = CountVoices(&numVoices);
60 for(voiceIndex = 1; voiceIndex < numVoices; ++voiceIndex) {
61 error = GetIndVoice(voiceIndex, &vspec);
62 error = GetVoiceDescription(&vspec, &vdesc, sizeof(vdesc));
63 // name is pascal string, i.e. the first byte is the length.
64 QString name = QString::fromLocal8Bit((const char*)&vdesc.name[1],
65 vdesc.name[0]);
66 if(name == selectedVoice) {
67 vspecref = &vspec;
68 if(vdesc.script != -1)
69 m_voiceScript = (CFStringBuiltInEncodings)vdesc.script;
70 else
71 m_voiceScript = (CFStringBuiltInEncodings)vdesc.reserved[0];
72 break;
73 }
74 }
75 if(voiceIndex == numVoices) {
76 // voice not found. Add user notification here and proceed with
77 // system default voice.
78 LOG_WARNING() << "Selected voice not found, using system default!";
79 GetVoiceDescription(&vspec, &vdesc, sizeof(vdesc));
80 if(vdesc.script != -1)
81 m_voiceScript = (CFStringBuiltInEncodings)vdesc.script;
82 else
83 m_voiceScript = (CFStringBuiltInEncodings)vdesc.reserved[0];
84 }
85
86 error = NewSpeechChannel(vspecref, &m_channel);
87 //SetSpeechInfo(channel, soSpeechDoneCallBack, speechDone);
88 Fixed rate = (Fixed)(0x10000 * RbSettings::subValue("carbon",
89 RbSettings::TtsSpeed).toInt());
90 if(rate != 0)
91 SetSpeechRate(m_channel, rate);
92
93 Fixed pitch = (Fixed)(0x10000 * RbSettings::subValue("carbon",
94 RbSettings::TtsPitch).toInt());
95 if(pitch != 0)
96 SetSpeechPitch(m_channel, pitch);
97
98 return (error == 0) ? true : false;
99}
100
101
102bool TTSCarbon::stop(void)
103{
104 DisposeSpeechChannel(m_channel);
105 return true;
106}
107
108
109void TTSCarbon::generateSettings(void)
110{
111 QStringList voiceNames;
112 QString systemVoice;
113 SInt16 numVoices;
114 OSErr error;
115 VoiceSpec vspec;
116 VoiceDescription vdesc;
117
118 // get system voice
119 error = GetVoiceDescription(NULL, &vdesc, sizeof(vdesc));
120 systemVoice
121 = QString::fromLocal8Bit((const char*)&vdesc.name[1], vdesc.name[0]);
122 // get list of all voices
123 CountVoices(&numVoices);
124 for(SInt16 i = 1; i < numVoices; ++i) {
125 error = GetIndVoice(i, &vspec);
126 error = GetVoiceDescription(&vspec, &vdesc, sizeof(vdesc));
127 // name is pascal string, i.e. the first byte is the length.
128 QString name
129 = QString::fromLocal8Bit((const char*)&vdesc.name[1], vdesc.name[0]);
130 voiceNames.append(name.trimmed());
131 }
132 // voice
133 EncTtsSetting* setting;
134 QString voice
135 = RbSettings::subValue("carbon", RbSettings::TtsVoice).toString();
136 if(voice.isEmpty())
137 voice = systemVoice;
138 setting = new EncTtsSetting(this, EncTtsSetting::eSTRINGLIST,
139 tr("Voice:"), voice, voiceNames, EncTtsSetting::eNOBTN);
140 insertSetting(ConfigVoice, setting);
141
142 // speed
143 int speed = RbSettings::subValue("carbon", RbSettings::TtsSpeed).toInt();
144 setting = new EncTtsSetting(this, EncTtsSetting::eINT,
145 tr("Speed (words/min):"), speed, 80, 500,
146 EncTtsSetting::eNOBTN);
147 insertSetting(ConfigSpeed, setting);
148
149 // pitch
150 int pitch = RbSettings::subValue("carbon", RbSettings::TtsPitch).toInt();
151 setting = new EncTtsSetting(this, EncTtsSetting::eINT,
152 tr("Pitch (0 for default):"), pitch, 0, 65,
153 EncTtsSetting::eNOBTN);
154 insertSetting(ConfigPitch, setting);
155
156}
157
158
159void TTSCarbon::saveSettings(void)
160{
161 // save settings in user config
162 RbSettings::setSubValue("carbon", RbSettings::TtsVoice,
163 getSetting(ConfigVoice)->current().toString());
164 RbSettings::setSubValue("carbon", RbSettings::TtsSpeed,
165 getSetting(ConfigSpeed)->current().toInt());
166 RbSettings::setSubValue("carbon", RbSettings::TtsPitch,
167 getSetting(ConfigPitch)->current().toInt());
168 RbSettings::sync();
169}
170
171
172/** @brief create wav file from text using the selected TTS voice.
173 */
174TTSStatus TTSCarbon::voice(QString text, QString wavfile, QString* errStr)
175{
176 TTSStatus status = NoError;
177 OSErr error;
178
179 char* tmpfile = NULL;
180 if(!wavfile.isEmpty()) {
181 QString aifffile = wavfile + ".aiff";
182 // FIXME: find out why we need to do this.
183 // Create a local copy of the temporary file filename.
184 // Not doing so causes weird issues (path contains trailing spaces)
185 unsigned int len = aifffile.size() + 1;
186 tmpfile = (char*)malloc(len * sizeof(char));
187 strncpy(tmpfile, aifffile.toLocal8Bit().constData(), len);
188 CFStringRef tmpfileref = CFStringCreateWithCString(kCFAllocatorDefault,
189 tmpfile, kCFStringEncodingUTF8);
190 CFURLRef urlref = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
191 tmpfileref, kCFURLPOSIXPathStyle, false);
192 SetSpeechInfo(m_channel, soOutputToFileWithCFURL, urlref);
193 }
194
195 // speak it.
196 // Convert the string to the encoding requested by the voice. Do this
197 // via CFString, as this allows to directly use the destination encoding
198 // as CFString uses the same values as the voice.
199
200 // allocate enough space to allow storing the string in a 2 byte encoding
201 unsigned int textlen = 2 * text.length() + 1;
202 char* textbuf = (char*)calloc(textlen, sizeof(char));
203 char* utf8data = (char*)text.toUtf8().constData();
204 int utf8bytes = text.toUtf8().size();
205 CFStringRef cfstring = CFStringCreateWithBytes(kCFAllocatorDefault,
206 (UInt8*)utf8data, utf8bytes,
207 kCFStringEncodingUTF8, (Boolean)false);
208 CFIndex usedBuf = 0;
209 CFRange range;
210 range.location = 0; // character in string to start.
211 range.length = text.length(); // number of _characters_ in string
212 // FIXME: check if converting between encodings was lossless.
213 CFStringGetBytes(cfstring, range, m_voiceScript, ' ',
214 false, (UInt8*)textbuf, textlen, &usedBuf);
215
216 error = SpeakText(m_channel, textbuf, (unsigned long)usedBuf);
217 while(SpeechBusy()) {
218 // FIXME: add small delay here to make calls less frequent
219 QCoreApplication::processEvents();
220 }
221 if(error != 0) {
222 *errStr = tr("Could not voice string");
223 status = FatalError;
224 }
225 free(textbuf);
226 CFRelease(cfstring);
227
228 if(!wavfile.isEmpty()) {
229 // convert the temporary aiff file to wav
230 if(status == NoError
231 && convertAiffToWav(tmpfile, wavfile.toLocal8Bit().constData()) != 0) {
232 *errStr = tr("Could not convert intermediate file");
233 status = FatalError;
234 }
235 // remove temporary aiff file
236 unlink(tmpfile);
237 free(tmpfile);
238 }
239
240 return status;
241}
242
243
244unsigned long TTSCarbon::be2u32(unsigned char* buf)
245{
246 return (buf[0]&0xff)<<24 | (buf[1]&0xff)<<16 | (buf[2]&0xff)<<8 | (buf[3]&0xff);
247}
248
249
250unsigned long TTSCarbon::be2u16(unsigned char* buf)
251{
252 return (buf[1]&0xff) | (buf[0]&0xff)<<8;
253}
254
255
256unsigned char* TTSCarbon::u32tobuf(unsigned char* buf, uint32_t val)
257{
258 buf[0] = val & 0xff;
259 buf[1] = (val>> 8) & 0xff;
260 buf[2] = (val>>16) & 0xff;
261 buf[3] = (val>>24) & 0xff;
262 return buf;
263}
264
265
266unsigned char* TTSCarbon::u16tobuf(unsigned char* buf, uint16_t val)
267{
268 buf[0] = val & 0xff;
269 buf[1] = (val>> 8) & 0xff;
270 return buf;
271}
272
273
274/** @brief convert 80 bit extended ("long double") to int.
275 * This is simplified to handle the usual audio sample rates. Everything else
276 * might break. If the value isn't supported it will return 0.
277 * Conversion taken from Rockbox aiff codec.
278 */
279unsigned int TTSCarbon::extended2int(unsigned char* buf)
280{
281 unsigned int result = 0;
282 /* value negative? */
283 if(buf[0] & 0x80)
284 return 0;
285 /* check exponent. Int can handle up to 2^31. */
286 int exponent = buf[0] << 8 | buf[1];
287 if(exponent < 0x4000 || exponent > (0x4000 + 30))
288 return 0;
289 result = ((buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5]) + 1;
290 result >>= (16 + 14 - buf[1]);
291 return result;
292}
293
294
295/** @brief Convert aiff file to wav. Returns 0 on success.
296 */
297int TTSCarbon::convertAiffToWav(const char* aiff, const char* wav)
298{
299 struct commchunk {
300 unsigned long chunksize;
301 unsigned short channels;
302 unsigned long frames;
303 unsigned short size;
304 int rate;
305 };
306
307 struct ssndchunk {
308 unsigned long chunksize;
309 unsigned long offset;
310 unsigned long blocksize;
311 };
312
313 FILE* in;
314 FILE* out;
315 unsigned char obuf[4];
316 unsigned char* buf;
317 /* minimum file size for a valid aiff file is 46 bytes:
318 * - FORM chunk: 12 bytes
319 * - COMM chunk: 18 bytes
320 * - SSND chunk: 16 bytes (with no actual data)
321 */
322 struct stat filestat;
323 stat(aiff, &filestat);
324 if(filestat.st_size < 46)
325 return -1;
326 /* read input file into memory */
327 buf = (unsigned char*)malloc(filestat.st_size * sizeof(unsigned char));
328 if(!buf) /* error out if malloc() failed */
329 return -1;
330 in = fopen(aiff, "rb");
331 if(fread(buf, 1, filestat.st_size, in) < filestat.st_size) {
332 printf("could not read file: not enought bytes read\n");
333 fclose(in);
334 free(buf);
335 return -1;
336 }
337 fclose(in);
338
339 /* check input file format */
340 if(memcmp(buf, "FORM", 4) | memcmp(&buf[8], "AIFF", 4)) {
341 printf("No valid AIFF header found.\n");
342 free(buf);
343 return -1;
344 }
345 /* read COMM chunk */
346 unsigned char* commstart = &buf[12];
347 struct commchunk comm;
348 if(memcmp(commstart, "COMM", 4)) {
349 printf("COMM chunk not at beginning.\n");
350 free(buf);
351 return -1;
352 }
353 comm.chunksize = be2u32(&commstart[4]);
354 comm.channels = be2u16(&commstart[8]);
355 comm.frames = be2u32(&commstart[10]);
356 comm.size = be2u16(&commstart[14]);
357 comm.rate = extended2int(&commstart[16]);
358
359 /* find SSND as next chunk */
360 unsigned char* ssndstart = commstart + 8 + comm.chunksize;
361 while(memcmp(ssndstart, "SSND", 4) && ssndstart < (buf + filestat.st_size)) {
362 printf("Skipping chunk.\n");
363 ssndstart += be2u32(&ssndstart[4]) + 8;
364 }
365 if(ssndstart > (buf + filestat.st_size)) {
366 free(buf);
367 return -1;
368 }
369
370 struct ssndchunk ssnd;
371 ssnd.chunksize = be2u32(&ssndstart[4]);
372 ssnd.offset = be2u32(&ssndstart[8]);
373 ssnd.blocksize = be2u32(&ssndstart[12]);
374
375 /* Calculate the total length of the resulting RIFF chunk.
376 * The length is given by frames * samples * bytes/sample.
377 * We need to add:
378 * - 16 bytes: fmt chunk header
379 * - 8 bytes: data chunk header
380 * - 4 bytes: wave chunk identifier
381 */
382 out = fopen(wav, "wb+");
383
384 /* write the wav header */
385 unsigned short blocksize = comm.channels * (comm.size >> 3);
386 unsigned long rifflen = blocksize * comm.frames + 28;
387 fwrite("RIFF", 1, 4, out);
388 fwrite(u32tobuf(obuf, rifflen), 1, 4, out);
389 fwrite("WAVE", 1, 4, out);
390
391 /* write the fmt chunk and chunk size (always 16) */
392 /* write fmt chunk header:
393 * header, size (always 0x10, format code (always 0x0001)
394 */
395 fwrite("fmt \x10\x00\x00\x00\x01\x00", 1, 10, out);
396 /* number of channels (2 bytes) */
397 fwrite(u16tobuf(obuf, comm.channels), 1, 2, out);
398 /* sampling rate (4 bytes) */
399 fwrite(u32tobuf(obuf, comm.rate), 1, 4, out);
400
401 /* data rate, i.e. bytes/sec */
402 fwrite(u32tobuf(obuf, comm.rate * blocksize), 1, 4, out);
403
404 /* data block size */
405 fwrite(u16tobuf(obuf, blocksize), 1, 2, out);
406
407 /* bits per sample */
408 fwrite(u16tobuf(obuf, comm.size), 1, 2, out);
409
410 /* write the data chunk */
411 /* chunk id */
412 fwrite("data", 1, 4, out);
413 /* chunk size: 4 bytes. */
414 unsigned long cs = blocksize * comm.frames;
415 fwrite(u32tobuf(obuf, cs), 1, 4, out);
416
417 /* write data */
418 unsigned char* data = ssndstart;
419 unsigned long pos = ssnd.chunksize;
420 /* byteswap if samples are 16 bit */
421 if(comm.size == 16) {
422 while(pos) {
423 obuf[1] = *data++ & 0xff;
424 obuf[0] = *data++ & 0xff;
425 fwrite(obuf, 1, 2, out);
426 pos -= 2;
427 }
428 }
429 /* 8 bit samples have need no conversion so we can bulk copy.
430 * Everything that is not 16 bit is considered 8. */
431 else {
432 fwrite(data, 1, pos, out);
433 }
434 /* number of bytes has to be even, even if chunksize is not. */
435 if(cs % 2) {
436 fwrite(obuf, 1, 1, out);
437 }
438
439 fclose(out);
440 free(buf);
441 return 0;
442}
443
diff --git a/utils/rbutilqt/base/ttscarbon.h b/utils/rbutilqt/base/ttscarbon.h
new file mode 100644
index 0000000000..2e9e84aa7d
--- /dev/null
+++ b/utils/rbutilqt/base/ttscarbon.h
@@ -0,0 +1,73 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2010 by Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef TTSCARBON_H
20#define TTSCARBON_H
21
22#include <QtCore>
23#include "ttsbase.h"
24
25#include <Carbon/Carbon.h>
26#include <inttypes.h>
27
28class TTSCarbon : public TTSBase
29{
30 Q_OBJECT
31 //! Enum to identify the settings
32 enum ConfigValuesCarbon
33 {
34 ConfigVoice,
35 ConfigSpeed,
36 ConfigPitch
37 };
38 public:
39 TTSCarbon(QObject *parent = NULL);
40
41 //! Child class should generate a clip
42 TTSStatus voice(QString text, QString wavfile, QString* errStr);
43 //! Child class should do startup
44 bool start(QString *errStr);
45 //! child class should stop
46 bool stop() ;
47 QString voiceVendor(void) { return QString(); }
48
49 // configuration
50 //! Child class should return true, when configuration is good
51 bool configOk();
52 //! Child class should generate and insertSetting(..) its settings
53 void generateSettings();
54 //! Child class should commit the Settings to permanent storage
55 void saveSettings();
56
57 Capabilities capabilities();
58
59 private:
60 SpeechChannel m_channel;
61 CFStringBuiltInEncodings m_voiceScript;
62
63 unsigned long be2u32(unsigned char* buf);
64 unsigned long be2u16(unsigned char* buf);
65 unsigned char* u32tobuf(unsigned char* buf, uint32_t val);
66 unsigned char* u16tobuf(unsigned char* buf, uint16_t val);
67 unsigned int extended2int(unsigned char* buf);
68 int convertAiffToWav(const char* aiff, const char* wav);
69
70};
71
72#endif // TTSCARBON_H
73
diff --git a/utils/rbutilqt/base/ttsespeak.h b/utils/rbutilqt/base/ttsespeak.h
new file mode 100644
index 0000000000..afe19fbdac
--- /dev/null
+++ b/utils/rbutilqt/base/ttsespeak.h
@@ -0,0 +1,42 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8*
9* Copyright (C) 2012 by Dominik Riebeling
10*
11* This program is free software; you can redistribute it and/or
12* modify it under the terms of the GNU General Public License
13* as published by the Free Software Foundation; either version 2
14* of the License, or (at your option) any later version.
15*
16* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17* KIND, either express or implied.
18*
19****************************************************************************/
20
21#ifndef TTSESPEAK_H
22#define TTSESPEAK_H
23
24#include <QtCore>
25#include "ttsexes.h"
26
27class TTSEspeak : public TTSExes
28{
29 Q_OBJECT
30 public:
31 TTSEspeak(QObject* parent=nullptr) : TTSExes(parent)
32 {
33 m_name = "espeak";
34
35 /* default to espeak */
36 m_TTSTemplate = "\"%exe\" %options -w \"%wavfile\" -- \"%text\"";
37 m_TTSSpeakTemplate = "\"%exe\" %options -- \"%text\"";
38 m_capabilities = TTSBase::CanSpeak;
39 }
40};
41
42#endif
diff --git a/utils/rbutilqt/base/ttsespeakng.h b/utils/rbutilqt/base/ttsespeakng.h
new file mode 100644
index 0000000000..55aba62e7d
--- /dev/null
+++ b/utils/rbutilqt/base/ttsespeakng.h
@@ -0,0 +1,41 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8*
9* Copyright (C) 2020 by Solomon Peachy
10*
11* This program is free software; you can redistribute it and/or
12* modify it under the terms of the GNU General Public License
13* as published by the Free Software Foundation; either version 2
14* of the License, or (at your option) any later version.
15*
16* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17* KIND, either express or implied.
18*
19****************************************************************************/
20
21#ifndef TTSESPEAKNG_H
22#define TTSESPEAKNG_H
23
24#include <QtCore>
25#include "ttsexes.h"
26
27class TTSEspeakNG : public TTSExes
28{
29 Q_OBJECT
30 public:
31 TTSEspeakNG(QObject* parent=nullptr) : TTSExes(parent)
32 {
33 m_name = "espeak-ng";
34
35 m_TTSTemplate = "\"%exe\" %options -w \"%wavfile\" -- \"%text\"";
36 m_TTSSpeakTemplate = "\"%exe\" %options -- \"%text\"";
37 m_capabilities = TTSBase::CanSpeak;
38 }
39};
40
41#endif
diff --git a/utils/rbutilqt/base/ttsexes.cpp b/utils/rbutilqt/base/ttsexes.cpp
new file mode 100644
index 0000000000..446725968f
--- /dev/null
+++ b/utils/rbutilqt/base/ttsexes.cpp
@@ -0,0 +1,127 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8*
9* Copyright (C) 2007 by Dominik Wenger
10*
11* All files in this archive are subject to the GNU General Public License.
12* See the file COPYING in the source tree root for full license agreement.
13*
14* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15* KIND, either express or implied.
16*
17****************************************************************************/
18
19#include <QtCore>
20#include "ttsexes.h"
21#include "utils.h"
22#include "rbsettings.h"
23#include "Logger.h"
24
25TTSExes::TTSExes(QObject* parent) : TTSBase(parent)
26{
27 /* default to espeak */
28 m_name = "espeak";
29 m_capabilities = TTSBase::CanSpeak;
30 m_TTSTemplate = "\"%exe\" %options -w \"%wavfile\" -- \"%text\"";
31 m_TTSSpeakTemplate = "\"%exe\" %options -- \"%text\"";
32}
33
34
35TTSBase::Capabilities TTSExes::capabilities()
36{
37 return m_capabilities;
38}
39
40void TTSExes::generateSettings()
41{
42 loadSettings();
43 insertSetting(eEXEPATH, new EncTtsSetting(this, EncTtsSetting::eSTRING,
44 tr("Path to TTS engine:"), m_TTSexec, EncTtsSetting::eBROWSEBTN));
45 insertSetting(eOPTIONS, new EncTtsSetting(this, EncTtsSetting::eSTRING,
46 tr("TTS engine options:"), m_TTSOpts));
47}
48
49void TTSExes::saveSettings()
50{
51 RbSettings::setSubValue(m_name, RbSettings::TtsPath,
52 getSetting(eEXEPATH)->current().toString());
53 RbSettings::setSubValue(m_name, RbSettings::TtsOptions,
54 getSetting(eOPTIONS)->current().toString());
55 RbSettings::sync();
56}
57
58
59void TTSExes::loadSettings(void)
60{
61 m_TTSexec = RbSettings::subValue(m_name, RbSettings::TtsPath).toString();
62 if(m_TTSexec.isEmpty()) m_TTSexec = Utils::findExecutable(m_name);
63 m_TTSOpts = RbSettings::subValue(m_name, RbSettings::TtsOptions).toString();
64}
65
66
67bool TTSExes::start(QString *errStr)
68{
69 loadSettings();
70
71 QFileInfo tts(m_TTSexec);
72 if(tts.exists())
73 {
74 return true;
75 }
76 else
77 {
78 *errStr = tr("TTS executable not found");
79 return false;
80 }
81}
82
83TTSStatus TTSExes::voice(QString text, QString wavfile, QString *errStr)
84{
85 (void) errStr;
86 QString execstring;
87 if(wavfile.isEmpty() && m_capabilities & TTSBase::CanSpeak) {
88 if(m_TTSSpeakTemplate.isEmpty()) {
89 LOG_ERROR() << "internal error: TTS announces CanSpeak "
90 "but template empty!";
91 return FatalError;
92 }
93 execstring = m_TTSSpeakTemplate;
94 }
95 else if(wavfile.isEmpty()) {
96 LOG_ERROR() << "no output file passed to voice() "
97 "but TTS can't speak directly.";
98 return FatalError;
99 }
100 else {
101 execstring = m_TTSTemplate;
102 }
103
104 execstring.replace("%exe",m_TTSexec);
105 execstring.replace("%options",m_TTSOpts);
106 execstring.replace("%wavfile",wavfile);
107 execstring.replace("%text",text);
108
109 QProcess::execute(execstring);
110
111 if(!wavfile.isEmpty() && !QFileInfo(wavfile).isFile()) {
112 LOG_ERROR() << "output file does not exist:" << wavfile;
113 return FatalError;
114 }
115 return NoError;
116
117}
118
119bool TTSExes::configOk()
120{
121 loadSettings();
122 if (QFileInfo::exists(m_TTSexec))
123 return true;
124 else
125 return false;
126}
127
diff --git a/utils/rbutilqt/base/ttsexes.h b/utils/rbutilqt/base/ttsexes.h
new file mode 100644
index 0000000000..5707c827fe
--- /dev/null
+++ b/utils/rbutilqt/base/ttsexes.h
@@ -0,0 +1,61 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8*
9* Copyright (C) 2009 by Dominik Wenger
10*
11* This program is free software; you can redistribute it and/or
12* modify it under the terms of the GNU General Public License
13* as published by the Free Software Foundation; either version 2
14* of the License, or (at your option) any later version.
15*
16* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17* KIND, either express or implied.
18*
19****************************************************************************/
20
21#ifndef TTSEXES_H
22#define TTSEXES_H
23
24#include <QtCore>
25#include "ttsbase.h"
26
27class TTSExes : public TTSBase
28{
29 enum ESettings
30 {
31 eEXEPATH,
32 eOPTIONS
33 };
34
35 Q_OBJECT
36 public:
37 TTSExes(QObject* parent=nullptr);
38 TTSStatus voice(QString text, QString wavfile, QString *errStr);
39 bool start(QString *errStr);
40 bool stop() {return true;}
41 QString voiceVendor(void) { return QString(); }
42 Capabilities capabilities();
43
44 // for settings
45 void generateSettings();
46 void saveSettings();
47 bool configOk();
48
49 private:
50 void loadSettings(void);
51
52 protected:
53 QString m_TTSTemplate;
54 QString m_TTSSpeakTemplate;
55 QString m_name;
56 QString m_TTSexec;
57 QString m_TTSOpts;
58 TTSBase::Capabilities m_capabilities;
59};
60
61#endif
diff --git a/utils/rbutilqt/base/ttsfestival.cpp b/utils/rbutilqt/base/ttsfestival.cpp
new file mode 100644
index 0000000000..d0ca400909
--- /dev/null
+++ b/utils/rbutilqt/base/ttsfestival.cpp
@@ -0,0 +1,412 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8*
9* Copyright (C) 2007 by Dominik Wenger
10*
11* All files in this archive are subject to the GNU General Public License.
12* See the file COPYING in the source tree root for full license agreement.
13*
14* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15* KIND, either express or implied.
16*
17****************************************************************************/
18
19#include <QtCore>
20#include <QTcpSocket>
21
22#include "ttsfestival.h"
23#include "utils.h"
24#include "rbsettings.h"
25#include "Logger.h"
26
27TTSFestival::~TTSFestival()
28{
29 LOG_INFO() << "Destroying instance";
30 stop();
31}
32
33TTSBase::Capabilities TTSFestival::capabilities()
34{
35 return RunInParallel;
36}
37
38void TTSFestival::generateSettings()
39{
40 // server path
41 QString exepath = RbSettings::subValue("festival-server",
42 RbSettings::TtsPath).toString();
43 if(exepath == "" ) exepath = Utils::findExecutable("festival");
44 insertSetting(eSERVERPATH,new EncTtsSetting(this,
45 EncTtsSetting::eSTRING, "Path to Festival server:",
46 exepath,EncTtsSetting::eBROWSEBTN));
47
48 // client path
49 QString clientpath = RbSettings::subValue("festival-client",
50 RbSettings::TtsPath).toString();
51 if(clientpath == "" ) clientpath = Utils::findExecutable("festival_client");
52 insertSetting(eCLIENTPATH,new EncTtsSetting(this,EncTtsSetting::eSTRING,
53 tr("Path to Festival client:"),
54 clientpath,EncTtsSetting::eBROWSEBTN));
55
56 // voice
57 EncTtsSetting* setting = new EncTtsSetting(this,
58 EncTtsSetting::eSTRINGLIST, tr("Voice:"),
59 RbSettings::subValue("festival", RbSettings::TtsVoice),
60 getVoiceList(), EncTtsSetting::eREFRESHBTN);
61 connect(setting, &EncTtsSetting::refresh,
62 this, &TTSFestival::updateVoiceList);
63 connect(setting, &EncTtsSetting::dataChanged,
64 this, &TTSFestival::clearVoiceDescription);
65 insertSetting(eVOICE,setting);
66
67 //voice description
68 setting = new EncTtsSetting(this,EncTtsSetting::eREADONLYSTRING,
69 tr("Voice description:"),"",EncTtsSetting::eREFRESHBTN);
70 connect(setting, &EncTtsSetting::refresh,
71 this, &TTSFestival::updateVoiceDescription);
72 insertSetting(eVOICEDESC,setting);
73}
74
75void TTSFestival::saveSettings()
76{
77 //save settings in user config
78 RbSettings::setSubValue("festival-server",
79 RbSettings::TtsPath,getSetting(eSERVERPATH)->current().toString());
80 RbSettings::setSubValue("festival-client",
81 RbSettings::TtsPath,getSetting(eCLIENTPATH)->current().toString());
82 RbSettings::setSubValue("festival",
83 RbSettings::TtsVoice,getSetting(eVOICE)->current().toString());
84
85 RbSettings::sync();
86}
87
88void TTSFestival::updateVoiceDescription()
89{
90 // get voice Info with current voice and path
91 currentPath = getSetting(eSERVERPATH)->current().toString();
92 QString info = getVoiceInfo(getSetting(eVOICE)->current().toString());
93 currentPath = "";
94
95 getSetting(eVOICEDESC)->setCurrent(info);
96}
97
98void TTSFestival::clearVoiceDescription()
99{
100 getSetting(eVOICEDESC)->setCurrent("");
101}
102
103void TTSFestival::updateVoiceList()
104{
105 currentPath = getSetting(eSERVERPATH)->current().toString();
106 QStringList voiceList = getVoiceList();
107 currentPath = "";
108
109 getSetting(eVOICE)->setList(voiceList);
110 if(voiceList.size() > 0) getSetting(eVOICE)->setCurrent(voiceList.at(0));
111 else getSetting(eVOICE)->setCurrent("");
112}
113
114void TTSFestival::startServer()
115{
116 if(!configOk())
117 return;
118
119 if(serverProcess.state() != QProcess::Running)
120 {
121 QString path;
122 /* currentPath is set by the GUI - if it's set, it is the currently set
123 path in the configuration GUI; if it's not set, use the saved path */
124 if (currentPath == "")
125 path = RbSettings::subValue("festival-server",RbSettings::TtsPath).toString();
126 else
127 path = currentPath;
128
129 serverProcess.start(QString("%1 --server").arg(path));
130 serverProcess.waitForStarted();
131
132 /* A friendlier version of a spinlock */
133 while (serverProcess.processId() == 0 && serverProcess.state() != QProcess::Running)
134 QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
135
136 if(serverProcess.state() == QProcess::Running)
137 LOG_INFO() << "Server is up and running";
138 else
139 LOG_ERROR() << "Server failed to start, state:"
140 << serverProcess.state();
141 }
142}
143
144bool TTSFestival::ensureServerRunning()
145{
146 if(serverProcess.state() != QProcess::Running)
147 {
148 startServer();
149 }
150 return serverProcess.state() == QProcess::Running;
151}
152
153bool TTSFestival::start(QString* errStr)
154{
155 LOG_INFO() << "Starting server with voice"
156 << RbSettings::subValue("festival", RbSettings::TtsVoice).toString();
157
158 bool running = ensureServerRunning();
159 if (!RbSettings::subValue("festival",RbSettings::TtsVoice).toString().isEmpty())
160 {
161 /* There's no harm in using both methods to set the voice .. */
162 QString voiceSelect = QString("(voice.select '%1)\n")
163 .arg(RbSettings::subValue("festival", RbSettings::TtsVoice).toString());
164 queryServer(voiceSelect, 3000);
165
166 if(prologFile.open())
167 {
168 prologFile.write(voiceSelect.toLatin1());
169 prologFile.close();
170 prologPath = QFileInfo(prologFile).absoluteFilePath();
171 LOG_INFO() << "Prolog created at" << prologPath;
172 }
173
174 }
175
176 if (!running)
177 (*errStr) = "Festival could not be started";
178 return running;
179}
180
181bool TTSFestival::stop()
182{
183 serverProcess.terminate();
184 serverProcess.kill();
185
186 return true;
187}
188
189TTSStatus TTSFestival::voice(QString text, QString wavfile, QString* errStr)
190{
191 LOG_INFO() << "Voicing" << text << "->" << wavfile;
192
193 QString path = RbSettings::subValue("festival-client",
194 RbSettings::TtsPath).toString();
195 QString cmd = QString("%1 --server localhost --otype riff --ttw --withlisp"
196 " --output \"%2\" --prolog \"%3\" - ").arg(path).arg(wavfile).arg(prologPath);
197 LOG_INFO() << "Client cmd:" << cmd;
198
199 QProcess clientProcess;
200 clientProcess.start(cmd);
201 clientProcess.write(QString("%1.\n").arg(text).toLatin1());
202 clientProcess.waitForBytesWritten();
203 clientProcess.closeWriteChannel();
204 clientProcess.waitForReadyRead();
205 QString response = clientProcess.readAll();
206 response = response.trimmed();
207 if(!response.contains("Utterance"))
208 {
209 LOG_WARNING() << "Could not voice string: " << response;
210 *errStr = tr("engine could not voice string");
211 return Warning;
212 /* do not stop the voicing process because of a single string
213 TODO: needs proper settings */
214 }
215 clientProcess.closeReadChannel(QProcess::StandardError);
216 clientProcess.closeReadChannel(QProcess::StandardOutput);
217 clientProcess.terminate();
218 clientProcess.kill();
219
220 return NoError;
221}
222
223bool TTSFestival::configOk()
224{
225 bool ret;
226 if (currentPath == "")
227 {
228 QString serverPath = RbSettings::subValue("festival-server",
229 RbSettings::TtsPath).toString();
230 QString clientPath = RbSettings::subValue("festival-client",
231 RbSettings::TtsPath).toString();
232
233 ret = QFileInfo(serverPath).isExecutable() &&
234 QFileInfo(clientPath).isExecutable();
235 if(RbSettings::subValue("festival",RbSettings::TtsVoice).toString().size() > 0
236 && voices.size() > 0)
237 ret = ret && (voices.indexOf(RbSettings::subValue("festival",
238 RbSettings::TtsVoice).toString()) != -1);
239 }
240 else /* If we're currently configuring the server, we need to know that
241 the entered path is valid */
242 ret = QFileInfo(currentPath).isExecutable();
243
244 return ret;
245}
246
247QStringList TTSFestival::getVoiceList()
248{
249 if(!configOk())
250 return QStringList();
251
252 if(voices.size() > 0)
253 {
254 LOG_INFO() << "Using voice cache";
255 return voices;
256 }
257
258 QString response = queryServer("(voice.list)", 10000);
259
260 // get the 2nd line. It should be (<voice_name>, <voice_name>)
261 response = response.mid(response.indexOf('\n') + 1, -1);
262 response = response.left(response.indexOf('\n')).trimmed();
263
264 voices = response.mid(1, response.size()-2).split(' ');
265
266 voices.sort();
267 if (voices.size() == 1 && voices[0].size() == 0)
268 voices.removeAt(0);
269 if (voices.size() > 0)
270 LOG_INFO() << "Voices:" << voices;
271 else
272 LOG_WARNING() << "No voices. Response was:" << response;
273
274 return voices;
275}
276
277QString TTSFestival::getVoiceInfo(QString voice)
278{
279 if(!configOk())
280 return "";
281
282 if(!getVoiceList().contains(voice))
283 return "";
284
285 if(voiceDescriptions.contains(voice))
286 return voiceDescriptions[voice];
287
288 QString response = queryServer(QString("(voice.description '%1)").arg(voice),
289 10000);
290
291 if (response == "")
292 {
293 voiceDescriptions[voice]=tr("No description available");
294 }
295 else
296 {
297 response = response.remove(QRegExp("(description \"*\")",
298 Qt::CaseInsensitive, QRegExp::Wildcard));
299 LOG_INFO() << "voiceInfo w/o descr:" << response;
300 response = response.remove(')');
301#if QT_VERSION >= 0x050e00
302 QStringList responseLines = response.split('(', Qt::SkipEmptyParts);
303#else
304 QStringList responseLines = response.split('(', QString::SkipEmptyParts);
305#endif
306 responseLines.removeAt(0); // the voice name itself
307
308 QString description;
309 foreach(QString line, responseLines)
310 {
311 line = line.remove('(');
312 line = line.simplified();
313
314 line[0] = line[0].toUpper(); // capitalize the key
315
316 int firstSpace = line.indexOf(' ');
317 if (firstSpace > 0)
318 {
319 // add a colon between the key and the value
320 line = line.insert(firstSpace, ':');
321 // capitalize the value
322 line[firstSpace+2] = line[firstSpace+2].toUpper();
323 }
324
325 description += line + "\n";
326 }
327 voiceDescriptions[voice] = description.trimmed();
328 }
329
330 return voiceDescriptions[voice];
331}
332
333QString TTSFestival::queryServer(QString query, int timeout)
334{
335 if(!configOk())
336 return "";
337
338 // this operation could take some time
339 emit busy();
340
341 LOG_INFO() << "queryServer with" << query;
342
343 if (!ensureServerRunning())
344 {
345 LOG_ERROR() << "queryServer: ensureServerRunning failed";
346 emit busyEnd();
347 return "";
348 }
349
350 QString response;
351
352 QDateTime endTime;
353 if(timeout > 0)
354 endTime = QDateTime::currentDateTime().addMSecs(timeout);
355
356 /* Festival is *extremely* unreliable. Although at this
357 * point we are sure that SIOD is accepting commands,
358 * we might end up with an empty response. Hence, the loop.
359 */
360 while(true)
361 {
362 QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
363 QTcpSocket socket;
364
365 socket.connectToHost("localhost", 1314);
366 socket.waitForConnected();
367
368 if(socket.state() == QAbstractSocket::ConnectedState)
369 {
370 socket.write(QString("%1\n").arg(query).toLatin1());
371 socket.waitForBytesWritten();
372 socket.waitForReadyRead();
373
374 response = socket.readAll().trimmed();
375
376 if (response != "LP" && response != "")
377 break;
378 }
379 socket.abort();
380 socket.disconnectFromHost();
381
382 if(timeout > 0 && QDateTime::currentDateTime() >= endTime)
383 {
384 emit busyEnd();
385 return "";
386 }
387 /* make sure we wait a little as we don't want to flood the server
388 * with requests */
389 QDateTime tmpEndTime = QDateTime::currentDateTime().addMSecs(500);
390 while(QDateTime::currentDateTime() < tmpEndTime)
391 QCoreApplication::processEvents(QEventLoop::AllEvents);
392 }
393 if(response == "nil")
394 {
395 emit busyEnd();
396 return "";
397 }
398
399 QStringList lines = response.split('\n');
400 if(lines.size() > 2)
401 {
402 lines.removeFirst(); /* should be LP */
403 lines.removeLast(); /* should be ft_StUfF_keyOK */
404 }
405 else
406 LOG_ERROR() << "Response too short:" << response;
407
408 emit busyEnd();
409 return lines.join("\n");
410
411}
412
diff --git a/utils/rbutilqt/base/ttsfestival.h b/utils/rbutilqt/base/ttsfestival.h
new file mode 100644
index 0000000000..5f6dc13ab5
--- /dev/null
+++ b/utils/rbutilqt/base/ttsfestival.h
@@ -0,0 +1,72 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8*
9* Copyright (C) 2009 by Dominik Wenger
10*
11* This program is free software; you can redistribute it and/or
12* modify it under the terms of the GNU General Public License
13* as published by the Free Software Foundation; either version 2
14* of the License, or (at your option) any later version.
15*
16* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17* KIND, either express or implied.
18*
19****************************************************************************/
20
21#ifndef TTSFESTIVAL_H
22#define TTSFESTIVAL_H
23
24#include <QTemporaryFile>
25#include "ttsbase.h"
26
27class TTSFestival : public TTSBase
28{
29 enum ESettings
30 {
31 eSERVERPATH,
32 eCLIENTPATH,
33 eVOICE,
34 eVOICEDESC
35 };
36
37 Q_OBJECT
38 public:
39 TTSFestival(QObject* parent=nullptr) : TTSBase(parent) {}
40 ~TTSFestival();
41 bool start(QString *errStr);
42 bool stop();
43 TTSStatus voice(QString text,QString wavfile, QString *errStr);
44 QString voiceVendor(void) { return QString(); }
45 Capabilities capabilities();
46
47 // for settings
48 bool configOk();
49 void generateSettings();
50 void saveSettings();
51
52 private slots:
53 void updateVoiceList();
54 void updateVoiceDescription();
55 void clearVoiceDescription();
56 private:
57 QTemporaryFile prologFile;
58 QString prologPath;
59 QString currentPath;
60 QStringList getVoiceList();
61 QString getVoiceInfo(QString voice);
62
63 inline void startServer();
64 inline bool ensureServerRunning();
65 QString queryServer(QString query, int timeout = -1);
66 QProcess serverProcess;
67 QStringList voices;
68 QMap<QString, QString> voiceDescriptions;
69};
70
71
72#endif
diff --git a/utils/rbutilqt/base/ttsflite.h b/utils/rbutilqt/base/ttsflite.h
new file mode 100644
index 0000000000..717c311a34
--- /dev/null
+++ b/utils/rbutilqt/base/ttsflite.h
@@ -0,0 +1,43 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8*
9* Copyright (C) 2012 by Dominik Riebeling
10*
11* This program is free software; you can redistribute it and/or
12* modify it under the terms of the GNU General Public License
13* as published by the Free Software Foundation; either version 2
14* of the License, or (at your option) any later version.
15*
16* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17* KIND, either express or implied.
18*
19****************************************************************************/
20
21#ifndef TTSFLITE_H
22#define TTSFLITE_H
23
24#include <QtCore>
25#include "ttsexes.h"
26
27class TTSFlite : public TTSExes
28{
29 Q_OBJECT
30 public:
31 TTSFlite(QObject* parent=nullptr) : TTSExes(parent)
32 {
33 m_name = "flite";
34
35 /* default to espeak */
36 m_TTSTemplate = "\"%exe\" %options -o \"%wavfile\" -t \"%text\"";
37 m_TTSSpeakTemplate = "";
38 m_capabilities = TTSBase::None;
39
40 }
41};
42
43#endif
diff --git a/utils/rbutilqt/base/ttsmimic.h b/utils/rbutilqt/base/ttsmimic.h
new file mode 100644
index 0000000000..f4d7a8beac
--- /dev/null
+++ b/utils/rbutilqt/base/ttsmimic.h
@@ -0,0 +1,41 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8*
9* Copyright (C) 2020 by Solomon Peachy
10*
11* This program is free software; you can redistribute it and/or
12* modify it under the terms of the GNU General Public License
13* as published by the Free Software Foundation; either version 2
14* of the License, or (at your option) any later version.
15*
16* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17* KIND, either express or implied.
18*
19****************************************************************************/
20
21#ifndef TTSMIMIC_H
22#define TTSMIMIC_H
23
24#include <QtCore>
25#include "ttsexes.h"
26
27class TTSMimic : public TTSExes
28{
29 Q_OBJECT
30 public:
31 TTSMimic(QObject* parent=nullptr) : TTSExes(parent)
32 {
33 m_name = "mimic";
34
35 m_TTSTemplate = "\"%exe\" %options -o \"%wavfile\" -t \"%text\"";
36 m_TTSSpeakTemplate = "\"%exe\" %options -t \"%text\"";
37 m_capabilities = TTSBase::CanSpeak;
38 }
39};
40
41#endif
diff --git a/utils/rbutilqt/base/ttsmssp.h b/utils/rbutilqt/base/ttsmssp.h
new file mode 100644
index 0000000000..817b9fde58
--- /dev/null
+++ b/utils/rbutilqt/base/ttsmssp.h
@@ -0,0 +1,43 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8*
9* Copyright (C) 2012 by Dominik Riebeling
10*
11* This program is free software; you can redistribute it and/or
12* modify it under the terms of the GNU General Public License
13* as published by the Free Software Foundation; either version 2
14* of the License, or (at your option) any later version.
15*
16* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17* KIND, either express or implied.
18*
19****************************************************************************/
20
21#ifndef TTSMSSP_H
22#define TTSMSSP_H
23
24#include "ttsbase.h"
25#include "ttssapi.h"
26
27class TTSMssp: public TTSSapi
28{
29 Q_OBJECT
30 public:
31 TTSMssp(QObject* parent=nullptr) : TTSSapi(parent)
32 {
33 m_TTSTemplate = "cscript //nologo \"%exe\" "
34 "/language:%lang /voice:\"%voice\" "
35 "/speed:%speed \"%options\" /mssp";
36 m_TTSVoiceTemplate = "cscript //nologo \"%exe\" "
37 "/language:%lang /listvoices /mssp";
38 m_TTSType = "mssp";
39 }
40
41};
42
43#endif
diff --git a/utils/rbutilqt/base/ttssapi.cpp b/utils/rbutilqt/base/ttssapi.cpp
new file mode 100644
index 0000000000..1b326b301e
--- /dev/null
+++ b/utils/rbutilqt/base/ttssapi.cpp
@@ -0,0 +1,274 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8*
9* Copyright (C) 2007 by Dominik Wenger
10*
11* All files in this archive are subject to the GNU General Public License.
12* See the file COPYING in the source tree root for full license agreement.
13*
14* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15* KIND, either express or implied.
16*
17****************************************************************************/
18
19#include "ttssapi.h"
20#include "utils.h"
21#include "rbsettings.h"
22#include "playerbuildinfo.h"
23#include "Logger.h"
24
25TTSSapi::TTSSapi(QObject* parent) : TTSBase(parent)
26{
27 m_TTSTemplate = "cscript //nologo \"%exe\" /language:%lang "
28 "/voice:\"%voice\" /speed:%speed \"%options\"";
29 m_TTSVoiceTemplate = "cscript //nologo \"%exe\" /language:%lang /listvoices";
30 m_TTSType = "sapi";
31 defaultLanguage = "english";
32 m_started = false;
33}
34
35TTSBase::Capabilities TTSSapi::capabilities()
36{
37 return None;
38}
39
40void TTSSapi::generateSettings()
41{
42 // language
43 QMap<QString, QVariant> langmap = PlayerBuildInfo::instance()->value(
44 PlayerBuildInfo::LanguageList).toMap();
45 EncTtsSetting* setting = new EncTtsSetting(this,
46 EncTtsSetting::eSTRINGLIST, tr("Language:"),
47 RbSettings::subValue(m_TTSType, RbSettings::TtsLanguage),
48 langmap.keys());
49 connect(setting, &EncTtsSetting::dataChanged, this, &TTSSapi::updateVoiceList);
50 insertSetting(eLANGUAGE,setting);
51 // voice
52 setting = new EncTtsSetting(this,
53 EncTtsSetting::eSTRINGLIST, tr("Voice:"),
54 RbSettings::subValue(m_TTSType, RbSettings::TtsVoice),
55 getVoiceList(RbSettings::subValue(m_TTSType,
56 RbSettings::TtsLanguage).toString()),
57 EncTtsSetting::eREFRESHBTN);
58 connect(setting, &EncTtsSetting::refresh, this, &TTSSapi::updateVoiceList);
59 insertSetting(eVOICE,setting);
60 //speed
61 int speed = RbSettings::subValue(m_TTSType, RbSettings::TtsSpeed).toInt();
62 if(speed > 10 || speed < -10)
63 speed = 0;
64 insertSetting(eSPEED, new EncTtsSetting(this,
65 EncTtsSetting::eINT, tr("Speed:"), speed, -10, 10));
66 // options
67 insertSetting(eOPTIONS, new EncTtsSetting(this,
68 EncTtsSetting::eSTRING, tr("Options:"),
69 RbSettings::subValue(m_TTSType, RbSettings::TtsOptions)));
70
71}
72
73void TTSSapi::saveSettings()
74{
75 //save settings in user config
76 RbSettings::setSubValue(m_TTSType, RbSettings::TtsLanguage,
77 getSetting(eLANGUAGE)->current().toString());
78 RbSettings::setSubValue(m_TTSType, RbSettings::TtsVoice,
79 getSetting(eVOICE)->current().toString());
80 RbSettings::setSubValue(m_TTSType, RbSettings::TtsSpeed,
81 getSetting(eSPEED)->current().toInt());
82 RbSettings::setSubValue(m_TTSType, RbSettings::TtsOptions,
83 getSetting(eOPTIONS)->current().toString());
84
85 RbSettings::sync();
86}
87
88void TTSSapi::updateVoiceList()
89{
90 LOG_INFO() << "updating voicelist";
91 QStringList voiceList = getVoiceList(getSetting(eLANGUAGE)->current().toString());
92 getSetting(eVOICE)->setList(voiceList);
93 if(voiceList.size() > 0) getSetting(eVOICE)->setCurrent(voiceList.at(0));
94 else getSetting(eVOICE)->setCurrent("");
95}
96
97bool TTSSapi::start(QString *errStr)
98{
99
100 m_TTSOpts = RbSettings::subValue(m_TTSType, RbSettings::TtsOptions).toString();
101 m_TTSLanguage =RbSettings::subValue(m_TTSType, RbSettings::TtsLanguage).toString();
102 m_TTSVoice=RbSettings::subValue(m_TTSType, RbSettings::TtsVoice).toString();
103 m_TTSSpeed=RbSettings::subValue(m_TTSType, RbSettings::TtsSpeed).toString();
104
105 QFile::remove(QDir::tempPath() +"/sapi_voice.vbs");
106 QFile::copy(":/builtin/sapi_voice.vbs",QDir::tempPath() + "/sapi_voice.vbs");
107 m_TTSexec = QDir::tempPath() +"/sapi_voice.vbs";
108
109 QFileInfo tts(m_TTSexec);
110 if(!tts.exists())
111 {
112 *errStr = tr("Could not copy the SAPI script");
113 return false;
114 }
115 // create the voice process
116 QString execstring = m_TTSTemplate;
117 execstring.replace("%exe",m_TTSexec);
118 execstring.replace("%options",m_TTSOpts);
119 execstring.replace("%lang",m_TTSLanguage);
120 execstring.replace("%voice",m_TTSVoice);
121 execstring.replace("%speed",m_TTSSpeed);
122
123 LOG_INFO() << "Start:" << execstring;
124 voicescript = new QProcess(nullptr);
125 //connect(voicescript,SIGNAL(readyReadStandardError()),this,SLOT(error()));
126 voicescript->start(execstring);
127 LOG_INFO() << "wait for process";
128 if(!voicescript->waitForStarted())
129 {
130 *errStr = tr("Could not start SAPI process");
131 LOG_ERROR() << "starting process timed out!";
132 return false;
133 }
134
135 if(!voicescript->waitForReadyRead(300))
136 {
137 *errStr = voicescript->readAllStandardError();
138 if(*errStr != "")
139 return false;
140 }
141
142 voicestream = new QTextStream(voicescript);
143#if QT_VERSION < 0x060000
144 voicestream->setCodec("UTF16-LE");
145#else
146 voicestream->setEncoding(QStringConverter::Utf16LE);
147#endif
148
149 m_started = true;
150 return true;
151}
152
153QString TTSSapi::voiceVendor(void)
154{
155 bool keeprunning = m_started;
156 QString vendor;
157 if(!m_started) {
158 QString error;
159 start(&error);
160 }
161 *voicestream << "QUERY\tVENDOR\r\n";
162 voicestream->flush();
163 while((vendor = voicestream->readLine()).isEmpty())
164 QCoreApplication::processEvents();
165
166 LOG_INFO() << "TTS vendor:" << vendor;
167 if(!keeprunning) {
168 stop();
169 }
170 return vendor;
171}
172
173QStringList TTSSapi::getVoiceList(QString language)
174{
175 QStringList result;
176
177 QFile::copy(":/builtin/sapi_voice.vbs",QDir::tempPath() + "/sapi_voice.vbs");
178 m_TTSexec = QDir::tempPath() +"/sapi_voice.vbs";
179
180 QFileInfo tts(m_TTSexec);
181 if(!tts.exists())
182 return result;
183
184 // create the voice process
185 QString execstring = m_TTSVoiceTemplate;
186 execstring.replace("%exe",m_TTSexec);
187 execstring.replace("%lang",language);
188
189 LOG_INFO() << "Start:" << execstring;
190 voicescript = new QProcess(nullptr);
191 voicescript->start(execstring);
192 LOG_INFO() << "wait for process";
193 if(!voicescript->waitForStarted()) {
194 LOG_INFO() << "process startup timed out!";
195 return result;
196 }
197 voicescript->closeWriteChannel();
198 voicescript->waitForReadyRead();
199
200 QString dataRaw = voicescript->readAllStandardError().data();
201 if(dataRaw.startsWith("Error")) {
202 LOG_INFO() << "Error:" << dataRaw;
203 }
204#if QT_VERSION >= 0x050e00
205 result = dataRaw.split(";", Qt::SkipEmptyParts);
206#else
207 result = dataRaw.split(";", QString::SkipEmptyParts);
208#endif
209 if(result.size() > 0)
210 {
211 result.sort();
212 result.removeFirst();
213 for(int i = 0; i< result.size();i++)
214 {
215 result[i] = result.at(i).simplified();
216 }
217 }
218
219 delete voicescript;
220 QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs",
221 QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
222 | QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
223 | QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup
224 | QFile::ReadOther | QFile::WriteOther | QFile::ExeOther );
225 QFile::remove(QDir::tempPath() +"/sapi_voice.vbs");
226 return result;
227}
228
229
230
231TTSStatus TTSSapi::voice(QString text,QString wavfile, QString *errStr)
232{
233 (void) errStr;
234 QString query = "SPEAK\t"+wavfile+"\t"+text;
235 LOG_INFO() << "voicing" << query;
236 // append newline to query. Done now to keep debug output more readable.
237 query.append("\r\n");
238 *voicestream << query;
239 *voicestream << "SYNC\tbla\r\n";
240 voicestream->flush();
241 // do NOT poll the output with readLine(), this causes sync issues!
242 voicescript->waitForReadyRead();
243
244 if(!QFileInfo(wavfile).isFile()) {
245 LOG_ERROR() << "output file does not exist:" << wavfile;
246 return FatalError;
247 }
248 return NoError;
249}
250
251bool TTSSapi::stop()
252{
253 *voicestream << "QUIT\r\n";
254 voicestream->flush();
255 voicescript->waitForFinished();
256 delete voicestream;
257 delete voicescript;
258 QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs",
259 QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
260 | QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
261 | QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup
262 | QFile::ReadOther | QFile::WriteOther | QFile::ExeOther );
263 QFile::remove(QDir::tempPath() +"/sapi_voice.vbs");
264 m_started = false;
265 return true;
266}
267
268bool TTSSapi::configOk()
269{
270 if(RbSettings::subValue(m_TTSType, RbSettings::TtsVoice).toString().isEmpty())
271 return false;
272 return true;
273}
274
diff --git a/utils/rbutilqt/base/ttssapi.h b/utils/rbutilqt/base/ttssapi.h
new file mode 100644
index 0000000000..25d7d3dab7
--- /dev/null
+++ b/utils/rbutilqt/base/ttssapi.h
@@ -0,0 +1,77 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8*
9* Copyright (C) 2009 by Dominik Wenger
10*
11* This program is free software; you can redistribute it and/or
12* modify it under the terms of the GNU General Public License
13* as published by the Free Software Foundation; either version 2
14* of the License, or (at your option) any later version.
15*
16* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17* KIND, either express or implied.
18*
19****************************************************************************/
20
21#ifndef TTSSAPI_H
22#define TTSSAPI_H
23
24#include "ttsbase.h"
25
26class TTSSapi : public TTSBase
27{
28 //! Enum to identify the settings
29 enum ESettings
30 {
31 eLANGUAGE,
32 eVOICE,
33 eSPEED,
34 eOPTIONS
35 };
36
37 Q_OBJECT
38 public:
39 TTSSapi(QObject* parent=nullptr);
40
41 TTSStatus voice(QString text,QString wavfile, QString *errStr);
42 bool start(QString *errStr);
43 bool stop();
44 QString voiceVendor(void);
45 Capabilities capabilities();
46
47 // for settings
48 bool configOk();
49 void generateSettings();
50 void saveSettings();
51
52 private slots:
53 void updateVoiceList();
54
55 private:
56 QStringList getVoiceList(QString language);
57
58 QProcess* voicescript;
59 QTextStream* voicestream;
60 QString defaultLanguage;
61
62 QString m_TTSexec;
63 QString m_TTSOpts;
64 QString m_TTSLanguage;
65 QString m_TTSVoice;
66 QString m_TTSSpeed;
67 bool m_started;
68
69 protected:
70 QString m_TTSTemplate;
71 QString m_TTSVoiceTemplate;
72 QString m_TTSType;
73};
74
75
76
77#endif
diff --git a/utils/rbutilqt/base/ttssapi4.h b/utils/rbutilqt/base/ttssapi4.h
new file mode 100644
index 0000000000..d6408eaa2c
--- /dev/null
+++ b/utils/rbutilqt/base/ttssapi4.h
@@ -0,0 +1,43 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8*
9* Copyright (C) 2012 by Dominik Riebeling
10*
11* This program is free software; you can redistribute it and/or
12* modify it under the terms of the GNU General Public License
13* as published by the Free Software Foundation; either version 2
14* of the License, or (at your option) any later version.
15*
16* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17* KIND, either express or implied.
18*
19****************************************************************************/
20
21#ifndef TTSSAPI4_H
22#define TTSSAPI4_H
23
24#include "ttsbase.h"
25#include "ttssapi.h"
26
27class TTSSapi4: public TTSSapi
28{
29 Q_OBJECT
30 public:
31 TTSSapi4(QObject* parent=nullptr) : TTSSapi(parent)
32 {
33 m_TTSTemplate = "cscript //nologo \"%exe\" "
34 "/language:%lang /voice:\"%voice\" "
35 "/speed:%speed \"%options\" /sapi4";
36 m_TTSVoiceTemplate = "cscript //nologo \"%exe\" "
37 "/language:%lang /listvoices /sapi4";
38 m_TTSType = "sapi4";
39 }
40
41};
42
43#endif
diff --git a/utils/rbutilqt/base/ttsswift.h b/utils/rbutilqt/base/ttsswift.h
new file mode 100644
index 0000000000..adbc674d78
--- /dev/null
+++ b/utils/rbutilqt/base/ttsswift.h
@@ -0,0 +1,40 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8*
9* Copyright (C) 2012 by Dominik Riebeling
10*
11* This program is free software; you can redistribute it and/or
12* modify it under the terms of the GNU General Public License
13* as published by the Free Software Foundation; either version 2
14* of the License, or (at your option) any later version.
15*
16* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17* KIND, either express or implied.
18*
19****************************************************************************/
20
21#ifndef TTSSWIFT_H
22#define TTSSWIFT_H
23
24#include <QtCore>
25#include "ttsexes.h"
26
27class TTSSwift : public TTSExes
28{
29 Q_OBJECT
30 public:
31 TTSSwift(QObject* parent=nullptr) : TTSExes(parent)
32 {
33 m_name = "swift";
34 m_TTSTemplate = "\"%exe\" %options -o \"%wavfile\" -- \"%text\"";
35 m_TTSSpeakTemplate = "";
36 m_capabilities = TTSBase::None;
37 }
38};
39
40#endif
diff --git a/utils/rbutilqt/base/uninstall.cpp b/utils/rbutilqt/base/uninstall.cpp
new file mode 100644
index 0000000000..5ab670a031
--- /dev/null
+++ b/utils/rbutilqt/base/uninstall.cpp
@@ -0,0 +1,126 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "uninstall.h"
21#include "utils.h"
22#include "Logger.h"
23
24Uninstaller::Uninstaller(QObject* parent,QString mountpoint): QObject(parent)
25{
26 m_mountpoint = mountpoint;
27}
28
29void Uninstaller::deleteAll(void)
30{
31 QString rbdir(m_mountpoint + ".rockbox/");
32 emit logItem(tr("Starting Uninstallation"), LOGINFO);
33 emit logProgress(0, 0);
34 Utils::recursiveRmdir(rbdir);
35 emit logProgress(1, 1);
36 emit logItem(tr("Finished Uninstallation"), LOGOK);
37 emit logFinished();
38}
39
40void Uninstaller::uninstall(void)
41{
42 emit logProgress(0, 0);
43 emit logItem(tr("Starting Uninstallation"), LOGINFO);
44
45 QSettings installlog(m_mountpoint + "/.rockbox/rbutil.log", QSettings::IniFormat, this);
46
47 for(int i=0; i< uninstallSections.size() ; i++)
48 {
49 emit logItem(tr("Uninstalling %1...").arg(uninstallSections.at(i)), LOGINFO);
50 QCoreApplication::processEvents();
51 // create list of all other install sections
52 QStringList sections = installlog.childGroups();
53 sections.removeAt(sections.indexOf(uninstallSections.at(i)));
54 installlog.beginGroup(uninstallSections.at(i));
55 QStringList toDeleteList = installlog.allKeys();
56 QStringList dirList;
57 installlog.endGroup();
58
59 // iterate over all entries
60 for(int j =0; j < toDeleteList.size(); j++ )
61 {
62 // check if current file is in use by another section
63 bool deleteFile = true;
64 for(int s = 0; s < sections.size(); s++)
65 {
66 installlog.beginGroup(sections.at(s));
67 if(installlog.contains(toDeleteList.at(j)))
68 {
69 deleteFile = false;
70 LOG_INFO() << "file still in use:" << toDeleteList.at(j);
71 }
72 installlog.endGroup();
73 }
74
75 installlog.beginGroup(uninstallSections.at(i));
76 QFileInfo toDelete(m_mountpoint + "/" + toDeleteList.at(j));
77 if(toDelete.isFile()) // if it is a file remove it
78 {
79 if(deleteFile && !QFile::remove(toDelete.filePath()))
80 emit logItem(tr("Could not delete %1")
81 .arg(toDelete.filePath()), LOGWARNING);
82 installlog.remove(toDeleteList.at(j));
83 LOG_INFO() << "deleted:" << toDelete.filePath();
84 }
85 else // if it is a dir, remember it for later deletion
86 {
87 // no need to keep track on folders still in use -- only empty
88 // folders will be rm'ed.
89 dirList << toDeleteList.at(j);
90 }
91 installlog.endGroup();
92 QCoreApplication::processEvents();
93 }
94 // delete the dirs
95 installlog.beginGroup(uninstallSections.at(i));
96 for(int j=0; j < dirList.size(); j++ )
97 {
98 installlog.remove(dirList.at(j));
99 QDir dir(m_mountpoint);
100 dir.rmdir(dirList.at(j)); // rm works only on empty folders
101 }
102
103 installlog.endGroup();
104 //installlog.removeGroup(uninstallSections.at(i))
105 }
106 uninstallSections.clear();
107 installlog.sync();
108 emit logProgress(1, 1);
109 emit logItem(tr("Uninstallation finished"), LOGOK);
110 emit logFinished();
111}
112
113QStringList Uninstaller::getAllSections()
114{
115 QSettings installlog(m_mountpoint + "/.rockbox/rbutil.log", QSettings::IniFormat, nullptr);
116 QStringList allSections = installlog.childGroups();
117 allSections.removeAt(allSections.lastIndexOf("Bootloader"));
118 return allSections;
119}
120
121
122bool Uninstaller::uninstallPossible()
123{
124 return QFileInfo::exists(m_mountpoint +"/.rockbox/rbutil.log");
125}
126
diff --git a/utils/rbutilqt/base/uninstall.h b/utils/rbutilqt/base/uninstall.h
new file mode 100644
index 0000000000..d8c9815117
--- /dev/null
+++ b/utils/rbutilqt/base/uninstall.h
@@ -0,0 +1,63 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21
22#ifndef UNINSTALL_H
23#define UNINSTALL_H
24
25#include <QtCore>
26
27#include "progressloggerinterface.h"
28
29
30class Uninstaller : public QObject
31{
32 Q_OBJECT
33public:
34 Uninstaller(QObject* parent,QString mountpoint) ;
35 ~Uninstaller(){}
36
37 void deleteAll(void);
38 void uninstall(void);
39
40 bool uninstallPossible();
41
42 QStringList getAllSections();
43
44 void setSections(QStringList sections) {uninstallSections = sections;}
45
46signals:
47 void logItem(QString, int); //! set logger item
48 void logProgress(int, int); //! set progress bar.
49 void logFinished(void);
50
51private slots:
52
53
54private:
55 QString m_mountpoint;
56
57 QStringList uninstallSections;
58};
59
60
61
62#endif
63
diff --git a/utils/rbutilqt/base/utils.cpp b/utils/rbutilqt/base/utils.cpp
new file mode 100644
index 0000000000..f2d3f04887
--- /dev/null
+++ b/utils/rbutilqt/base/utils.cpp
@@ -0,0 +1,1062 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include "utils.h"
20#include "rockboxinfo.h"
21#include "system.h"
22#include "rbsettings.h"
23#include "playerbuildinfo.h"
24#include "Logger.h"
25
26#if !defined(_UNICODE)
27#define _UNICODE
28#endif
29
30#include <QtCore>
31#include <QDebug>
32#include <cstdlib>
33#include <stdio.h>
34
35#if defined(Q_OS_LINUX) || defined(Q_OS_MACX)
36#include <sys/statvfs.h>
37#endif
38#if defined(Q_OS_LINUX) || defined(Q_OS_MACX)
39#include <stdio.h>
40#endif
41#if defined(Q_OS_LINUX)
42#include <mntent.h>
43#endif
44#if defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
45#include <sys/param.h>
46#include <sys/ucred.h>
47#include <sys/mount.h>
48#endif
49#if defined(Q_OS_WIN32)
50#include <stdio.h>
51#include <tchar.h>
52#include <windows.h>
53#include <setupapi.h>
54#include <winioctl.h>
55#include <tlhelp32.h>
56#endif
57#if defined(Q_OS_MACX)
58#include <Carbon/Carbon.h>
59#include <CoreFoundation/CoreFoundation.h>
60#include <CoreServices/CoreServices.h>
61#include <IOKit/IOKitLib.h>
62#endif
63
64// recursive function to delete a dir with files
65bool Utils::recursiveRmdir( const QString &dirName )
66{
67 QString dirN = dirName;
68 QDir dir(dirN);
69 // make list of entries in directory
70 QStringList list = dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot);
71 QFileInfo fileInfo;
72 QString curItem;
73 for(int i = 0; i < list.size(); i++){ // loop through all items of list
74 QString name = list.at(i);
75 curItem = dirN + "/" + name;
76 fileInfo.setFile(curItem);
77 if(fileInfo.isDir()) // is directory
78 recursiveRmdir(curItem); // call recRmdir() recursively for
79 // deleting subdirectory
80 else // is file
81 QFile::remove(curItem); // ok, delete file
82 }
83 dir.cdUp();
84 return dir.rmdir(dirN); // delete empty dir and return if (now empty)
85 // dir-removing was successfull
86}
87
88
89//! @brief resolves the given path, ignoring case.
90//! @param path absolute path to resolve.
91//! @return returns exact casing of path, empty string if path not found.
92QString Utils::resolvePathCase(QString path)
93{
94 int start;
95 QString realpath;
96#if QT_VERSION >= 0x050e00
97 QStringList elems = path.split("/", Qt::SkipEmptyParts);
98#else
99 QStringList elems = path.split("/", QString::SkipEmptyParts);
100#endif
101
102 if(path.isEmpty())
103 return QString();
104#if defined(Q_OS_WIN32)
105 // on windows we must make sure to start with the first entry (i.e. the
106 // drive letter) instead of a single / to make resolving work.
107 start = 1;
108 realpath = elems.at(0) + "/";
109#else
110 start = 0;
111 realpath = "/";
112#endif
113
114 for(int i = start; i < elems.size(); i++) {
115 QStringList direlems
116 = QDir(realpath).entryList(QDir::AllEntries|QDir::Hidden|QDir::System);
117 if(direlems.contains(elems.at(i), Qt::CaseInsensitive)) {
118 // need to filter using QRegExp as QStringList::filter(QString)
119 // matches any substring
120 QString expr = QString("^" + elems.at(i) + "$");
121 QRegExp rx = QRegExp(expr, Qt::CaseInsensitive);
122 QStringList a = direlems.filter(rx);
123
124 if(a.size() != 1)
125 return QString("");
126 if(!realpath.endsWith("/"))
127 realpath += "/";
128 realpath += a.at(0);
129 }
130 else
131 return QString("");
132 }
133 LOG_INFO() << "resolving path" << path << "->" << realpath;
134 return realpath;
135}
136
137
138QString Utils::filesystemType(QString path)
139{
140#if defined(Q_OS_LINUX)
141 FILE *mn = setmntent("/etc/mtab", "r");
142 if(!mn)
143 return QString("");
144
145 struct mntent *ent;
146 while((ent = getmntent(mn))) {
147 if(QString(ent->mnt_dir) == path) {
148 endmntent(mn);
149 LOG_INFO() << "device type is" << ent->mnt_type;
150 return QString(ent->mnt_type);
151 }
152 }
153 endmntent(mn);
154#endif
155
156#if defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
157 int num;
158 struct statfs *mntinf;
159
160 num = getmntinfo(&mntinf, MNT_WAIT);
161 while(num--) {
162 if(QString(mntinf->f_mntonname) == path) {
163 LOG_INFO() << "device type is" << mntinf->f_fstypename;
164 return QString(mntinf->f_fstypename);
165 }
166 mntinf++;
167 }
168#endif
169
170#if defined(Q_OS_WIN32)
171 wchar_t t[64];
172 memset(t, 0, 32);
173 if(GetVolumeInformationW((LPCWSTR)path.utf16(),
174 NULL, 0, NULL, NULL, NULL, t, 64)) {
175 LOG_INFO() << "device type is" << t;
176 return QString::fromWCharArray(t);
177 }
178#endif
179 return QString("-");
180}
181
182
183QString Utils::filesystemName(QString path)
184{
185 QString name;
186#if defined(Q_OS_WIN32)
187 wchar_t volname[MAX_PATH+1];
188 bool res = GetVolumeInformationW((LPTSTR)path.utf16(), volname, MAX_PATH+1,
189 NULL, NULL, NULL, NULL, 0);
190 if(res) {
191 name = QString::fromWCharArray(volname);
192 }
193#endif
194#if defined(Q_OS_MACX)
195 // BSD label does not include folder.
196 QString bsd = Utils::resolveDevicename(path).remove("/dev/");
197 if(bsd.isEmpty()) {
198 return name;
199 }
200 OSStatus result;
201 ItemCount index = 1;
202
203 do {
204 FSVolumeRefNum volrefnum;
205 HFSUniStr255 volname;
206
207 result = FSGetVolumeInfo(kFSInvalidVolumeRefNum, index, &volrefnum,
208 kFSVolInfoFSInfo, NULL, &volname, NULL);
209
210 if(result == noErr) {
211 GetVolParmsInfoBuffer volparms;
212 /* PBHGetVolParmsSync() is not available for 64bit while
213 FSGetVolumeParms() is available in 10.5+. Thus we need to use
214 PBHGetVolParmsSync() for 10.4, and that also requires 10.4 to
215 always use 32bit.
216 Qt 4 supports 32bit on 10.6 Cocoa only.
217 */
218#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
219 if(FSGetVolumeParms(volrefnum, &volparms, sizeof(volparms)) == noErr)
220#else
221 HParamBlockRec hpb;
222 hpb.ioParam.ioNamePtr = NULL;
223 hpb.ioParam.ioVRefNum = volrefnum;
224 hpb.ioParam.ioBuffer = (Ptr)&volparms;
225 hpb.ioParam.ioReqCount = sizeof(volparms);
226 if(PBHGetVolParmsSync(&hpb) == noErr)
227#endif
228 {
229 if(volparms.vMServerAdr == 0) {
230 if(bsd == (char*)volparms.vMDeviceID) {
231 name = QString::fromUtf16((const ushort*)volname.unicode,
232 (int)volname.length);
233 break;
234 }
235 }
236 }
237 }
238 index++;
239 } while(result == noErr);
240#endif
241
242 LOG_INFO() << "Volume name of" << path << "is" << name;
243 return name;
244}
245
246
247//! @brief figure the free disk space on a filesystem
248//! @param path path on the filesystem to check
249//! @return size in bytes
250qulonglong Utils::filesystemFree(QString path)
251{
252 qulonglong size = filesystemSize(path, FilesystemFree);
253 LOG_INFO() << "free disk space for" << path << size;
254 return size;
255}
256
257
258qulonglong Utils::filesystemTotal(QString path)
259{
260 qulonglong size = filesystemSize(path, FilesystemTotal);
261 LOG_INFO() << "total disk space for" << path << size;
262 return size;
263}
264
265
266qulonglong Utils::filesystemSize(QString path, enum Utils::Size type)
267{
268 qulonglong size = 0;
269#if defined(Q_OS_LINUX) || defined(Q_OS_MACX)
270 // the usage of statfs() is deprecated by the LSB so use statvfs().
271 struct statvfs fs;
272 int ret;
273
274 ret = statvfs(qPrintable(path), &fs);
275
276 if(ret == 0) {
277 if(type == FilesystemFree) {
278 size = (qulonglong)fs.f_frsize * (qulonglong)fs.f_bavail;
279 }
280 if(type == FilesystemTotal) {
281 size = (qulonglong)fs.f_frsize * (qulonglong)fs.f_blocks;
282 }
283 if(type == FilesystemClusterSize) {
284 size = (qulonglong)fs.f_frsize;
285 }
286 }
287#endif
288#if defined(Q_OS_WIN32)
289 BOOL ret;
290 ULARGE_INTEGER freeAvailBytes;
291 ULARGE_INTEGER totalNumberBytes;
292
293 ret = GetDiskFreeSpaceExW((LPCTSTR)path.utf16(), &freeAvailBytes,
294 &totalNumberBytes, NULL);
295 if(ret) {
296 if(type == FilesystemFree) {
297 size = freeAvailBytes.QuadPart;
298 }
299 if(type == FilesystemTotal) {
300 size = totalNumberBytes.QuadPart;
301 }
302 if(type == FilesystemClusterSize) {
303 DWORD sectorsPerCluster;
304 DWORD bytesPerSector;
305 DWORD freeClusters;
306 DWORD totalClusters;
307 ret = GetDiskFreeSpaceW((LPCTSTR)path.utf16(), &sectorsPerCluster,
308 &bytesPerSector, &freeClusters, &totalClusters);
309 if(ret) {
310 size = bytesPerSector * sectorsPerCluster;
311 }
312 }
313 }
314#endif
315 return size;
316}
317
318//! \brief searches for a Executable in the Environement Path
319QString Utils::findExecutable(QString name)
320{
321 //try autodetect tts
322#if defined(Q_OS_LINUX) || defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
323#if QT_VERSION >= 0x050e00
324 QStringList path = QString(getenv("PATH")).split(":", Qt::SkipEmptyParts);
325#else
326 QStringList path = QString(getenv("PATH")).split(":", QString::SkipEmptyParts);
327#endif
328#elif defined(Q_OS_WIN)
329#if QT_VERSION >= 0x050e00
330 QStringList path = QString(getenv("PATH")).split(";", Qt::SkipEmptyParts);
331#else
332 QStringList path = QString(getenv("PATH")).split(";", QString::SkipEmptyParts);
333#endif
334#endif
335 LOG_INFO() << "system path:" << path;
336 for(int i = 0; i < path.size(); i++)
337 {
338 QString executable = QDir::fromNativeSeparators(path.at(i)) + "/" + name;
339#if defined(Q_OS_WIN)
340 executable += ".exe";
341#if QT_VERSION >= 0x050e00
342 QStringList ex = executable.split("\"", Qt::SkipEmptyParts);
343#else
344 QStringList ex = executable.split("\"", QString::SkipEmptyParts);
345#endif
346 executable = ex.join("");
347#endif
348 if(QFileInfo(executable).isExecutable())
349 {
350 LOG_INFO() << "findExecutable: found" << executable;
351 return QDir::toNativeSeparators(executable);
352 }
353 }
354 LOG_INFO() << "findExecutable: could not find" << name;
355 return "";
356}
357
358
359/** @brief checks different Enviroment things. Ask if user wants to continue.
360 * @param permission if it should check for permission
361 * @return string with error messages if problems occurred, empty strings if none.
362 */
363QString Utils::checkEnvironment(bool permission)
364{
365 LOG_INFO() << "checking environment";
366 QString text = "";
367
368 // check permission
369 if(permission)
370 {
371#if defined(Q_OS_WIN32)
372 if(System::userPermissions() != System::ADMIN)
373 {
374 text += tr("<li>Permissions insufficient for bootloader "
375 "installation.\nAdministrator priviledges are necessary.</li>");
376 }
377#endif
378 }
379
380 // Check TargetId
381 RockboxInfo rbinfo(RbSettings::value(RbSettings::Mountpoint).toString());
382 QString installed = rbinfo.target();
383 if(!installed.isEmpty() && installed !=
384 RbSettings::value(RbSettings::CurrentPlatform).toString().split(".").at(0))
385 {
386 text += tr("<li>Target mismatch detected.<br/>"
387 "Installed target: %1<br/>Selected target: %2.</li>")
388 .arg(PlayerBuildInfo::instance()->value(
389 PlayerBuildInfo::DisplayName, installed).toString(),
390 PlayerBuildInfo::instance()->value(
391 PlayerBuildInfo::DisplayName).toString());
392 }
393
394 if(!text.isEmpty())
395 return tr("Problem detected:") + "<ul>" + text + "</ul>";
396 else
397 return text;
398}
399
400/** @brief Trim version string from filename to version part only.
401 * @param s Version string
402 * @return Version part of string if found, input string on error.
403 */
404QString Utils::trimVersionString(QString s)
405{
406 QRegExp r = QRegExp(".*([\\d\\.]+\\d+[a-z]?).*");
407 if(r.indexIn(s) != -1) {
408 return r.cap(1);
409 }
410 return s;
411}
412
413/** @brief Compare two version strings.
414 * @param s1 first version string
415 * @param s2 second version string
416 * @return 0 if strings identical, 1 if second is newer, -1 if first.
417 */
418int Utils::compareVersionStrings(QString s1, QString s2)
419{
420 LOG_INFO() << "comparing version strings" << s1 << "and" << s2;
421 QString a = s1.trimmed();
422 QString b = s2.trimmed();
423 // if strings are identical return 0.
424 if(a.isEmpty())
425 return 1;
426 if(b.isEmpty())
427 return -1;
428
429 while(!a.isEmpty() || !b.isEmpty()) {
430 // trim all leading non-digits and non-dots (dots are removed afterwards)
431 a.remove(QRegExp("^[^\\d\\.]*"));
432 b.remove(QRegExp("^[^\\d\\.]*"));
433
434 // trim all trailing non-digits for conversion (QString::toInt()
435 // requires this). Copy strings first as replace() changes the string.
436 QString numa = a;
437 QString numb = b;
438 numa.remove(QRegExp("\\D+.*$"));
439 numb.remove(QRegExp("\\D+.*$"));
440
441 // convert to number
442 bool ok1, ok2;
443 unsigned int vala = numa.toUInt(&ok1);
444 unsigned int valb = numb.toUInt(&ok2);
445 // if none of the numbers converted successfully we're at trailing garbage.
446 if(!ok1 && !ok2)
447 break;
448 if(!ok1)
449 return 1;
450 if(!ok2)
451 return -1;
452
453 // if numbers mismatch we have a decision.
454 if(vala != valb)
455 return (vala > valb) ? -1 : 1;
456
457 // trim leading digits.
458 a.remove(QRegExp("^\\d*"));
459 b.remove(QRegExp("^\\d*"));
460
461 // If only one of the following characters is a dot that one is
462 // "greater" then anything else. Make sure it's followed by a number,
463 // Otherwise it might be the end of the string or suffix. Do this
464 // before version addon characters check to avoid stopping too early.
465 bool adot = a.contains(QRegExp("^[a-zA-Z]*\\.[0-9]"));
466 bool bdot = b.contains(QRegExp("^[a-zA-Z]*\\.[0-9]"));
467 if(adot && !bdot)
468 return -1;
469 if(!adot && bdot)
470 return 1;
471 // if number is immediately followed by a character consider it as
472 // version addon (like 1.2.3b). In this case compare characters and end
473 // (version numbers like 1.2b.3 aren't handled).
474 QChar ltra;
475 QChar ltrb;
476 if(a.contains(QRegExp("^[a-zA-Z]")))
477 ltra = a.at(0);
478 if(b.contains(QRegExp("^[a-zA-Z]")))
479 ltrb = b.at(0);
480 if(ltra != ltrb)
481 return (ltra < ltrb) ? 1 : -1;
482
483 // both are identical or no addon characters, ignore.
484 // remove modifiers and following dot.
485 a.remove(QRegExp("^[a-zA-Z]*\\."));
486 b.remove(QRegExp("^[a-zA-Z]*\\."));
487 }
488
489 // no differences found.
490 return 0;
491}
492
493
494/** Resolve mountpoint to devicename / disk number
495 * @param path mountpoint path / drive letter
496 * @return devicename / disk number
497 */
498QString Utils::resolveDevicename(QString path)
499{
500 LOG_INFO() << "resolving device name" << path;
501#if defined(Q_OS_LINUX)
502 FILE *mn = setmntent("/etc/mtab", "r");
503 if(!mn)
504 return QString("");
505
506 struct mntent *ent;
507 while((ent = getmntent(mn))) {
508 // check for valid filesystem type.
509 // Linux can handle hfs (and hfsplus), so consider it a valid file
510 // system. Otherwise resolving the device name would fail, which in
511 // turn would make it impossible to warn about a MacPod.
512 if(QString(ent->mnt_dir) == path
513 && (QString(ent->mnt_type).contains("vfat", Qt::CaseInsensitive)
514 || QString(ent->mnt_type).contains("hfs", Qt::CaseInsensitive))) {
515 endmntent(mn);
516 LOG_INFO() << "device name is" << ent->mnt_fsname;
517 return QString(ent->mnt_fsname);
518 }
519 }
520 endmntent(mn);
521
522#endif
523
524#if defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
525 int num;
526 struct statfs *mntinf;
527
528 num = getmntinfo(&mntinf, MNT_WAIT);
529 while(num--) {
530 // check for valid filesystem type. OS X can handle hfs (hfs+ is
531 // treated as hfs), BSD should be the same.
532 if(QString(mntinf->f_mntonname) == path
533 && (QString(mntinf->f_fstypename).contains("msdos", Qt::CaseInsensitive)
534 || QString(mntinf->f_fstypename).contains("hfs", Qt::CaseInsensitive))) {
535 LOG_INFO() << "device name is" << mntinf->f_mntfromname;
536 return QString(mntinf->f_mntfromname);
537 }
538 mntinf++;
539 }
540#endif
541
542#if defined(Q_OS_WIN32)
543 DWORD written;
544 HANDLE h;
545 TCHAR uncpath[MAX_PATH];
546 UCHAR buffer[0x400];
547 PVOLUME_DISK_EXTENTS extents = (PVOLUME_DISK_EXTENTS)buffer;
548
549 _stprintf(uncpath, _TEXT("\\\\.\\%c:"), path.toLatin1().at(0));
550 h = CreateFile(uncpath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
551 NULL, OPEN_EXISTING, 0, NULL);
552 if(h == INVALID_HANDLE_VALUE) {
553 //LOG_INFO() << "error getting extents for" << uncpath;
554 return "";
555 }
556 // get the extents
557 if(DeviceIoControl(h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
558 NULL, 0, extents, sizeof(buffer), &written, NULL)) {
559 if(extents->NumberOfDiskExtents == 1) {
560 CloseHandle(h);
561 LOG_INFO() << "device name is" << extents->Extents[0].DiskNumber;
562 return QString("%1").arg(extents->Extents[0].DiskNumber);
563 }
564 LOG_INFO() << "resolving device name: volume spans multiple disks!";
565 }
566 CloseHandle(h);
567#endif
568 return QString("");
569
570}
571
572
573/** resolve device name to mount point / drive letter
574 * @param device device name / disk number
575 * @return mount point / drive letter
576 */
577QString Utils::resolveMountPoint(QString device)
578{
579 LOG_INFO() << "resolving mountpoint:" << device;
580
581#if defined(Q_OS_LINUX)
582 FILE *mn = setmntent("/etc/mtab", "r");
583 if(!mn)
584 return QString("");
585
586 struct mntent *ent;
587 while((ent = getmntent(mn))) {
588 // Check for valid filesystem. Allow hfs too, as an Ipod might be a
589 // MacPod.
590 if(QString(ent->mnt_fsname) == device) {
591 QString result;
592 if(QString(ent->mnt_type).contains("vfat", Qt::CaseInsensitive)
593 || QString(ent->mnt_type).contains("hfs", Qt::CaseInsensitive)) {
594 LOG_INFO() << "resolved mountpoint is:" << ent->mnt_dir;
595 result = QString(ent->mnt_dir);
596 }
597 else {
598 LOG_INFO() << "mountpoint is wrong filesystem!";
599 }
600 endmntent(mn);
601 return result;
602 }
603 }
604 endmntent(mn);
605
606#endif
607
608#if defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
609 int num;
610 struct statfs *mntinf;
611
612 num = getmntinfo(&mntinf, MNT_WAIT);
613 while(num--) {
614 // Check for valid filesystem. Allow hfs too, as an Ipod might be a
615 // MacPod.
616 if(QString(mntinf->f_mntfromname) == device) {
617 if(QString(mntinf->f_fstypename).contains("msdos", Qt::CaseInsensitive)
618 || QString(mntinf->f_fstypename).contains("hfs", Qt::CaseInsensitive)) {
619 LOG_INFO() << "resolved mountpoint is:" << mntinf->f_mntonname;
620 return QString(mntinf->f_mntonname);
621 }
622 else {
623 LOG_INFO() << "mountpoint is wrong filesystem!";
624 return QString();
625 }
626 }
627 mntinf++;
628 }
629#endif
630
631#if defined(Q_OS_WIN32)
632 QString result;
633 unsigned int driveno = device.replace(QRegExp("^.*([0-9]+)"), "\\1").toInt();
634
635 int letter;
636 for(letter = 'A'; letter <= 'Z'; letter++) {
637 if(resolveDevicename(QString(letter)).toUInt() == driveno) {
638 result = letter;
639 LOG_INFO() << "resolved mountpoint is:" << result;
640 break;
641 }
642 }
643 if(!result.isEmpty())
644 return result + ":/";
645#endif
646 LOG_INFO() << "resolving mountpoint failed!";
647 return QString("");
648}
649
650
651QStringList Utils::mountpoints(enum MountpointsFilter type)
652{
653 QStringList supported;
654 QStringList tempList;
655#if defined(Q_OS_WIN32)
656 supported << "FAT32" << "FAT16" << "FAT12" << "FAT" << "HFS";
657 QFileInfoList list = QDir::drives();
658 for(int i=0; i<list.size();i++)
659 {
660 wchar_t t[32];
661 memset(t, 0, 32);
662 if(GetVolumeInformationW((LPCWSTR)list.at(i).absolutePath().utf16(),
663 NULL, 0, NULL, NULL, NULL, t, 32) == 0) {
664 // on error empty retrieved type -- don't rely on
665 // GetVolumeInformation not changing it.
666 memset(t, 0, sizeof(t));
667 }
668
669 QString fstype = QString::fromWCharArray(t);
670 if(type == MountpointsAll || supported.contains(fstype)) {
671 tempList << list.at(i).absolutePath();
672 LOG_INFO() << "Added:" << list.at(i).absolutePath()
673 << "type" << fstype;
674 }
675 else {
676 LOG_INFO() << "Ignored:" << list.at(i).absolutePath()
677 << "type" << fstype;
678 }
679 }
680
681#elif defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
682 supported << "vfat" << "msdos" << "hfs";
683 int num;
684 struct statfs *mntinf;
685
686 num = getmntinfo(&mntinf, MNT_WAIT);
687 while(num--) {
688 if(type == MountpointsAll || supported.contains(mntinf->f_fstypename)) {
689 tempList << QString(mntinf->f_mntonname);
690 LOG_INFO() << "Added:" << mntinf->f_mntonname
691 << "is" << mntinf->f_mntfromname << "type" << mntinf->f_fstypename;
692 }
693 else {
694 LOG_INFO() << "Ignored:" << mntinf->f_mntonname
695 << "is" << mntinf->f_mntfromname << "type" << mntinf->f_fstypename;
696 }
697 mntinf++;
698 }
699#elif defined(Q_OS_LINUX)
700 supported << "vfat" << "msdos" << "hfsplus";
701 FILE *mn = setmntent("/etc/mtab", "r");
702 if(!mn)
703 return QStringList("");
704
705 struct mntent *ent;
706 while((ent = getmntent(mn))) {
707 if(type == MountpointsAll || supported.contains(ent->mnt_type)) {
708 tempList << QString(ent->mnt_dir);
709 LOG_INFO() << "Added:" << ent->mnt_dir
710 << "is" << ent->mnt_fsname << "type" << ent->mnt_type;
711 }
712 else {
713 LOG_INFO() << "Ignored:" << ent->mnt_dir
714 << "is" << ent->mnt_fsname << "type" << ent->mnt_type;
715 }
716 }
717 endmntent(mn);
718
719#else
720#error Unknown Platform
721#endif
722 return tempList;
723}
724
725
726/** Check if a process with a given name is running
727 * @param names list of names to filter on. All processes if empty list.
728 * @return list of processname, process ID pairs.
729 */
730QMap<QString, QList<int> > Utils::findRunningProcess(QStringList names)
731{
732 QMap<QString, QList<int> > processlist;
733 QMap<QString, QList<int> > found;
734#if defined(Q_OS_WIN32)
735 HANDLE hdl;
736 PROCESSENTRY32 entry;
737 bool result;
738
739 hdl = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
740 if(hdl == INVALID_HANDLE_VALUE) {
741 LOG_ERROR() << "CreateToolhelp32Snapshot failed.";
742 return found;
743 }
744 entry.dwSize = sizeof(PROCESSENTRY32);
745 entry.szExeFile[0] = '\0';
746 if(!Process32First(hdl, &entry)) {
747 LOG_ERROR() << "Process32First failed.";
748 return found;
749 }
750
751 do {
752 int pid = entry.th32ProcessID; // FIXME: DWORD vs int!
753 QString name = QString::fromWCharArray(entry.szExeFile);
754 if(processlist.find(name) == processlist.end()) {
755 processlist.insert(name, QList<int>());
756 }
757 processlist[name].append(pid);
758 entry.dwSize = sizeof(PROCESSENTRY32);
759 entry.szExeFile[0] = '\0';
760 result = Process32Next(hdl, &entry);
761 } while(result);
762 CloseHandle(hdl);
763#endif
764#if defined(Q_OS_MACX)
765 ProcessSerialNumber psn = { 0, kNoProcess };
766 OSErr err;
767 do {
768 pid_t pid;
769 err = GetNextProcess(&psn);
770 err = GetProcessPID(&psn, &pid);
771 if(err == noErr) {
772 char buf[32] = {0};
773 ProcessInfoRec info;
774 memset(&info, 0, sizeof(ProcessInfoRec));
775 info.processName = (unsigned char*)buf;
776 info.processInfoLength = sizeof(ProcessInfoRec);
777 err = GetProcessInformation(&psn, &info);
778 if(err == noErr) {
779 // some processes start with nonprintable characters. Skip those.
780 int i;
781 for(i = 0; i < 32; i++) {
782 if(isprint(buf[i])) break;
783 }
784 // avoid adding duplicates.
785 QString name = QString::fromUtf8(&buf[i]);
786 if(processlist.find(name) == processlist.end()) {
787 processlist.insert(name, QList<int>());
788 }
789 processlist[name].append(pid);
790 }
791 }
792 } while(err == noErr);
793#endif
794#if defined(Q_OS_LINUX)
795 // not implemented for Linux!
796#endif
797 // Filter for names (unless empty)
798 if(names.size() > 0) {
799 for(int i = 0; i < names.size(); ++i) {
800 QStringList k(processlist.keys());
801#if defined(Q_OS_WIN32)
802 // the process name might be truncated. Allow the extension to be partial.
803 int index = k.indexOf(QRegExp(names.at(i) + "(\\.(e(x(e?)?)?)?)?",
804 Qt::CaseInsensitive));
805#else
806 int index = k.indexOf(names.at(i));
807#endif
808 if(index != -1) {
809 found.insert(k[index], processlist[k[index]]);
810 }
811 }
812 }
813 else {
814 found = processlist;
815 }
816 LOG_INFO() << "Looking for processes" << names << "found" << found;
817 return found;
818}
819
820
821/** Suspends/resumes processes
822 * @param pidlist a list of PIDs to suspend/resume
823 * @param suspend processes are suspended if true, or resumed when false
824 * @return a list of PIDs successfully suspended/resumed
825 */
826QList<int> Utils::suspendProcess(QList<int> pidlist, bool suspend)
827{
828 QList<int> result;
829#if defined(Q_OS_WIN32)
830 // Enable debug privilege
831 HANDLE hToken = NULL;
832 LUID seDebugValue;
833 TOKEN_PRIVILEGES tNext, tPrev;
834 DWORD sPrev;
835 if(LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &seDebugValue)) {
836 if(OpenProcessToken(GetCurrentProcess(),
837 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
838 memset(&tNext, 0, sizeof(tNext));
839 tNext.PrivilegeCount = 1;
840 tNext.Privileges[0].Luid = seDebugValue;
841 tNext.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
842 if(!AdjustTokenPrivileges(hToken, FALSE, &tNext, sizeof(tNext),
843 &tPrev, &sPrev) || GetLastError() != 0) {
844 CloseHandle(hToken);
845 hToken = NULL;
846 LOG_ERROR() << "AdjustTokenPrivileges(next) error" << GetLastError();
847 }
848 }
849 else {
850 LOG_ERROR() << "OpenProcessToken error" << GetLastError();
851 }
852 }
853 else {
854 LOG_ERROR() << "LookupPrivilegeValue error" << GetLastError();
855 }
856
857 // Suspend/resume threads
858 for(int i = 0; i < pidlist.size(); i++) {
859 HANDLE hdl = INVALID_HANDLE_VALUE;
860 THREADENTRY32 entry;
861 int n_fails = 0;
862
863 hdl = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
864 if(hdl == INVALID_HANDLE_VALUE) {
865 LOG_ERROR() << "CreateToolhelp32Snapshot error" << GetLastError();
866 continue;
867 }
868 entry.dwSize = sizeof(THREADENTRY32);
869 if(!Thread32First(hdl, &entry)) {
870 LOG_ERROR() << "Process32First error" << GetLastError();
871 CloseHandle(hdl);
872 continue;
873 }
874
875 do {
876 if(entry.th32OwnerProcessID != (DWORD)(pidlist[i]))
877 continue;
878 HANDLE thr = OpenThread(THREAD_SUSPEND_RESUME,
879 FALSE, entry.th32ThreadID);
880 if(!thr) {
881 LOG_ERROR() << "OpenThread" << entry.th32ThreadID
882 << "error" << GetLastError();
883 n_fails++;
884 continue;
885 }
886 if(suspend) {
887 // Execution of the specified thread is suspended and
888 // the thread's suspend count is incremented.
889 if(SuspendThread(thr) == (DWORD)(-1)) {
890 LOG_ERROR() << "SuspendThread" << entry.th32ThreadID
891 << "error" << GetLastError();
892 n_fails++;
893 }
894 }
895 else {
896 // Decrements a thread's suspend count. When the
897 // suspend count is decremented to zero, the
898 // execution of the thread is resumed.
899 if(ResumeThread(thr) == (DWORD)(-1)) {
900 LOG_ERROR() << "ResumeThread" << entry.th32ThreadID
901 << "error" << GetLastError();
902 n_fails++;
903 }
904 }
905 CloseHandle(thr);
906 } while(Thread32Next(hdl, &entry));
907 if (!n_fails)
908 result.append(pidlist[i]);
909 CloseHandle(hdl);
910 }
911
912 // Restore previous debug privilege
913 if (hToken) {
914 if(!AdjustTokenPrivileges(hToken, FALSE,
915 &tPrev, sPrev, NULL, NULL) || GetLastError() != 0) {
916 LOG_ERROR() << "AdjustTokenPrivileges(prev) error" << GetLastError();
917 }
918 CloseHandle(hToken);
919 }
920#endif
921#if defined(Q_OS_MACX)
922 int signal = suspend ? SIGSTOP : SIGCONT;
923 for(int i = 0; i < pidlist.size(); i++) {
924 pid_t pid = pidlist[i];
925 if(kill(pid, signal) != 0) {
926 LOG_ERROR() << "kill signal" << signal
927 << "for PID" << pid << "error:" << errno;
928 }
929 else {
930 result.append(pidlist[i]);
931 }
932 }
933#endif
934#if defined(Q_OS_LINUX)
935 // not implemented for Linux!
936#endif
937 LOG_INFO() << (suspend ? "Suspending" : "Resuming")
938 << "PIDs" << pidlist << "result" << result;
939 return result;
940}
941
942
943/** Eject device from PC.
944 * Request the OS to eject the player.
945 * @param device mountpoint of the device
946 * @return true on success, fals otherwise.
947 */
948bool Utils::ejectDevice(QString device)
949{
950#if defined(Q_OS_WIN32)
951 /* See http://support.microsoft.com/kb/165721 on the procedure to eject a
952 * device. */
953 bool success = false;
954 int i;
955 HANDLE hdl;
956 DWORD bytesReturned;
957 TCHAR volume[8];
958
959 /* CreateFile */
960 _stprintf(volume, _TEXT("\\\\.\\%c:"), device.toLatin1().at(0));
961 hdl = CreateFile(volume, GENERIC_READ | GENERIC_WRITE,
962 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
963 OPEN_EXISTING, 0, NULL);
964 if(hdl == INVALID_HANDLE_VALUE)
965 return false;
966
967 /* lock volume to make sure no other application is accessing the volume.
968 * Try up to 10 times. */
969 for(i = 0; i < 10; i++) {
970 if(DeviceIoControl(hdl, FSCTL_LOCK_VOLUME,
971 NULL, 0, NULL, 0, &bytesReturned, NULL))
972 break;
973 /* short break before retry */
974 Sleep(100);
975 }
976 if(i < 10) {
977 /* successfully locked, now dismount */
978 if(DeviceIoControl(hdl, FSCTL_DISMOUNT_VOLUME,
979 NULL, 0, NULL, 0, &bytesReturned, NULL)) {
980 /* make sure media can be removed. */
981 PREVENT_MEDIA_REMOVAL pmr;
982 pmr.PreventMediaRemoval = false;
983 if(DeviceIoControl(hdl, IOCTL_STORAGE_MEDIA_REMOVAL,
984 &pmr, sizeof(PREVENT_MEDIA_REMOVAL),
985 NULL, 0, &bytesReturned, NULL)) {
986 /* eject the media */
987 if(DeviceIoControl(hdl, IOCTL_STORAGE_EJECT_MEDIA,
988 NULL, 0, NULL, 0, &bytesReturned, NULL))
989 success = true;
990 }
991 }
992 }
993 /* close handle */
994 CloseHandle(hdl);
995 return success;
996
997#endif
998#if defined(Q_OS_MACX)
999 // FIXME: FSUnmountVolumeSync is deprecated starting with 10.8.
1000 // Use DADiskUnmount / DiskArbitration framework eventually.
1001 // BSD label does not include folder.
1002 QString bsd = Utils::resolveDevicename(device).remove("/dev/");
1003 OSStatus result;
1004 ItemCount index = 1;
1005 bool found = false;
1006
1007 do {
1008 FSVolumeRefNum volrefnum;
1009
1010 result = FSGetVolumeInfo(kFSInvalidVolumeRefNum, index, &volrefnum,
1011 kFSVolInfoFSInfo, NULL, NULL, NULL);
1012 if(result == noErr) {
1013 GetVolParmsInfoBuffer volparms;
1014 /* See above -- PBHGetVolParmsSync() is not available for 64bit,
1015 * and FSGetVolumeParms() on 10.5+ only. */
1016#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
1017 if(FSGetVolumeParms(volrefnum, &volparms, sizeof(volparms)) == noErr)
1018#else
1019 HParamBlockRec hpb;
1020 hpb.ioParam.ioNamePtr = NULL;
1021 hpb.ioParam.ioVRefNum = volrefnum;
1022 hpb.ioParam.ioBuffer = (Ptr)&volparms;
1023 hpb.ioParam.ioReqCount = sizeof(volparms);
1024 if(PBHGetVolParmsSync(&hpb) == noErr)
1025#endif
1026 {
1027 if(volparms.vMServerAdr == 0) {
1028 if(bsd == (char*)volparms.vMDeviceID) {
1029 pid_t dissenter;
1030 result = FSUnmountVolumeSync(volrefnum, 0, &dissenter);
1031 found = true;
1032 break;
1033 }
1034 }
1035 }
1036 }
1037 index++;
1038 } while(result == noErr);
1039 if(result == noErr && found)
1040 return true;
1041
1042#endif
1043#if defined(Q_OS_LINUX)
1044 (void)device;
1045#endif
1046 return false;
1047}
1048
1049
1050qint64 Utils::recursiveFolderSize(QString path)
1051{
1052 qint64 size = 0;
1053 QList<QFileInfo> items = QDir(path).entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
1054 for (auto item: items) {
1055 size += item.size();
1056 }
1057 QList<QString> folders = QDir(path).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
1058 for (auto folder: folders) {
1059 size += recursiveFolderSize(path + "/" + folder);
1060 }
1061 return size;
1062}
diff --git a/utils/rbutilqt/base/utils.h b/utils/rbutilqt/base/utils.h
new file mode 100644
index 0000000000..7f27c7d321
--- /dev/null
+++ b/utils/rbutilqt/base/utils.h
@@ -0,0 +1,64 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21
22#ifndef UTILS_H
23#define UTILS_H
24
25#include <QtCore/QObject>
26
27#include <QString>
28#include <QUrl>
29
30class Utils : public QObject
31{
32public:
33 enum Size {
34 FilesystemTotal,
35 FilesystemFree,
36 FilesystemClusterSize,
37 };
38 enum MountpointsFilter {
39 MountpointsAll,
40 MountpointsSupported,
41 };
42
43 static bool recursiveRmdir(const QString &dirName);
44 static QString resolvePathCase(QString path);
45 static qulonglong filesystemFree(QString path);
46 static qulonglong filesystemTotal(QString path);
47 static qulonglong filesystemSize(QString path, enum Size type);
48 static QString filesystemType(QString path);
49 static QString findExecutable(QString name);
50 static QString checkEnvironment(bool permission);
51 static int compareVersionStrings(QString s1, QString s2);
52 static QString trimVersionString(QString s);
53 static QString filesystemName(QString path);
54 static QStringList mountpoints(enum MountpointsFilter type = MountpointsAll);
55 static QString resolveDevicename(QString path);
56 static QString resolveMountPoint(QString device);
57 static QMap<QString, QList<int> > findRunningProcess(QStringList names);
58 static QList<int> suspendProcess(QList<int> pidlist, bool suspend);
59 static bool ejectDevice(QString device);
60 static qint64 recursiveFolderSize(QString path);
61};
62
63#endif
64
diff --git a/utils/rbutilqt/base/voicefile.cpp b/utils/rbutilqt/base/voicefile.cpp
new file mode 100644
index 0000000000..eabf7a721a
--- /dev/null
+++ b/utils/rbutilqt/base/voicefile.cpp
@@ -0,0 +1,362 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "voicefile.h"
21#include "utils.h"
22#include "rockboxinfo.h"
23#include "rbsettings.h"
24#include "playerbuildinfo.h"
25#include "ziputil.h"
26#include "Logger.h"
27
28VoiceFileCreator::VoiceFileCreator(QObject* parent) :QObject(parent)
29{
30 m_wavtrimThreshold=500;
31}
32
33void VoiceFileCreator::abort()
34{
35 m_abort = true;
36 emit aborted();
37}
38
39bool VoiceFileCreator::createVoiceFile()
40{
41 m_talkList.clear();
42 m_abort = false;
43 emit logItem(tr("Starting Voicefile generation"),LOGINFO);
44
45 // test if tempdir exists
46 if(!QDir(QDir::tempPath()+"/rbvoice/").exists())
47 {
48 QDir(QDir::tempPath()).mkdir("rbvoice");
49 }
50 m_path = QDir::tempPath() + "/rbvoice/";
51
52 // read rockbox-info.txt
53 RockboxInfo info(m_mountpoint);
54 if(!info.success())
55 {
56 emit logItem(tr("could not find rockbox-info.txt"),LOGERROR);
57 emit done(true);
58 return false;
59 }
60 QString target = info.target();
61 QString features = info.features();
62 m_targetid = info.targetID().toInt();
63 m_versionstring = info.version();
64 m_voiceformat = info.voicefmt();
65 QString version = m_versionstring.left(m_versionstring.indexOf("-")).remove("r");
66
67 // check if voicefile is present on target
68 QString fn = m_mountpoint + "/.rockbox/langs/voicestrings.zip";
69 LOG_INFO() << "searching for zipped voicestrings at" << fn;
70 if(QFileInfo(fn).isFile()) {
71 // search for binary voice strings file in archive
72 ZipUtil z(this);
73 if(z.open(fn)) {
74 QStringList contents = z.files();
75 int index;
76 for(index = 0; index < contents.size(); ++index) {
77 // strip any path, we don't know the structure in the zip
78 if(QFileInfo(contents.at(index)).baseName() == m_lang) {
79 break;
80 }
81 }
82 if(index < contents.size()) {
83 LOG_INFO() << "extracting strings file from zip";
84 // extract strings
85 QTemporaryFile stringsfile;
86 stringsfile.open();
87 QString sfn = stringsfile.fileName();
88 // ZipUtil::extractArchive() only compares the filename.
89 if(z.extractArchive(sfn, QFileInfo(contents.at(index)).fileName())) {
90 emit logItem(tr("Extracted voice strings from installation"), LOGINFO);
91
92 stringsfile.seek(0);
93 QByteArray data = stringsfile.readAll();
94 const char* buf = data.constData();
95 // check file header
96 // header (4 bytes): cookie = 9a, version = 06, targetid, options
97 // subheader for each user. Only "core" for now.
98 // subheader (6 bytes): count (2bytes), size (2bytes), offset (2bytes)
99 if(buf[0] != (char)0x9a || buf[1] != 0x06 || buf[2] != m_targetid) {
100 emit logItem(tr("Extracted voice strings incompatible"), LOGINFO);
101 }
102 else {
103 QMap<int, QString> voicestrings;
104
105 /* skip header */
106 int idx = 10;
107 do {
108 unsigned int id = ((unsigned char)buf[idx])<<8
109 | ((unsigned char)buf[idx+1]);
110 // need to use strlen here, since QString::size()
111 // returns number of characters, not bytes.
112 int len = strlen(&buf[idx + 2]);
113 voicestrings[id] = QString::fromUtf8(&buf[idx+2]);
114 idx += 2 + len + 1;
115
116 } while(idx < data.size());
117
118 stringsfile.close();
119
120 // create input file suitable for voicefont from strings.
121 QTemporaryFile voicefontlist;
122 voicefontlist.open();
123 m_filename = voicefontlist.fileName();
124 for(int i = 0; i < voicestrings.size(); ++i) {
125 QByteArray qba;
126 qba = QString("id: %1_%2\n")
127 .arg(voicestrings.keys().at(i) < 0x8000 ? "LANG" : "VOICE")
128 .arg(voicestrings.keys().at(i)).toUtf8();
129 voicefontlist.write(qba);
130 qba = QString("voice: \"%1\"\n").arg(
131 voicestrings[voicestrings.keys().at(i)]).toUtf8();
132 voicefontlist.write(qba);
133 }
134 voicefontlist.close();
135
136 // everything successful, now create the actual voice file.
137 create();
138 return true;
139 }
140
141 }
142 }
143 }
144 }
145 emit logItem(tr("Could not retrieve strings from installation, downloading"), LOGINFO);
146 // if either no zip with voice strings is found or something went wrong
147 // retrieving the necessary files we'll end up here, trying to get the
148 // genlang output as previously from the webserver.
149
150 // prepare download url
151 QString genlang = PlayerBuildInfo::instance()->value(
152 PlayerBuildInfo::GenlangUrl).toString();
153 genlang.replace("%LANG%", m_lang);
154 genlang.replace("%TARGET%", target);
155 genlang.replace("%REVISION%", version);
156 genlang.replace("%FEATURES%", features);
157 QUrl genlangUrl(genlang);
158 LOG_INFO() << "downloading" << genlangUrl;
159
160 //download the correct genlang output
161 QTemporaryFile *downloadFile = new QTemporaryFile(this);
162 downloadFile->open();
163 m_filename = downloadFile->fileName();
164 downloadFile->close();
165 // get the real file.
166 getter = new HttpGet(this);
167 getter->setFile(downloadFile);
168
169 connect(getter, &HttpGet::done, this, &VoiceFileCreator::downloadDone);
170 connect(getter, &HttpGet::dataReadProgress, this, &VoiceFileCreator::logProgress);
171 connect(this, &VoiceFileCreator::aborted, getter, &HttpGet::abort);
172 emit logItem(tr("Downloading voice info..."),LOGINFO);
173 getter->getFile(genlangUrl);
174 return true;
175 }
176
177
178void VoiceFileCreator::downloadDone(bool error)
179{
180 LOG_INFO() << "download done, error:" << error;
181
182 // update progress bar
183 emit logProgress(1,1);
184 if(getter->httpResponse() != 200 && !getter->isCached()) {
185 emit logItem(tr("Download error: received HTTP error %1.")
186 .arg(getter->httpResponse()),LOGERROR);
187 emit done(true);
188 return;
189 }
190
191 if(getter->isCached())
192 emit logItem(tr("Cached file used."), LOGINFO);
193 if(error)
194 {
195 emit logItem(tr("Download error: %1").arg(getter->errorString()),LOGERROR);
196 emit done(true);
197 return;
198 }
199 else
200 emit logItem(tr("Download finished."),LOGINFO);
201
202 QCoreApplication::processEvents();
203 create();
204}
205
206
207void VoiceFileCreator::create(void)
208{
209 //open downloaded file
210 QFile genlang(m_filename);
211 if(!genlang.open(QIODevice::ReadOnly))
212 {
213 emit logItem(tr("failed to open downloaded file"),LOGERROR);
214 emit done(true);
215 return;
216 }
217
218 //read in downloaded file
219 emit logItem(tr("Reading strings..."),LOGINFO);
220 QTextStream in(&genlang);
221#if QT_VERSION < 0x060000
222 in.setCodec("UTF-8");
223#else
224 in.setEncoding(QStringConverter::Utf8);
225#endif
226 QString id, voice;
227 bool idfound = false;
228 bool voicefound=false;
229 bool useCorrection = RbSettings::value(RbSettings::UseTtsCorrections).toBool();
230 while (!in.atEnd())
231 {
232 QString line = in.readLine();
233 if(line.contains("id:")) //ID found
234 {
235 id = line.remove("id:").remove('"').trimmed();
236 idfound = true;
237 }
238 else if(line.contains("voice:")) // voice found
239 {
240 voice = line.remove("voice:").remove('"').trimmed();
241 voice = voice.remove("<").remove(">");
242 voicefound=true;
243 }
244
245 if(idfound && voicefound)
246 {
247 TalkGenerator::TalkEntry entry;
248 entry.toSpeak = voice;
249 entry.wavfilename = m_path + "/" + id + ".wav";
250 //voicefont wants them with .mp3 extension
251 entry.talkfilename = m_path + "/" + id + ".mp3";
252 entry.voiced = false;
253 entry.encoded = false;
254 if(id == "VOICE_PAUSE")
255 {
256 QFile::copy(":/builtin/VOICE_PAUSE.wav",m_path + "/VOICE_PAUSE.wav");
257 entry.wavfilename = m_path + "/VOICE_PAUSE.wav";
258 entry.voiced = true;
259 m_talkList.append(entry);
260 }
261 else if(entry.toSpeak.isEmpty()) {
262 LOG_WARNING() << "Empty voice string for ID" << id;
263 }
264 else {
265 m_talkList.append(entry);
266 }
267 idfound=false;
268 voicefound=false;
269 }
270 }
271 genlang.close();
272
273 // check for empty list
274 if(m_talkList.size() == 0)
275 {
276 emit logItem(tr("The downloaded file was empty!"),LOGERROR);
277 emit done(true);
278 return;
279 }
280
281 // generate files
282 {
283 TalkGenerator generator(this);
284 // set language for string correction. If not set no correction will be made.
285 if(useCorrection)
286 generator.setLang(m_lang);
287 connect(&generator, &TalkGenerator::done, this, &VoiceFileCreator::done);
288 connect(&generator, &TalkGenerator::logItem, this, &VoiceFileCreator::logItem);
289 connect(&generator, &TalkGenerator::logProgress, this, &VoiceFileCreator::logProgress);
290 connect(this, &VoiceFileCreator::aborted, &generator, &TalkGenerator::abort);
291
292 if(generator.process(&m_talkList, m_wavtrimThreshold) == TalkGenerator::eERROR)
293 {
294 cleanup();
295 emit logProgress(0,1);
296 emit done(true);
297 return;
298 }
299 }
300
301 //make voicefile
302 emit logItem(tr("Creating voicefiles..."),LOGINFO);
303 FILE* ids2 = fopen(m_filename.toLocal8Bit(), "r");
304 if (ids2 == nullptr)
305 {
306 cleanup();
307 emit logItem(tr("Error opening downloaded file"),LOGERROR);
308 emit done(true);
309 return;
310 }
311
312 FILE* output = fopen(QString(m_mountpoint + "/.rockbox/langs/" + m_lang
313 + ".voice").toLocal8Bit(), "wb");
314 if (output == nullptr)
315 {
316 cleanup();
317 fclose(ids2);
318 emit logItem(tr("Error opening output file"),LOGERROR);
319 emit done(true);
320 return;
321 }
322
323 LOG_INFO() << "Running voicefont, format" << m_voiceformat;
324 voicefont(ids2,m_targetid,m_path.toLocal8Bit().data(), output, m_voiceformat);
325 // ids2 and output are closed by voicefont().
326
327 //cleanup
328 cleanup();
329
330 // Add Voice file to the install log
331 QSettings installlog(m_mountpoint + "/.rockbox/rbutil.log", QSettings::IniFormat, nullptr);
332 installlog.beginGroup(QString("Voice (self created, %1)").arg(m_lang));
333 installlog.setValue("/.rockbox/langs/" + m_lang + ".voice", m_versionstring);
334 installlog.endGroup();
335 installlog.sync();
336
337 emit logProgress(1,1);
338 emit logItem(tr("successfully created."),LOGOK);
339
340 emit done(false);
341}
342
343//! \brief Cleans up Files potentially left in the temp dir
344//!
345void VoiceFileCreator::cleanup()
346{
347 emit logItem(tr("Cleaning up..."),LOGINFO);
348
349 for(int i=0; i < m_talkList.size(); i++)
350 {
351 if(QFile::exists(m_talkList[i].wavfilename))
352 QFile::remove(m_talkList[i].wavfilename);
353 if(QFile::exists(m_talkList[i].talkfilename))
354 QFile::remove(m_talkList[i].talkfilename);
355
356 QCoreApplication::processEvents();
357 }
358 emit logItem(tr("Finished"),LOGINFO);
359
360 return;
361}
362
diff --git a/utils/rbutilqt/base/voicefile.h b/utils/rbutilqt/base/voicefile.h
new file mode 100644
index 0000000000..d34535e700
--- /dev/null
+++ b/utils/rbutilqt/base/voicefile.h
@@ -0,0 +1,77 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21
22#ifndef VOICEFILE_H
23#define VOICEFILE_H
24
25#include <QtCore>
26#include "progressloggerinterface.h"
27
28#include "httpget.h"
29#include "voicefont.h"
30#include "talkgenerator.h"
31
32class VoiceFileCreator :public QObject
33{
34 Q_OBJECT
35public:
36 VoiceFileCreator(QObject* parent);
37
38 //start creation
39 bool createVoiceFile();
40
41 void setMountPoint(QString mountpoint) {m_mountpoint =mountpoint; }
42 void setLang(QString name) { m_lang = name; }
43 void setWavtrimThreshold(int th){m_wavtrimThreshold = th;}
44
45public slots:
46 void abort();
47
48signals:
49 void done(bool);
50 void aborted();
51 void logItem(QString, int); //! set logger item
52 void logProgress(int, int); //! set progress bar.
53
54private slots:
55 void downloadDone(bool error);
56
57private:
58
59 void create(void);
60 void cleanup();
61
62 HttpGet *getter;
63 QString m_filename; //the temporary file
64 QString m_mountpoint; //mountpoint of the device
65 QString m_path; //path where the wav and mp3 files are stored to
66 int m_targetid; //the target id
67 QString m_lang; // the language which will be spoken
68 QString m_versionstring; // version string to be used for logging
69 int m_wavtrimThreshold;
70 int m_voiceformat;
71
72 bool m_abort;
73 QList<TalkGenerator::TalkEntry> m_talkList;
74};
75
76#endif
77
diff --git a/utils/rbutilqt/base/zipinstaller.cpp b/utils/rbutilqt/base/zipinstaller.cpp
new file mode 100644
index 0000000000..8de6c53b16
--- /dev/null
+++ b/utils/rbutilqt/base/zipinstaller.cpp
@@ -0,0 +1,204 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "zipinstaller.h"
21#include "utils.h"
22#include "ziputil.h"
23#include "Logger.h"
24
25ZipInstaller::ZipInstaller(QObject* parent) :
26 QObject(parent),
27 m_unzip(true), m_usecache(false), m_getter(nullptr)
28{
29}
30
31
32void ZipInstaller::install()
33{
34 LOG_INFO() << "initializing installation";
35
36 m_runner = 0;
37 connect(this, &ZipInstaller::cont, this, &ZipInstaller::installContinue);
38 m_url = m_urllist.at(m_runner);
39 m_logsection = m_loglist.at(m_runner);
40 m_logver = m_verlist.at(m_runner);
41 installStart();
42}
43
44
45void ZipInstaller::abort()
46{
47 LOG_INFO() << "Aborted";
48 emit internalAborted();
49}
50
51
52void ZipInstaller::installContinue()
53{
54 LOG_INFO() << "continuing installation";
55
56 m_runner++; // this gets called when a install finished, so increase first.
57 LOG_INFO() << "runner done:" << m_runner << "/" << m_urllist.size();
58 if(m_runner < m_urllist.size()) {
59 emit logItem(tr("done."), LOGOK);
60 m_url = m_urllist.at(m_runner);
61 m_logsection = m_loglist.at(m_runner);
62 if(m_runner < m_verlist.size()) m_logver = m_verlist.at(m_runner);
63 else m_logver = "";
64 installStart();
65 }
66 else {
67 emit logItem(tr("Package installation finished successfully."), LOGOK);
68 emit done(false);
69 return;
70 }
71}
72
73
74void ZipInstaller::installStart()
75{
76 LOG_INFO() << "starting installation";
77
78 emit logItem(tr("Downloading file %1.%2").arg(QFileInfo(m_url).baseName(),
79 QFileInfo(m_url).completeSuffix()),LOGINFO);
80
81 // temporary file needs to be opened to get the filename
82 // make sure to get a fresh one on each run.
83 // making this a parent of the temporary file ensures the file gets deleted
84 // after the class object gets destroyed.
85 m_downloadFile = new QTemporaryFile(this);
86 m_downloadFile->open();
87 m_file = m_downloadFile->fileName();
88 m_downloadFile->close();
89 // get the real file.
90 if(m_getter != nullptr) m_getter->deleteLater();
91 m_getter = new HttpGet(this);
92 if(m_usecache) {
93 m_getter->setCache(true);
94 }
95 m_getter->setFile(m_downloadFile);
96
97 connect(m_getter, &HttpGet::done, this, &ZipInstaller::downloadDone);
98 connect(m_getter, &HttpGet::dataReadProgress, this, &ZipInstaller::logProgress);
99 connect(this, &ZipInstaller::internalAborted, m_getter, &HttpGet::abort);
100
101 m_getter->getFile(QUrl(m_url));
102}
103
104
105void ZipInstaller::downloadDone(bool error)
106{
107 LOG_INFO() << "download done, error:" << error;
108 QStringList zipContents; // needed later
109 // update progress bar
110
111 emit logProgress(1, 1);
112 if(m_getter->httpResponse() != 200 && !m_getter->isCached()) {
113 emit logItem(tr("Download error: received HTTP error %1\n%2")
114 .arg(m_getter->httpResponse()).arg(m_getter->errorString()),
115 LOGERROR);
116 emit done(true);
117 return;
118 }
119 if(m_getter->isCached())
120 emit logItem(tr("Cached file used."), LOGINFO);
121 if(error) {
122 emit logItem(tr("Download error: %1").arg(m_getter->errorString()), LOGERROR);
123 emit done(true);
124 return;
125 }
126 else emit logItem(tr("Download finished."),LOGOK);
127 QCoreApplication::processEvents();
128 if(m_unzip) {
129 // unzip downloaded file
130 LOG_INFO() << "about to unzip" << m_file << "to" << m_mountpoint;
131
132 emit logItem(tr("Extracting file."), LOGINFO);
133 QCoreApplication::processEvents();
134
135 ZipUtil zip(this);
136 connect(&zip, &ZipUtil::logProgress, this, &ZipInstaller::logProgress);
137 connect(&zip, &ZipUtil::logItem, this, &ZipInstaller::logItem);
138 zip.open(m_file, QuaZip::mdUnzip);
139 // check for free space. Make sure after installation will still be
140 // some room for operating (also includes calculation mistakes due to
141 // cluster sizes on the player).
142 if((qint64)Utils::filesystemFree(m_mountpoint)
143 < (zip.totalUncompressedSize(
144 Utils::filesystemSize(m_mountpoint, Utils::FilesystemClusterSize))
145 + 1000000)) {
146 emit logItem(tr("Not enough disk space! Aborting."), LOGERROR);
147 emit logProgress(1, 1);
148 emit done(true);
149 return;
150 }
151 zipContents = zip.files();
152 if(!zip.extractArchive(m_mountpoint)) {
153 emit logItem(tr("Extraction failed!"), LOGERROR);
154 emit logProgress(1, 1);
155 emit done(true);
156 return;
157 }
158 zip.close();
159 }
160 else {
161 if (m_target.isEmpty())
162 m_target = QUrl(m_url).fileName();
163 QString destfile = m_mountpoint + "/" + m_target;
164 // only copy the downloaded file to the output location / name
165 emit logItem(tr("Installing file."), LOGINFO);
166 LOG_INFO() << "saving downloaded file (no extraction) to" << destfile;
167
168 m_downloadFile->open(); // copy fails if file is not opened (filename issue?)
169 // make sure the required path is existing
170 QString path = QFileInfo(destfile).absolutePath();
171 QDir p;
172 p.mkpath(path);
173 // QFile::copy() doesn't overwrite files, so remove old one first
174 // TODO: compare old and new file and fail if those are different.
175 QFile(destfile).remove();
176 if(!m_downloadFile->copy(destfile)) {
177 emit logItem(tr("Installing file failed."), LOGERROR);
178 emit done(true);
179 return;
180 }
181
182 // add file to log
183 zipContents.append(m_target);
184 }
185 if(m_logver.isEmpty()) {
186 // if no version info is set use the timestamp of the server file.
187 m_logver = m_getter->timestamp().toString(Qt::ISODate);
188 }
189
190 emit logItem(tr("Creating installation log"),LOGINFO);
191 QSettings installlog(m_mountpoint + "/.rockbox/rbutil.log", QSettings::IniFormat, nullptr);
192
193 installlog.beginGroup(m_logsection);
194 for(int i = 0; i < zipContents.size(); i++)
195 {
196 installlog.setValue(zipContents.at(i), m_logver);
197 }
198 installlog.endGroup();
199 installlog.sync();
200
201 emit cont();
202}
203
204
diff --git a/utils/rbutilqt/base/zipinstaller.h b/utils/rbutilqt/base/zipinstaller.h
new file mode 100644
index 0000000000..59a0f785d9
--- /dev/null
+++ b/utils/rbutilqt/base/zipinstaller.h
@@ -0,0 +1,88 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2007 by Dominik Wenger
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21
22#ifndef ZIPINSTALLER_H
23#define ZIPINSTALLER_H
24
25#include <QtCore>
26
27#include "progressloggerinterface.h"
28#include "httpget.h"
29#include "Logger.h"
30
31/** Install a file or zip.
32 * Downloads file(s) from a given URL, and installs by either extracting or
33 * copying it to the target path set by setMountpoint().
34 */
35class ZipInstaller : public QObject
36{
37 Q_OBJECT
38public:
39 ZipInstaller(QObject* parent);
40 ~ZipInstaller(){}
41 void install(void);
42 void setMountPoint(QString mountpoint) {m_mountpoint = mountpoint;}
43 void setUrl(QString url){m_urllist = QStringList(url);}
44 void setUrl(QStringList url) { m_urllist = url; }
45 void setLogSection(QString name) {m_loglist = QStringList(name);}
46 void setLogSection(QStringList name) { m_loglist = name; }
47 void setLogVersion(QString v = "")
48 { m_verlist = QStringList(v); LOG_INFO() << m_verlist;}
49 void setLogVersion(QStringList v)
50 { m_verlist = v; LOG_INFO() << m_verlist;}
51 /** Change between copy and unzip mode. */
52 void setUnzip(bool i) { m_unzip = i; }
53 /** Set target filename for copy mode.
54 * If not set the filename part of the download URL is used. */
55 void setTarget(QString t) { m_target = t; }
56 void setCache(bool c) { m_usecache = c; }
57
58public slots:
59 void abort(void);
60
61private slots:
62 void downloadDone(bool);
63 void installStart(void);
64 void installContinue(void);
65
66signals:
67 void done(bool error);
68 void cont();
69 void logItem(QString, int); //! set logger item
70 void logProgress(int, int); //! set progress bar.
71 void internalAborted(void);
72
73private:
74 QString m_url, m_file, m_mountpoint, m_logsection, m_logver;
75 QStringList m_urllist, m_loglist, m_verlist;
76 bool m_unzip;
77 QString m_target;
78 int m_runner;
79 bool m_usecache;
80
81 HttpGet *m_getter;
82 QTemporaryFile *m_downloadFile;
83};
84
85
86
87#endif
88
diff --git a/utils/rbutilqt/base/ziputil.cpp b/utils/rbutilqt/base/ziputil.cpp
new file mode 100644
index 0000000000..45119f7d99
--- /dev/null
+++ b/utils/rbutilqt/base/ziputil.cpp
@@ -0,0 +1,302 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2011 Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include <QDebug>
21#include "ziputil.h"
22#include "progressloggerinterface.h"
23#include "Logger.h"
24
25#include "quazip/quazip.h"
26#include "quazip/quazipfile.h"
27#include "quazip/quazipfileinfo.h"
28
29
30ZipUtil::ZipUtil(QObject* parent) : ArchiveUtil(parent)
31{
32 m_zip = nullptr;
33}
34
35
36ZipUtil::~ZipUtil()
37{
38 if(m_zip) {
39 delete m_zip;
40 }
41}
42
43//! @brief open zip file.
44//! @param zipfile path to zip file
45//! @param mode open mode (see QuaZip::Mode)
46//! @return true on success, false otherwise
47bool ZipUtil::open(QString& zipfile, QuaZip::Mode mode)
48{
49 m_zip = new QuaZip(zipfile);
50 return m_zip->open(mode);
51}
52
53
54//! @brief close zip file.
55//! @return true on success, false otherwise
56bool ZipUtil::close(void)
57{
58 if(!m_zip) {
59 return false;
60 }
61
62 int error = UNZ_OK;
63 if(m_zip->isOpen()) {
64 m_zip->close();
65 error = m_zip->getZipError();
66 }
67 delete m_zip;
68 m_zip = nullptr;
69 return (error == UNZ_OK) ? true : false;
70}
71
72
73//! @brief extract currently opened archive
74//! @brief dest path to extract archive to, can be filename when extracting a
75//! single file.
76//! @brief file file to extract from archive, full archive if empty.
77//! @return true on success, false otherwise
78bool ZipUtil::extractArchive(const QString& dest, QString file)
79{
80 LOG_INFO() << "extractArchive" << dest << file;
81 bool result = true;
82 if(!m_zip) {
83 return false;
84 }
85 QuaZipFile *currentFile = new QuaZipFile(m_zip);
86 int entries = m_zip->getEntriesCount();
87 int current = 0;
88 // construct the filename when extracting a single file from an archive.
89 // if the given destination is a full path use it as output name,
90 // otherwise use it as path to place the file as named in the archive.
91 QString singleoutfile;
92 if(!file.isEmpty() && QFileInfo(dest).isDir()) {
93 singleoutfile = dest + "/" + file;
94 }
95 else if(!file.isEmpty()){
96 singleoutfile = dest;
97 }
98 for(bool more = m_zip->goToFirstFile(); more; more = m_zip->goToNextFile())
99 {
100 ++current;
101 // if the entry is a path ignore it. Path existence is ensured separately.
102 if(m_zip->getCurrentFileName().split("/").last() == "")
103 continue;
104 // some tools set the MS-DOS file attributes. Check those for D flag,
105 // since in some cases a folder entry does not end with a /
106 QuaZipFileInfo fi;
107 currentFile->getFileInfo(&fi);
108 if(fi.externalAttr & 0x10) // FAT entry bit 4 indicating directory
109 continue;
110
111 QString outfilename;
112 if(!singleoutfile.isEmpty()
113 && QFileInfo(m_zip->getCurrentFileName()).fileName() == file) {
114 outfilename = singleoutfile;
115 }
116 else if(singleoutfile.isEmpty()) {
117 outfilename = dest + "/" + m_zip->getCurrentFileName();
118 }
119 if(outfilename.isEmpty())
120 continue;
121 QFile outputFile(outfilename);
122 // make sure the output path exists
123 if(!QDir().mkpath(QFileInfo(outfilename).absolutePath())) {
124 result = false;
125 emit logItem(tr("Creating output path failed"), LOGERROR);
126 LOG_INFO() << "creating output path failed for:"
127 << outfilename;
128 break;
129 }
130 if(!outputFile.open(QFile::WriteOnly)) {
131 result = false;
132 emit logItem(tr("Creating output file failed"), LOGERROR);
133 LOG_INFO() << "creating output file failed:"
134 << outfilename;
135 break;
136 }
137 currentFile->open(QIODevice::ReadOnly);
138 outputFile.write(currentFile->readAll());
139 if(currentFile->getZipError() != UNZ_OK) {
140 result = false;
141 emit logItem(tr("Error during Zip operation"), LOGERROR);
142 LOG_INFO() << "QuaZip error:" << currentFile->getZipError()
143 << "on file" << currentFile->getFileName();
144 break;
145 }
146 currentFile->close();
147 outputFile.close();
148
149 emit logProgress(current, entries);
150 }
151 delete currentFile;
152 emit logProgress(1, 1);
153
154 return result;
155}
156
157
158//! @brief append a folder to current archive
159//! @param source source folder
160//! @param basedir base folder for archive. Will get stripped from zip paths.
161//! @return true on success, false otherwise
162bool ZipUtil::appendDirToArchive(QString& source, QString& basedir)
163{
164 bool result = true;
165 if(!m_zip || !m_zip->isOpen()) {
166 LOG_INFO() << "Zip file not open!";
167 return false;
168 }
169 // get a list of all files and folders. Needed for progress info and avoids
170 // recursive calls.
171 QDirIterator iterator(source, QDirIterator::Subdirectories);
172 QStringList fileList;
173 while(iterator.hasNext()) {
174 iterator.next();
175 // skip folders, we can't add them.
176 if(!QFileInfo(iterator.filePath()).isDir()) {
177 fileList.append(iterator.filePath());
178 }
179 }
180 LOG_INFO() << "Adding" << fileList.size() << "files to archive";
181
182 int max = fileList.size();
183 for(int i = 0; i < max; i++) {
184 QString current = fileList.at(i);
185 if(!appendFileToArchive(current, basedir)) {
186 LOG_ERROR() << "Error appending file" << current
187 << "to archive" << m_zip->getZipName();
188 result = false;
189 break;
190 }
191 emit logProgress(i, max);
192 }
193 return result;
194}
195
196
197//! @brief append a single file to current archive
198//!
199bool ZipUtil::appendFileToArchive(QString& file, QString& basedir)
200{
201 bool result = true;
202 if(!m_zip || !m_zip->isOpen()) {
203 LOG_ERROR() << "Zip file not open!";
204 return false;
205 }
206 // skip folders, we can't add them.
207 QFileInfo fileinfo(file);
208 if(fileinfo.isDir()) {
209 return false;
210 }
211 QString infile = fileinfo.canonicalFilePath();
212 QString newfile = infile;
213 newfile.remove(QDir(basedir).canonicalPath() + "/");
214
215 QuaZipFile fout(m_zip);
216 QFile fin(file);
217
218 if(!fin.open(QFile::ReadOnly)) {
219 LOG_ERROR() << "Could not open file for reading:" << file;
220 return false;
221 }
222 if(!fout.open(QIODevice::WriteOnly, QuaZipNewInfo(newfile, infile))) {
223 fin.close();
224 LOG_ERROR() << "Could not open file for writing:" << newfile;
225 return false;
226 }
227
228 result = (fout.write(fin.readAll()) < 0) ? false : true;
229 fin.close();
230 fout.close();
231 return result;
232}
233
234
235//! @brief calculate total size of extracted files
236qint64 ZipUtil::totalUncompressedSize(unsigned int clustersize)
237{
238 qint64 uncompressed = 0;
239
240 QList<QuaZipFileInfo> items = contentProperties();
241 if(items.size() == 0) {
242 return -1;
243 }
244 int max = items.size();
245 if(clustersize > 0) {
246 for(int i = 0; i < max; ++i) {
247 qint64 item = items.at(i).uncompressedSize;
248 uncompressed += (item + clustersize - (item % clustersize));
249 }
250 }
251 else {
252 for(int i = 0; i < max; ++i) {
253 uncompressed += items.at(i).uncompressedSize;
254 }
255 }
256 if(clustersize > 0) {
257 LOG_INFO() << "calculation rounded to cluster size for each file:"
258 << clustersize;
259 }
260 LOG_INFO() << "size of archive files uncompressed:"
261 << uncompressed;
262 return uncompressed;
263}
264
265
266QStringList ZipUtil::files(void)
267{
268 QList<QuaZipFileInfo> items = contentProperties();
269 QStringList fileList;
270 if(items.size() == 0) {
271 return fileList;
272 }
273 int max = items.size();
274 for(int i = 0; i < max; ++i) {
275 fileList.append(items.at(i).name);
276 }
277 return fileList;
278}
279
280
281QList<QuaZipFileInfo> ZipUtil::contentProperties()
282{
283 QList<QuaZipFileInfo> items;
284 if(!m_zip || !m_zip->isOpen()) {
285 LOG_ERROR() << "Zip file not open!";
286 return items;
287 }
288 QuaZipFileInfo info;
289 QuaZipFile currentFile(m_zip);
290 for(bool more = m_zip->goToFirstFile(); more; more = m_zip->goToNextFile())
291 {
292 currentFile.getFileInfo(&info);
293 if(currentFile.getZipError() != UNZ_OK) {
294 LOG_ERROR() << "QuaZip error:" << currentFile.getZipError()
295 << "on file" << currentFile.getFileName();
296 return QList<QuaZipFileInfo>();
297 }
298 items.append(info);
299 }
300 return items;
301}
302
diff --git a/utils/rbutilqt/base/ziputil.h b/utils/rbutilqt/base/ziputil.h
new file mode 100644
index 0000000000..25c3dce391
--- /dev/null
+++ b/utils/rbutilqt/base/ziputil.h
@@ -0,0 +1,53 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2011 Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef ZIPUTIL_H
20#define ZIPUTIL_H
21
22#include <QtCore>
23#include "archiveutil.h"
24#include "quazip/quazip.h"
25#include "quazip/quazipfile.h"
26#include "quazip/quazipfileinfo.h"
27
28class ZipUtil : public ArchiveUtil
29{
30 Q_OBJECT
31
32 public:
33 ZipUtil(QObject* parent);
34 ~ZipUtil();
35 bool open(QString& zipfile, QuaZip::Mode mode = QuaZip::mdUnzip);
36 virtual bool close(void);
37 virtual bool extractArchive(const QString& dest, QString file = "");
38 bool appendDirToArchive(QString& source, QString& basedir);
39 bool appendFileToArchive(QString& file, QString& basedir);
40 qint64 totalUncompressedSize(unsigned int clustersize = 0);
41 virtual QStringList files(void);
42
43 signals:
44 void logProgress(int, int);
45 void logItem(QString, int);
46
47 private:
48 QList<QuaZipFileInfo> contentProperties();
49 QuaZip* m_zip;
50
51};
52#endif
53