1
0
Fork 0
forked from len0rd/rockbox

rbutil: Merge rbutil with utils folder.

rbutil uses several components from the utils folder, and can be
considered part of utils too. Having it in a separate folder is an
arbitrary split that doesn't help anymore these days, so merge them.

This also allows other utils to easily use libtools.make without the
need to navigate to a different folder.

Change-Id: I3fc2f4de19e3e776553efb5dea5f779dfec0dc21
This commit is contained in:
Dominik Riebeling 2021-12-15 21:04:28 +01:00
parent 6c6f0757d7
commit c876d3bbef
494 changed files with 13 additions and 13 deletions

63
utils/rbutilqt/INSTALL Normal file
View file

@ -0,0 +1,63 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
This is Rockbox Utility, the graphical installer and housekeeping utility
for Rockbox.
Requirements:
=============
- gcc
- Qt 4.5.0 or later
- at least the following folders from Rockbox svn:
o lib/rbcodec/codecs/libspeex
o rbutil/
o tools/
- libusb (Linux only)
Building:
=========
- run qmake
- run make
Build Options:
==============
- static
add "-config static" to the qmake call to build a static binary. Note that
this also requires that you have a statically built version of Qt.
- dbg
add "-config dbg" to build with debug symbols.
- silent
add "-config silent" to the qmake call. This is an undocumented feature of
Qt itself. Note that this doesn't seem to work on Windows and Mac OS X until
recent versions of Qt.
Installation / Deployment:
==========================
Rockbox Utility is build as all-in-one binary. This means for distribution
you need:
- RockboxUtility (main binary)
- rbutil_*.qm (translation files, only for non-static builds)
You don't need any other files like configuration files, especially
rbutil.ini which will get included into the binary.
The script utils/common/deploy-rbutil.py automates building deployment
binaries. For a complete list of paths to get from svn to build please check
its source.
The website for Rockbox Utility is
http://www.rockbox.org/wiki/RockboxUtility

24
utils/rbutilqt/Info.plist Normal file
View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>RockboxUtility</string>
<key>CFBundleName</key>
<string>Rockbox Utility</string>
<key>CFBundleShortVersionString</key>
<string>1.4.1 ($Rev$)</string>
<key>CFBundleIconFile</key>
<string>rbutilqt.icns</string>
<key>CFBundleIdentifier</key>
<string>com.rockbox.rbutil</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSHighResolutionCapable</key>
<string>True</string>
</dict>
</plist>

View file

@ -0,0 +1,79 @@
# __________ __ ___.
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
# \/ \/ \/ \/ \/
#
ifndef V
SILENT = @
endif
TARGET_DIR ?= $(abspath .)/build/
BUILD_DIR ?= $(abspath .)/build/
# allow using this Makefile directly. Otherwise those variables are set via
# the generated one.
ifndef RBBASE_DIR
RBBASE_DIR := $(dir $(lastword $(MAKEFILE_LIST)))/../../
endif
EXTRALIBS_CC ?= gcc
EXTRALIBS_AR ?= ar
# re-export variables that might have been set via command line.
# If set via command line sub-makefiles cannot override them, so use different
# variable names in the calling makefile.
export APPVERSION=\"rbutil\"
export SYS_SPEEX
export TARGET_DIR
export RBBASE_DIR
export CFLAGS=$(EXTRALIB_CFLAGS)
export CXXFLAGS=$(EXTRALIB_CXXFLAGS)
export CC=$(EXTRALIBS_CC)
export CXX=$(EXTRALIBS_CXX)
export AR=$(EXTRALIBS_AR)
export ISYSROOT=$(EXTRALIB_ISYSROOT)
libs: librbspeex libucl libipodpatcher libsansapatcher libmkamsboot libmktccboot libmkmpioboot libchinachippatcher libmkimxboot libmks5lboot libbspatch libbz2 librbtomcrypt
# To support cross compiles, we explicitly pass the CC flag below for
# all tools which override CC or CXX in their makefiles. CXX is only
# used by mkimxboot.
librbtomcrypt:
$(SILENT) $(MAKE) -C $(RBBASE_DIR)/utils/tomcrypt BUILD_DIR=$(BUILD_DIR)/tomcrypt librbtomcrypt.a
librbspeex:
$(SILENT) $(MAKE) -C $(RBBASE_DIR)/tools/rbspeex BUILD_DIR=$(BUILD_DIR)/libspeex librbspeex.a
libucl:
$(SILENT)$(MAKE) -C $(RBBASE_DIR)/tools/ucl/src BUILD_DIR=$(BUILD_DIR)/ucl libucl.a
libipodpatcher:
$(SILENT)$(MAKE) -C $(RBBASE_DIR)/utils/ipodpatcher BUILD_DIR=$(BUILD_DIR)/ipodpatcher libipodpatcher.a CC=$(CC)
libsansapatcher:
$(SILENT)$(MAKE) -C $(RBBASE_DIR)/utils/sansapatcher BUILD_DIR=$(BUILD_DIR)/sansapatcher libsansapatcher.a CC=$(CC)
libmkamsboot:
$(SILENT)$(MAKE) -C $(RBBASE_DIR)/utils/mkamsboot BUILD_DIR=$(BUILD_DIR)/mkamsboot libmkamsboot.a CC=$(CC)
libmktccboot:
$(SILENT)$(MAKE) -C $(RBBASE_DIR)/utils/mktccboot BUILD_DIR=$(BUILD_DIR)/mktccboot libmktccboot.a CC=$(CC)
libmkmpioboot:
$(SILENT)$(MAKE) -C $(RBBASE_DIR)/utils/mkmpioboot BUILD_DIR=$(BUILD_DIR)/mkmpioboot libmkmpioboot.a CC=$(CC)
libchinachippatcher:
$(SILENT)$(MAKE) -C $(RBBASE_DIR)/utils/chinachippatcher BUILD_DIR=$(BUILD_DIR)/chinachippatcher libchinachippatcher.a CC=$(CC)
libmkimxboot:
$(SILENT)$(MAKE) -C $(RBBASE_DIR)/utils/mkimxboot BUILD_DIR=$(BUILD_DIR)/mkimxboot libmkimxboot.a CC=$(CC) CXX=$(CXX)
libmks5lboot:
$(SILENT)$(MAKE) -C $(RBBASE_DIR)/utils/mks5lboot BUILD_DIR=$(BUILD_DIR)/mks5lboot libmks5lboot.a CC=$(CC)
libbz2:
$(SILENT)$(MAKE) -C $(RBBASE_DIR)/utils/bzip2 BUILD_DIR=$(BUILD_DIR)/bzip2 libbz2.a CC=$(CC)
libbspatch:
$(SILENT)$(MAKE) -C $(RBBASE_DIR)/utils/bspatch BUILD_DIR=$(BUILD_DIR)/bspatch libbspatch.a CC=$(CC)

View file

@ -0,0 +1,12 @@
[Desktop Entry]
GenericName=Rockbox Installer and Maintainance Tool
Version=1.0
Hidden=false
Name=Rockbox Utility
Type=Application
Terminal=false
StartupNotify=true
Icon=rockbox-clef
Exec=RockboxUtility
Categories=Utility;

198
utils/rbutilqt/aboutbox.ui Normal file
View file

@ -0,0 +1,198 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0" >
<class>aboutBox</class>
<widget class="QWidget" name="aboutBox" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>640</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle" >
<string>About Rockbox Utility</string>
</property>
<layout class="QGridLayout" name="gridLayout" >
<item row="0" column="0" colspan="3" >
<layout class="QHBoxLayout" >
<item>
<widget class="QLabel" name="label" >
<property name="text" >
<string/>
</property>
<property name="pixmap" >
<pixmap>:/icons/rockbox-5.png</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelTitle" >
<property name="text" >
<string>The Rockbox Utility</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0" colspan="3" >
<widget class="QLabel" name="labelText" >
<property name="text" >
<string>Installer and housekeeping utility for the Rockbox open source digital audio player firmware.&lt;br/&gt;© The Rockbox Team.&lt;br/&gt;Released under the GNU General Public License v2.&lt;br/&gt;Uses icons by the &lt;a href=&quot;http://tango.freedesktop.org/&quot;&gt;Tango Project&lt;/a&gt;.&lt;br/&gt;&lt;center&gt;&lt;a href=&quot;http://www.rockbox.org&quot;&gt;http://www.rockbox.org&lt;/a&gt;&lt;/center&gt;</string>
</property>
<property name="textFormat" >
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap" >
<bool>true</bool>
</property>
<property name="openExternalLinks" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3" >
<widget class="QTabWidget" name="tabWidget" >
<property name="currentIndex" >
<number>0</number>
</property>
<widget class="QWidget" name="creditsTab" >
<attribute name="title" >
<string>&amp;Credits</string>
</attribute>
<layout class="QGridLayout" >
<item row="0" column="0" >
<widget class="QTextBrowser" name="browserCredits" >
<property name="focusPolicy" >
<enum>Qt::TabFocus</enum>
</property>
<property name="acceptDrops" >
<bool>false</bool>
</property>
<property name="autoFillBackground" >
<bool>true</bool>
</property>
<property name="autoFormatting" >
<set>QTextEdit::AutoNone</set>
</property>
<property name="tabChangesFocus" >
<bool>true</bool>
</property>
<property name="textInteractionFlags" >
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
<property name="openExternalLinks" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="licenceTab" >
<attribute name="title" >
<string>&amp;License</string>
</attribute>
<layout class="QGridLayout" >
<item row="0" column="0" >
<widget class="QTextBrowser" name="browserLicense" >
<property name="focusPolicy" >
<enum>Qt::TabFocus</enum>
</property>
<property name="horizontalScrollBarPolicy" >
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="tabChangesFocus" >
<bool>true</bool>
</property>
<property name="lineWrapMode" >
<enum>QTextEdit::WidgetWidth</enum>
</property>
<property name="textInteractionFlags" >
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="licenseSpeex" >
<attribute name="title" >
<string>L&amp;ibraries</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2" >
<item row="0" column="0" >
<widget class="QTextBrowser" name="browserLicenses"/>
</item>
</layout>
</widget>
</widget>
</item>
<item row="3" column="0" >
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="1" >
<widget class="QPushButton" name="okButton" >
<property name="text" >
<string>&amp;Ok</string>
</property>
</widget>
</item>
<item row="3" column="2" >
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources>
<include location="rbutilqt.qrc" />
</resources>
<connections>
<connection>
<sender>okButton</sender>
<signal>clicked()</signal>
<receiver>aboutBox</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel" >
<x>210</x>
<y>398</y>
</hint>
<hint type="destinationlabel" >
<x>210</x>
<y>210</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -0,0 +1,30 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2013 Amaury Pouly
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include <QDebug>
#include "archiveutil.h"
ArchiveUtil::ArchiveUtil(QObject* parent)
:QObject(parent)
{
}
ArchiveUtil::~ArchiveUtil()
{
}

View file

@ -0,0 +1,41 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2013 Amaury Pouly
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef ARCHIVEUTIL_H
#define ARCHIVEUTIL_H
#include <QtCore>
class ArchiveUtil : public QObject
{
Q_OBJECT
public:
ArchiveUtil(QObject* parent);
~ArchiveUtil();
virtual bool close(void) = 0;
virtual bool extractArchive(const QString& dest, QString file = "") = 0;
virtual QStringList files(void) = 0;
signals:
void logProgress(int, int);
void logItem(QString, int);
};
#endif

View file

@ -0,0 +1,376 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "autodetection.h"
#include "rbsettings.h"
#include "playerbuildinfo.h"
#include "../ipodpatcher/ipodpatcher.h"
#include "../sansapatcher/sansapatcher.h"
#include "system.h"
#include "utils.h"
#include "rockboxinfo.h"
#include "Logger.h"
Autodetection::Autodetection(QObject* parent): QObject(parent)
{
}
bool Autodetection::detect(void)
{
QMap<PlayerStatus, QString> states;
states[PlayerOk] = "Ok";
states[PlayerAmbiguous] = "Ambiguous";
states[PlayerError] = "Error";
states[PlayerIncompatible] = "Incompatible";
states[PlayerMtpMode] = "MtpMode";
// clear detection state
m_detected.clear();
detectUsb();
mergeMounted();
mergePatcher();
// if any entry with usbdevices containing a value is left that entry
// hasn't been merged later. This indicates a problem during detection
// (ambiguous player but refining it failed). In this case create an entry
// for eacho of those so the user can select.
QList<struct Detected> detected;
for(int i = 0; i < m_detected.size(); ++i) {
int j = m_detected.at(i).usbdevices.size();
if(j > 0) {
struct Detected entry = m_detected.at(i);
while(j--) {
struct Detected d;
d.device = entry.usbdevices.at(j);
d.mountpoint = entry.mountpoint;
d.status = PlayerAmbiguous;
detected.append(d);
}
}
else {
detected.append(m_detected.at(i));
}
}
m_detected = detected;
for(int i = 0; i < m_detected.size(); ++i) {
LOG_INFO() << "Detected player:" << m_detected.at(i).device
<< "at" << m_detected.at(i).mountpoint
<< states[m_detected.at(i).status];
}
return m_detected.size() > 0;
}
/** @brief detect devices based on usb pid / vid.
*/
void Autodetection::detectUsb()
{
// usb pid detection
QList<uint32_t> attached;
attached = System::listUsbIds();
int i = attached.size();
while(i--) {
QStringList a = PlayerBuildInfo::instance()->value(PlayerBuildInfo::UsbIdTargetList, attached.at(i)).toStringList();
if(a.size() > 0) {
struct Detected d;
d.status = PlayerOk;
d.usbdevices = a;
m_detected.append(d);
LOG_INFO() << "[USB] detected supported player" << d.usbdevices;
}
QStringList b = PlayerBuildInfo::instance()->value(PlayerBuildInfo::UsbIdErrorList, attached.at(i)).toStringList();
if(b.size() > 0) {
struct Detected d;
d.status = PlayerMtpMode;
d.usbdevices = b;
m_detected.append(d);
LOG_WARNING() << "[USB] detected problem with player" << d.device;
}
QString idstring = QString("%1").arg(attached.at(i), 8, 16, QChar('0'));
if(!PlayerBuildInfo::instance()->value(
PlayerBuildInfo::DisplayName, idstring).toString().isEmpty()) {
struct Detected d;
d.status = PlayerIncompatible;
d.device = idstring;
m_detected.append(d);
LOG_WARNING() << "[USB] detected incompatible player" << d.device;
}
}
}
// Merge players detected by checking mounted filesystems for known files:
// - rockbox-info.txt / rbutil.log
// - player specific files
void Autodetection::mergeMounted(void)
{
QStringList mounts = Utils::mountpoints(Utils::MountpointsSupported);
LOG_INFO() << "paths to check:" << mounts;
for(int i = 0; i < mounts.size(); i++)
{
// do the file checking
QDir dir(mounts.at(i));
if(dir.exists())
{
// check logfile first.
if(QFile(mounts.at(i) + "/.rockbox/rbutil.log").exists()) {
QSettings log(mounts.at(i) + "/.rockbox/rbutil.log",
QSettings::IniFormat, this);
if(!log.value("platform").toString().isEmpty()) {
struct Detected d;
d.device = log.value("platform").toString();
d.mountpoint = mounts.at(i);
d.status = PlayerOk;
updateDetectedDevice(d);
LOG_INFO() << "rbutil.log detected:"
<< log.value("platform").toString() << mounts.at(i);
}
}
// check rockbox-info.txt afterwards.
RockboxInfo info(mounts.at(i));
if(info.success())
{
struct Detected d;
d.device = info.target();
d.mountpoint = mounts.at(i);
d.status = PlayerOk;
updateDetectedDevice(d);
LOG_INFO() << "rockbox-info.txt detected:"
<< info.target() << mounts.at(i);
}
// check for some specific files in root folder
QDir root(mounts.at(i));
QStringList rootentries = root.entryList(QDir::Files);
if(rootentries.contains("archos.mod", Qt::CaseInsensitive))
{
// archos.mod in root folder -> Archos Player
struct Detected d;
d.device = "player";
d.mountpoint = mounts.at(i);
d.status = PlayerOk;
updateDetectedDevice(d);
}
if(rootentries.contains("ONDIOST.BIN", Qt::CaseInsensitive))
{
// ONDIOST.BIN in root -> Ondio FM
struct Detected d;
d.device = "ondiofm";
d.mountpoint = mounts.at(i);
d.status = PlayerOk;
updateDetectedDevice(d);
}
if(rootentries.contains("ONDIOSP.BIN", Qt::CaseInsensitive))
{
// ONDIOSP.BIN in root -> Ondio SP
struct Detected d;
d.device = "ondiosp";
d.mountpoint = mounts.at(i);
d.status = PlayerOk;
updateDetectedDevice(d);
}
if(rootentries.contains("ajbrec.ajz", Qt::CaseInsensitive))
{
LOG_INFO() << "ajbrec.ajz found. Trying detectAjbrec()";
struct Detected d;
d.device = detectAjbrec(mounts.at(i));
d.mountpoint = mounts.at(i);
d.status = PlayerOk;
if(!d.device.isEmpty()) {
LOG_INFO() << d.device;
updateDetectedDevice(d);
}
}
// detection based on player specific folders
QStringList rootfolders = root.entryList(QDir::Dirs
| QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
if(rootfolders.contains("GBSYSTEM", Qt::CaseInsensitive))
{
// GBSYSTEM folder -> Gigabeat
struct Detected d;
d.device = "gigabeatf";
d.mountpoint = mounts.at(i);
updateDetectedDevice(d);
}
}
}
#if 0
// Ipods have a folder "iPod_Control" in the root.
for(int i = 0; i < m_detected.size(); ++i) {
struct Detected entry = m_detected.at(i);
for(int j = 0; j < entry.usbdevices.size(); ++j) {
// limit this to Ipods only.
if(!entry.usbdevices.at(j).startsWith("ipod")
&& !entry.device.startsWith("ipod")) {
continue;
}
// look for iPod_Control on all supported volumes.
for(int k = 0; k < mounts.size(); k++) {
QDir root(mounts.at(k));
QStringList rootfolders = root.entryList(QDir::Dirs
| QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
if(rootfolders.contains("iPod_Control", Qt::CaseInsensitive)) {
entry.mountpoint = mounts.at(k);
m_detected.takeAt(i);
m_detected.append(entry);
}
}
}
}
#endif
}
void Autodetection::mergePatcher(void)
{
int n;
// try ipodpatcher
// initialize sector buffer. Needed.
struct ipod_t ipod;
ipod.sectorbuf = nullptr;
ipod_alloc_buffer(&ipod, BUFFER_SIZE);
n = ipod_scan(&ipod);
// FIXME: handle more than one Ipod connected in ipodpatcher.
if(n == 1) {
LOG_INFO() << "Ipod found:" << ipod.modelstr << "at" << ipod.diskname;
// since resolveMountPoint is doing exact matches we need to select
// the correct partition.
QString mp(ipod.diskname);
#ifdef Q_OS_LINUX
mp.append("2");
#endif
#ifdef Q_OS_MACX
mp.append("s2");
#endif
struct Detected d;
d.device = ipod.targetname;
d.mountpoint = Utils::resolveMountPoint(mp);
// if the found ipod is a macpod also notice it as device with problem.
if(ipod.macpod)
d.status = PlayerWrongFilesystem;
else
d.status = PlayerOk;
updateDetectedDevice(d);
}
else {
LOG_INFO() << "ipodpatcher: no Ipod found." << n;
}
ipod_dealloc_buffer(&ipod);
// try sansapatcher
// initialize sector buffer. Needed.
struct sansa_t sansa;
sansa_alloc_buffer(&sansa, BUFFER_SIZE);
n = sansa_scan(&sansa);
if(n == 1) {
LOG_INFO() << "Sansa found:"
<< sansa.targetname << "at" << sansa.diskname;
QString mp(sansa.diskname);
#ifdef Q_OS_LINUX
mp.append("1");
#endif
#ifdef Q_OS_MACX
mp.append("s1");
#endif
struct Detected d;
d.device = QString("sansa%1").arg(sansa.targetname);
d.mountpoint = Utils::resolveMountPoint(mp);
d.status = PlayerOk;
updateDetectedDevice(d);
}
else {
LOG_INFO() << "sansapatcher: no Sansa found." << n;
}
sansa_dealloc_buffer(&sansa);
}
QString Autodetection::detectAjbrec(QString root)
{
QFile f(root + "/ajbrec.ajz");
char header[24];
f.open(QIODevice::ReadOnly);
if(!f.read(header, 24)) return QString();
f.close();
// check the header of the file.
// recorder v1 had a 6 bytes sized header
// recorder v2, FM, Ondio SP and FM have a 24 bytes header.
// recorder v1 has the binary length in the first 4 bytes, so check
// for them first.
int len = (header[0]<<24) | (header[1]<<16) | (header[2]<<8) | header[3];
LOG_INFO() << "abjrec.ajz possible bin length:" << len
<< "file len:" << f.size();
if((f.size() - 6) == len)
return "recorder";
// size didn't match, now we need to assume we have a headerlength of 24.
switch(header[11]) {
case 2:
return "recorderv2";
case 4:
return "fmrecorder";
case 8:
return "ondiofm";
case 16:
return "ondiosp";
default:
break;
}
return QString();
}
int Autodetection::findDetectedDevice(QString device)
{
int i = m_detected.size();
while(i--) {
if(m_detected.at(i).usbdevices.contains(device))
return i;
}
i = m_detected.size();
while(i--) {
if(m_detected.at(i).device == device)
return i;
}
return -1;
}
void Autodetection::updateDetectedDevice(Detected& entry)
{
int index = findDetectedDevice(entry.device);
if(index < 0) {
m_detected.append(entry);
}
else {
m_detected.takeAt(index);
m_detected.append(entry);
}
}

View file

@ -0,0 +1,72 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef AUTODETECTION_H_
#define AUTODETECTION_H_
#include <QObject>
#include <QString>
#include <QList>
#include <QStringList>
class Autodetection :public QObject
{
Q_OBJECT
public:
Autodetection(QObject* parent=nullptr);
enum PlayerStatus {
PlayerOk,
PlayerIncompatible,
PlayerMtpMode,
PlayerWrongFilesystem,
PlayerError,
PlayerAmbiguous,
};
struct Detected {
QString device;
QStringList usbdevices;
QString mountpoint;
enum PlayerStatus status;
};
bool detect();
QList<struct Detected> detected(void) { return m_detected; }
private:
QString resolveMountPoint(QString);
void detectUsb(void);
void mergeMounted(void);
void mergePatcher(void);
QString detectAjbrec(QString);
int findDetectedDevice(QString device);
void updateDetectedDevice(struct Detected& entry);
QList<struct Detected> m_detected;
QList<int> m_usbconid;
};
#endif /*AUTODETECTION_H_*/

View file

@ -0,0 +1,201 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "bootloaderinstallbase.h"
#include "bootloaderinstallams.h"
#include "Logger.h"
#include "../mkamsboot/mkamsboot.h"
BootloaderInstallAms::BootloaderInstallAms(QObject *parent)
: BootloaderInstallBase(parent)
{
}
QString BootloaderInstallAms::ofHint()
{
return tr("Bootloader installation requires you to provide "
"a copy of the original Sandisk firmware (bin file). "
"This firmware file will be patched and then installed to your "
"player along with the rockbox bootloader. "
"You need to download this file yourself due to legal "
"reasons. Please browse the "
"<a href='http://forums.sandisk.com/sansa/'>Sansa Forums</a> "
"or refer to the "
"<a href='http://www.rockbox.org/manual.shtml'>manual</a> and "
"the <a href='http://www.rockbox.org/wiki/SansaAMS'>SansaAMS</a> "
"wiki page on how to obtain this file.<br/>"
"<b>Note:</b> This file is not present on your player and will "
"disappear automatically after installing it.<br/><br/>"
"Press Ok to continue and browse your computer for the firmware "
"file.");
}
bool BootloaderInstallAms::install(void)
{
if(m_offile.isEmpty())
return false;
LOG_INFO() << "installing bootloader";
// download firmware from server
emit logItem(tr("Downloading bootloader file"), LOGINFO);
connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallAms::installStage2);
downloadBlStart(m_blurl);
return true;
}
void BootloaderInstallAms::installStage2(void)
{
LOG_INFO() << "installStage2";
unsigned char* buf;
unsigned char* of_packed;
int of_packedsize;
unsigned char* rb_packed;
int rb_packedsize;
off_t len;
struct md5sums sum;
char md5sum[33]; /* 32 hex digits, plus terminating zero */
int n;
int model;
int firmware_size;
int bootloader_size;
int patchable;
int totalsize;
char errstr[200];
sum.md5 = md5sum;
m_tempfile.open();
QString bootfile = m_tempfile.fileName();
m_tempfile.close();
/* Load bootloader file */
rb_packed = load_rockbox_file(bootfile.toLocal8Bit().data(), &model,
&bootloader_size,&rb_packedsize,
errstr,sizeof(errstr));
if (rb_packed == nullptr)
{
LOG_ERROR() << "could not load bootloader: " << bootfile;
emit logItem(errstr, LOGERROR);
emit logItem(tr("Could not load %1").arg(bootfile), LOGERROR);
emit done(true);
return;
}
/* Load original firmware file */
buf = load_of_file(m_offile.toLocal8Bit().data(), model, &len, &sum,
&firmware_size, &of_packed ,&of_packedsize,
errstr, sizeof(errstr));
if (buf == nullptr)
{
LOG_ERROR() << "could not load OF: " << m_offile;
emit logItem(errstr, LOGERROR);
emit logItem(tr("Could not load %1").arg(m_offile), LOGERROR);
free(rb_packed);
emit done(true);
return;
}
/* check total size */
patchable = check_sizes(sum.model, rb_packedsize, bootloader_size,
of_packedsize, firmware_size, &totalsize, errstr, sizeof(errstr));
if (!patchable)
{
LOG_ERROR() << "No room to insert bootloader";
emit logItem(errstr, LOGERROR);
emit logItem(tr("No room to insert bootloader, try another firmware version"),
LOGERROR);
free(buf);
free(of_packed);
free(rb_packed);
emit done(true);
return;
}
/* patch the firmware */
emit logItem(tr("Patching Firmware..."), LOGINFO);
patch_firmware(sum.model,firmware_revision(sum.model),firmware_size,buf,
len,of_packed,of_packedsize,rb_packed,rb_packedsize);
/* write out file */
QFile out(m_blfile);
if(!out.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
LOG_ERROR() << "Could not open" << m_blfile << "for writing";
emit logItem(tr("Could not open %1 for writing").arg(m_blfile),LOGERROR);
free(buf);
free(of_packed);
free(rb_packed);
emit done(true);
return;
}
n = out.write((char*)buf, len);
if (n != len)
{
LOG_ERROR() << "Could not write firmware file";
emit logItem(tr("Could not write firmware file"),LOGERROR);
free(buf);
free(of_packed);
free(rb_packed);
emit done(true);
return;
}
out.close();
free(buf);
free(of_packed);
free(rb_packed);
//end of install
LOG_INFO() << "install successfull";
emit logItem(tr("Success: modified firmware file created"), LOGINFO);
logInstall(LogAdd);
emit done(false);
return;
}
bool BootloaderInstallAms::uninstall(void)
{
emit logItem(tr("To uninstall, perform a normal upgrade with an unmodified "
"original firmware"), LOGINFO);
logInstall(LogRemove);
emit done(true);
return false;
}
BootloaderInstallBase::BootloaderType BootloaderInstallAms::installed(void)
{
return BootloaderUnknown;
}
BootloaderInstallBase::Capabilities BootloaderInstallAms::capabilities(void)
{
return (Install | NeedsOf);
}

View file

@ -0,0 +1,42 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef BOOTLOADERINSTALLAMS_H
#define BOOTLOADERINSTALLAMS_H
#include <QtCore>
#include "bootloaderinstallbase.h"
//! bootloader installation derivate based on mkamsboot
class BootloaderInstallAms : public BootloaderInstallBase
{
Q_OBJECT
public:
BootloaderInstallAms(QObject *parent);
bool install(void);
bool uninstall(void);
BootloaderInstallBase::BootloaderType installed(void);
Capabilities capabilities(void);
QString ofHint();
private:
private slots:
void installStage2(void);
};
#endif

View file

@ -0,0 +1,302 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "bootloaderinstallbase.h"
#include "utils.h"
#include "ziputil.h"
#include "mspackutil.h"
#include "Logger.h"
#if defined(Q_OS_MACX)
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#endif
BootloaderInstallBase::BootloaderType BootloaderInstallBase::installed(void)
{
return BootloaderUnknown;
}
BootloaderInstallBase::Capabilities BootloaderInstallBase::capabilities(void)
{
return Capabilities();
}
void BootloaderInstallBase::downloadBlStart(QUrl source)
{
m_http.setFile(&m_tempfile);
m_http.setCache(true);
connect(&m_http, &HttpGet::done, this, &BootloaderInstallBase::downloadBlFinish);
// connect the http read signal to our logProgess *signal*
// to immediately emit it without any helper function.
connect(&m_http, &HttpGet::dataReadProgress,
this, &BootloaderInstallBase::logProgress);
m_http.getFile(source);
}
void BootloaderInstallBase::downloadReqFinished(int id, bool error)
{
LOG_INFO() << "Download Request" << id
<< "finished, error:" << m_http.errorString();
downloadBlFinish(error);
}
void BootloaderInstallBase::downloadBlFinish(bool error)
{
LOG_INFO() << "Downloading bootloader finished, error:"
<< error;
// update progress bar
emit logProgress(100, 100);
if(m_http.httpResponse() != 200) {
emit logItem(tr("Download error: received HTTP error %1.")
.arg(m_http.errorString()), LOGERROR);
emit done(true);
return;
}
if(error) {
emit logItem(tr("Download error: %1")
.arg(m_http.errorString()), LOGERROR);
emit done(true);
return;
}
else if(m_http.isCached())
emit logItem(tr("Download finished (cache used)."), LOGOK);
else
emit logItem(tr("Download finished."), LOGOK);
QCoreApplication::processEvents();
m_blversion = m_http.timestamp();
emit downloadDone();
}
void BootloaderInstallBase::installBlfile(void)
{
LOG_INFO() << "installBlFile(void)";
}
void BootloaderInstallBase::progressAborted(void)
{
LOG_INFO() << "progressAborted(void)";
emit installAborted();
}
//! @brief backup OF file.
//! @param to folder to write backup file to. Folder will get created.
//! @return true on success, false on error.
bool BootloaderInstallBase::backup(QString to)
{
LOG_INFO() << "Backing up bootloader file";
QDir targetDir(".");
emit logItem(tr("Creating backup of original firmware file."), LOGINFO);
if(!targetDir.mkpath(to)) {
emit logItem(tr("Creating backup folder failed"), LOGERROR);
return false;
}
QString tofile = to + "/" + QFileInfo(m_blfile).fileName();
LOG_INFO() << "trying to backup" << m_blfile << "to" << tofile;
if(!QFile::copy(Utils::resolvePathCase(m_blfile), tofile)) {
emit logItem(tr("Creating backup copy failed."), LOGERROR);
return false;
}
emit logItem(tr("Backup created."), LOGOK);
return true;
}
//! @brief log installation to logfile.
//! @param mode action to perform. 0: add to log, 1: remove from log.
//! @return 0 on success
int BootloaderInstallBase::logInstall(LogMode mode)
{
int result = 0;
QString section = m_blurl.path().section('/', -1);
QSettings s(m_logfile, QSettings::IniFormat, this);
emit logItem(tr("Creating installation log"), LOGINFO);
if(mode == LogAdd) {
s.setValue("Bootloader/" + section, m_blversion.toString(Qt::ISODate));
LOG_INFO() << "Writing log, version:"
<< m_blversion.toString(Qt::ISODate);
}
else {
s.remove("Bootloader/" + section);
}
s.sync();
emit logItem(tr("Installation log created"), LOGOK);
return result;
}
#if defined(Q_OS_MACX)
void BootloaderInstallBase::waitRemount()
{
m_remountTries = 600;
emit logItem(tr("Waiting for system to remount player"), LOGINFO);
QTimer::singleShot(100, this, SLOT(checkRemount()));
}
#endif
void BootloaderInstallBase::checkRemount()
{
#if defined(Q_OS_MACX)
if(m_remountTries--) {
int status = 0;
// check if device has been remounted
QCoreApplication::processEvents();
int num;
struct statfs *mntinf;
num = getmntinfo(&mntinf, MNT_WAIT);
while(num--) {
if(QString(mntinf->f_mntfromname).startsWith(m_remountDevice)
&& QString(mntinf->f_fstypename).contains("msdos", Qt::CaseInsensitive))
status = 1;
mntinf++;
}
if(!status) {
// still not remounted, restart timer.
QTimer::singleShot(500, this, SLOT(checkRemount()));
LOG_INFO() << "Player not remounted yet" << m_remountDevice;
}
else {
emit logItem(tr("Player remounted"), LOGINFO);
emit remounted(true);
}
}
else {
emit logItem(tr("Timeout on remount"), LOGERROR);
emit remounted(false);
}
#endif
}
//! @brief set list of possible bootloader files and pick the existing one.
//! @param sl list of possible bootloader files.
void BootloaderInstallBase::setBlFile(QStringList sl)
{
// figue which of the possible bootloader filenames is correct.
for(int a = 0; a < sl.size(); a++) {
if(!Utils::resolvePathCase(sl.at(a)).isEmpty()) {
m_blfile = sl.at(a);
}
}
if(m_blfile.isEmpty()) {
m_blfile = sl.at(0);
}
}
bool BootloaderInstallBase::setOfFile(QString of, QStringList blfile)
{
bool found = false;
ArchiveUtil *util = nullptr;
// check if we're actually looking for a zip file. If so we must avoid
// trying to unzip it.
bool wantZip = false;
for (int i = 0; i < blfile.size(); i++)
{
if (blfile.at(i).endsWith(".zip"))
wantZip = true;
}
// try ZIP first
ZipUtil *zu = new ZipUtil(this);
if(zu->open(of) && !wantZip)
{
emit logItem(tr("Zip file format detected"), LOGINFO);
util = zu;
}
else
delete zu;
// if ZIP failed, try CAB
if(util == nullptr)
{
MsPackUtil *msu = new MsPackUtil(this);
if(msu->open(of))
{
emit logItem(tr("CAB file format detected"), LOGINFO);
util = msu;
}
else
delete msu;
}
// check if the file set is in zip format
if(util) {
QStringList contents = util->files();
LOG_INFO() << "archive contains:" << contents;
for(int i = 0; i < blfile.size(); ++i) {
// strip any path, we don't know the structure in the zip
QString f = QFileInfo(blfile.at(i)).fileName();
LOG_INFO() << "searching archive for" << f;
// contents.indexOf() works case sensitive. Since the filename
// casing is unknown (and might change) do this manually.
// FIXME: support files in folders
for(int j = 0; j < contents.size(); ++j) {
if(contents.at(j).compare(f, Qt::CaseInsensitive) == 0) {
found = true;
emit logItem(tr("Extracting firmware %1 from archive")
.arg(f), LOGINFO);
// store in class temporary file
m_tempof.open();
m_offile = m_tempof.fileName();
m_tempof.close();
if(!util->extractArchive(m_offile, contents.at(j))) {
emit logItem(tr("Error extracting firmware from archive"), LOGERROR);
found = false;
break;
}
break;
}
}
}
if(!found) {
emit logItem(tr("Could not find firmware in archive"), LOGERROR);
}
delete util;
}
else {
m_offile = of;
found = true;
}
return found;
}

View file

@ -0,0 +1,118 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef BOOTLOADERINSTALLBASE_H
#define BOOTLOADERINSTALLBASE_H
#include <QtCore>
#include "progressloggerinterface.h"
#include "httpget.h"
//! baseclass for all Bootloader installs
class BootloaderInstallBase : public QObject
{
Q_OBJECT
public:
enum Capability
{ Install = 0x01, Uninstall = 0x02, Backup = 0x04,
IsFile = 0x08, IsRaw = 0x10, NeedsOf = 0x20,
CanCheckInstalled = 0x40, CanCheckVersion = 0x80 };
Q_DECLARE_FLAGS(Capabilities, Capability)
enum BootloaderType
{ BootloaderNone, BootloaderRockbox, BootloaderOther, BootloaderUnknown };
BootloaderInstallBase(QObject *parent) : QObject(parent)
{ }
//! install the bootloader, must be implemented
virtual bool install(void) = 0;
//! uninstall the bootloader, must be implemented
virtual bool uninstall(void) = 0;
//! returns the installed bootloader
virtual BootloaderType installed(void)=0;
//! returns the capabilities of the bootloader class
virtual Capabilities capabilities(void)=0;
//! returns a OF Firmware hint or empty if there is none
virtual QString ofHint() {return QString();}
//! backup a already installed bootloader
bool backup(QString to);
//! set the different filenames and paths
void setBlFile(QStringList f);
void setBlUrl(QUrl u)
{ m_blurl = u; }
void setLogfile(QString f)
{ m_logfile = f; }
bool setOfFile(QString of, QStringList blfile);
//! returns a port Install Hint or empty if there is none
//! static and in the base class, so the installer classes dont need to
// be modified for new targets
static QString postinstallHints(QString model);
//! returns the correct BootloaderInstaller object for the requested type
static BootloaderInstallBase* createBootloaderInstaller(QObject* parent,QString type);
protected slots:
void downloadReqFinished(int id, bool error);
void downloadBlFinish(bool error);
void installBlfile(void);
void progressAborted(void);
// NOTE: we need to keep this slot even on targets that don't need it
// -- using the preprocessor here confused moc.
void checkRemount(void);
protected:
enum LogMode
{ LogAdd, LogRemove };
void downloadBlStart(QUrl source);
int logInstall(LogMode mode);
HttpGet m_http; //! http download object
QString m_blfile; //! bootloader filename on player
QString m_logfile; //! file for installation log
QUrl m_blurl; //! bootloader download URL
QTemporaryFile m_tempfile; //! temporary file for download
QTemporaryFile m_tempof; //! temporary file for OF extracted from archive
QDateTime m_blversion; //! download timestamp used for version information
QString m_offile; //! path to the offile
#if defined(Q_OS_MACX)
void waitRemount(void);
int m_remountTries;
QString m_remountDevice;
#endif
signals:
void downloadDone(void); //! internal signal sent when download finished.
void installAborted(void); //! internal signal sent on abort button click.
void done(bool);
void logItem(QString, int); //! set logger item
void logProgress(int, int); //! set progress bar.
// NOTE: we need to keep this signal even on targets that don't need it
// -- using the preprocessor here confused moc.
void remounted(bool);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(BootloaderInstallBase::Capabilities)
#endif

View file

@ -0,0 +1,178 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2020 by Solomon Peachy
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include <QtDebug>
#include "bootloaderinstallbase.h"
#include "bootloaderinstallbspatch.h"
#include "../bspatch/bspatch.h"
#include "Logger.h"
/* class for running bspatch() in a separate thread to keep the UI responsive. */
class BootloaderThreadBSPatch : public QThread
{
public:
void run(void);
void setInputFile(QString f)
{ m_inputfile = f; }
void setOutputFile(QString f)
{ m_outputfile = f; }
void setBootloaderFile(QString f)
{ m_bootfile = f; }
int error(void)
{ return m_error; }
private:
QString m_inputfile;
QString m_bootfile;
QString m_outputfile;
int m_error;
};
void BootloaderThreadBSPatch::run(void)
{
LOG_INFO() << "Thread started.";
m_error = apply_bspatch(m_inputfile.toLocal8Bit().constData(),
m_outputfile.toLocal8Bit().constData(),
m_bootfile.toLocal8Bit().constData());
LOG_INFO() << "Thread finished, result:" << m_error;
}
BootloaderInstallBSPatch::BootloaderInstallBSPatch(QObject *parent)
: BootloaderInstallBase(parent)
{
m_thread = nullptr;
}
QString BootloaderInstallBSPatch::ofHint()
{
return tr("Bootloader installation requires you to provide "
"the correct verrsion of the original firmware file. "
"This file will be patched with the Rockbox bootloader and "
"installed to your player. You need to download this file "
"yourself due to legal reasons. Please refer to the "
"<a href='http://www.rockbox.org/wiki/'>rockbox wiki</a> "
"pages on how to obtain this file.<br/>"
"Press Ok to continue and browse your computer for the firmware "
"file.");
}
/** Start bootloader installation.
*/
bool BootloaderInstallBSPatch::install(void)
{
if(!QFileInfo(m_offile).isReadable())
{
LOG_ERROR() << "could not read original firmware file"
<< m_offile;
emit logItem(tr("Could not read original firmware file"), LOGERROR);
return false;
}
LOG_INFO() << "downloading bootloader";
// download bootloader from server
emit logItem(tr("Downloading bootloader file"), LOGINFO);
connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallBSPatch::installStage2);
downloadBlStart(m_blurl);
return true;
}
void BootloaderInstallBSPatch::installStage2(void)
{
LOG_INFO() << "patching file...";
emit logItem(tr("Patching file..."), LOGINFO);
m_tempfile.open();
// we have not detailed progress on the patching so just show a busy
// indicator instead.
emit logProgress(0, 0);
m_patchedFile.open();
m_thread = new BootloaderThreadBSPatch();
m_thread->setInputFile(m_offile);
m_thread->setBootloaderFile(m_tempfile.fileName());
m_thread->setOutputFile(m_patchedFile.fileName());
m_tempfile.close();
m_patchedFile.close();
connect(m_thread, &QThread::finished, this, &BootloaderInstallBSPatch::installStage3);
m_thread->start();
}
void BootloaderInstallBSPatch::installStage3(void)
{
int err = m_thread->error();
emit logProgress(1, 1);
// if the patch failed
if (err != 0)
{
LOG_ERROR() << "Could not patch the original firmware file";
emit logItem(tr("Patching the original firmware failed"), LOGERROR);
emit done(true);
return;
}
LOG_INFO() << "Original Firmware succesfully patched";
emit logItem(tr("Succesfully patched firmware file"), LOGINFO);
// if a bootloader is already present delete it.
QString fwfile(m_blfile);
if(QFileInfo(fwfile).isFile())
{
LOG_INFO() << "deleting old target file";
QFile::remove(fwfile);
}
// place (new) bootloader. Copy, since the temporary file will be removed
// automatically.
LOG_INFO() << "moving patched bootloader to" << fwfile;
if(m_patchedFile.copy(fwfile))
{
emit logItem(tr("Bootloader successful installed"), LOGOK);
logInstall(LogAdd);
emit done(false);
}
else
{
emit logItem(tr("Patched bootloader could not be installed"), LOGERROR);
emit done(true);
}
// clean up thread object.
delete m_thread;
return;
}
bool BootloaderInstallBSPatch::uninstall(void)
{
emit logItem(tr("To uninstall, perform a normal upgrade with an unmodified "
"original firmware."), LOGINFO);
logInstall(LogRemove);
emit done(true);
return false;
}
BootloaderInstallBase::BootloaderType BootloaderInstallBSPatch::installed(void)
{
return BootloaderUnknown;
}
BootloaderInstallBase::Capabilities BootloaderInstallBSPatch::capabilities(void)
{
return (Install | NeedsOf);
}

View file

@ -0,0 +1,47 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2020 Solomon Peachy
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef BOOTLOADERINSTALLBSPATCH_H
#define BOOTLOADERINSTALLBSPATCH_H
#include <QtCore>
#include "bootloaderinstallbase.h"
class BootloaderThreadBSPatch;
//! bootloader installation class for devices handled by mkimxboot.
class BootloaderInstallBSPatch : public BootloaderInstallBase
{
Q_OBJECT
public:
BootloaderInstallBSPatch(QObject *parent);
bool install(void);
bool uninstall(void);
BootloaderInstallBase::BootloaderType installed(void);
Capabilities capabilities(void);
QString ofHint();
private slots:
void installStage2(void);
void installStage3(void);
private:
BootloaderThreadBSPatch *m_thread;
QTemporaryFile m_patchedFile;
};
#endif

View file

@ -0,0 +1,133 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2009 by Maurus Cuelenaere
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "bootloaderinstallbase.h"
#include "bootloaderinstallchinachip.h"
#include "../chinachippatcher/chinachip.h"
BootloaderInstallChinaChip::BootloaderInstallChinaChip(QObject *parent)
: BootloaderInstallBase(parent)
{
(void)parent;
}
QString BootloaderInstallChinaChip::ofHint()
{
return tr("Bootloader installation requires you to provide "
"a firmware file of the original firmware (HXF file). "
"You need to download this file yourself due to legal "
"reasons. Please refer to the "
"<a href='http://www.rockbox.org/manual.shtml'>manual</a> and the "
"<a href='http://www.rockbox.org/wiki/OndaVX747"
"#Download_and_extract_a_recent_ve'>OndaVX747</a> wiki page on "
"how to obtain this file.<br/>"
"Press Ok to continue and browse your computer for the firmware "
"file.");
}
bool BootloaderInstallChinaChip::install()
{
if(m_offile.isEmpty())
return false;
emit logItem(tr("Downloading bootloader file"), LOGINFO);
connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallChinaChip::installStage2);
downloadBlStart(m_blurl);
return true;
}
void BootloaderInstallChinaChip::installStage2()
{
enum cc_error result;
bool error = true;
m_tempfile.open();
QString blfile = m_tempfile.fileName();
m_tempfile.close();
QString backupfile = QFileInfo(m_blfile).absoluteDir().absoluteFilePath("ccpmp.bin");
result = chinachip_patch(m_offile.toLocal8Bit(), blfile.toLocal8Bit(),
m_blfile.toLocal8Bit(), backupfile.toLocal8Bit());
switch(result) {
case E_OK:
error = false;
break;
case E_OPEN_FIRMWARE:
emit logItem(tr("Could not open firmware file"), LOGERROR);
break;
case E_OPEN_BOOTLOADER:
emit logItem(tr("Could not open bootloader file"), LOGERROR);
break;
case E_MEMALLOC:
emit logItem(tr("Could not allocate memory"), LOGERROR);
break;
case E_LOAD_FIRMWARE:
emit logItem(tr("Could not load firmware file"), LOGERROR);
break;
case E_INVALID_FILE:
emit logItem(tr("File is not a valid ChinaChip firmware"), LOGERROR);
break;
case E_NO_CCPMP:
emit logItem(tr("Could not find ccpmp.bin in input file"), LOGERROR);
break;
case E_OPEN_BACKUP:
emit logItem(tr("Could not open backup file for ccpmp.bin"), LOGERROR);
break;
case E_WRITE_BACKUP:
emit logItem(tr("Could not write backup file for ccpmp.bin"), LOGERROR);
break;
case E_LOAD_BOOTLOADER:
emit logItem(tr("Could not load bootloader file"), LOGERROR);
break;
case E_GET_TIME:
emit logItem(tr("Could not get current time"), LOGERROR);
break;
case E_OPEN_OUTFILE:
emit logItem(tr("Could not open output file"), LOGERROR);
break;
case E_WRITE_OUTFILE:
emit logItem(tr("Could not write output file"), LOGERROR);
break;
default:
emit logItem(tr("Unexpected error from chinachippatcher"), LOGERROR);
break;
}
emit done(error);
}
bool BootloaderInstallChinaChip::uninstall()
{
/* TODO: only way is to restore the OF */
return false;
}
BootloaderInstallBase::BootloaderType BootloaderInstallChinaChip::installed()
{
/* TODO: find a way to figure this out */
return BootloaderUnknown;
}
BootloaderInstallBase::Capabilities BootloaderInstallChinaChip::capabilities()
{
return (Install | IsFile | NeedsOf);
}

View file

@ -0,0 +1,41 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2009 by Maurus Cuelenaere
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef BOOTLOADERINSTALLCCPMP_H
#define BOOTLOADERINSTALLCCPMP_H
#include <QtCore>
#include "bootloaderinstallbase.h"
class BootloaderInstallChinaChip : public BootloaderInstallBase
{
Q_OBJECT
public:
BootloaderInstallChinaChip(QObject *parent = nullptr);
bool install(void);
bool uninstall(void);
BootloaderInstallBase::BootloaderType installed(void);
Capabilities capabilities(void);
QString ofHint();
private slots:
void installStage2(void);
};
#endif // BOOTLOADERINSTALLCCPMP_H

View file

@ -0,0 +1,159 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include <QtDebug>
#include "bootloaderinstallfile.h"
#include "utils.h"
#include "Logger.h"
BootloaderInstallFile::BootloaderInstallFile(QObject *parent)
: BootloaderInstallBase(parent)
{
}
bool BootloaderInstallFile::install(void)
{
emit logItem(tr("Downloading bootloader"), LOGINFO);
LOG_INFO() << "installing bootloader";
downloadBlStart(m_blurl);
connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallFile::installStage2);
return true;
}
void BootloaderInstallFile::installStage2(void)
{
emit logItem(tr("Installing Rockbox bootloader"), LOGINFO);
QCoreApplication::processEvents();
// if an old bootloader is present (Gigabeat) move it out of the way.
QString fwfile(Utils::resolvePathCase(m_blfile));
if(!fwfile.isEmpty()) {
QString moved = Utils::resolvePathCase(m_blfile) + ".ORIG";
LOG_INFO() << "renaming" << fwfile << "to" << moved;
QFile::rename(fwfile, moved);
}
// if no old file found resolve path without basename
QFileInfo fi(m_blfile);
QString absPath = Utils::resolvePathCase(fi.absolutePath());
// if it's not possible to locate the base path try to create it
if(absPath.isEmpty()) {
QStringList pathElements = m_blfile.split("/");
// remove filename from list and save last path element
pathElements.removeLast();
QString lastElement = pathElements.last();
// remove last path element for base
pathElements.removeLast();
QString basePath = pathElements.join("/");
// check for base and bail out if not found. Otherwise create folder.
absPath = Utils::resolvePathCase(basePath);
QDir d(absPath);
d.mkpath(lastElement);
absPath = Utils::resolvePathCase(fi.absolutePath());
if(absPath.isEmpty()) {
emit logItem(tr("Error accessing output folder"), LOGERROR);
emit done(true);
return;
}
}
fwfile = absPath + "/" + fi.fileName();
// place (new) bootloader
m_tempfile.open();
LOG_INFO() << "renaming" << m_tempfile.fileName()
<< "to" << fwfile;
m_tempfile.close();
if(!Utils::resolvePathCase(fwfile).isEmpty()) {
emit logItem(tr("A firmware file is already present on player"), LOGERROR);
emit done(true);
return;
}
if(m_tempfile.copy(fwfile)) {
emit logItem(tr("Bootloader successful installed"), LOGOK);
}
else {
emit logItem(tr("Copying modified firmware file failed"), LOGERROR);
emit done(true);
return;
}
logInstall(LogAdd);
emit done(false);
}
bool BootloaderInstallFile::uninstall(void)
{
LOG_INFO() << "Uninstalling bootloader";
emit logItem(tr("Removing Rockbox bootloader"), LOGINFO);
// check if a .ORIG file is present, and allow moving it back.
QString origbl = Utils::resolvePathCase(m_blfile + ".ORIG");
if(origbl.isEmpty()) {
emit logItem(tr("No original firmware file found."), LOGERROR);
emit done(true);
return false;
}
QString fwfile = Utils::resolvePathCase(m_blfile);
if(!QFile::remove(fwfile)) {
emit logItem(tr("Can't remove Rockbox bootloader file."), LOGERROR);
emit done(true);
return false;
}
if(!QFile::rename(origbl, fwfile)) {
emit logItem(tr("Can't restore bootloader file."), LOGERROR);
emit done(true);
return false;
}
emit logItem(tr("Original bootloader restored successfully."), LOGOK);
logInstall(LogRemove);
emit logProgress(1, 1);
emit done(false);
return true;
}
//! @brief check if bootloader is installed.
//! @return BootloaderRockbox, BootloaderOther or BootloaderUnknown.
BootloaderInstallBase::BootloaderType BootloaderInstallFile::installed(void)
{
LOG_INFO() << "checking installed bootloader";
if(!Utils::resolvePathCase(m_blfile).isEmpty()
&& !Utils::resolvePathCase(m_blfile + ".ORIG").isEmpty())
return BootloaderRockbox;
else if(!Utils::resolvePathCase(m_blfile).isEmpty())
return BootloaderOther;
else
return BootloaderUnknown;
}
BootloaderInstallBase::Capabilities BootloaderInstallFile::capabilities(void)
{
LOG_INFO() << "getting capabilities";
return Install | Uninstall | IsFile | CanCheckInstalled | Backup;
}

View file

@ -0,0 +1,48 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef BOOTLOADERINSTALLFILE_H
#define BOOTLOADERINSTALLFILE_H
#include <QtCore>
#include "progressloggerinterface.h"
#include "bootloaderinstallbase.h"
//! install a bootloader by putting a single file on the player.
// This installation method is used by Iaudio (firmware is flashed
// automatically) and Gigabeat (Firmware is a file, OF needs to get
// renamed).
class BootloaderInstallFile : public BootloaderInstallBase
{
Q_OBJECT
public:
BootloaderInstallFile(QObject *parent);
bool install(void);
bool uninstall(void);
BootloaderInstallBase::BootloaderType installed(void);
Capabilities capabilities(void);
private slots:
void installStage2(void);
private:
};
#endif

View file

@ -0,0 +1,140 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2012 Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* This file is a modified version of the AMS installer by Dominik Wenger
*
****************************************************************************/
#include <QtCore>
#include "bootloaderinstallhelper.h"
#include "bootloaderinstallmi4.h"
#include "bootloaderinstallhex.h"
#include "bootloaderinstallipod.h"
#include "bootloaderinstallsansa.h"
#include "bootloaderinstallfile.h"
#include "bootloaderinstallchinachip.h"
#include "bootloaderinstallams.h"
#include "bootloaderinstalltcc.h"
#include "bootloaderinstallmpio.h"
#include "bootloaderinstallimx.h"
#include "bootloaderinstalls5l.h"
#include "bootloaderinstallbspatch.h"
BootloaderInstallBase* BootloaderInstallHelper::createBootloaderInstaller(QObject* parent, QString type)
{
if(type == "mi4") {
return new BootloaderInstallMi4(parent);
}
else if(type == "hex") {
return new BootloaderInstallHex(parent);
}
else if(type == "sansa") {
return new BootloaderInstallSansa(parent);
}
else if(type == "ipod") {
return new BootloaderInstallIpod(parent);
}
else if(type == "file") {
return new BootloaderInstallFile(parent);
}
else if(type == "chinachip") {
return new BootloaderInstallChinaChip(parent);
}
else if(type == "ams") {
return new BootloaderInstallAms(parent);
}
else if(type == "tcc") {
return new BootloaderInstallTcc(parent);
}
else if(type == "mpio") {
return new BootloaderInstallMpio(parent);
}
else if(type == "imx") {
return new BootloaderInstallImx(parent);
}
else if(type == "s5l") {
return new BootloaderInstallS5l(parent);
}
else if(type == "bspatch") {
return new BootloaderInstallBSPatch(parent);
}
else {
return nullptr;
}
}
//! @brief Return post install hints string.
//! @param model model string
//! @return hints.
QString BootloaderInstallHelper::postinstallHints(QString model)
{
bool hint = false;
QString msg = QObject::tr("Bootloader installation is almost complete. "
"Installation <b>requires</b> you to perform the "
"following steps manually:");
msg += "<ol>";
if(model != "sansafuzeplus") {
msg += QObject::tr("<li>Safely remove your player.</li>");
}
if(model == "iriverh100" || model == "iriverh120" || model == "iriverh300"
|| model == "ondavx747" || model == "agptekrocker"
|| model == "xduoox3" || model == "xduoox3ii" || model == "xduoox20") {
hint = true;
msg += QObject::tr("<li>Reboot your player into the original firmware.</li>"
"<li>Perform a firmware upgrade using the update functionality "
"of the original firmware. Please refer to your player's manual "
"on details.<br/><b>Important:</b> updating the firmware is a "
"critical process that must not be interrupted. <b>Make sure the "
"player is charged before starting the firmware update "
"process.</b></li>"
"<li>After the firmware has been updated reboot your player.</li>");
}
if(model == "sansafuzeplus") {
hint = true;
msg += QObject::tr("<li>Remove any previously inserted microSD card</li>");
msg += QObject::tr("<li>Disconnect your player. The player will reboot and "
"perform an update of the original firmware. "
"Please refer to your players manual on details.<br/>"
"<b>Important:</b> updating the firmware is a "
"critical process that must not be interrupted. <b>Make sure the "
"player is charged before disconnecting the player.</b></li>"
"<li>After the firmware has been updated reboot your player.</li>");
}
if(model == "iaudiox5" || model == "iaudiom5"
|| model == "iaudiox5v" || model == "iaudiom3" || model == "mpioh200") {
hint = true;
msg += QObject::tr("<li>Turn the player off</li>"
"<li>Insert the charger</li>");
}
if(model == "gigabeatf") {
hint = true;
msg += QObject::tr("<li>Unplug USB and power adaptors</li>"
"<li>Hold <i>Power</i> to turn the player off</li>"
"<li>Toggle the battery switch on the player</li>"
"<li>Hold <i>Power</i> to boot into Rockbox</li>");
}
msg += "</ol>";
msg += QObject::tr("<p><b>Note:</b> You can safely install other parts first, but "
"the above steps are <b>required</b> to finish the installation!</p>");
if(hint)
return msg;
else
return QString();
}

View file

@ -0,0 +1,36 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2012 Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* This file is a modified version of the AMS installer by Dominik Wenger
*
****************************************************************************/
#ifndef BOOTLOADERINSTALLHELPER_H
#define BOOTLOADERINSTALLHELPER_H
#include <QtCore>
#include "bootloaderinstallbase.h"
class BootloaderInstallHelper : public QObject
{
Q_OBJECT
public:
static BootloaderInstallBase* createBootloaderInstaller(QObject* parent, QString type);
static QString postinstallHints(QString model);
};
#endif

View file

@ -0,0 +1,271 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "bootloaderinstallbase.h"
#include "bootloaderinstallhex.h"
#include "utils.h"
#include "Logger.h"
#include "../../tools/iriver.h"
#include "../../tools/mkboot.h"
struct md5s {
const char* orig;
const char* patched;
};
struct md5s md5sums[] = {
#include "irivertools/h100sums.h"
{ nullptr, nullptr },
#include "irivertools/h120sums.h"
{ nullptr, nullptr },
#include "irivertools/h300sums.h"
{ nullptr, nullptr }
};
BootloaderInstallHex::BootloaderInstallHex(QObject *parent)
: BootloaderInstallBase(parent)
{
}
QString BootloaderInstallHex::ofHint()
{
return tr("Bootloader installation requires you to provide "
"a firmware file of the original firmware (hex file). "
"You need to download this file yourself due to legal "
"reasons. Please refer to the "
"<a href='http://www.rockbox.org/manual.shtml'>manual</a> and the "
"<a href='http://www.rockbox.org/wiki/IriverBoot"
"#Download_and_extract_a_recent_ve'>IriverBoot</a> wiki page on "
"how to obtain this file.<br/>"
"Press Ok to continue and browse your computer for the firmware "
"file.");
}
bool BootloaderInstallHex::install(void)
{
if(m_offile.isEmpty())
return false;
m_hashindex = -1;
// md5sum hex file
emit logItem(tr("checking MD5 hash of input file ..."), LOGINFO);
QByteArray filedata;
// read hex file into QByteArray
QFile file(m_offile);
file.open(QIODevice::ReadOnly);
filedata = file.readAll();
file.close();
QString hash = QCryptographicHash::hash(filedata,
QCryptographicHash::Md5).toHex();
LOG_INFO() << "hexfile hash:" << hash;
if(file.error() != QFile::NoError) {
emit logItem(tr("Could not verify original firmware file"), LOGERROR);
emit done(true);
return false;
}
// check hash and figure model from md5sum
int i = sizeof(md5sums) / sizeof(struct md5s);
m_model = 4;
// 3: h300, 2: h120, 1: h100, 0:invalid
while(i--) {
if(md5sums[i].orig == nullptr)
m_model--;
if(!qstrcmp(md5sums[i].orig, hash.toLatin1()))
break;
}
if(i < 0) {
emit logItem(tr("Firmware file not recognized."), LOGERROR);
return false;
}
else {
emit logItem(tr("MD5 hash ok"), LOGOK);
m_hashindex = i;
}
// check model agains download link.
QString match[] = {"", "h100", "h120", "h300"};
if(!m_blurl.path().contains(match[m_model])) {
emit logItem(tr("Firmware file doesn't match selected player."),
LOGERROR);
return false;
}
emit logItem(tr("Descrambling file"), LOGINFO);
m_descrambled.open();
int result;
result = iriver_decode(m_offile.toLatin1().data(),
m_descrambled.fileName().toLatin1().data(), FALSE, STRIP_NONE);
LOG_INFO() << "iriver_decode():" << result;
if(result < 0) {
emit logItem(tr("Error in descramble: %1").arg(scrambleError(result)), LOGERROR);
return false;
}
// download firmware from server
emit logItem(tr("Downloading bootloader file"), LOGINFO);
connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallHex::installStage2);
downloadBlStart(m_blurl);
return true;
}
void BootloaderInstallHex::installStage2(void)
{
emit logItem(tr("Adding bootloader to firmware file"), LOGINFO);
QCoreApplication::processEvents();
// local temp file
QTemporaryFile tempbin;
tempbin.open();
QString tempbinName = tempbin.fileName();
tempbin.close();
// get temporary files filenames -- external tools need this.
m_descrambled.open();
QString descrambledName = m_descrambled.fileName();
m_descrambled.close();
m_tempfile.open();
QString tempfileName = m_tempfile.fileName();
m_tempfile.close();
int origin = 0;
switch(m_model) {
case 3:
origin = 0x3f0000;
break;
case 2:
case 1:
origin = 0x1f0000;
break;
default:
origin = 0;
break;
}
// iriver decode already done in stage 1
int result;
if((result = mkboot_iriver(descrambledName.toLocal8Bit().constData(),
tempfileName.toLocal8Bit().constData(),
tempbinName.toLocal8Bit().constData(), origin)) < 0)
{
QString error;
switch(result) {
case -1: error = tr("could not open input file"); break;
case -2: error = tr("reading header failed"); break;
case -3: error = tr("reading firmware failed"); break;
case -4: error = tr("can't open bootloader file"); break;
case -5: error = tr("reading bootloader file failed"); break;
case -6: error = tr("can't open output file"); break;
case -7: error = tr("writing output file failed"); break;
}
emit logItem(tr("Error in patching: %1").arg(error), LOGERROR);
emit done(true);
return;
}
QTemporaryFile targethex;
targethex.open();
QString targethexName = targethex.fileName();
if((result = iriver_encode(tempbinName.toLocal8Bit().constData(),
targethexName.toLocal8Bit().constData(), FALSE)) < 0)
{
emit logItem(tr("Error in scramble: %1").arg(scrambleError(result)), LOGERROR);
targethex.close();
emit done(true);
return;
}
// finally check the md5sum of the created file
QByteArray filedata;
filedata = targethex.readAll();
targethex.close();
QString hash = QCryptographicHash::hash(filedata,
QCryptographicHash::Md5).toHex();
LOG_INFO() << "created hexfile hash:" << hash;
emit logItem(tr("Checking modified firmware file"), LOGINFO);
if(hash != QString(md5sums[m_hashindex].patched)) {
emit logItem(tr("Error: modified file checksum wrong"), LOGERROR);
targethex.remove();
emit done(true);
return;
}
// finally copy file to player
if(!Utils::resolvePathCase(m_blfile).isEmpty()) {
emit logItem(tr("A firmware file is already present on player"), LOGERROR);
emit done(true);
return;
}
if(targethex.copy(m_blfile)) {
emit logItem(tr("Success: modified firmware file created"), LOGINFO);
}
else {
emit logItem(tr("Copying modified firmware file failed"), LOGERROR);
emit done(true);
return;
}
logInstall(LogAdd);
emit done(false);
return;
}
bool BootloaderInstallHex::uninstall(void)
{
emit logItem(tr("Uninstallation not possible, only installation info removed"), LOGINFO);
logInstall(LogRemove);
emit done(true);
return false;
}
BootloaderInstallBase::BootloaderType BootloaderInstallHex::installed(void)
{
return BootloaderUnknown;
}
BootloaderInstallBase::Capabilities BootloaderInstallHex::capabilities(void)
{
return (Install | NeedsOf);
}
QString BootloaderInstallHex::scrambleError(int err)
{
QString error;
switch(err) {
case -1: error = tr("Can't open input file"); break;
case -2: error = tr("Can't open output file"); break;
case -3: error = tr("invalid file: header length wrong"); break;
case -4: error = tr("invalid file: unrecognized header"); break;
case -5: error = tr("invalid file: \"length\" field wrong"); break;
case -6: error = tr("invalid file: \"length2\" field wrong"); break;
case -7: error = tr("invalid file: internal checksum error"); break;
case -8: error = tr("invalid file: \"length3\" field wrong"); break;
default: error = tr("unknown"); break;
}
return error;
}

View file

@ -0,0 +1,53 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef BOOTLOADERINSTALLHEX_H
#define BOOTLOADERINSTALLHEX_H
#include <QtCore>
#include "bootloaderinstallbase.h"
// bootloader installation derivate based on fwpatcher
// This will patch a given hex file using (de)scramble / mkboot
// and put it on the player.
class BootloaderInstallHex : public BootloaderInstallBase
{
Q_OBJECT
public:
BootloaderInstallHex(QObject *parent = nullptr);
bool install(void);
bool uninstall(void);
BootloaderInstallBase::BootloaderType installed(void);
Capabilities capabilities(void);
QString ofHint();
private:
int m_hashindex;
int m_model;
QTemporaryFile m_descrambled;
QString scrambleError(int);
private slots:
void installStage2(void);
};
#endif

View file

@ -0,0 +1,193 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2011 by Jean-Louis Biasini
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include <QtDebug>
#include "bootloaderinstallbase.h"
#include "bootloaderinstallimx.h"
#include "../mkimxboot/mkimxboot.h"
#include "Logger.h"
// class for running mkimxboot() in a separate thread to keep the UI responsive.
class BootloaderThreadImx : public QThread
{
public:
void run(void);
void setInputFile(QString f)
{ m_inputfile = f; }
void setOutputFile(QString f)
{ m_outputfile = f; }
void setBootloaderFile(QString f)
{ m_bootfile = f; }
enum imx_error_t error(void)
{ return m_error; }
private:
QString m_inputfile;
QString m_bootfile;
QString m_outputfile;
enum imx_error_t m_error;
};
void BootloaderThreadImx::run(void)
{
LOG_INFO() << "Thread started.";
struct imx_option_t opt;
memset(&opt, 0, sizeof(opt));
opt.debug = false;
opt.output = IMX_DUALBOOT;
opt.fw_variant = VARIANT_DEFAULT;
m_error = mkimxboot(m_inputfile.toLocal8Bit().constData(),
m_bootfile.toLocal8Bit().constData(),
m_outputfile.toLocal8Bit().constData(), opt);
LOG_INFO() << "Thread finished, result:" << m_error;
}
BootloaderInstallImx::BootloaderInstallImx(QObject *parent)
: BootloaderInstallBase(parent)
{
m_thread = nullptr;
}
QString BootloaderInstallImx::ofHint()
{
return tr("Bootloader installation requires you to provide "
"a copy of the original Sandisk firmware (firmware.sb file). "
"This file will be patched with the Rockbox bootloader and "
"installed to your player. You need to download this file "
"yourself due to legal reasons. Please browse the "
"<a href='http://forums.sandisk.com/sansa/'>Sansa Forums</a> "
"or refer to the "
"<a href= 'http://www.rockbox.org/wiki/SansaFuzePlus'>SansaFuzePlus</a> "
"wiki page on how to obtain this file.<br/>"
"Press Ok to continue and browse your computer for the firmware "
"file.");
}
/** Start bootloader installation.
*/
bool BootloaderInstallImx::install(void)
{
if(!QFileInfo(m_offile).isReadable())
{
LOG_ERROR() << "could not read original firmware file"
<< m_offile;
emit logItem(tr("Could not read original firmware file"), LOGERROR);
return false;
}
LOG_INFO() << "downloading bootloader";
// download bootloader from server
emit logItem(tr("Downloading bootloader file"), LOGINFO);
connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallImx::installStage2);
downloadBlStart(m_blurl);
return true;
}
void BootloaderInstallImx::installStage2(void)
{
LOG_INFO() << "patching file...";
emit logItem(tr("Patching file..."), LOGINFO);
m_tempfile.open();
// we have not detailed progress on the patching so just show a busy
// indicator instead.
emit logProgress(0, 0);
m_patchedFile.open();
m_thread = new BootloaderThreadImx();
m_thread->setInputFile(m_offile);
m_thread->setBootloaderFile(m_tempfile.fileName());
m_thread->setOutputFile(m_patchedFile.fileName());
m_tempfile.close();
m_patchedFile.close();
connect(m_thread, &QThread::finished, this, &BootloaderInstallImx::installStage3);
connect(m_thread, SIGNAL(terminated()), this, SLOT(installStage3()));
m_thread->start();
}
void BootloaderInstallImx::installStage3(void)
{
enum imx_error_t err = m_thread->error();
emit logProgress(1, 1);
// if the patch failed
if (err != IMX_SUCCESS)
{
LOG_ERROR() << "Could not patch the original firmware file";
emit logItem(tr("Patching the original firmware failed"), LOGERROR);
emit done(true);
return;
}
LOG_INFO() << "Original Firmware succesfully patched";
emit logItem(tr("Succesfully patched firmware file"), LOGINFO);
// if a bootloader is already present delete it.
QString fwfile(m_blfile);
if(QFileInfo(fwfile).isFile())
{
LOG_INFO() << "deleting old target file";
QFile::remove(fwfile);
}
// place (new) bootloader. Copy, since the temporary file will be removed
// automatically.
LOG_INFO() << "moving patched bootloader to" << fwfile;
if(m_patchedFile.copy(fwfile))
{
emit logItem(tr("Bootloader successful installed"), LOGOK);
logInstall(LogAdd);
emit done(false);
}
else
{
emit logItem(tr("Patched bootloader could not be installed"), LOGERROR);
emit done(true);
}
// clean up thread object.
delete m_thread;
return;
}
bool BootloaderInstallImx::uninstall(void)
{
emit logItem(tr("To uninstall, perform a normal upgrade with an unmodified "
"original firmware."), LOGINFO);
logInstall(LogRemove);
emit done(true);
return false;
}
BootloaderInstallBase::BootloaderType BootloaderInstallImx::installed(void)
{
return BootloaderUnknown;
}
BootloaderInstallBase::Capabilities BootloaderInstallImx::capabilities(void)
{
return (Install | NeedsOf);
}

View file

@ -0,0 +1,47 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2011 by Jean-Louis Biasini
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef BOOTLOADERINSTALLIMX_H
#define BOOTLOADERINSTALLIMX_H
#include <QtCore>
#include "bootloaderinstallbase.h"
class BootloaderThreadImx;
//! bootloader installation class for devices handled by mkimxboot.
class BootloaderInstallImx : public BootloaderInstallBase
{
Q_OBJECT
public:
BootloaderInstallImx(QObject *parent);
bool install(void);
bool uninstall(void);
BootloaderInstallBase::BootloaderType installed(void);
Capabilities capabilities(void);
QString ofHint();
private slots:
void installStage2(void);
void installStage3(void);
private:
BootloaderThreadImx *m_thread;
QTemporaryFile m_patchedFile;
};
#endif

View file

@ -0,0 +1,272 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "bootloaderinstallbase.h"
#include "bootloaderinstallipod.h"
#include "../ipodpatcher/ipodpatcher.h"
#include "utils.h"
#include "Logger.h"
BootloaderInstallIpod::BootloaderInstallIpod(QObject *parent)
: BootloaderInstallBase(parent)
{
(void)parent;
// initialize sector buffer. The sector buffer is part of the ipod_t
// structure, so a second instance of this class will have its own buffer.
ipod_alloc_buffer(&ipod, BUFFER_SIZE);
}
BootloaderInstallIpod::~BootloaderInstallIpod()
{
if(ipod.sectorbuf) {
ipod_dealloc_buffer(&ipod);
}
}
bool BootloaderInstallIpod::install(void)
{
if(ipod.sectorbuf == nullptr) {
emit logItem(tr("Error: can't allocate buffer memory!"), LOGERROR);
emit done(true);
return false;
}
// save buffer pointer before cleaning up ipod_t structure
unsigned char* sb = ipod.sectorbuf;
memset(&ipod, 0, sizeof(struct ipod_t));
ipod.sectorbuf = sb;
if(!ipodInitialize(&ipod)) {
emit done(true);
return false;
}
if(ipod.nimages <= 0) {
emit logItem(tr("Failed to read firmware directory"), LOGERROR);
emit done(true);
return false;
}
if(getmodel(&ipod,(ipod.ipod_directory[ipod.ososimage].vers>>8)) < 0) {
emit logItem(tr("Unknown version number in firmware (%1)").arg(
ipod.ipod_directory[ipod.ososimage].vers), LOGERROR);
emit done(true);
return false;
}
if(ipod.macpod) {
emit logItem(tr("Warning: This is a MacPod, Rockbox only runs on WinPods. \n"
"See http://www.rockbox.org/wiki/IpodConversionToFAT32"), LOGERROR);
emit done(true);
return false;
}
emit logItem(tr("Downloading bootloader file"), LOGINFO);
downloadBlStart(m_blurl);
connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallIpod::installStage2);
return true;
}
void BootloaderInstallIpod::installStage2(void)
{
emit logItem(tr("Installing Rockbox bootloader"), LOGINFO);
QCoreApplication::processEvents();
if(ipod_reopen_rw(&ipod) < 0) {
emit logItem(tr("Could not open Ipod in R/W mode"), LOGERROR);
emit done(true);
return;
}
QCoreApplication::processEvents();
m_tempfile.open();
QString blfile = m_tempfile.fileName();
m_tempfile.close();
if(add_bootloader(&ipod, blfile.toLatin1().data(), FILETYPE_DOT_IPOD) == 0) {
emit logItem(tr("Successfull added bootloader"), LOGOK);
ipod_close(&ipod);
#if defined(Q_OS_MACX)
m_remountDevice = ipod.diskname;
connect(this, SIGNAL(remounted(bool)), this, SLOT(installStage3(bool)));
waitRemount();
#else
installStage3(true);
#endif
}
else {
emit logItem(tr("Failed to add bootloader"), LOGERROR);
ipod_close(&ipod);
emit done(true);
return;
}
}
void BootloaderInstallIpod::installStage3(bool mounted)
{
if(mounted) {
logInstall(LogAdd);
emit logItem(tr("Bootloader Installation complete."), LOGINFO);
emit done(false);
return;
}
else {
emit logItem(tr("Writing log aborted"), LOGERROR);
emit done(true);
}
LOG_INFO() << "version installed:"
<< m_blversion.toString(Qt::ISODate);
}
bool BootloaderInstallIpod::uninstall(void)
{
emit logItem(tr("Uninstalling bootloader"), LOGINFO);
QCoreApplication::processEvents();
if(!ipodInitialize(&ipod)) {
emit done(true);
return false;
}
if (ipod.nimages <= 0) {
emit logItem(tr("Failed to read firmware directory"),LOGERROR);
emit done(true);
return false;
}
if (getmodel(&ipod,(ipod.ipod_directory[ipod.ososimage].vers>>8)) < 0) {
emit logItem(tr("Unknown version number in firmware (%1)").arg(
ipod.ipod_directory[ipod.ososimage].vers), LOGERROR);
emit done(true);
return false;
}
if (ipod_reopen_rw(&ipod) < 0) {
emit logItem(tr("Could not open Ipod in R/W mode"), LOGERROR);
emit done(true);
return false;
}
if (ipod_has_bootloader(&ipod) == 0) {
emit logItem(tr("No bootloader detected."), LOGERROR);
emit done(true);
return false;
}
if (delete_bootloader(&ipod)==0) {
emit logItem(tr("Successfully removed bootloader"), LOGOK);
logInstall(LogRemove);
emit logProgress(1, 1);
emit done(false);
ipod_close(&ipod);
return true;
}
else {
emit logItem(tr("Removing bootloader failed."), LOGERROR);
emit done(true);
ipod_close(&ipod);
return false;
}
}
BootloaderInstallBase::BootloaderType BootloaderInstallIpod::installed(void)
{
BootloaderInstallBase::BootloaderType result = BootloaderRockbox;
if(!ipodInitialize(&ipod)) {
LOG_INFO() << "installed: BootloaderUnknown";
result = BootloaderUnknown;
}
else {
read_directory(&ipod);
getmodel(&ipod,(ipod.ipod_directory[ipod.ososimage].vers>>8));
if(!ipod_has_bootloader(&ipod)) {
result = BootloaderOther;
}
else {
LOG_INFO() << "installed: BootloaderRockbox";
}
}
ipod_close(&ipod);
return result;
}
BootloaderInstallBase::Capabilities BootloaderInstallIpod::capabilities(void)
{
return (Install | Uninstall | IsRaw);
}
/** @initialize Ipod by opening its file handle and checking if its an ipod.
* Note: the caller has to make sure the file handle gets closed!
*/
bool BootloaderInstallIpod::ipodInitialize(struct ipod_t *ipod)
{
if(!m_blfile.isEmpty()) {
QString devicename = Utils::resolveDevicename(m_blfile);
if(devicename.isEmpty()) {
emit logItem(tr("Error: could not retrieve device name"), LOGERROR);
return false;
}
#if defined(Q_OS_WIN32)
sprintf(ipod->diskname, "\\\\.\\PhysicalDrive%i", devicename.toInt());
#elif defined(Q_OS_MACX)
sprintf(ipod->diskname, "%s",
qPrintable(devicename.remove(QRegExp("s[0-9]+$"))));
#else
sprintf(ipod->diskname, "%s",
qPrintable(devicename.remove(QRegExp("[0-9]+$"))));
#endif
LOG_INFO() << "ipodpatcher: overriding scan, using"
<< ipod->diskname;
}
else {
emit logItem(tr("Error: no mountpoint specified!"), LOGERROR);
LOG_ERROR() << "no mountpoint specified!";
}
int result = ipod_open(ipod, 1);
if(result == -2) {
emit logItem(tr("Could not open Ipod: permission denied"), LOGERROR);
return false;
}
else if(result < 0) {
emit logItem(tr("Could not open Ipod"), LOGERROR);
return false;
}
if(read_partinfo(ipod, 1) < 0) {
emit logItem(tr("Error reading partition table - possibly not an Ipod"), LOGERROR);
ipod_close(ipod);
return false;
}
if(ipod->pinfo[0].start == 0) {
emit logItem(tr("No firmware partition on disk"), LOGERROR);
ipod_close(ipod);
return false;
}
read_directory(ipod);
return true;
}

View file

@ -0,0 +1,51 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef BOOTLOADERINSTALLIPOD_H
#define BOOTLOADERINSTALLIPOD_H
#include <QtCore>
#include "bootloaderinstallbase.h"
#include "../ipodpatcher/ipodpatcher.h"
// installer class derivate for Ipod installation
// based on ipodpatcher.
class BootloaderInstallIpod : public BootloaderInstallBase
{
Q_OBJECT
public:
BootloaderInstallIpod(QObject *parent);
~BootloaderInstallIpod();
bool install(void);
bool uninstall(void);
BootloaderInstallBase::BootloaderType installed(void);
Capabilities capabilities(void);
private slots:
void installStage2(void);
void installStage3(bool mounted);
private:
bool ipodInitialize(struct ipod_t *);
struct ipod_t ipod;
};
#endif

View file

@ -0,0 +1,162 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include <QtDebug>
#include <QtDebug>
#include "bootloaderinstallmi4.h"
#include "utils.h"
#include "Logger.h"
BootloaderInstallMi4::BootloaderInstallMi4(QObject *parent)
: BootloaderInstallBase(parent)
{
}
bool BootloaderInstallMi4::install(void)
{
emit logItem(tr("Downloading bootloader"), LOGINFO);
LOG_INFO() << "installing bootloader";
downloadBlStart(m_blurl);
connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallMi4::installStage2);
return true;
}
void BootloaderInstallMi4::installStage2(void)
{
emit logItem(tr("Installing Rockbox bootloader"), LOGINFO);
QCoreApplication::processEvents();
// move old bootloader out of the way
QString fwfile(Utils::resolvePathCase(m_blfile));
QFile oldbl(fwfile);
QString moved = QFileInfo(Utils::resolvePathCase(m_blfile)).absolutePath()
+ "/OF.mi4";
if(!QFileInfo::exists(moved)) {
LOG_INFO() << "renaming" << fwfile << "to" << moved;
oldbl.rename(moved);
}
else {
LOG_INFO() << "OF.mi4 already present, not renaming again.";
oldbl.remove();
}
// place new bootloader
m_tempfile.open();
LOG_INFO() << "renaming" << m_tempfile.fileName()
<< "to" << fwfile;
m_tempfile.close();
if(!Utils::resolvePathCase(fwfile).isEmpty()) {
emit logItem(tr("A firmware file is already present on player"), LOGERROR);
emit done(true);
return;
}
if(m_tempfile.copy(fwfile)) {
emit logItem(tr("Bootloader successful installed"), LOGOK);
}
else {
emit logItem(tr("Copying modified firmware file failed"), LOGERROR);
emit done(true);
return;
}
emit logItem(tr("Bootloader successful installed"), LOGOK);
logInstall(LogAdd);
emit done(false);
}
bool BootloaderInstallMi4::uninstall(void)
{
LOG_INFO() << "Uninstalling bootloader";
// check if it's actually a Rockbox bootloader
emit logItem(tr("Checking for Rockbox bootloader"), LOGINFO);
if(installed() != BootloaderRockbox) {
emit logItem(tr("No Rockbox bootloader found"), LOGERROR);
emit done(true);
return false;
}
// check if OF file present
emit logItem(tr("Checking for original firmware file"), LOGINFO);
QString original = QFileInfo(Utils::resolvePathCase(m_blfile)).absolutePath()
+ "/OF.mi4";
if(Utils::resolvePathCase(original).isEmpty()) {
emit logItem(tr("Error finding original firmware file"), LOGERROR);
emit done(true);
return false;
}
// finally remove RB bootloader
QString resolved = Utils::resolvePathCase(m_blfile);
QFile blfile(resolved);
blfile.remove();
QFile::rename(Utils::resolvePathCase(original), resolved);
emit logItem(tr("Rockbox bootloader successful removed"), LOGINFO);
logInstall(LogRemove);
emit logProgress(1, 1);
emit done(false);
return true;
}
//! check if a bootloader is installed and return its state.
BootloaderInstallBase::BootloaderType BootloaderInstallMi4::installed(void)
{
// for MI4 files we can check if we actually have a RB bootloader
// installed.
// RB bootloader has "RBBL" at 0x1f8 in the mi4 file.
// make sure to resolve case to prevent case issues
QString resolved;
resolved = Utils::resolvePathCase(m_blfile);
if(resolved.isEmpty()) {
LOG_INFO() << "installed: BootloaderNone";
return BootloaderNone;
}
QFile f(resolved);
f.open(QIODevice::ReadOnly);
f.seek(0x1f8);
char magic[4];
f.read(magic, 4);
f.close();
if(!memcmp(magic, "RBBL", 4)) {
LOG_INFO() << "installed: BootloaderRockbox";
return BootloaderRockbox;
}
else {
LOG_INFO() << "installed: BootloaderOther";
return BootloaderOther;
}
}
BootloaderInstallBase::Capabilities BootloaderInstallMi4::capabilities(void)
{
LOG_INFO() << "getting capabilities";
return Install | Uninstall | Backup | IsFile | CanCheckInstalled | CanCheckVersion;
}

View file

@ -0,0 +1,48 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef BOOTLOADERINSTALLMI4_H
#define BOOTLOADERINSTALLMI4_H
#include <QtCore>
#include "progressloggerinterface.h"
#include "bootloaderinstallbase.h"
// mi4 bootloader file based installation.
// Puts the bootloader file to the correct location and
// renames the OF to OF.mi4.
class BootloaderInstallMi4 : public BootloaderInstallBase
{
Q_OBJECT
public:
BootloaderInstallMi4(QObject *parent);
bool install(void);
bool uninstall(void);
BootloaderInstallBase::BootloaderType installed(void);
Capabilities capabilities(void);
private slots:
void installStage2(void);
private:
};
#endif

View file

@ -0,0 +1,143 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Wenger
* $Id: bootloaderinstallams.cpp 24778 2010-02-19 23:45:29Z funman $
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "bootloaderinstallbase.h"
#include "bootloaderinstallmpio.h"
#include "Logger.h"
#include "../mkmpioboot/mkmpioboot.h"
BootloaderInstallMpio::BootloaderInstallMpio(QObject *parent)
: BootloaderInstallBase(parent)
{
}
QString BootloaderInstallMpio::ofHint()
{
return tr("Bootloader installation requires you to provide "
"a firmware file of the original firmware (bin file). "
"You need to download this file yourself due to legal "
"reasons. Please refer to the "
"<a href='http://www.rockbox.org/manual.shtml'>manual</a> and "
"the <a href='http://www.rockbox.org/wiki/MPIOHD200Port'>MPIOHD200Port</a> "
"wiki page on how to obtain this file.<br/>"
"Press Ok to continue and browse your computer for the firmware "
"file.");
}
bool BootloaderInstallMpio::install(void)
{
if(m_offile.isEmpty())
return false;
LOG_INFO() << "installing bootloader";
// download firmware from server
emit logItem(tr("Downloading bootloader file"), LOGINFO);
connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallMpio::installStage2);
downloadBlStart(m_blurl);
return true;
}
void BootloaderInstallMpio::installStage2(void)
{
LOG_INFO() << "installStage2";
int origin = 0xe0000; /* MPIO HD200 bootloader address */
m_tempfile.open();
QString bootfile = m_tempfile.fileName();
m_tempfile.close();
int ret = mkmpioboot(m_offile.toLocal8Bit().data(),
bootfile.toLocal8Bit().data(), m_blfile.toLocal8Bit().data(), origin);
if(ret != 0)
{
QString error;
switch(ret)
{
case -1:
error = tr("Could not open the original firmware.");
break;
case -2:
error = tr("Could not read the original firmware.");
break;
case -3:
error = tr("Loaded firmware file does not look like MPIO original firmware file.");
break;
case -4:
error = tr("Could not open downloaded bootloader.");
break;
case -5:
error = tr("Place for bootloader in OF file not empty.");
break;
case -6:
error = tr("Could not read the downloaded bootloader.");
break;
case -7:
error = tr("Bootloader checksum error.");
break;
case -8:
error = tr("Could not open output file.");
break;
case -9:
error = tr("Could not write output file.");
break;
default:
error = tr("Unknown error number: %1").arg(ret);
break;
}
LOG_ERROR() << "Patching original firmware failed:" << error;
emit logItem(tr("Patching original firmware failed: %1").arg(error), LOGERROR);
emit done(true);
return;
}
//end of install
LOG_INFO() << "install successful";
emit logItem(tr("Success: modified firmware file created"), LOGINFO);
logInstall(LogAdd);
emit done(false);
return;
}
bool BootloaderInstallMpio::uninstall(void)
{
emit logItem(tr("To uninstall, perform a normal upgrade with an unmodified "
"original firmware"), LOGINFO);
logInstall(LogRemove);
emit done(true);
return false;
}
BootloaderInstallBase::BootloaderType BootloaderInstallMpio::installed(void)
{
return BootloaderUnknown;
}
BootloaderInstallBase::Capabilities BootloaderInstallMpio::capabilities(void)
{
return (Install | NeedsOf);
}

View file

@ -0,0 +1,43 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Wenger
* $Id: bootloaderinstallams.h 22317 2009-08-15 13:04:21Z Domonoky $
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef BOOTLOADERINSTALLMPIO_H
#define BOOTLOADERINSTALLMPIO_H
#include <QtCore>
#include "bootloaderinstallbase.h"
//! bootloader installation derivate based on mkmpioboot
class BootloaderInstallMpio : public BootloaderInstallBase
{
Q_OBJECT
public:
BootloaderInstallMpio(QObject *parent);
bool install(void);
bool uninstall(void);
BootloaderInstallBase::BootloaderType installed(void);
Capabilities capabilities(void);
QString ofHint();
private:
private slots:
void installStage2(void);
};
#endif

View file

@ -0,0 +1,437 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "bootloaderinstallbase.h"
#include "bootloaderinstalls5l.h"
#include "Logger.h"
#include "utils.h"
#include "system.h"
#include "rbsettings.h"
#include "playerbuildinfo.h"
#include "../mks5lboot/mks5lboot.h"
BootloaderInstallS5l::BootloaderInstallS5l(QObject *parent)
: BootloaderInstallBase(parent)
{
}
bool BootloaderInstallS5l::install(void)
{
LOG_INFO() << "installing bootloader";
doInstall = true;
return installStage1();
}
bool BootloaderInstallS5l::uninstall(void)
{
LOG_INFO() << "uninstalling bootloader";
doInstall = false;
return installStage1();
}
bool BootloaderInstallS5l::installStage1(void)
{
LOG_INFO() << "installStage1";
mntpoint = RbSettings::value(RbSettings::Mountpoint).toString();
if (!Utils::mountpoints(Utils::MountpointsSupported).contains(mntpoint)) {
LOG_ERROR() << "iPod not mounted:" << mntpoint;
emit logItem(tr("Could not find mounted iPod."), LOGERROR);
emit done(true);
return false;
}
if (doInstall) {
// download firmware from server
emit logItem(tr("Downloading bootloader file..."), LOGINFO);
connect(this, &BootloaderInstallBase::downloadDone,
this, &BootloaderInstallS5l::installStageMkdfu);
downloadBlStart(m_blurl);
}
else {
installStageMkdfu();
}
return true;
}
void BootloaderInstallS5l::installStageMkdfu(void)
{
int dfu_type;
QString dfu_arg;
char errstr[200];
LOG_INFO() << "installStageMkdfu";
setProgress(0);
aborted = false;
connect(this, &BootloaderInstallBase::installAborted,
this, &BootloaderInstallS5l::abortInstall);
connect(this, &BootloaderInstallBase::done,
this, &BootloaderInstallS5l::installDone);
if (doInstall) {
dfu_type = DFU_INST;
m_tempfile.open();
dfu_arg = m_tempfile.fileName();
m_tempfile.close();
}
else {
dfu_type = DFU_UNINST;
dfu_arg = RbSettings::value(RbSettings::Platform).toString();
}
// build DFU image
dfu_buf = mkdfu(dfu_type, dfu_arg.toLocal8Bit().data(),
&dfu_size, errstr, sizeof(errstr));
if (!dfu_buf) {
LOG_ERROR() << "mkdfu() failed:" << errstr;
emit logItem(errstr, LOGERROR);
emit logItem(tr("Could not make DFU image."), LOGERROR);
emit done(true);
return;
}
LOG_INFO() << "preparing installStageWaitForEject";
emit logItem(tr("Ejecting iPod..."), LOGINFO);
setProgress(10);
scanTimer.invalidate();
installStageWaitForEject();
}
void BootloaderInstallS5l::installStageWaitForEject(void)
{
if (!updateProgress())
return; /* aborted */
if (!scanTimer.isValid() || (scanTimer.elapsed() > 3000)) {
scanSuccess = Utils::ejectDevice(mntpoint);
if (!scanSuccess) {
scanSuccess = !Utils::mountpoints(
Utils::MountpointsSupported).contains(mntpoint);
}
scanTimer.start();
}
if (!scanSuccess) {
if (!actionShown) {
emit logItem(tr("Action required:\n\n"
"Please make sure no programs are accessing "
"files on the device. If ejecting still fails "
"please use your computers eject functionality."),
LOGWARNING);
actionShown = true;
}
QTimer::singleShot(250, this, &BootloaderInstallS5l::installStageWaitForEject);
return;
}
emit logItem(tr("Device successfully ejected."), LOGINFO);
LOG_INFO() << "preparing installStageWaitForProcs";
setProgress(40, 18);
scanTimer.invalidate();
installStageWaitForProcs();
}
void BootloaderInstallS5l::installStageWaitForProcs(void)
{
if (!updateProgress())
return; /* aborted */
if (!scanTimer.isValid() || (scanTimer.elapsed() > 1000)) {
scanSuccess = Utils::findRunningProcess(QStringList("iTunes")).isEmpty();
scanTimer.start();
}
if (!scanSuccess) {
if (!actionShown) {
emit logItem(tr("Action required:\n\n"
"Quit iTunes application."), LOGWARNING);
actionShown = true;
}
QTimer::singleShot(250, this, &BootloaderInstallS5l::installStageWaitForProcs);
return;
}
if (actionShown) {
emit logItem(tr("iTunes closed."), LOGINFO);
if (!updateProgress())
return; /* aborted */
}
QList<int> helperPids = Utils::findRunningProcess(
#if defined(Q_OS_WIN32)
QStringList("iTunesHelper"))["iTunesHelper.exe"];
#else
QStringList("iTunesHelper"))["iTunesHelper"];
#endif
suspendedPids = Utils::suspendProcess(helperPids, true);
if (suspendedPids.size() != helperPids.size()) {
emit logItem(tr("Could not suspend iTunesHelper. Stop it "
"using the Task Manager, and try again."), LOGERROR);
emit done(true);
return;
}
LOG_INFO() << "preparing installStageWaitForSpindown";
// for Windows: skip waiting if the HDD was ejected a time ago
if (progressTimer.elapsed() < progressTimeout)
emit logItem(tr("Waiting for HDD spin-down..."), LOGINFO);
installStageWaitForSpindown();
}
void BootloaderInstallS5l::installStageWaitForSpindown(void)
{
if (!updateProgress())
return; /* aborted */
if (progressTimer.elapsed() < progressTimeout) {
QTimer::singleShot(250, this, &BootloaderInstallS5l::installStageWaitForSpindown);
return;
}
LOG_INFO() << "preparing installStageWaitForDfu";
emit logItem(tr("Waiting for DFU mode..."), LOGINFO);
emit logItem(tr("Action required:\n\n"
"Press and hold SELECT+MENU buttons, after "
"about 12 seconds a new action will require "
"you to release the buttons, DO IT QUICKLY, "
"otherwise the process could fail."), LOGWARNING);
scanTimer.invalidate();
installStageWaitForDfu();
}
void BootloaderInstallS5l::installStageWaitForDfu(void)
{
if (!updateProgress())
return; /* aborted */
if (!scanTimer.isValid() || (scanTimer.elapsed() > 2000)) {
scanSuccess = System::listUsbIds().contains(0x05ac1223);
scanTimer.start();
}
if (!scanSuccess) {
QTimer::singleShot(250, this, &BootloaderInstallS5l::installStageWaitForDfu);
return;
}
emit logItem(tr("DFU mode detected."), LOGINFO);
emit logItem(tr("Action required:\n\n"
"Release SELECT+MENU buttons and wait..."), LOGWARNING);
// Once the iPod enters DFU mode, the device will reset again if
// SELECT+MENU remains pressed for another 8 seconds. To avoid a
// reset while the NOR is being written, we wait ~10 seconds
// before sending the DFU image.
LOG_INFO() << "preparing installStageSendDfu";
setProgress(60, 10);
installStageSendDfu();
}
void BootloaderInstallS5l::installStageSendDfu(void)
{
if (!updateProgress())
return; /* aborted */
if (progressTimer.elapsed() < progressTimeout) {
QTimer::singleShot(250, this, &BootloaderInstallS5l::installStageSendDfu);
return;
}
if (!System::listUsbIds().contains(0x05ac1223)) {
LOG_ERROR() << "device not in DFU mode";
emit logItem(tr("Device is not in DFU mode. It seems that "
"the previous required action failed, please "
"try again."), LOGERROR);
emit done(true);
return;
}
emit logItem(tr("Transfering DFU image..."), LOGINFO);
if (!updateProgress())
return; /* aborted */
char errstr[200];
if (!ipoddfu_send(0x1223, dfu_buf, dfu_size, errstr, sizeof(errstr))) {
LOG_ERROR() << "ipoddfu_send() failed:" << errstr;
#if defined(Q_OS_WIN32)
if (strstr(errstr, "DFU device not found"))
{
emit logItem(tr("No valid DFU USB driver found.\n\n"
"Install iTunes (or the Apple Device Driver) "
"and try again."),
LOGERROR);
}
else
#endif
{
emit logItem(errstr, LOGERROR);
emit logItem(tr("Could not transfer DFU image."), LOGERROR);
}
emit done(true);
return;
}
emit logItem(tr("DFU transfer completed."), LOGINFO);
LOG_INFO() << "preparing installStageWaitForRemount";
emit logItem(tr("Restarting iPod, waiting for remount..."), LOGINFO);
setProgress(99, 45);
scanTimer.invalidate();
installStageWaitForRemount();
}
void BootloaderInstallS5l::installStageWaitForRemount(void)
{
if (!updateProgress())
return; /* aborted */
if (!scanTimer.isValid() || (scanTimer.elapsed() > 5000)) {
scanSuccess = Utils::mountpoints(
Utils::MountpointsSupported).contains(mntpoint);
scanTimer.start();
}
if (!scanSuccess) {
if (!actionShown && (progressTimer.elapsed() > progressTimeout)) {
emit logItem(tr("Action required:\n\n"
"Could not remount the device, try to do it "
"manually. If the iPod didn't restart, force "
"a reset by pressing SELECT+MENU buttons "
"for about 5 seconds. If the problem could "
"not be solved then click 'Abort' to cancel."),
LOGWARNING);
actionShown = true;
}
QTimer::singleShot(250, this, &BootloaderInstallS5l::installStageWaitForRemount);
return;
}
emit logItem(tr("Device remounted."), LOGINFO);
if (doInstall)
emit logItem(tr("Bootloader successfully installed."), LOGOK);
else
emit logItem(tr("Bootloader successfully uninstalled."), LOGOK);
logInstall(doInstall ? LogAdd : LogRemove);
emit logProgress(1, 1);
emit done(false);
}
void BootloaderInstallS5l::installDone(bool status)
{
LOG_INFO() << "installDone, status:" << status;
if (Utils::suspendProcess(suspendedPids, false).size() != suspendedPids.size())
emit logItem(tr("Could not resume iTunesHelper."), LOGWARNING);
}
void BootloaderInstallS5l::abortInstall(void)
{
LOG_INFO() << "abortInstall";
aborted = true;
disconnect(this, &BootloaderInstallBase::installAborted,
this, &BootloaderInstallS5l::abortInstall);
}
bool BootloaderInstallS5l::abortDetected(void)
{
if (aborted) {
LOG_ERROR() << "abortDetected";
if (doInstall)
emit logItem(tr("Install aborted by user."), LOGERROR);
else
emit logItem(tr("Uninstall aborted by user."), LOGERROR);
emit done(true);
return true;
}
return false;
}
void BootloaderInstallS5l::setProgress(int progress, int secondsTimeout)
{
progressTimer.start();
progressTimeout = secondsTimeout * 1000;
progOrigin = progTarget;
progTarget = progress;
actionShown = false;
}
bool BootloaderInstallS5l::updateProgress(void)
{
if (progressTimeout) {
progCurrent = qMin(progTarget, progOrigin +
static_cast<int>(progressTimer.elapsed())
* (progTarget - progOrigin) / progressTimeout);
}
else {
progCurrent = progTarget;
}
emit logProgress(progCurrent, 100);
QCoreApplication::sendPostedEvents();
QCoreApplication::processEvents();
return !abortDetected();
}
BootloaderInstallBase::BootloaderType BootloaderInstallS5l::installed(void)
{
bool rbblInstalled;
QString device = Utils::resolveDevicename(m_blfile);
if (device.isEmpty()) {
LOG_INFO() << "installed: BootloaderUnknown";
return BootloaderUnknown;
}
// rely on logfile
QString logfile = RbSettings::value(RbSettings::Mountpoint).toString()
+ "/.rockbox/rbutil.log";
QSettings s(logfile, QSettings::IniFormat, this);
QString section = PlayerBuildInfo::instance()->value(
PlayerBuildInfo::BootloaderName).toString().section('/', -1);
rbblInstalled = s.contains("Bootloader/" + section);
if (rbblInstalled) {
LOG_INFO() << "installed: BootloaderRockbox";
return BootloaderRockbox;
}
else {
LOG_INFO() << "installed: BootloaderOther";
return BootloaderOther;
}
}
BootloaderInstallBase::Capabilities BootloaderInstallS5l::capabilities(void)
{
return (Install | Uninstall);
}

View file

@ -0,0 +1,71 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef BOOTLOADERINSTALLS5L_H
#define BOOTLOADERINSTALLS5L_H
#include <QtCore>
#include "bootloaderinstallbase.h"
//! bootloader installation derivate based on mks5lboot
class BootloaderInstallS5l : public BootloaderInstallBase
{
Q_OBJECT
public:
BootloaderInstallS5l(QObject *parent);
bool install(void);
bool uninstall(void);
BootloaderInstallBase::BootloaderType installed(void);
Capabilities capabilities(void);
private slots:
bool installStage1(void);
void installStageMkdfu(void);
void installStageWaitForEject(void);
void installStageWaitForSpindown(void);
void installStageWaitForProcs(void);
void installStageWaitForDfu(void);
void installStageSendDfu(void);
void installStageWaitForRemount(void);
void abortInstall(void);
void installDone(bool);
private:
bool doInstall;
QString mntpoint;
unsigned char* dfu_buf;
int dfu_size;
QList<int> suspendedPids;
bool aborted;
bool abortDetected(void);
QElapsedTimer scanTimer;
bool scanSuccess;
// progress
QElapsedTimer progressTimer;
int progressTimeout;
int progCurrent;
int progOrigin;
int progTarget;
bool actionShown;
void setProgress(int, int=0);
bool updateProgress(void);
};
#endif

View file

@ -0,0 +1,286 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "bootloaderinstallbase.h"
#include "bootloaderinstallsansa.h"
#include "Logger.h"
#include "../sansapatcher/sansapatcher.h"
#include "utils.h"
BootloaderInstallSansa::BootloaderInstallSansa(QObject *parent)
: BootloaderInstallBase(parent)
{
(void)parent;
// initialize sector buffer. The sector buffer is part of the sansa_t
// structure, so a second instance of this class will have its own buffer.
sansa_alloc_buffer(&sansa, BUFFER_SIZE);
}
BootloaderInstallSansa::~BootloaderInstallSansa()
{
if(sansa.sectorbuf) {
sansa_dealloc_buffer(&sansa);
}
}
/** Start bootloader installation.
*/
bool BootloaderInstallSansa::install(void)
{
if(sansa.sectorbuf == nullptr) {
emit logItem(tr("Error: can't allocate buffer memory!"), LOGERROR);
return false;
emit done(true);
}
emit logItem(tr("Searching for Sansa"), LOGINFO);
int n = sansa_scan(&sansa);
if(n == -1) {
emit logItem(tr("Permission for disc access denied!\n"
"This is required to install the bootloader"),
LOGERROR);
emit done(true);
return false;
}
if(n == 0) {
emit logItem(tr("No Sansa detected!"), LOGERROR);
emit done(true);
return false;
}
if(sansa.hasoldbootloader) {
emit logItem(tr("OLD ROCKBOX INSTALLATION DETECTED, ABORTING.\n"
"You must reinstall the original Sansa firmware before running\n"
"sansapatcher for the first time.\n"
"See http://www.rockbox.org/wiki/SansaE200Install\n"),
LOGERROR);
emit done(true);
return false;
}
emit logItem(tr("Downloading bootloader file"), LOGINFO);
downloadBlStart(m_blurl);
connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallSansa::installStage2);
return true;
}
/** Finish bootloader installation.
*/
void BootloaderInstallSansa::installStage2(void)
{
unsigned char* buf = nullptr;
unsigned int len;
emit logItem(tr("Installing Rockbox bootloader"), LOGINFO);
QCoreApplication::processEvents();
if(!sansaInitialize(&sansa)) {
emit done(true);
return;
}
if(sansa_reopen_rw(&sansa) < 0) {
emit logItem(tr("Could not open Sansa in R/W mode"), LOGERROR);
emit done(true);
return;
}
// check model -- if sansapatcher reports a c200 don't install an e200
// bootloader and vice versa.
// The model is available in the mi4 file at offset 0x1fc and matches
// the targetname set by sansapatcher.
emit logItem(tr("Checking downloaded bootloader"), LOGINFO);
m_tempfile.open();
QString blfile = m_tempfile.fileName();
char magic[4];
m_tempfile.seek(0x1fc);
m_tempfile.read(magic, 4);
m_tempfile.close();
if(memcmp(sansa.targetname, magic, 4) != 0) {
emit logItem(tr("Bootloader mismatch! Aborting."), LOGERROR);
LOG_INFO("Targetname: %s, mi4 magic: %c%c%c%c",
sansa.targetname, magic[0], magic[1], magic[2], magic[3]);
emit done(true);
sansa_close(&sansa);
return;
}
len = sansa_read_bootloader(&sansa, blfile.toLatin1().data(), &buf);
if(sansa_add_bootloader(&sansa, buf, len) == 0) {
emit logItem(tr("Successfully installed bootloader"), LOGOK);
sansa_close(&sansa);
#if defined(Q_OS_MACX)
m_remountDevice = sansa.diskname;
connect(this, SIGNAL(remounted(bool)), this, SLOT(installStage3(bool)));
waitRemount();
#else
installStage3(true);
#endif
}
else {
emit logItem(tr("Failed to install bootloader"), LOGERROR);
sansa_close(&sansa);
emit done(true);
return;
}
}
void BootloaderInstallSansa::installStage3(bool mounted)
{
if(mounted) {
logInstall(LogAdd);
emit logItem(tr("Bootloader Installation complete."), LOGINFO);
emit done(false);
return;
}
else {
emit logItem(tr("Writing log aborted"), LOGERROR);
emit done(true);
}
LOG_INFO() << "version installed:"
<< m_blversion.toString(Qt::ISODate);
}
/** Uninstall the bootloader.
*/
bool BootloaderInstallSansa::uninstall(void)
{
emit logItem(tr("Uninstalling bootloader"), LOGINFO);
QCoreApplication::processEvents();
if(!sansaInitialize(&sansa)) {
emit done(true);
return false;
}
if (sansa.hasoldbootloader) {
emit logItem(tr("OLD ROCKBOX INSTALLATION DETECTED, ABORTING.\n"
"You must reinstall the original Sansa firmware before running\n"
"sansapatcher for the first time.\n"
"See http://www.rockbox.org/wiki/SansaE200Install\n"),
LOGERROR);
emit done(true);
return false;
}
if (sansa_reopen_rw(&sansa) < 0) {
emit logItem(tr("Could not open Sansa in R/W mode"), LOGERROR);
emit done(true);
return false;
}
if (sansa_delete_bootloader(&sansa)==0) {
emit logItem(tr("Successfully removed bootloader"), LOGOK);
logInstall(LogRemove);
emit logProgress(1, 1);
emit done(false);
sansa_close(&sansa);
return true;
}
else {
emit logItem(tr("Removing bootloader failed."),LOGERROR);
emit done(true);
sansa_close(&sansa);
return false;
}
return false;
}
/** Check if bootloader is already installed
*/
BootloaderInstallBase::BootloaderType BootloaderInstallSansa::installed(void)
{
int num;
if(!sansaInitialize(&sansa)) {
return BootloaderUnknown;
}
if((num = sansa_list_images(&sansa)) == 2) {
sansa_close(&sansa);
return BootloaderRockbox;
}
else if(num == 1) {
sansa_close(&sansa);
return BootloaderOther;
}
return BootloaderUnknown;
}
bool BootloaderInstallSansa::sansaInitialize(struct sansa_t *sansa)
{
if(!m_blfile.isEmpty()) {
QString devicename = Utils::resolveDevicename(m_blfile);
if(devicename.isEmpty()) {
emit logItem(tr("Error: could not retrieve device name"), LOGERROR);
return false;
}
#if defined(Q_OS_WIN32)
sprintf(sansa->diskname, "\\\\.\\PhysicalDrive%i", devicename.toInt());
#elif defined(Q_OS_MACX)
sprintf(sansa->diskname,
"%s", qPrintable(devicename.remove(QRegExp("s[0-9]+$"))));
#else
sprintf(sansa->diskname,
"%s", qPrintable(devicename.remove(QRegExp("[0-9]+$"))));
#endif
LOG_INFO() << "sansapatcher: overriding scan, using"
<< sansa->diskname;
}
else if(sansa_scan(sansa) != 1) {
emit logItem(tr("Can't find Sansa"), LOGERROR);
return false;
}
if (sansa_open(sansa, 0) < 0) {
emit logItem(tr("Could not open Sansa"), LOGERROR);
return false;
}
if (sansa_read_partinfo(sansa,0) < 0) {
emit logItem(tr("Could not read partition table"), LOGERROR);
sansa_close(sansa);
return false;
}
int i = is_sansa(sansa);
if(i < 0) {
emit logItem(tr("Disk is not a Sansa (Error %1), aborting.").arg(i), LOGERROR);
sansa_close(sansa);
return false;
}
return true;
}
/** Get capabilities of subclass installer.
*/
BootloaderInstallBase::Capabilities BootloaderInstallSansa::capabilities(void)
{
return (Install | Uninstall | IsRaw | CanCheckInstalled);
}

View file

@ -0,0 +1,51 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2008 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef BOOTLOADERINSTALLSANSA_H
#define BOOTLOADERINSTALLSANSA_H
#include <QtCore>
#include "bootloaderinstallbase.h"
#include "sansapatcher.h"
// bootloader installation class for devices handled by sansapatcher.
class BootloaderInstallSansa : public BootloaderInstallBase
{
Q_OBJECT
public:
BootloaderInstallSansa(QObject *parent = nullptr);
~BootloaderInstallSansa();
bool install(void);
bool uninstall(void);
BootloaderInstallBase::BootloaderType installed(void);
Capabilities capabilities(void);
private:
bool sansaInitialize(struct sansa_t *);
struct sansa_t sansa;
private slots:
void installStage2(void);
void installStage3(bool);
};
#endif

View file

@ -0,0 +1,165 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2009 by Tomer Shalev
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* This file is a modified version of the AMS installer by Dominik Wenger
*
****************************************************************************/
#include <QtCore>
#include "bootloaderinstallbase.h"
#include "bootloaderinstalltcc.h"
#include "../mktccboot/mktccboot.h"
BootloaderInstallTcc::BootloaderInstallTcc(QObject *parent)
: BootloaderInstallBase(parent)
{
}
QString BootloaderInstallTcc::ofHint()
{
return tr("Bootloader installation requires you to provide "
"a firmware file of the original firmware (bin file). "
"You need to download this file yourself due to legal "
"reasons. Please refer to the "
"<a href='http://www.rockbox.org/manual.shtml'>manual</a> and the "
"<a href='http://www.rockbox.org/wiki/CowonD2Info'>CowonD2Info</a> "
"wiki page on how to obtain the file.<br/>"
"Press Ok to continue and browse your computer for the firmware "
"file.");
}
bool BootloaderInstallTcc::install(void)
{
if(m_offile.isEmpty())
return false;
// Download firmware from server
emit logItem(tr("Downloading bootloader file"), LOGINFO);
connect(this, &BootloaderInstallBase::downloadDone, this, &BootloaderInstallTcc::installStage2);
downloadBlStart(m_blurl);
return true;
}
void BootloaderInstallTcc::installStage2(void)
{
unsigned char *of_buf, *boot_buf = nullptr, *patched_buf = nullptr;
int n, of_size, boot_size, patched_size;
char errstr[200];
bool ret = false;
m_tempfile.open();
QString bootfile = m_tempfile.fileName();
m_tempfile.close();
/* Construct path for write out.
* Combine path of m_blfile with filename from OF */
QString outfilename = QFileInfo(m_blfile).absolutePath() + "/" +
QFileInfo(m_offile).fileName();
/* Write out file */
QFile out(outfilename);
/* Load original firmware file */
of_buf = file_read(m_offile.toLocal8Bit().data(), &of_size);
if (of_buf == nullptr)
{
emit logItem(errstr, LOGERROR);
emit logItem(tr("Could not load %1").arg(m_offile), LOGERROR);
goto exit;
}
/* A CRC test in order to reject non OF file */
if (test_firmware_tcc(of_buf, of_size))
{
emit logItem(errstr, LOGERROR);
emit logItem(tr("Unknown OF file used: %1").arg(m_offile), LOGERROR);
goto exit;
}
/* Load bootloader file */
boot_buf = file_read(bootfile.toLocal8Bit().data(), &boot_size);
if (boot_buf == nullptr)
{
emit logItem(errstr, LOGERROR);
emit logItem(tr("Could not load %1").arg(bootfile), LOGERROR);
goto exit;
}
/* Patch the firmware */
emit logItem(tr("Patching Firmware..."), LOGINFO);
patched_buf = patch_firmware_tcc(of_buf, of_size, boot_buf, boot_size,
&patched_size);
if (patched_buf == nullptr)
{
emit logItem(errstr, LOGERROR);
emit logItem(tr("Could not patch firmware"), LOGERROR);
goto exit;
}
if(!out.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
emit logItem(tr("Could not open %1 for writing").arg(m_blfile),
LOGERROR);
goto exit;
}
n = out.write((char*)patched_buf, patched_size);
out.close();
if (n != patched_size)
{
emit logItem(tr("Could not write firmware file"), LOGERROR);
goto exit;
}
/* End of install */
emit logItem(tr("Success: modified firmware file created"), LOGINFO);
logInstall(LogAdd);
ret = true;
exit:
if (of_buf)
free(of_buf);
if (boot_buf)
free(boot_buf);
if (patched_buf)
free(patched_buf);
emit done(ret);
}
bool BootloaderInstallTcc::uninstall(void)
{
emit logItem(tr("To uninstall, perform a normal upgrade with an unmodified original firmware"), LOGINFO);
logInstall(LogRemove);
emit done(true);
return false;
}
BootloaderInstallBase::BootloaderType BootloaderInstallTcc::installed(void)
{
return BootloaderUnknown;
}
BootloaderInstallBase::Capabilities BootloaderInstallTcc::capabilities(void)
{
return (Install | NeedsOf);
}

View file

@ -0,0 +1,44 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2009 by Tomer Shalev
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* This file is a modified version of the AMS installer by Dominik Wenger
*
****************************************************************************/
#ifndef BOOTLOADERINSTALLTCC_H
#define BOOTLOADERINSTALLTCC_H
#include <QtCore>
#include "bootloaderinstallbase.h"
//! bootloader installation derivate based on mktccboot
class BootloaderInstallTcc : public BootloaderInstallBase
{
Q_OBJECT
public:
BootloaderInstallTcc(QObject *parent);
bool install(void);
bool uninstall(void);
BootloaderInstallBase::BootloaderType installed(void);
Capabilities capabilities(void);
QString ofHint();
private:
private slots:
void installStage2(void);
};
#endif

View file

@ -0,0 +1,86 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "encoderbase.h"
#include "utils.h"
#include "rbsettings.h"
#include "encoderrbspeex.h"
#include "encoderlame.h"
#include "encoderexe.h"
#include "Logger.h"
/*********************************************************************
* Encoder Base
**********************************************************************/
QMap<QString,QString> EncoderBase::encoderList;
EncoderBase::EncoderBase(QObject *parent): EncTtsSettingInterface(parent)
{
}
// initialize list of encoders
void EncoderBase::initEncodernamesList()
{
encoderList["rbspeex"] = "Rockbox Speex Encoder";
encoderList["lame"] = "Lame Mp3 Encoder";
}
// get nice name for a specific encoder
QString EncoderBase::getEncoderName(QString encoder)
{
if(encoderList.isEmpty())
initEncodernamesList();
return encoderList.value(encoder);
}
// get a specific encoder object
EncoderBase* EncoderBase::getEncoder(QObject* parent,QString encoder)
{
EncoderBase* enc;
if(encoder == "lame")
{
enc = new EncoderLame(parent);
if (!enc->configOk())
{
LOG_WARNING() << "Could not load lame dll, falling back to command "
"line lame. This is notably slower.";
delete enc;
enc = new EncoderExe(encoder, parent);
}
return enc;
}
else // rbspeex is default
{
enc = new EncoderRbSpeex(parent);
return enc;
}
}
QStringList EncoderBase::getEncoderList()
{
if(encoderList.isEmpty())
initEncodernamesList();
return encoderList.keys();
}

View file

@ -0,0 +1,63 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef ENCODERS_H
#define ENCODERS_H
#include <QtCore>
#include "encttssettings.h"
class EncoderBase : public EncTtsSettingInterface
{
Q_OBJECT
public:
EncoderBase(QObject *parent );
//! Child class should encode a wav file
virtual bool encode(QString input,QString output) =0;
//! Child class should do startup
virtual bool start()=0;
//! Child class should stop
virtual bool stop()=0;
// settings
//! Child class should return true when configuration is ok
virtual bool configOk()=0;
//! Child class should fill in settingsList
virtual void generateSettings() = 0;
//! Child class should commit from SettingsList to permanent storage
virtual void saveSettings() = 0;
// static functions
static QString getEncoderName(QString name);
static EncoderBase* getEncoder(QObject* parent,QString name);
static QStringList getEncoderList(void);
private:
static void initEncodernamesList(void);
protected:
static QMap<QString,QString> encoderList;
};
#endif

View file

@ -0,0 +1,78 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "encoderexe.h"
#include "rbsettings.h"
#include "utils.h"
#include "Logger.h"
EncoderExe::EncoderExe(QString name, QObject *parent) : EncoderBase(parent),
m_name(name)
{
}
void EncoderExe::generateSettings()
{
QString exepath = RbSettings::subValue(m_name,RbSettings::EncoderPath).toString();
if(exepath.isEmpty()) exepath = Utils::findExecutable(m_name);
insertSetting(eEXEPATH, new EncTtsSetting(this, EncTtsSetting::eSTRING,
tr("Path to Encoder:"), exepath, EncTtsSetting::eBROWSEBTN));
insertSetting(eEXEOPTIONS, new EncTtsSetting(this, EncTtsSetting::eSTRING,
tr("Encoder options:"), RbSettings::subValue(m_name, RbSettings::EncoderOptions)));
}
void EncoderExe::saveSettings()
{
RbSettings::setSubValue(m_name, RbSettings::EncoderPath, getSetting(eEXEPATH)->current().toString());
RbSettings::setSubValue(m_name, RbSettings::EncoderOptions, getSetting(eEXEOPTIONS)->current().toString());
RbSettings::sync();
}
bool EncoderExe::start()
{
m_EncExec = RbSettings::subValue(m_name, RbSettings::EncoderPath).toString();
m_EncOpts = RbSettings::subValue(m_name, RbSettings::EncoderOptions).toString();
QFileInfo enc(m_EncExec);
return enc.exists();
}
bool EncoderExe::encode(QString input,QString output)
{
if (!configOk())
return false;
QStringList args;
args << m_EncOpts;
args << input;
args << output;
int result = QProcess::execute(m_EncExec, args);
return result == 0;
}
bool EncoderExe::configOk()
{
QString path = RbSettings::subValue(m_name, RbSettings::EncoderPath).toString();
return QFileInfo::exists(path);
}

View file

@ -0,0 +1,53 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef ENCODEREXES_H
#define ENCODEREXES_H
#include <QtCore>
#include "encoderbase.h"
class EncoderExe : public EncoderBase
{
enum ESettings
{
eEXEPATH,
eEXEOPTIONS
};
Q_OBJECT
public:
EncoderExe(QString name,QObject *parent = nullptr);
bool encode(QString input,QString output);
bool start();
bool stop() {return true;}
// setting
bool configOk();
void generateSettings();
void saveSettings();
private:
QString m_name;
QString m_EncExec;
QString m_EncOpts;
};
#endif

View file

@ -0,0 +1,312 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2012 Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "encoderlame.h"
#include "rbsettings.h"
#include "lame/lame.h"
#include "Logger.h"
/** Resolve a symbol from loaded library.
*/
#define SYMBOLRESOLVE(symbol, type) \
do { m_##symbol = (type)lib.resolve(#symbol); \
if(!m_##symbol) return; \
LOG_INFO() << "Resolved symbol " #symbol; } \
while(0)
EncoderLame::EncoderLame(QObject *parent) : EncoderBase(parent),
lib("libmp3lame", this), m_symbolsResolved(false)
{
lib.load();
if (!lib.isLoaded()) {
LOG_WARNING() << "Loading mp3lame lib failed:" << lib.errorString();
return;
}
SYMBOLRESOLVE(get_lame_short_version, const char* (*)());
SYMBOLRESOLVE(lame_set_out_samplerate, int (*)(lame_global_flags*, int));
SYMBOLRESOLVE(lame_set_in_samplerate, int (*)(lame_global_flags*, int));
SYMBOLRESOLVE(lame_set_num_channels, int (*)(lame_global_flags*, int));
SYMBOLRESOLVE(lame_set_scale, int (*)(lame_global_flags*, float));
SYMBOLRESOLVE(lame_set_mode, int (*)(lame_global_flags*, MPEG_mode));
SYMBOLRESOLVE(lame_set_VBR, int (*)(lame_global_flags*, vbr_mode));
SYMBOLRESOLVE(lame_set_VBR_quality, int (*)(lame_global_flags*, float));
SYMBOLRESOLVE(lame_set_VBR_max_bitrate_kbps, int (*)(lame_global_flags*, int));
SYMBOLRESOLVE(lame_set_bWriteVbrTag, int (*)(lame_global_flags*, int));
SYMBOLRESOLVE(lame_init, lame_global_flags* (*)());
SYMBOLRESOLVE(lame_init_params, int (*)(lame_global_flags*));
SYMBOLRESOLVE(lame_encode_buffer, int (*)(lame_global_flags*, short int*, short int*, int, unsigned char*, int));
SYMBOLRESOLVE(lame_encode_flush, int (*)(lame_global_flags*, unsigned char*, int));
SYMBOLRESOLVE(lame_close, int (*)(lame_global_flags*));
m_encoderVolume = RbSettings::subValue("lame", RbSettings::EncoderVolume).toDouble();
m_encoderQuality = RbSettings::subValue("lame", RbSettings::EncoderQuality).toDouble();
m_symbolsResolved = true;
}
void EncoderLame::generateSettings()
{
// no settings for now.
// show lame version.
if(m_symbolsResolved) {
double quality = RbSettings::subValue("lame",
RbSettings::EncoderQuality).toDouble();
// default quality is 0.999.
if(quality < 0) {
quality = 0.99;
}
insertSetting(LAMEVERSION, new EncTtsSetting(this, EncTtsSetting::eREADONLYSTRING,
tr("LAME"), QString(m_get_lame_short_version())));
insertSetting(VOLUME, new EncTtsSetting(this, EncTtsSetting::eDOUBLE,
tr("Volume"),
RbSettings::subValue("lame", RbSettings::EncoderVolume).toDouble(),
0.0, 2.0));
insertSetting(QUALITY, new EncTtsSetting(this, EncTtsSetting::eDOUBLE,
tr("Quality"), quality, 0.0, 1.0));
}
else {
insertSetting(LAMEVERSION, new EncTtsSetting(this, EncTtsSetting::eREADONLYSTRING,
tr("LAME"), tr("Could not find libmp3lame!")));
}
}
void EncoderLame::saveSettings()
{
if(m_symbolsResolved) {
RbSettings::setSubValue("lame", RbSettings::EncoderVolume,
getSetting(VOLUME)->current().toDouble());
RbSettings::setSubValue("lame", RbSettings::EncoderQuality,
getSetting(QUALITY)->current().toDouble());
m_encoderVolume =
RbSettings::subValue("lame", RbSettings::EncoderVolume).toDouble();
m_encoderQuality =
RbSettings::subValue("lame", RbSettings::EncoderQuality).toDouble();
}
}
bool EncoderLame::start()
{
if(!m_symbolsResolved) {
return false;
}
// try to get config from settings
return true;
}
bool EncoderLame::encode(QString input,QString output)
{
LOG_INFO() << "Encoding" << QDir::cleanPath(input);
if(!m_symbolsResolved) {
LOG_ERROR() << "Symbols not successfully resolved, cannot run!";
return false;
}
QFile fin(input);
QFile fout(output);
// initialize encoder
lame_global_flags *gfp;
unsigned char header[12];
unsigned char chunkheader[8];
unsigned int datalength = 0;
unsigned int channels = 0;
unsigned int samplerate = 0;
unsigned int samplesize = 0;
int num_samples = 0;
int ret;
unsigned char* mp3buf;
int mp3buflen;
short int* wavbuf;
int wavbuflen;
gfp = m_lame_init();
m_lame_set_out_samplerate(gfp, 12000); // resample to 12kHz
// scale input volume
m_lame_set_scale(gfp, m_encoderVolume);
m_lame_set_mode(gfp, MONO); // mono output mode
m_lame_set_VBR(gfp, vbr_default); // enable default VBR mode
// VBR quality
m_lame_set_VBR_quality(gfp, m_encoderQuality);
m_lame_set_VBR_max_bitrate_kbps(gfp, 64); // maximum bitrate 64kbps
m_lame_set_bWriteVbrTag(gfp, 0); // disable LAME tag.
if(!fin.open(QIODevice::ReadOnly)) {
LOG_ERROR() << "Could not open input file" << input;
return false;
}
// read RIFF header
fin.read((char*)header, 12);
if(memcmp("RIFF", header, 4) != 0) {
LOG_ERROR() << "RIFF header not found!"
<< header[0] << header[1] << header[2] << header[3];
fin.close();
return false;
}
if(memcmp("WAVE", &header[8], 4) != 0) {
LOG_ERROR() << "WAVE FOURCC not found!"
<< header[8] << header[9] << header[10] << header[11];
fin.close();
return false;
}
// search for fmt chunk
do {
// read fmt
fin.read((char*)chunkheader, 8);
int chunkdatalen = chunkheader[4] | chunkheader[5]<<8
| chunkheader[6]<<16 | chunkheader[7]<<24;
if(memcmp("fmt ", chunkheader, 4) == 0) {
// fmt found, read rest of chunk.
// NOTE: This code ignores the format tag value.
// Ideally this should be checked as well. However, rbspeex doesn't
// check the format tag either when reading wave files, so if
// problems arise we should notice pretty soon. Furthermore, the
// input format used should be known. In case some TTS uses a
// different wave encoding some time this needs to get adjusted.
if(chunkdatalen < 16) {
LOG_ERROR() << "fmt chunk too small!";
}
else {
unsigned char *buf = new unsigned char[chunkdatalen];
fin.read((char*)buf, chunkdatalen);
channels = buf[2] | buf[3]<<8;
samplerate = buf[4] | buf[5]<<8 | buf[6]<<16 | buf[7]<<24;
samplesize = buf[14] | buf[15]<<8;
delete[] buf;
}
}
// read data
else if(memcmp("data", chunkheader, 4) == 0) {
datalength = chunkdatalen;
break;
}
else {
// unknown chunk, just skip its data.
LOG_WARNING() << "unknown chunk, skipping."
<< chunkheader[0] << chunkheader[1]
<< chunkheader[2] << chunkheader[3];
fin.seek(fin.pos() + chunkdatalen);
}
} while(!fin.atEnd());
// check format
if(channels == 0 || samplerate == 0 || samplesize == 0 || datalength == 0) {
LOG_ERROR() << "invalid format. Channels:" << channels
<< "Samplerate:" << samplerate << "Samplesize:" << samplesize
<< "Data chunk length:" << datalength;
fin.close();
return false;
}
num_samples = (datalength / channels / (samplesize/8));
// set input format values
m_lame_set_in_samplerate(gfp, samplerate);
m_lame_set_num_channels(gfp, channels);
// initialize encoder.
ret = m_lame_init_params(gfp);
if(ret != 0) {
LOG_ERROR() << "lame_init_params() failed with" << ret;
fin.close();
return false;
}
// we're dealing with rather small files here (100kB-ish), so don't care
// about the possible output size and simply allocate the same number of
// bytes the input file has. This wastes space but should be ok.
// Put an upper limit of 8MiB.
if(datalength > 8*1024*1024) {
LOG_ERROR() << "Input file too large:" << datalength;
fin.close();
return false;
}
mp3buflen = datalength;
wavbuflen = datalength;
mp3buf = new unsigned char[mp3buflen];
wavbuf = new short int[wavbuflen];
#if defined(Q_OS_MACX)
// handle byte order -- the host might not be LE.
if(samplesize == 8) {
// no need to convert.
fin.read((char*)wavbuf, wavbuflen);
}
else if(samplesize == 16) {
// read LE 16bit words. Since the input format is either mono or
// interleaved there's no need to care for that.
unsigned int pos = 0;
char word[2];
while(pos < datalength) {
fin.read(word, 2);
wavbuf[pos++] = (word[0]&0xff) | ((word[1]<<8)&0xff00);
}
}
else {
LOG_ERROR() << "Unknown samplesize:" << samplesize;
fin.close();
delete[] mp3buf;
delete[] wavbuf;
return false;
}
#else
// all systems but OS X are considered LE.
fin.read((char*)wavbuf, wavbuflen);
#endif
fin.close();
// encode data.
fout.open(QIODevice::ReadWrite);
ret = m_lame_encode_buffer(gfp, wavbuf, wavbuf, num_samples, mp3buf, mp3buflen);
if(ret < 0) {
LOG_ERROR() << "Error during encoding:" << ret;
}
if(fout.write((char*)mp3buf, ret) != (unsigned int)ret) {
LOG_ERROR() << "Writing mp3 data failed!" << ret;
fout.close();
delete[] mp3buf;
delete[] wavbuf;
return false;
}
// flush remaining data
ret = m_lame_encode_flush(gfp, mp3buf, mp3buflen);
if(fout.write((char*)mp3buf, ret) != (unsigned int)ret) {
LOG_ERROR() << "Writing final mp3 data failed!";
fout.close();
delete[] mp3buf;
delete[] wavbuf;
return false;
}
// shut down encoder and clean up.
m_lame_close(gfp);
fout.close();
delete[] mp3buf;
delete[] wavbuf;
return true;
}
/** Check if the current configuration is usable.
* Since we're loading a library dynamically in the constructor test if that
* succeeded. Otherwise the "configuration" is not usable, even though the
* problem is not necessarily related to configuration values set by the user.
*/
bool EncoderLame::configOk()
{
return m_symbolsResolved;
}

View file

@ -0,0 +1,72 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2012 Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef ENCODERLAME_H
#define ENCODERLAME_H
#include <QtCore>
#include "encoderbase.h"
#include "lame/lame.h"
class EncoderLame : public EncoderBase
{
enum ESettings
{
LAMEVERSION,
VOLUME,
QUALITY,
};
Q_OBJECT
public:
EncoderLame(QObject *parent = nullptr);
bool encode(QString input,QString output);
bool start();
bool stop() {return true;}
// for settings view
bool configOk();
void generateSettings();
void saveSettings();
private:
QLibrary lib;
const char*(*m_get_lame_short_version)(void);
int (*m_lame_set_out_samplerate)(lame_global_flags*, int);
int (*m_lame_set_in_samplerate)(lame_global_flags*, int);
int (*m_lame_set_num_channels)(lame_global_flags*, int);
int (*m_lame_set_scale)(lame_global_flags*, float);
int (*m_lame_set_mode)(lame_global_flags*, MPEG_mode);
int (*m_lame_set_VBR)(lame_global_flags*, vbr_mode);
int (*m_lame_set_VBR_quality)(lame_global_flags*, float);
int (*m_lame_set_VBR_max_bitrate_kbps)(lame_global_flags*, int);
int (*m_lame_set_bWriteVbrTag)(lame_global_flags*, int);
lame_global_flags*(*m_lame_init)(void);
int (*m_lame_init_params)(lame_global_flags*);
int (*m_lame_encode_buffer)(lame_global_flags*, short int[], short
int[], int, unsigned char*, int);
int (*m_lame_encode_flush)(lame_global_flags*, unsigned char*, int);
int (*m_lame_close)(lame_global_flags*);
bool m_symbolsResolved;
double m_encoderVolume;
double m_encoderQuality;
};
#endif

View file

@ -0,0 +1,119 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "encoderrbspeex.h"
#include "rbsettings.h"
#include "rbspeex.h"
#include "Logger.h"
EncoderRbSpeex::EncoderRbSpeex(QObject *parent) : EncoderBase(parent)
{
}
void EncoderRbSpeex::generateSettings()
{
loadSettings();
insertSetting(eVOLUME, new EncTtsSetting(this, EncTtsSetting::eDOUBLE,
tr("Volume:"), volume, 0.0, 2.0));
insertSetting(eQUALITY, new EncTtsSetting(this, EncTtsSetting::eDOUBLE,
tr("Quality:"), quality, 0, 10.0));
insertSetting(eCOMPLEXITY, new EncTtsSetting(this, EncTtsSetting::eINT,
tr("Complexity:"), complexity, 0, 10));
insertSetting(eNARROWBAND,new EncTtsSetting(this, EncTtsSetting::eBOOL,
tr("Use Narrowband:"), narrowband));
}
void EncoderRbSpeex::saveSettings()
{
//save settings in user config
RbSettings::setSubValue("rbspeex",RbSettings::EncoderVolume,
getSetting(eVOLUME)->current().toDouble());
RbSettings::setSubValue("rbspeex",RbSettings::EncoderQuality,
getSetting(eQUALITY)->current().toDouble());
RbSettings::setSubValue("rbspeex",RbSettings::EncoderComplexity,
getSetting(eCOMPLEXITY)->current().toInt());
RbSettings::setSubValue("rbspeex",RbSettings::EncoderNarrowBand,
getSetting(eNARROWBAND)->current().toBool());
RbSettings::sync();
}
void EncoderRbSpeex::loadSettings(void)
{
// try to get config from settings
quality = RbSettings::subValue("rbspeex", RbSettings::EncoderQuality).toDouble();
if(quality < 0) {
quality = 8.0;
}
complexity = RbSettings::subValue("rbspeex", RbSettings::EncoderComplexity).toInt();
volume = RbSettings::subValue("rbspeex", RbSettings::EncoderVolume).toDouble();
narrowband = RbSettings::subValue("rbspeex", RbSettings::EncoderNarrowBand).toBool();
}
bool EncoderRbSpeex::start()
{
// make sure configuration parameters are set.
loadSettings();
return true;
}
bool EncoderRbSpeex::encode(QString input,QString output)
{
LOG_INFO() << "Encoding " << input << " to "<< output;
char errstr[512];
FILE *fin,*fout;
if ((fin = fopen(input.toLocal8Bit(), "rb")) == nullptr) {
LOG_ERROR() << "Error: could not open input file\n";
return false;
}
if ((fout = fopen(output.toLocal8Bit(), "wb")) == nullptr) {
LOG_ERROR() << "Error: could not open output file\n";
fclose(fin);
return false;
}
int ret = encode_file(fin, fout, quality, complexity, narrowband, volume,
errstr, sizeof(errstr));
fclose(fout);
fclose(fin);
if (!ret) {
/* Attempt to delete unfinished output */
LOG_ERROR() << "Error:" << errstr;
QFile(output).remove();
return false;
}
return true;
}
bool EncoderRbSpeex::configOk()
{
// check config. Make sure current settings are loaded.
loadSettings();
if(volume <= 0 || quality <= 0 || complexity <= 0)
return false;
else
return true;
}

View file

@ -0,0 +1,61 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef ENCODERRBSPEEX_H
#define ENCODERRBSPEEX_H
#include <QtCore>
#include "encoderbase.h"
class EncoderRbSpeex : public EncoderBase
{
enum ESettings
{
eVOLUME,
eQUALITY,
eCOMPLEXITY,
eNARROWBAND
};
Q_OBJECT
public:
EncoderRbSpeex(QObject *parent = nullptr);
bool encode(QString input,QString output);
bool start();
bool stop() {return true;}
// for settings view
bool configOk();
void generateSettings();
void saveSettings();
private:
void loadSettings(void);
float quality;
float volume;
int complexity;
bool narrowband;
float defaultQuality;
float defaultVolume;
int defaultComplexity;
bool defaultBand;
};
#endif

View file

@ -0,0 +1,70 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
* $Id: encoders.h 17902 2008-06-30 22:09:45Z bluebrother $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "encttssettings.h"
EncTtsSetting::EncTtsSetting(QObject* parent,ESettingType type,QString name,QVariant current, EButton btn) : QObject(parent)
{
m_btn = btn;
m_name =name;
m_type =type;
m_currentValue = current;
}
EncTtsSetting::EncTtsSetting(QObject* parent,ESettingType type,QString name,QVariant current,QStringList list,EButton btn) : QObject(parent)
{
m_btn = btn;
m_name =name;
m_type =type;
m_currentValue = current;
m_list = list;
}
EncTtsSetting::EncTtsSetting(QObject* parent,ESettingType type,QString name,QVariant current,QVariant min,QVariant max, EButton btn) : QObject(parent)
{
m_btn = btn;
m_name =name;
m_type =type;
m_currentValue = current;
m_minValue = min;
m_maxValue = max;
}
void EncTtsSetting::setCurrent(QVariant current,bool noticeGui)
{
m_currentValue = current;
emit dataChanged();
if(noticeGui) emit updateGui();
}
//! insert a setting
void EncTtsSettingInterface::insertSetting(int id,EncTtsSetting* setting)
{
settingsList.insert(id,setting);
}
//! retrieve a specific setting
EncTtsSetting* EncTtsSettingInterface::getSetting(int id)
{
return settingsList.at(id);
}

View file

@ -0,0 +1,128 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef ENCTTSSETTINGS_H
#define ENCTTSSETTINGS_H
#include <QtCore>
//! \brief This class stores everything needed to display a Setting.
//!
class EncTtsSetting : public QObject
{
Q_OBJECT
public:
enum ESettingType
{
eBASE,
eBOOL,
eDOUBLE,
eINT,
eSTRING,
eREADONLYSTRING,
eSTRINGLIST,
};
enum EButton
{
eNOBTN,
eBROWSEBTN,
eREFRESHBTN
};
//! constructor for a String or Bool setting
EncTtsSetting(QObject* parent,ESettingType type,QString name,QVariant current,EButton btn = eNOBTN);
//! contructor for a Stringlist setting, ie a enumeration
EncTtsSetting(QObject* parent,ESettingType type,QString name,QVariant current,QStringList list,EButton btn = eNOBTN);
//! constructor for a setting with a min-max range
EncTtsSetting(QObject* parent,ESettingType type,QString name,QVariant current,QVariant min,QVariant max,EButton = eNOBTN);
//! get currentValue
QVariant current() {return m_currentValue;}
//! set currentValue
void setCurrent(QVariant current,bool noticeGui=true);
//! get name of the Setting
QString name() {return m_name;}
//! get the type of the setting
ESettingType type() {return m_type;}
//! get what type of button this setting needs
EButton button() {return m_btn;}
//! get the minValue (only valid for a range setting, ie eDOUBLE or eINT)
QVariant min() {return m_minValue; }
//! get the maxValue (only valid for a range setting, ie eDOUBLE or eINT)
QVariant max() {return m_maxValue; }
//! get the enumerationlist (only valid for eSTRINGLIST settings)
QStringList list() {return m_list;}
//! set the enumeration list
void setList(QStringList list){m_list = list;}
signals:
//! connect to this signal if you want to get noticed when the data changes
void dataChanged();
//! connect to this if you want to react on refresh button
void refresh();
//! will be emited when the gui should update this setting
void updateGui();
private:
ESettingType m_type;
EButton m_btn;
QString m_name;
QVariant m_currentValue;
QVariant m_minValue;
QVariant m_maxValue;
QStringList m_list;
};
//! \brief this class is the Interface for Encoder and TTS engines, to display settings
//! It wraps nearly everything needed, only updateModel() and commitModel() needs to be reimplemented
//!
class EncTtsSettingInterface : public QObject
{
Q_OBJECT
public:
EncTtsSettingInterface(QObject* parent) : QObject(parent) {}
//! get the Settings list
QList<EncTtsSetting*> getSettings() {generateSettings(); return settingsList;}
//! Chlid class should commit the from SettingsList to permanent storage
virtual void saveSettings() = 0;
signals:
void busy(); // emit this if a operation takes time
void busyEnd(); // emit this at the end of a busy section
protected:
//! Child class should fill in the setttingsList
virtual void generateSettings() = 0;
//! insert a setting
void insertSetting(int id,EncTtsSetting* setting);
//! retrieve a specific setting
EncTtsSetting* getSetting(int id);
private:
//! The setting storage.
QList<EncTtsSetting*> settingsList;
};
#endif

View file

@ -0,0 +1,256 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2013 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtNetwork>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include "httpget.h"
#include "Logger.h"
QString HttpGet::m_globalUserAgent; //< globally set user agent for requests
QDir HttpGet::m_globalCache; //< global cach path value for new objects
QNetworkProxy HttpGet::m_globalProxy;
HttpGet::HttpGet(QObject *parent)
: QObject(parent),
m_mgr(this),
m_reply(nullptr),
m_cache(nullptr),
m_cachedir(m_globalCache),
m_outputFile(nullptr),
m_proxy(QNetworkProxy::NoProxy)
{
setCache(true);
connect(&m_mgr, &QNetworkAccessManager::finished, this,
static_cast<void (HttpGet::*)(QNetworkReply*)>(&HttpGet::requestFinished));
m_lastServerTimestamp = QDateTime();
}
/** @brief set cache path
* @param d new directory to use as cache path
*/
void HttpGet::setCache(const QDir& d)
{
if(m_cache && m_cachedir == d.absolutePath())
return;
m_cachedir.setPath(d.absolutePath());
setCache(true);
}
/** @brief enable / disable cache useage
* @param c set cache usage
*/
void HttpGet::setCache(bool c)
{
// don't change cache if it's already (un)set.
if(c && m_cache) return;
if(!c && !m_cache) return;
// don't delete the old cache directly, it might still be in use. Just
// instruct it to delete itself later.
if(m_cache) m_cache->deleteLater();
m_cache = nullptr;
QString path = m_cachedir.absolutePath();
if(!c || m_cachedir.absolutePath().isEmpty()) {
LOG_INFO() << "disabling download cache";
}
else {
// append the cache path to make it unique in case the path points to
// the system temporary path. In that case using it directly might
// cause problems. Extra path also used in configure dialog.
path += "/rbutil-cache";
LOG_INFO() << "setting cache folder to" << path;
m_cache = new QNetworkDiskCache(this);
m_cache->setCacheDirectory(path);
}
m_mgr.setCache(m_cache);
}
/** @brief read all downloaded data into a buffer
* @return data
*/
QByteArray HttpGet::readAll()
{
return m_data;
}
/** @brief Set and enable Proxy to use.
* @param proxy Proxy URL.
*/
void HttpGet::setProxy(const QUrl &proxy)
{
LOG_INFO() << "Proxy set to" << proxy;
m_proxy.setType(QNetworkProxy::HttpProxy);
m_proxy.setHostName(proxy.host());
m_proxy.setPort(proxy.port());
m_proxy.setUser(proxy.userName());
m_proxy.setPassword(proxy.password());
m_mgr.setProxy(m_proxy);
}
/** @brief Enable or disable use of previously set proxy.
* @param enable Enable proxy.
*/
void HttpGet::setProxy(bool enable)
{
if(enable) m_mgr.setProxy(m_proxy);
else m_mgr.setProxy(QNetworkProxy::NoProxy);
}
/** @brief Set output file.
*
* Set filename for storing the downloaded file to. If no file is set the
* downloaded file will not be stored to disk but kept in memory. The result
* can then be retrieved using readAll().
*
* @param file Output file.
*/
void HttpGet::setFile(QFile *file)
{
m_outputFile = file;
}
void HttpGet::abort()
{
if(m_reply) m_reply->abort();
}
void HttpGet::requestFinished(QNetworkReply* reply)
{
m_lastStatusCode
= reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
LOG_INFO() << "Request finished, status code:" << m_lastStatusCode;
m_lastServerTimestamp
= reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().toLocalTime();
LOG_INFO() << "Data from cache:"
<< reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool();
m_lastRequestCached =
reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool();
if(reply->attribute(QNetworkRequest::RedirectionTargetAttribute).isValid()) {
// handle relative URLs using QUrl::resolved()
QUrl org = reply->request().url();
QUrl url = QUrl(org).resolved(
reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl());
// reconstruct query
#if QT_VERSION < 0x050000
QList<QPair<QByteArray, QByteArray> > qitms = org.encodedQueryItems();
for(int i = 0; i < qitms.size(); ++i)
url.addEncodedQueryItem(qitms.at(i).first, qitms.at(i).second);
#else
url.setQuery(org.query());
#endif
LOG_INFO() << "Redirected to" << url;
startRequest(url);
return;
}
else if(m_lastStatusCode == 200 ||
(reply->url().scheme() == "file" && reply->error() == 0)) {
// callers might not be aware if the request is file:// so fake 200.
m_lastStatusCode = 200;
m_data = reply->readAll();
if(m_outputFile && m_outputFile->open(QIODevice::WriteOnly)) {
m_outputFile->write(m_data);
m_outputFile->close();
}
emit done(false);
}
else {
m_data.clear();
emit done(true);
}
reply->deleteLater();
m_reply = nullptr;
}
void HttpGet::downloadProgress(qint64 received, qint64 total)
{
emit dataReadProgress((int)received, (int)total);
}
void HttpGet::startRequest(QUrl url)
{
LOG_INFO() << "Request started";
QNetworkRequest req(url);
if(!m_globalUserAgent.isEmpty())
req.setRawHeader("User-Agent", m_globalUserAgent.toLatin1());
m_reply = m_mgr.get(req);
#if QT_VERSION < 0x050f00
connect(m_reply,
static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),
this, &HttpGet::networkError);
#else
connect(m_reply, &QNetworkReply::errorOccurred, this, &HttpGet::networkError);
#endif
connect(m_reply, &QNetworkReply::downloadProgress, this, &HttpGet::downloadProgress);
}
void HttpGet::networkError(QNetworkReply::NetworkError error)
{
LOG_ERROR() << "NetworkError occured:" << error << m_reply->errorString();
m_lastErrorString = m_reply->errorString();
}
/** @brief Retrieve the file pointed to by url.
*
* Note: This also handles file:// URLs. Be aware that QUrl requires file://
* URLs to be absolute, i.e. file://filename.txt doesn't work. Use
* QDir::absoluteFilePath() to convert to an absolute path first.
*
* @param url URL to download.
*/
void HttpGet::getFile(const QUrl &url)
{
LOG_INFO() << "Get URI" << url.toString();
m_data.clear();
startRequest(url);
}
/** @brief Retrieve string representation for most recent error.
* @return Error string.
*/
QString HttpGet::errorString(void)
{
return m_lastErrorString;
}
/** @brief Return last HTTP response code.
* @return Response code.
*/
int HttpGet::httpResponse(void)
{
return m_lastStatusCode;
}

View file

@ -0,0 +1,111 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Riebeling
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef HTTPGET_H
#define HTTPGET_H
#include <QtCore>
#include <QtNetwork>
#include <QNetworkAccessManager>
#include "Logger.h"
class HttpGet : public QObject
{
Q_OBJECT
public:
HttpGet(QObject *parent = nullptr);
void getFile(const QUrl &url);
void setProxy(const QUrl &url);
void setProxy(bool);
QString errorString(void);
void setFile(QFile*);
void setCache(const QDir&);
void setCache(bool);
int httpResponse(void);
QByteArray readAll(void);
bool isCached()
{ return m_lastRequestCached; }
QDateTime timestamp(void)
{ return m_lastServerTimestamp; }
//< set global cache path
static void setGlobalCache(const QDir& d)
{
LOG_INFO() << "Global cache set to" << d.absolutePath();
m_globalCache = d;
}
//< set global proxy value
static void setGlobalProxy(const QUrl& p)
{
LOG_INFO() << "setting global proxy" << p;
if(!p.isValid() || p.isEmpty()) {
HttpGet::m_globalProxy.setType(QNetworkProxy::NoProxy);
}
else {
HttpGet::m_globalProxy.setType(QNetworkProxy::HttpProxy);
HttpGet::m_globalProxy.setHostName(p.host());
HttpGet::m_globalProxy.setPort(p.port());
HttpGet::m_globalProxy.setUser(p.userName());
HttpGet::m_globalProxy.setPassword(p.password());
}
QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy);
QNetworkProxy::setApplicationProxy(HttpGet::m_globalProxy);
}
//< set global user agent string
static void setGlobalUserAgent(const QString& u)
{ m_globalUserAgent = u; }
public slots:
void abort(void);
signals:
void done(bool);
void dataReadProgress(int, int);
void requestFinished(int, bool);
void headerFinished(void);
private slots:
void requestFinished(QNetworkReply* reply);
void startRequest(QUrl url);
void downloadProgress(qint64 received, qint64 total);
void networkError(QNetworkReply::NetworkError error);
private:
static QString m_globalUserAgent;
static QNetworkProxy m_globalProxy;
QNetworkAccessManager m_mgr;
QNetworkReply *m_reply;
QNetworkDiskCache *m_cache;
QDir m_cachedir;
static QDir m_globalCache; //< global cache path value
QByteArray m_data;
QFile *m_outputFile;
int m_lastStatusCode;
QString m_lastErrorString;
QDateTime m_lastServerTimestamp;
bool m_lastRequestCached;
QNetworkProxy m_proxy;
};
#endif

View file

@ -0,0 +1,164 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2013 Amaury Pouly
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "Logger.h"
#include "mspackutil.h"
#include "progressloggerinterface.h"
MsPackUtil::MsPackUtil(QObject* parent)
:ArchiveUtil(parent)
{
m_cabd = mspack_create_cab_decompressor(nullptr);
m_cabinet = nullptr;
if(!m_cabd)
LOG_ERROR() << "CAB decompressor creation failed!";
}
MsPackUtil::~MsPackUtil()
{
close();
if(m_cabd)
mspack_destroy_cab_decompressor(m_cabd);
}
bool MsPackUtil::open(QString& mspackfile)
{
close();
if(m_cabd == nullptr)
{
LOG_ERROR() << "No CAB decompressor available: cannot open file!";
return false;
}
m_cabinet = m_cabd->search(m_cabd, QFile::encodeName(mspackfile).constData());
return m_cabinet != nullptr;
}
bool MsPackUtil::close(void)
{
if(m_cabd && m_cabinet)
m_cabd->close(m_cabd, m_cabinet);
m_cabinet = nullptr;
return true;
}
bool MsPackUtil::extractArchive(const QString& dest, QString file)
{
LOG_INFO() << "extractArchive" << dest << file;
if(!m_cabinet)
{
LOG_ERROR() << "CAB file not open!";
return false;
}
// construct the filename when extracting a single file from an archive.
// if the given destination is a full path use it as output name,
// otherwise use it as path to place the file as named in the archive.
QString singleoutfile;
if(!file.isEmpty() && QFileInfo(dest).isDir())
singleoutfile = dest + "/" + file;
else if(!file.isEmpty())
singleoutfile = dest;
struct mscabd_file *f = m_cabinet->files;
if(f == nullptr)
{
LOG_WARNING() << "CAB doesn't contain file" << file;
return true;
}
bool found = false;
while(f)
{
QString name = QFile::decodeName(f->filename);
name.replace("\\", "/");
if(name.at(0) == '/')
name.remove(0, 1);
if(name == file || file.isEmpty())
{
QString path;
if(!singleoutfile.isEmpty())
path = singleoutfile;
else
path = dest + "/" + name;
// make sure the output path exists
if(!QDir().mkpath(QFileInfo(path).absolutePath()))
{
emit logItem(tr("Creating output path failed"), LOGERROR);
LOG_ERROR() << "creating output path failed for:" << path;
emit logProgress(1, 1);
return false;
}
int ret = m_cabd->extract(m_cabd, f, QFile::encodeName(path).constData());
if(ret != MSPACK_ERR_OK)
{
emit logItem(tr("Error during CAB operation"), LOGERROR);
LOG_ERROR() << "mspack error: " << ret
<< "(" << errorStringMsPack(ret) << ")";
emit logProgress(1, 1);
return false;
}
found = true;
}
f = f->next;
}
emit logProgress(1, 1);
return found;
}
QStringList MsPackUtil::files(void)
{
QStringList list;
if(!m_cabinet)
{
LOG_WARNING() << "CAB file not open!";
return list;
}
struct mscabd_file *file = m_cabinet->files;
while(file)
{
QString name = QFile::decodeName(file->filename);
name.replace("\\", "/");
if(name.at(0) == '/')
name.remove(0, 1);
list.append(name);
file = file->next;
}
return list;
}
QString MsPackUtil::errorStringMsPack(int error) const
{
switch(error)
{
case MSPACK_ERR_OK: return "Ok";
case MSPACK_ERR_ARGS: return "Bad arguments";
case MSPACK_ERR_OPEN: return "Open error";
case MSPACK_ERR_READ: return "Read error";
case MSPACK_ERR_WRITE: return "Write error";
case MSPACK_ERR_SEEK: return "Seek error";
case MSPACK_ERR_NOMEMORY: return "Out of memory";
case MSPACK_ERR_SIGNATURE: return "Bad signature";
case MSPACK_ERR_DATAFORMAT: return "Bad data format";
case MSPACK_ERR_CHECKSUM: return "Checksum error";
case MSPACK_ERR_CRUNCH: return "Compression error";
case MSPACK_ERR_DECRUNCH: return "Decompression error";
default: return "Unknown";
}
}

View file

@ -0,0 +1,51 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2013 Amaury Pouly
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef MSPACKUTIL_H
#define MSPACKUTIL_H
#include <QtCore>
#include "archiveutil.h"
#include "mspack/mspack.h"
class MsPackUtil : public ArchiveUtil
{
Q_OBJECT
public:
// archive types can be ORed
MsPackUtil(QObject* parent);
~MsPackUtil();
bool open(QString& mspackfile);
virtual bool close(void);
virtual bool extractArchive(const QString& dest, QString file = "");
virtual QStringList files(void);
signals:
void logProgress(int, int);
void logItem(QString, int);
private:
QString errorStringMsPack(int error) const;
struct mscab_decompressor* m_cabd;
struct mscabd_cabinet *m_cabinet;
};
#endif

View file

@ -0,0 +1,362 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2020 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "playerbuildinfo.h"
#include "rbsettings.h"
#include "Logger.h"
PlayerBuildInfo* PlayerBuildInfo::infoInstance = nullptr;
PlayerBuildInfo* PlayerBuildInfo::instance()
{
if (infoInstance == nullptr) {
infoInstance = new PlayerBuildInfo();
}
return infoInstance;
}
// server infos
const static struct {
PlayerBuildInfo::BuildInfo item;
const char* name;
} ServerInfoList[] = {
{ PlayerBuildInfo::BuildVoiceLangs, "voices/:version:" },
{ PlayerBuildInfo::BuildVersion, ":build:/:target:" },
{ PlayerBuildInfo::BuildUrl, ":build:/build_url" },
{ PlayerBuildInfo::BuildVoiceUrl, ":build:/voice_url" },
{ PlayerBuildInfo::BuildManualUrl, ":build:/manual_url" },
{ PlayerBuildInfo::BuildSourceUrl, ":build:/source_url" },
{ PlayerBuildInfo::BuildFontUrl, ":build:/font_url" },
// other URLs -- those are not directly related to the build, but handled here.
{ PlayerBuildInfo::DoomUrl, "other/doom_url" },
{ PlayerBuildInfo::Duke3DUrl, "other/duke3d_url" },
{ PlayerBuildInfo::PuzzFontsUrl, "other/puzzfonts_url" },
{ PlayerBuildInfo::QuakeUrl, "other/quake_url" },
{ PlayerBuildInfo::Wolf3DUrl, "other/wolf3d_url" },
{ PlayerBuildInfo::XWorldUrl, "other/xworld_url" },
{ PlayerBuildInfo::MidiPatchsetUrl, "other/patcheset_url" },
};
const static struct {
PlayerBuildInfo::DeviceInfo item;
const char* name;
} PlayerInfoList[] = {
{ PlayerBuildInfo::BuildStatus, "status/:target:" },
{ PlayerBuildInfo::DisplayName, ":target:/name" },
{ PlayerBuildInfo::BootloaderMethod, ":target:/bootloadermethod" },
{ PlayerBuildInfo::BootloaderName, ":target:/bootloadername" },
{ PlayerBuildInfo::BootloaderFile, ":target:/bootloaderfile" },
{ PlayerBuildInfo::BootloaderFilter, ":target:/bootloaderfilter" },
{ PlayerBuildInfo::Encoder, ":target:/encoder" },
{ PlayerBuildInfo::Brand, ":target:/brand" },
{ PlayerBuildInfo::PlayerPicture, ":target:/playerpic" },
{ PlayerBuildInfo::TargetNamesAll, "_targets/all" },
{ PlayerBuildInfo::TargetNamesEnabled, "_targets/enabled" },
{ PlayerBuildInfo::LanguageInfo, "languages/:target:" },
{ PlayerBuildInfo::LanguageList, "_languages/list" },
{ PlayerBuildInfo::UsbIdErrorList, "_usb/error" },
{ PlayerBuildInfo::UsbIdTargetList, "_usb/target" },
};
const static struct {
PlayerBuildInfo::SystemUrl item;
const char* name;
} PlayerSystemUrls[] = {
{ PlayerBuildInfo::BootloaderUrl, "bootloader/download_url" },
{ PlayerBuildInfo::BuildInfoUrl, "build_info_url" },
{ PlayerBuildInfo::GenlangUrl, "genlang_url" },
{ PlayerBuildInfo::ThemesUrl, "themes_url" },
{ PlayerBuildInfo::ThemesInfoUrl, "themes_info_url" },
{ PlayerBuildInfo::RbutilUrl, "rbutil_url" },
};
PlayerBuildInfo::PlayerBuildInfo() :
serverInfo(nullptr),
playerInfo(":/ini/rbutil.ini", QSettings::IniFormat)
{
}
void PlayerBuildInfo::setBuildInfo(QString file)
{
if (serverInfo)
delete serverInfo;
LOG_INFO() << "updated:" << file;
serverInfo = new QSettings(file, QSettings::IniFormat);
}
QVariant PlayerBuildInfo::value(BuildInfo item, BuildType type)
{
// locate setting item in server info file
int i = 0;
while(ServerInfoList[i].item != item)
i++;
// split of variant for target.
// we can have an optional variant part in the target string.
// For build info we don't use that.
QString target = RbSettings::value(RbSettings::CurrentPlatform).toString().split('.').at(0);
QString s = ServerInfoList[i].name;
s.replace(":target:", target);
QString v;
switch(type) {
case TypeRelease:
v = "release";
break;
case TypeCandidate:
v = "release-candidate";
break;
case TypeDaily:
v = "daily";
break;
case TypeDevel:
v = "development";
break;
}
QVariant result = QString();
if (!serverInfo)
return result;
QStringList version = serverInfo->value(v + "/" + target, "").toStringList();
s.replace(":build:", v);
s.replace(":version:", version.at(0));
// get value from server build-info
// we need to get a version string, otherwise the data is invalid.
// For invalid data return an empty string.
if(version.at(0).isEmpty()) {
LOG_INFO() << s << "(version invalid)";
return result;
}
if(!s.isEmpty())
result = serverInfo->value(s);
// depending on the actual value we need more replacements.
switch(item) {
case BuildVersion:
result = result.toStringList().at(0);
break;
case BuildUrl:
if(version.size() > 1) {
// version info has an URL appended. Takes precendence.
result = version.at(1);
}
break;
case BuildVoiceLangs:
if (type == TypeDaily)
s = "voices/daily";
result = serverInfo->value(s);
break;
case BuildManualUrl:
{
// special case: if playerInfo has a non-empty manualname entry for the
// target, use that as target for the manual name.
QString manualtarget = playerInfo.value(target + "/manualname", "").toString();
if(!manualtarget.isEmpty())
target = manualtarget;
break;
}
default:
break;
}
// if the value is a string we can replace some patterns.
// if we cannot convert it (f.e. for a QStringList) we leave as-is, since
// the conversion would return an empty type.
if (result.canConvert(QMetaType::QString))
result = result.toString()
.replace("%TARGET%", target)
.replace("%VERSION%", version.at(0));
LOG_INFO() << "B:" << s << result;
return result;
}
QVariant PlayerBuildInfo::value(DeviceInfo item, QString target)
{
// locate setting item in server info file
int i = 0;
while(PlayerInfoList[i].item != item)
i++;
// split of variant for target.
// we can have an optional variant part in the target string.
// For device info we use this.
if (target.isEmpty())
target = RbSettings::value(RbSettings::CurrentPlatform).toString();
QVariant result = QString();
QString s = PlayerInfoList[i].name;
s.replace(":target:", target);
switch(item) {
case BuildStatus:
{
// build status is the only value that doesn't depend on the version
// but the selected target instead.
bool ok = false;
if (serverInfo)
result = serverInfo->value(s).toInt(&ok);
if (!ok)
result = -1;
break;
}
case TargetNamesAll:
// list of all internal target names. Doesn't depend on the passed target.
result = targetNames(true);
break;
case TargetNamesEnabled:
// list of all non-disabled target names. Doesn't depend on the passed target.
result = targetNames(false);
break;
case LanguageList:
// Return a map (language, display string).
{
// need to use (QString, QVariant) here, so we can put the map into
// a QVariant by itself.
QMap<QString, QVariant> m;
playerInfo.beginGroup("languages");
QStringList a = playerInfo.childKeys();
for(int i = 0; i < a.size(); i++) {
QStringList v = playerInfo.value(a.at(i)).toStringList();
m[v.at(0)] = v.at(1);
}
playerInfo.endGroup();
result = m;
}
break;
default:
result = playerInfo.value(s);
break;
}
LOG_INFO() << "T:" << s << result;
return result;
}
QVariant PlayerBuildInfo::value(DeviceInfo item, unsigned int match)
{
QStringList result;
int i = 0;
while(PlayerInfoList[i].item != item)
i++;
QString s = PlayerInfoList[i].name;
switch(item) {
case UsbIdErrorList:
{
// go through all targets and find the one indicated by the usb id "target".
// return list of matching players (since it could be more than one)
QStringList targets = targetNames(true);
for(int i = 0; i < targets.size(); i++) {
QStringList usbids = playerInfo.value(targets.at(i) + "/usberror").toStringList();
for(int j = 0; j < usbids.size(); j++) {
if(usbids.at(j).toUInt(nullptr, 0) == match) {
result << targets.at(i);
}
}
}
break;
}
case UsbIdTargetList:
{
QStringList targets = targetNames(true);
for(int i = 0; i < targets.size(); i++) {
QStringList usbids = playerInfo.value(targets.at(i) + "/usbid").toStringList();
for(int j = 0; j < usbids.size(); j++) {
if(usbids.at(j).toUInt(nullptr, 0) == match) {
result << targets.at(i);
}
}
}
break;
}
default:
break;
}
LOG_INFO() << "T:" << s << result;
return result;
}
QVariant PlayerBuildInfo::value(SystemUrl item)
{
// locate setting item in server info file
int i = 0;
while(PlayerSystemUrls[i].item != item)
i++;
QVariant result = playerInfo.value(PlayerSystemUrls[i].name);
LOG_INFO() << "U:" << PlayerSystemUrls[i].name << result;
return result;
}
QString PlayerBuildInfo::statusAsString(QString platform)
{
QString result;
switch(value(BuildStatus, platform).toInt())
{
case STATUS_RETIRED:
result = tr("Stable (Retired)");
break;
case STATUS_UNUSABLE:
result = tr("Unusable");
break;
case STATUS_UNSTABLE:
result = tr("Unstable");
break;
case STATUS_STABLE:
result = tr("Stable");
break;
default:
result = tr("Unknown");
break;
}
return result;
}
QStringList PlayerBuildInfo::targetNames(bool all)
{
QStringList result;
playerInfo.beginGroup("platforms");
QStringList a = playerInfo.childKeys();
playerInfo.endGroup();
for(int i = 0; i < a.size(); i++)
{
QString target = playerInfo.value("platforms/" + a.at(i), "null").toString();
if(playerInfo.value(target + "/status").toString() != "disabled" || all) {
result.append(target);
}
}
return result;
}

View file

@ -0,0 +1,123 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2020 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef PLAYERBUILDINFO_H
#define PLAYERBUILDINFO_H
#include <QSettings>
#define STATUS_RETIRED 0
#define STATUS_UNUSABLE 1
#define STATUS_UNSTABLE 2
#define STATUS_STABLE 3
// Provide information about both player and builds.
// For build info data retrieved from the build server has to be passed.
class PlayerBuildInfo : public QObject
{
Q_OBJECT
public:
enum BuildType {
TypeRelease,
TypeCandidate,
TypeDaily,
TypeDevel,
};
enum BuildInfo {
BuildUrl,
BuildVersion,
BuildManualUrl,
BuildVoiceUrl,
BuildVoiceLangs,
BuildSourceUrl,
BuildFontUrl,
DoomUrl,
Duke3DUrl,
PuzzFontsUrl,
QuakeUrl,
Wolf3DUrl,
XWorldUrl,
MidiPatchsetUrl,
};
enum DeviceInfo {
BuildStatus,
DisplayName,
BootloaderMethod,
BootloaderName,
BootloaderFile,
BootloaderFilter,
Encoder,
Brand,
PlayerPicture,
TargetNamesAll,
TargetNamesEnabled,
LanguageInfo,
LanguageList,
UsbIdErrorList,
UsbIdTargetList,
};
enum SystemUrl {
BootloaderUrl,
BuildInfoUrl,
GenlangUrl,
ThemesUrl,
ThemesInfoUrl,
RbutilUrl,
};
static PlayerBuildInfo* instance();
//! Update with build information from server
void setBuildInfo(QString file);
// Get information about a device. This data does not depend on the build type.
QVariant value(DeviceInfo item, QString target = "");
// Get information about a device. Make a numeric match
// (only sensible implementation for USB IDs)
QVariant value(DeviceInfo item, unsigned int match);
// Get build information for currently selected player.
QVariant value(BuildInfo item, BuildType type);
// Get fixed download URL information
QVariant value(SystemUrl item);
QString statusAsString(QString target = "");
protected:
explicit PlayerBuildInfo();
private:
//! Return a list with all target names (as used internally).
//! @all false filter out all targets with status = disabled.
QStringList targetNames(bool all);
static PlayerBuildInfo* infoInstance;
QSettings* serverInfo;
QSettings playerInfo;
};
#endif

View file

@ -0,0 +1,60 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef PROGRESSLOGGERINTERFACE_H
#define PROGRESSLOGGERINTERFACE_H
#include <QtCore>
enum {
LOGNOICON, LOGOK, LOGINFO, LOGWARNING, LOGERROR
};
class ProgressloggerInterface : public QObject
{
Q_OBJECT
public:
ProgressloggerInterface(QObject* parent) : QObject(parent) {}
virtual void setProgressValue(int value)=0;
virtual void setProgressMax(int max)=0;
virtual int getProgressMax()=0;
signals:
void aborted();
public slots:
virtual void addItem(const QString &text, int flag)=0; //! add a string to the list, with icon
virtual void close()=0;
virtual void show()=0;
virtual void setRunning()=0;
virtual void setFinished()=0;
private:
};
#endif

View file

@ -0,0 +1,207 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "rbsettings.h"
#include "playerbuildinfo.h"
#include <QSettings>
#include "Logger.h"
#if defined(Q_OS_LINUX)
#include <unistd.h>
#endif
// user settings
const static struct {
RbSettings::UserSettings setting;
const char* name;
const char* def;
} UserSettingsList[] = {
{ RbSettings::RbutilVersion, "rbutil_version", "" },
{ RbSettings::ShowChangelog, "show_changelog", "false" },
{ RbSettings::CurrentPlatform, "platform", "" },
{ RbSettings::Mountpoint, "mountpoint", "" },
{ RbSettings::CachePath, "cachepath", "" },
{ RbSettings::Build, "build", "" },
{ RbSettings::ProxyType, "proxytype", "" },
{ RbSettings::Proxy, "proxy", "" },
{ RbSettings::OfPath, "ofpath", "" },
{ RbSettings::Platform, "platform", "" },
{ RbSettings::Language, "lang", "" },
{ RbSettings::BackupPath, "backuppath", "" },
{ RbSettings::InstallRockbox, "install_rockbox", "true" },
{ RbSettings::InstallFonts, "install_fonts", "true" },
{ RbSettings::InstallThemes, "install_themes", "false" },
{ RbSettings::InstallPluginData, "install_plugin_data", "true" },
{ RbSettings::InstallVoice, "install_voice", "false" },
{ RbSettings::InstallManual, "install_manual", "false" },
#if defined(Q_OS_WIN32)
{ RbSettings::Tts, "tts", "sapi" },
#elif defined(Q_OS_MACX)
{ RbSettings::Tts, "tts", "carbon" },
#else
{ RbSettings::Tts, "tts", "espeak" },
#endif
{ RbSettings::UseTtsCorrections, "use_tts_corrections", "true" },
{ RbSettings::TalkFolders, "talk_folders", "" },
{ RbSettings::TalkProcessFiles, "talk_process_files", "true" },
{ RbSettings::TalkProcessFolders, "talk_process_folders", "true" },
{ RbSettings::TalkRecursive, "talk_recursive", "true" },
{ RbSettings::TalkSkipExisting, "talk_skip_existing", "true" },
{ RbSettings::TalkStripExtensions, "talk_strip_extensions","true" },
{ RbSettings::TalkIgnoreFiles, "talk_ignore_files", "false" },
{ RbSettings::TalkIgnoreWildcards, "talk_ignore_wildcards","" },
{ RbSettings::VoiceLanguage, "voicelanguage", "" },
{ RbSettings::TtsLanguage, ":tts:/language", "" },
{ RbSettings::TtsOptions, ":tts:/options", "" },
{ RbSettings::TtsPitch, ":tts:/pitch", "0" },
{ RbSettings::TtsPath, ":tts:/path", "" },
{ RbSettings::TtsVoice, ":tts:/voice", "" },
{ RbSettings::EncoderPath, ":encoder:/encoderpath", "" },
{ RbSettings::EncoderOptions, ":encoder:/encoderoptions", "" },
{ RbSettings::CacheDisabled, "cachedisable", "false" },
{ RbSettings::TtsUseSapi4, "sapi/useSapi4", "false" },
{ RbSettings::EncoderNarrowBand, ":encoder:/narrowband", "false" },
{ RbSettings::WavtrimThreshold, "wavtrimthreshold", "500"},
{ RbSettings::TtsSpeed, ":tts:/speed", "175" },
{ RbSettings::EncoderComplexity, ":encoder:/complexity", "10" },
{ RbSettings::EncoderQuality, ":encoder:/quality", "-1.0" },
{ RbSettings::EncoderVolume, ":encoder:/volume", "1.0" },
};
//! pointer to setting object to NULL
QSettings* RbSettings::userSettings = nullptr;
void RbSettings::ensureRbSettingsExists()
{
if(userSettings == nullptr)
{
// portable installation:
// check for a configuration file in the program folder.
QFileInfo config;
config.setFile(QCoreApplication::instance()->applicationDirPath()
+ "/RockboxUtility.ini");
if(config.isFile())
{
userSettings = new QSettings(QCoreApplication::instance()->applicationDirPath()
+ "/RockboxUtility.ini", QSettings::IniFormat, nullptr);
LOG_INFO() << "configuration: portable";
}
else
{
userSettings = new QSettings(QSettings::IniFormat,
QSettings::UserScope, "rockbox.org", "RockboxUtility",nullptr);
LOG_INFO() << "configuration: system";
}
}
}
void RbSettings::sync()
{
ensureRbSettingsExists();
userSettings->sync();
#if defined(Q_OS_LINUX)
// when using sudo it runs rbutil with uid 0 but unfortunately without a
// proper login shell, thus the configuration file gets placed in the
// calling users $HOME. This in turn will cause issues if trying to
// run rbutil later as user. Try to detect this case via the environment
// variable SUDO_UID and SUDO_GID and if set chown the user config file.
if(getuid() == 0)
{
char* realuser = getenv("SUDO_UID");
char* realgroup = getenv("SUDO_GID");
if(realuser != nullptr && realgroup != nullptr)
{
int realuid = atoi(realuser);
int realgid = atoi(realgroup);
// chown is attribute warn_unused_result, but in case this fails
// we can't do anything useful about it. Notifying the user
// is somewhat pointless. Add hack to suppress compiler warning.
if(chown(qPrintable(userSettings->fileName()), realuid, realgid))
{ }
}
}
#endif
}
QString RbSettings::userSettingFilename()
{
ensureRbSettingsExists();
return userSettings->fileName();
}
QVariant RbSettings::value(enum UserSettings setting)
{
QString empty;
return subValue(empty, setting);
}
QVariant RbSettings::subValue(QString sub, enum UserSettings setting)
{
ensureRbSettingsExists();
// locate setting item
int i = 0;
while(UserSettingsList[i].setting != setting)
i++;
QString s = constructSettingPath(UserSettingsList[i].name, sub);
LOG_INFO() << "GET U:" << s << userSettings->value(s).toString();
return userSettings->value(s, UserSettingsList[i].def);
}
void RbSettings::setValue(enum UserSettings setting , QVariant value)
{
QString empty;
return setSubValue(empty, setting, value);
}
void RbSettings::setSubValue(QString sub, enum UserSettings setting, QVariant value)
{
ensureRbSettingsExists();
// locate setting item
int i = 0;
while(UserSettingsList[i].setting != setting)
i++;
QString s = constructSettingPath(UserSettingsList[i].name, sub);
userSettings->setValue(s, value);
LOG_INFO() << "SET U:" << s << userSettings->value(s).toString();
}
QString RbSettings::constructSettingPath(QString path, QString substitute)
{
// anything to substitute?
if(path.contains(':')) {
QString platform = userSettings->value("platform").toString();
if(!substitute.isEmpty()) {
path.replace(":tts:", substitute);
path.replace(":encoder:", substitute);
}
else {
path.replace(":tts:", userSettings->value("tts").toString());
path.replace(":encoder:", PlayerBuildInfo::instance()->value(
PlayerBuildInfo::Encoder, platform).toString());
}
path.replace(":platform:", platform);
}
return path;
}

View file

@ -0,0 +1,105 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef RBSETTINGS_H
#define RBSETTINGS_H
#include <QtCore>
class RbSettings : public QObject
{
Q_OBJECT
public:
//! All user settings
enum UserSettings {
RbutilVersion,
ShowChangelog,
CurrentPlatform,
Mountpoint,
CachePath,
Build,
ProxyType,
Proxy,
OfPath,
Platform,
Language,
BackupPath,
InstallRockbox,
InstallFonts,
InstallThemes,
InstallPluginData,
InstallVoice,
InstallManual,
Tts,
UseTtsCorrections,
TalkFolders,
TalkProcessFiles,
TalkProcessFolders,
TalkRecursive,
TalkSkipExisting,
TalkStripExtensions,
TalkIgnoreFiles,
TalkIgnoreWildcards,
VoiceLanguage,
TtsLanguage,
TtsOptions,
TtsPath,
TtsVoice,
TtsPitch,
EncoderPath,
EncoderOptions,
WavtrimThreshold,
EncoderComplexity,
TtsSpeed,
CacheDisabled,
TtsUseSapi4,
EncoderNarrowBand,
EncoderQuality,
EncoderVolume,
};
//! call this to flush the user Settings
static void sync();
//! returns the filename of the usersettings file
static QString userSettingFilename();
//! get a value from user settings
static QVariant value(enum UserSettings setting);
//! set a user setting value
static void setValue(enum UserSettings setting , QVariant value);
//! get a user setting from a subvalue (ie for encoders and tts engines)
static QVariant subValue(QString sub, enum UserSettings setting);
//! set a user setting from a subvalue (ie for encoders and tts engines)
static void setSubValue(QString sub, enum UserSettings setting, QVariant value);
private:
//! you shouldnt call this, its a fully static calls
RbSettings() {}
//! create the setting objects if neccessary
static void ensureRbSettingsExists();
//! create a settings path, substitute platform, tts and encoder
static QString constructSettingPath(QString path, QString substitute = QString());
//! pointers to our setting object
static QSettings *userSettings;
};
#endif

View file

@ -0,0 +1,83 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "rockboxinfo.h"
#include <QtCore>
#include <QDebug>
#include "Logger.h"
RockboxInfo::RockboxInfo(QString mountpoint, QString fname)
{
LOG_INFO() << "Getting version info from rockbox-info.txt";
QFile file(mountpoint + "/" + fname);
m_success = false;
m_voicefmt = 400; // default value for compatibility
if(!file.exists())
return;
if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
// read file contents
QRegExp hash("^Version:\\s+(r?)([0-9a-fM]+)");
QRegExp version("^Version:\\s+(\\S.*)");
QRegExp release("^Version:\\s+([0-9\\.]+)\\s*$");
QRegExp target("^Target:\\s+(\\S.*)");
QRegExp features("^Features:\\s+(\\S.*)");
QRegExp targetid("^Target id:\\s+(\\S.*)");
QRegExp memory("^Memory:\\s+(\\S.*)");
QRegExp voicefmt("^Voice format:\\s+(\\S.*)");
while (!file.atEnd())
{
QString line = file.readLine().trimmed();
if(version.indexIn(line) >= 0) {
m_version = version.cap(1);
}
if(release.indexIn(line) >= 0) {
m_release = release.cap(1);
}
if(hash.indexIn(line) >= 0) {
// git hashes are usually at least 7 characters.
// svn revisions are expected to be at least 4 digits.
if(hash.cap(2).size() > 3)
m_revision = hash.cap(2);
}
else if(target.indexIn(line) >= 0) {
m_target = target.cap(1);
}
else if(features.indexIn(line) >= 0) {
m_features = features.cap(1);
}
else if(targetid.indexIn(line) >= 0) {
m_targetid = targetid.cap(1);
}
else if(memory.indexIn(line) >= 0) {
m_ram = memory.cap(1).toInt();
}
else if(voicefmt.indexIn(line) >= 0) {
m_voicefmt = voicefmt.cap(1).toInt();
}
}
file.close();
m_success = true;
return;
}

View file

@ -0,0 +1,54 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef ROCKBOXINFO_H
#define ROCKBOXINFO_H
#include <QString>
class RockboxInfo
{
public:
RockboxInfo(QString mountpoint, QString fname = "/.rockbox/rockbox-info.txt");
QString version() {return m_version;}
QString features(){return m_features;}
QString targetID() {return m_targetid;}
QString target() {return m_target;}
int ram() { return m_ram; }
int voicefmt() { return m_voicefmt; }
bool success() { return m_success; }
QString revision(void) { return m_revision; }
QString release(void) { return m_release; }
private:
QString m_version;
QString m_revision;
QString m_release;
QString m_features;
QString m_targetid;
QString m_target;
int m_ram;
int m_voicefmt;
bool m_success;
};
#endif

View file

@ -0,0 +1,519 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "system.h"
#include <QtCore>
#include <QDebug>
#include <cstdlib>
#include <stdio.h>
// Windows Includes
#if defined(Q_OS_WIN32)
#if defined(UNICODE)
#define _UNICODE
#endif
#include <windows.h>
#include <tchar.h>
#include <lm.h>
#include <windows.h>
#include <setupapi.h>
#endif
// Linux and Mac includes
#if defined(Q_OS_LINUX) || defined(Q_OS_MACX)
#include <sys/utsname.h>
#include <unistd.h>
#include <pwd.h>
#endif
// Linux includes
#if defined(Q_OS_LINUX)
#include <libusb-1.0/libusb.h>
#include <mntent.h>
#endif
// Mac includes
#if defined(Q_OS_MACX)
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <CoreServices/CoreServices.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/usb/IOUSBLib.h>
#endif
#include "utils.h"
#include "rbsettings.h"
#include "Logger.h"
/** @brief detect permission of user (only Windows at moment).
* @return enum userlevel.
*/
#if defined(Q_OS_WIN32)
enum System::userlevel System::userPermissions(void)
{
LPUSER_INFO_1 buf = NULL;
wchar_t userbuf[UNLEN];
DWORD usersize = UNLEN;
BOOL status;
enum userlevel result = ERR;
status = GetUserNameW(userbuf, &usersize);
if(!status)
return ERR;
if(NetUserGetInfo(NULL, userbuf, (DWORD)1, (LPBYTE*)&buf) == NERR_Success) {
switch(buf->usri1_priv) {
case USER_PRIV_GUEST:
result = GUEST;
break;
case USER_PRIV_USER:
result = USER;
break;
case USER_PRIV_ADMIN:
result = ADMIN;
break;
default:
result = ERR;
break;
}
}
if(buf != NULL)
NetApiBufferFree(buf);
return result;
}
/** @brief detects user permissions (only Windows at moment).
* @return a user readable string with the permission.
*/
QString System::userPermissionsString(void)
{
QString result;
int perm = userPermissions();
switch(perm) {
case GUEST:
result = tr("Guest");
break;
case ADMIN:
result = tr("Admin");
break;
case USER:
result = tr("User");
break;
default:
result = tr("Error");
break;
}
return result;
}
#endif
/** @brief detects current Username.
* @return string with Username.
*/
QString System::userName(void)
{
#if defined(Q_OS_WIN32)
wchar_t userbuf[UNLEN];
DWORD usersize = UNLEN;
if(GetUserNameW(userbuf, &usersize) == 0)
return QString();
return QString::fromWCharArray(userbuf);
#endif
#if defined(Q_OS_LINUX) || defined(Q_OS_MACX)
struct passwd *user;
user = getpwuid(geteuid());
return QString(user->pw_name);
#endif
}
/** @brief detects the OS Version
* @return String with OS Version.
*/
QString System::osVersionString(void)
{
QString result;
#if defined(Q_OS_WIN32)
SYSTEM_INFO sysinfo;
OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
GetSystemInfo(&sysinfo);
result = QString("Windows version %1.%2, ").arg(osvi.dwMajorVersion).arg(osvi.dwMinorVersion);
if(osvi.szCSDVersion)
result += QString("build %1 (%2)").arg(osvi.dwBuildNumber)
.arg(QString::fromWCharArray(osvi.szCSDVersion));
else
result += QString("build %1").arg(osvi.dwBuildNumber);
result += QString("<br/>CPU: %1, %2 processor(s)").arg(sysinfo.dwProcessorType)
.arg(sysinfo.dwNumberOfProcessors);
#endif
#if defined(Q_OS_LINUX) || defined(Q_OS_MACX)
struct utsname u;
int ret;
ret = uname(&u);
#if defined(Q_OS_MACX)
SInt32 cores;
Gestalt(gestaltCountOfCPUs, &cores);
#else
long cores = sysconf(_SC_NPROCESSORS_ONLN);
#endif
if(ret != -1) {
result = QString("CPU: %1, %2 processor(s)").arg(u.machine).arg(cores);
result += QString("<br/>System: %2<br/>Release: %3<br/>Version: %4")
.arg(u.sysname).arg(u.release).arg(u.version);
}
else {
result = QString("(Error when retrieving system information)");
}
#if defined(Q_OS_MACX)
SInt32 major;
SInt32 minor;
SInt32 bugfix;
Gestalt(gestaltSystemVersionMajor, &major);
Gestalt(gestaltSystemVersionMinor, &minor);
Gestalt(gestaltSystemVersionBugFix, &bugfix);
result += QString("<br/>OS X %1.%2.%3 ").arg(major).arg(minor).arg(bugfix);
// 1: 86k, 2: ppc, 10: i386
SInt32 arch;
Gestalt(gestaltSysArchitecture, &arch);
switch(arch) {
case 1:
result.append("(86k)");
break;
case 2:
result.append("(ppc)");
break;
case 10:
result.append("(x86)");
break;
default:
result.append("(unknown)");
break;
}
#endif
#endif
result += QString("<br/>Qt version %1").arg(qVersion());
return result;
}
QList<uint32_t> System::listUsbIds(void)
{
return listUsbDevices().keys();
}
/** @brief detect devices based on usb pid / vid.
* @return list with usb VID / PID values.
*/
QMultiMap<uint32_t, QString> System::listUsbDevices(void)
{
QMultiMap<uint32_t, QString> usbids;
// usb pid detection
LOG_INFO() << "Searching for USB devices";
#if defined(Q_OS_LINUX)
libusb_device **devs;
if(libusb_init(nullptr) != 0) {
LOG_ERROR() << "Initializing libusb-1 failed.";
return usbids;
}
if(libusb_get_device_list(nullptr, &devs) < 1) {
LOG_ERROR() << "Error getting device list.";
return usbids;
}
libusb_device *dev;
int i = 0;
while((dev = devs[i++]) != nullptr) {
QString name;
unsigned char buf[256];
uint32_t id;
struct libusb_device_descriptor descriptor;
if(libusb_get_device_descriptor(dev, &descriptor) == 0) {
id = descriptor.idVendor << 16 | descriptor.idProduct;
libusb_device_handle *dh;
if(libusb_open(dev, &dh) == 0) {
libusb_get_string_descriptor_ascii(dh, descriptor.iManufacturer, buf, 256);
name += QString::fromLatin1((char*)buf) + " ";
libusb_get_string_descriptor_ascii(dh, descriptor.iProduct, buf, 256);
name += QString::fromLatin1((char*)buf);
libusb_close(dh);
}
if(name.isEmpty())
name = tr("(no description available)");
if(id) {
usbids.insert(id, name);
LOG_INFO("USB: 0x%08x, %s", id, name.toLocal8Bit().data());
}
}
}
libusb_free_device_list(devs, 1);
libusb_exit(nullptr);
#endif
#if defined(Q_OS_MACX)
kern_return_t result = KERN_FAILURE;
CFMutableDictionaryRef usb_matching_dictionary;
io_iterator_t usb_iterator = IO_OBJECT_NULL;
usb_matching_dictionary = IOServiceMatching(kIOUSBDeviceClassName);
result = IOServiceGetMatchingServices(kIOMasterPortDefault, usb_matching_dictionary,
&usb_iterator);
if(result) {
LOG_ERROR() << "USB: IOKit: Could not get matching services.";
return usbids;
}
io_object_t usbCurrentObj;
while((usbCurrentObj = IOIteratorNext(usb_iterator))) {
uint32_t id;
QString name;
/* get vendor ID */
CFTypeRef vidref = NULL;
int vid = 0;
vidref = IORegistryEntryCreateCFProperty(usbCurrentObj, CFSTR("idVendor"),
kCFAllocatorDefault, 0);
CFNumberGetValue((CFNumberRef)vidref, kCFNumberIntType, &vid);
CFRelease(vidref);
/* get product ID */
CFTypeRef pidref = NULL;
int pid = 0;
pidref = IORegistryEntryCreateCFProperty(usbCurrentObj, CFSTR("idProduct"),
kCFAllocatorDefault, 0);
CFNumberGetValue((CFNumberRef)pidref, kCFNumberIntType, &pid);
CFRelease(pidref);
id = vid << 16 | pid;
/* get product vendor */
char vendor_buf[256];
CFIndex vendor_buflen = 256;
CFTypeRef vendor_name_ref = NULL;
vendor_name_ref = IORegistryEntrySearchCFProperty(usbCurrentObj,
kIOServicePlane, CFSTR("USB Vendor Name"),
kCFAllocatorDefault, 0);
if(vendor_name_ref != NULL) {
CFStringGetCString((CFStringRef)vendor_name_ref, vendor_buf, vendor_buflen,
kCFStringEncodingUTF8);
name += QString::fromUtf8(vendor_buf) + " ";
CFRelease(vendor_name_ref);
}
else {
name += QObject::tr("(unknown vendor name) ");
}
/* get product name */
char product_buf[256];
CFIndex product_buflen = 256;
CFTypeRef product_name_ref = NULL;
product_name_ref = IORegistryEntrySearchCFProperty(usbCurrentObj,
kIOServicePlane, CFSTR("USB Product Name"),
kCFAllocatorDefault, 0);
if(product_name_ref != NULL) {
CFStringGetCString((CFStringRef)product_name_ref, product_buf, product_buflen,
kCFStringEncodingUTF8);
name += QString::fromUtf8(product_buf);
CFRelease(product_name_ref);
}
else {
name += QObject::tr("(unknown product name)");
}
if(id) {
usbids.insertMulti(id, name);
LOG_INFO() << "USB:" << QString("0x%1").arg(id, 8, 16) << name;
}
}
IOObjectRelease(usb_iterator);
#endif
#if defined(Q_OS_WIN32)
HDEVINFO deviceInfo;
SP_DEVINFO_DATA infoData;
DWORD i;
// Iterate over all devices
// by doing it this way it's unneccessary to use GUIDs which might be not
// present in current MinGW. It also seemed to be more reliably than using
// a GUID.
// See KB259695 for an example.
deviceInfo = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
infoData.cbSize = sizeof(SP_DEVINFO_DATA);
for(i = 0; SetupDiEnumDeviceInfo(deviceInfo, i, &infoData); i++) {
DWORD data;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
QString description;
// get device descriptor first
// for some reason not doing so results in bad things (tm)
while(!SetupDiGetDeviceRegistryProperty(deviceInfo, &infoData,
SPDRP_DEVICEDESC, &data, (PBYTE)buffer, buffersize, &buffersize)) {
if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
if(buffer) free(buffer);
// double buffer size to avoid problems as per KB888609
buffer = (LPTSTR)malloc(buffersize * 2);
}
else {
break;
}
}
if(!buffer) {
LOG_WARNING() << "Got no device description"
<< "(SetupDiGetDeviceRegistryProperty), item" << i;
continue;
}
description = QString::fromWCharArray(buffer);
// now get the hardware id, which contains PID and VID.
while(!SetupDiGetDeviceRegistryProperty(deviceInfo, &infoData,
SPDRP_HARDWAREID, &data, (PBYTE)buffer, buffersize, &buffersize)) {
if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
if(buffer) free(buffer);
// double buffer size to avoid problems as per KB888609
buffer = (LPTSTR)malloc(buffersize * 2);
}
else {
break;
}
}
if(buffer) {
// convert buffer text to upper case to avoid depending on the case of
// the keys (W7 uses different casing than XP at least), in addition
// XP may use "Vid_" and "Pid_".
QString data = QString::fromWCharArray(buffer).toUpper();
QRegExp rex("USB\\\\VID_([0-9A-F]{4})&PID_([0-9A-F]{4}).*");
if(rex.indexIn(data) >= 0) {
uint32_t id;
id = rex.cap(1).toUInt(0, 16) << 16 | rex.cap(2).toUInt(0, 16);
usbids.insert(id, description);
LOG_INFO() << "USB:" << QString("0x%1").arg(id, 8, 16);
}
free(buffer);
}
}
SetupDiDestroyDeviceInfoList(deviceInfo);
#endif
return usbids;
}
/** @brief detects current system proxy
* @return QUrl with proxy or empty
*/
QUrl System::systemProxy(void)
{
#if defined(Q_OS_LINUX)
return QUrl(getenv("http_proxy"));
#elif defined(Q_OS_WIN32)
HKEY hk;
wchar_t proxyval[80];
DWORD buflen = 80;
long ret;
DWORD enable;
DWORD enalen = sizeof(DWORD);
ret = RegOpenKeyEx(HKEY_CURRENT_USER,
_TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"),
0, KEY_QUERY_VALUE, &hk);
if(ret != ERROR_SUCCESS) return QUrl("");
ret = RegQueryValueEx(hk, _TEXT("ProxyServer"), NULL, NULL, (LPBYTE)proxyval, &buflen);
if(ret != ERROR_SUCCESS) return QUrl("");
ret = RegQueryValueEx(hk, _TEXT("ProxyEnable"), NULL, NULL, (LPBYTE)&enable, &enalen);
if(ret != ERROR_SUCCESS) return QUrl("");
RegCloseKey(hk);
//LOG_INFO() << QString::fromWCharArray(proxyval) << QString("%1").arg(enable);
if(enable != 0)
return QUrl("http://" + QString::fromWCharArray(proxyval));
else
return QUrl("");
#elif defined(Q_OS_MACX)
CFDictionaryRef dictref;
CFStringRef stringref;
CFNumberRef numberref;
int enable = 0;
int port = 0;
unsigned int bufsize = 0;
char *buf;
QUrl proxy;
dictref = SCDynamicStoreCopyProxies(NULL);
if(dictref == NULL)
return proxy;
numberref = (CFNumberRef)CFDictionaryGetValue(dictref, kSCPropNetProxiesHTTPEnable);
if(numberref != NULL)
CFNumberGetValue(numberref, kCFNumberIntType, &enable);
if(enable == 1) {
// get proxy string
stringref = (CFStringRef)CFDictionaryGetValue(dictref, kSCPropNetProxiesHTTPProxy);
if(stringref != NULL) {
// get number of characters. CFStringGetLength uses UTF-16 code pairs
bufsize = CFStringGetLength(stringref) * 2 + 1;
buf = (char*)malloc(sizeof(char) * bufsize);
if(buf == NULL) {
LOG_ERROR() << "can't allocate memory for proxy string!";
CFRelease(dictref);
return QUrl("");
}
CFStringGetCString(stringref, buf, bufsize, kCFStringEncodingUTF16);
numberref = (CFNumberRef)CFDictionaryGetValue(dictref, kSCPropNetProxiesHTTPPort);
if(numberref != NULL)
CFNumberGetValue(numberref, kCFNumberIntType, &port);
proxy.setScheme("http");
proxy.setHost(QString::fromUtf16((unsigned short*)buf));
proxy.setPort(port);
free(buf);
}
}
CFRelease(dictref);
return proxy;
#else
return QUrl("");
#endif
}

View file

@ -0,0 +1,53 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef SYSTEM_H
#define SYSTEM_H
#include <QtCore/QObject>
#include <inttypes.h>
#include <QMultiMap>
#include <QString>
#include <QUrl>
class System : public QObject
{
public:
System() {}
#if defined(Q_OS_WIN32)
enum userlevel { ERR, GUEST, USER, ADMIN };
static enum userlevel userPermissions(void);
static QString userPermissionsString(void);
#endif
static QString userName(void);
static QString osVersionString(void);
static QList<uint32_t> listUsbIds(void);
static QMultiMap<uint32_t, QString> listUsbDevices(void);
static QUrl systemProxy(void);
};
#endif

View file

@ -0,0 +1,300 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "talkfile.h"
#include "rbsettings.h"
#include "Logger.h"
TalkFileCreator::TalkFileCreator(QObject* parent): QObject(parent)
{
}
//! \brief Creates Talkfiles.
//!
//! \param logger A pointer to a Loggerobject
bool TalkFileCreator::createTalkFiles()
{
m_abort = false;
QString errStr;
emit logItem(tr("Starting Talk file generation for folder %1")
.arg(m_dir), LOGINFO);
emit logProgress(0,0);
QCoreApplication::processEvents();
// read in Maps of paths - file/dirnames
emit logItem(tr("Reading Filelist..."),LOGINFO);
if(createTalkList(m_mountpoint + "/" + m_dir) == false)
{
emit logItem(tr("Talk file creation aborted"),LOGERROR);
doAbort();
return false;
}
QCoreApplication::processEvents();
// generate entries
TalkGenerator generator(this);
// no string corrections yet: do not set language for TalkGenerator.
connect(&generator, &TalkGenerator::done, this, &TalkFileCreator::done);
connect(&generator, &TalkGenerator::logItem, this, &TalkFileCreator::logItem);
connect(&generator, &TalkGenerator::logProgress, this, &TalkFileCreator::logProgress);
connect(this, &TalkFileCreator::aborted, &generator, &TalkGenerator::abort);
if(generator.process(&m_talkList) == TalkGenerator::eERROR)
{
doAbort();
return false;
}
// Copying talk files
emit logItem(tr("Copying Talkfiles..."),LOGINFO);
if(copyTalkFiles(&errStr) == false)
{
emit logItem(errStr,LOGERROR);
doAbort();
return false;
}
// Deleting left overs
if( !cleanup())
return false;
emit logItem(tr("Finished creating Talk files"),LOGOK);
emit logProgress(1,1);
emit done(false);
return true;
}
//! \brief Strips everything after and including the last dot in a string. If there is no dot, nothing is changed
//!
//! \param filename The filename from which to strip the Extension
//! \returns the modified string
QString TalkFileCreator::stripExtension(QString filename)
{
// only strip extension if there is a dot in the filename and there are chars before the dot
if(filename.lastIndexOf(".") != -1 && filename.left(filename.lastIndexOf(".")) != "")
return filename.left(filename.lastIndexOf("."));
else
return filename;
}
//! \brief Does needed Tasks when we need to abort. Cleans up Files. Stops the Logger, Stops TTS and Encoder
//!
void TalkFileCreator::doAbort()
{
cleanup();
emit logProgress(0,1);
emit done(true);
}
//! \brief creates a list of what to generate
//!
//! \param startDir The directory from which to start scanning
bool TalkFileCreator::createTalkList(QDir startDir)
{
LOG_INFO() << "generating list of files" << startDir;
m_talkList.clear();
// create Iterator
QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags;
if(m_recursive)
flags = QDirIterator::Subdirectories;
QDirIterator it(startDir,flags);
//create temp directory
QDir tempDir(QDir::tempPath()+ "/talkfiles/");
if(!tempDir.exists())
tempDir.mkpath(QDir::tempPath()+ "/talkfiles/");
// read in Maps of paths - file/dirnames
while (it.hasNext())
{
it.next();
if(m_abort)
{
return false;
}
QFileInfo fileInf = it.fileInfo();
// its a dir
if(fileInf.isDir())
{
QDir dir = fileInf.dir();
// insert into List
if(!dir.dirName().isEmpty() && m_talkFolders)
{
// check if we should ignore it
if(m_generateOnlyNew && QFileInfo::exists(dir.path() + "/_dirname.talk"))
{
continue;
}
//generate entry
TalkGenerator::TalkEntry entry;
entry.toSpeak = dir.dirName();
entry.wavfilename = QDir::tempPath() + "/talkfiles/"
+ QCryptographicHash::hash(entry.toSpeak.toUtf8(),
QCryptographicHash::Md5).toHex() + ".wav";
entry.talkfilename = QDir::tempPath() + "/talkfiles/"
+ QCryptographicHash::hash(entry.toSpeak.toUtf8(),
QCryptographicHash::Md5).toHex() + ".talk";
entry.target = dir.path() + "/_dirname.talk";
entry.voiced = false;
entry.encoded = false;
LOG_INFO() << "toSpeak:" << entry.toSpeak
<< "target:" << entry.target
<< "intermediates:" << entry.wavfilename << entry.talkfilename;
m_talkList.append(entry);
}
}
else // its a File
{
// insert into List
if( !fileInf.fileName().isEmpty() && !fileInf.fileName().endsWith(".talk") && m_talkFiles)
{
//test if we should ignore this file
bool match = false;
for(int i=0; i < m_ignoreFiles.size();i++)
{
QRegExp rx(m_ignoreFiles[i].trimmed());
rx.setPatternSyntax(QRegExp::Wildcard);
if(rx.exactMatch(fileInf.fileName()))
match = true;
}
if(match)
continue;
// check if we should ignore it
if(m_generateOnlyNew && QFileInfo::exists(fileInf.path() + "/" + fileInf.fileName() + ".talk"))
{
continue;
}
//generate entry
TalkGenerator::TalkEntry entry;
if(m_stripExtensions)
entry.toSpeak = stripExtension(fileInf.fileName());
else
entry.toSpeak = fileInf.fileName();
entry.wavfilename = QDir::tempPath() + "/talkfiles/"
+ QCryptographicHash::hash(entry.toSpeak.toUtf8(),
QCryptographicHash::Md5).toHex() + ".wav";
entry.talkfilename = QDir::tempPath() + "/talkfiles/"
+ QCryptographicHash::hash(entry.toSpeak.toUtf8(),
QCryptographicHash::Md5).toHex() + ".talk";
entry.target = fileInf.path() + "/" + fileInf.fileName() + ".talk";
entry.voiced = false;
entry.encoded = false;
LOG_INFO() << "toSpeak:" << entry.toSpeak
<< "target:" << entry.target
<< "intermediates:"
<< entry.wavfilename << entry.talkfilename;
m_talkList.append(entry);
}
}
QCoreApplication::processEvents();
}
LOG_INFO() << "list created, entries:" << m_talkList.size();
return true;
}
//! \brief copys Talkfiles from the temp dir to the target. Progress and installlog is handled inside
//!
//! \param errString Pointer to a QString where the error cause is written.
//! \returns true on success, false on error or user abort
bool TalkFileCreator::copyTalkFiles(QString* errString)
{
int progressMax = m_talkList.size();
int m_progress = 0;
emit logProgress(m_progress,progressMax);
QSettings installlog(m_mountpoint + "/.rockbox/rbutil.log", QSettings::IniFormat, nullptr);
installlog.beginGroup("talkfiles");
for(int i=0; i < m_talkList.size(); i++)
{
if(m_abort)
{
*errString = tr("File copy aborted");
return false;
}
// skip not encoded files
if(m_talkList[i].encoded == false)
{
emit logProgress(++m_progress,progressMax);
continue; // this file was skipped in one of the previous steps
}
// remove target if it exists, and if we should overwrite it
if(QFile::exists(m_talkList[i].target))
QFile::remove(m_talkList[i].target);
// copying
LOG_INFO() << "copying" << m_talkList[i].talkfilename
<< "to" << m_talkList[i].target;
if(!QFile::copy(m_talkList[i].talkfilename,m_talkList[i].target))
{
*errString = tr("Copying of %1 to %2 failed").arg(m_talkList[i].talkfilename).arg(m_talkList[i].target);
return false;
}
// add to installlog
QString now = QDate::currentDate().toString("yyyyMMdd");
installlog.setValue(m_talkList[i].target.remove(0,m_mountpoint.length()),now);
emit logProgress(++m_progress,progressMax);
QCoreApplication::processEvents();
}
installlog.endGroup();
installlog.sync();
return true;
}
//! \brief Cleans up Files potentially left in the temp dir
//!
bool TalkFileCreator::cleanup()
{
emit logItem(tr("Cleaning up..."),LOGINFO);
for(int i=0; i < m_talkList.size(); i++)
{
if(QFile::exists(m_talkList[i].wavfilename))
QFile::remove(m_talkList[i].wavfilename);
if(QFile::exists(m_talkList[i].talkfilename))
QFile::remove(m_talkList[i].talkfilename);
QCoreApplication::processEvents();
}
emit logItem(tr("Finished"),LOGINFO);
return true;
}
//! \brief slot, which is connected to the abort of the Logger. Sets a flag, so Creating Talkfiles ends at the next possible position
//!
void TalkFileCreator::abort()
{
m_abort = true;
emit aborted();
}

View file

@ -0,0 +1,83 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TALKFILE_H
#define TALKFILE_H
#include <QtCore>
#include "progressloggerinterface.h"
#include "talkgenerator.h"
class TalkFileCreator :public QObject
{
Q_OBJECT
public:
TalkFileCreator(QObject* parent);
bool createTalkFiles();
void setDir(QString dir) {m_dir = dir;}
void setMountPoint(QString mountpoint) {m_mountpoint = mountpoint;}
void setGenerateOnlyNew(bool ov) {m_generateOnlyNew = ov;}
void setRecursive(bool ov) {m_recursive = ov;}
void setStripExtensions(bool ov) {m_stripExtensions = ov;}
void setTalkFolders(bool ov) {m_talkFolders = ov;}
void setTalkFiles(bool ov) {m_talkFiles = ov;}
void setIgnoreFiles(QStringList wildcards) {m_ignoreFiles = wildcards;}
public slots:
void abort();
signals:
void done(bool);
void aborted();
void logItem(QString, int); //! set logger item
void logProgress(int, int); //! set progress bar.
private:
bool cleanup();
QString stripExtension(QString filename);
void doAbort();
void resetProgress(int max);
bool copyTalkFiles(QString* errString);
bool createTalkList(QDir startDir);
QString m_dir;
QString m_mountpoint;
bool m_generateOnlyNew;
bool m_recursive;
bool m_stripExtensions;
bool m_talkFolders;
bool m_talkFiles;
QStringList m_ignoreFiles;
bool m_abort;
QList<TalkGenerator::TalkEntry> m_talkList;
};
#endif

View file

@ -0,0 +1,337 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "talkgenerator.h"
#include "rbsettings.h"
#include "playerbuildinfo.h"
#include "wavtrim.h"
#include "Logger.h"
TalkGenerator::TalkGenerator(QObject* parent): QObject(parent)
{
}
//! \brief Creates Talkfiles.
//!
TalkGenerator::Status TalkGenerator::process(QList<TalkEntry>* list,int wavtrimth)
{
m_abort = false;
QString errStr;
bool warnings = false;
//tts
emit logItem(tr("Starting TTS Engine"), LOGINFO);
m_tts = TTSBase::getTTS(this, RbSettings::value(RbSettings::Tts).toString());
if(!m_tts)
{
LOG_ERROR() << "getting the TTS object failed!";
emit logItem(tr("Init of TTS engine failed"), LOGERROR);
emit done(true);
return eERROR;
}
if(!m_tts->start(&errStr))
{
emit logItem(errStr.trimmed(),LOGERROR);
emit logItem(tr("Init of TTS engine failed"), LOGERROR);
emit done(true);
return eERROR;
}
QCoreApplication::processEvents();
// Encoder
emit logItem(tr("Starting Encoder Engine"),LOGINFO);
m_enc = EncoderBase::getEncoder(this, PlayerBuildInfo::instance()->value(
PlayerBuildInfo::Encoder).toString());
if(!m_enc->start())
{
emit logItem(tr("Init of Encoder engine failed"),LOGERROR);
emit done(true);
m_tts->stop();
return eERROR;
}
QCoreApplication::processEvents();
emit logProgress(0,0);
// Voice entries
emit logItem(tr("Voicing entries..."),LOGINFO);
Status voiceStatus= voiceList(list,wavtrimth);
if(voiceStatus == eERROR)
{
m_tts->stop();
m_enc->stop();
emit done(true);
return eERROR;
}
else if( voiceStatus == eWARNING)
warnings = true;
QCoreApplication::processEvents();
// Encoding Entries
emit logItem(tr("Encoding files..."),LOGINFO);
Status encoderStatus = encodeList(list);
if( encoderStatus == eERROR)
{
m_tts->stop();
m_enc->stop();
emit done(true);
return eERROR;
}
else if( voiceStatus == eWARNING)
warnings = true;
QCoreApplication::processEvents();
m_tts->stop();
m_enc->stop();
emit logProgress(1,1);
if(warnings)
return eWARNING;
return eOK;
}
//! \brief Voices a List of string
//!
TalkGenerator::Status TalkGenerator::voiceList(QList<TalkEntry>* list,int wavtrimth)
{
int progressMax = list->size();
int m_progress = 0;
emit logProgress(m_progress,progressMax);
QStringList errors;
QStringList duplicates;
bool warnings = false;
for(int i=0; i < list->size(); i++)
{
if(m_abort)
{
emit logItem(tr("Voicing aborted"), LOGERROR);
return eERROR;
}
// skip duplicated wav entrys
if(!duplicates.contains(list->at(i).wavfilename))
duplicates.append(list->at(i).wavfilename);
else
{
LOG_INFO() << "duplicate skipped";
(*list)[i].voiced = true;
emit logProgress(++m_progress,progressMax);
continue;
}
// skip already voiced entrys
if(list->at(i).voiced == true)
{
emit logProgress(++m_progress,progressMax);
continue;
}
// skip entry whith empty text
if(list->at(i).toSpeak == "")
{
emit logProgress(++m_progress,progressMax);
continue;
}
// voice entry
QString error;
LOG_INFO() << "voicing: " << list->at(i).toSpeak
<< "to" << list->at(i).wavfilename;
TTSStatus status = m_tts->voice(list->at(i).toSpeak,
list->at(i).wavfilename, &error);
if(status == Warning)
{
warnings = true;
emit logItem(tr("Voicing of %1 failed: %2").arg(list->at(i).toSpeak).arg(error),
LOGWARNING);
}
else if (status == FatalError)
{
emit logItem(tr("Voicing of %1 failed: %2").arg(list->at(i).toSpeak).arg(error),
LOGERROR);
return eERROR;
}
else
(*list)[i].voiced = true;
// wavtrim if needed
if(wavtrimth != -1)
{
char buffer[255];
if(wavtrim(list->at(i).wavfilename.toLocal8Bit().data(),
wavtrimth, buffer, 255))
{
LOG_ERROR() << "wavtrim returned error on"
<< list->at(i).wavfilename;
return eERROR;
}
}
emit logProgress(++m_progress,progressMax);
QCoreApplication::processEvents();
}
if(warnings)
return eWARNING;
else
return eOK;
}
//! \brief Encodes a List of strings
//!
TalkGenerator::Status TalkGenerator::encodeList(QList<TalkEntry>* list)
{
QStringList duplicates;
int progressMax = list->size();
int m_progress = 0;
emit logProgress(m_progress,progressMax);
for(int i=0; i < list->size(); i++)
{
if(m_abort)
{
emit logItem(tr("Encoding aborted"), LOGERROR);
return eERROR;
}
//skip non-voiced entrys
if(list->at(i).voiced == false)
{
LOG_WARNING() << "non voiced entry detected:"
<< list->at(i).toSpeak;
emit logProgress(++m_progress,progressMax);
continue;
}
//skip duplicates
if(!duplicates.contains(list->at(i).talkfilename))
duplicates.append(list->at(i).talkfilename);
else
{
LOG_INFO() << "duplicate skipped";
(*list)[i].encoded = true;
emit logProgress(++m_progress,progressMax);
continue;
}
//encode entry
LOG_INFO() << "encoding " << list->at(i).wavfilename
<< "to" << list->at(i).talkfilename;
if(!m_enc->encode(list->at(i).wavfilename,list->at(i).talkfilename))
{
emit logItem(tr("Encoding of %1 failed").arg(
QFileInfo(list->at(i).wavfilename).baseName()), LOGERROR);
return eERROR;
}
(*list)[i].encoded = true;
emit logProgress(++m_progress,progressMax);
QCoreApplication::processEvents();
}
return eOK;
}
//! \brief slot, which is connected to the abort of the Logger.
//Sets a flag, so Creating Talkfiles ends at the next possible position
//!
void TalkGenerator::abort()
{
m_abort = true;
}
QString TalkGenerator::correctString(QString s)
{
QString corrected = s;
int i = 0;
int max = m_corrections.size();
while(i < max) {
corrected = corrected.replace(QRegExp(m_corrections.at(i).search,
m_corrections.at(i).modifier.contains("i")
? Qt::CaseInsensitive : Qt::CaseSensitive),
m_corrections.at(i).replace);
i++;
}
if(corrected != s)
LOG_INFO() << "corrected string" << s << "to" << corrected;
return corrected;
m_abort = true;
}
void TalkGenerator::setLang(QString name)
{
m_lang = name;
// re-initialize corrections list
m_corrections.clear();
QFile correctionsFile(":/builtin/voice-corrections.txt");
correctionsFile.open(QIODevice::ReadOnly);
QString engine = RbSettings::value(RbSettings::Tts).toString();
TTSBase* tts = TTSBase::getTTS(this,RbSettings::value(RbSettings::Tts).toString());
if(!tts)
{
LOG_ERROR() << "getting the TTS object failed!";
return;
}
QString vendor = tts->voiceVendor();
delete tts;
if(m_lang.isEmpty())
m_lang = "english";
LOG_INFO() << "building string corrections list for"
<< m_lang << engine << vendor;
QTextStream stream(&correctionsFile);
while(!stream.atEnd()) {
QString line = stream.readLine();
if(line.startsWith(" ") || line.length() < 10)
continue;
// separator is first character
QString separator = line.at(0);
line.remove(0, 1);
QStringList items = line.split(separator);
// we need to have at least 6 separate entries.
if(items.size() < 6)
continue;
QRegExp re_lang(items.at(0));
QRegExp re_engine(items.at(1));
QRegExp re_vendor(items.at(2));
if(!re_lang.exactMatch(m_lang)) {
continue;
}
if(!re_vendor.exactMatch(vendor)) {
continue;
}
if(!re_engine.exactMatch(engine)) {
continue;
}
struct CorrectionItems co;
co.search = items.at(3);
co.replace = items.at(4);
// Qt uses backslash for back references, Perl uses dollar sign.
co.replace.replace(QRegExp("\\$(\\d+)"), "\\\\1");
co.modifier = items.at(5);
m_corrections.append(co);
}
correctionsFile.close();
}

View file

@ -0,0 +1,91 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TALKGENERATOR_H
#define TALKGENERATOR_H
#include <QtCore>
#include "progressloggerinterface.h"
#include "encoderbase.h"
#include "ttsbase.h"
//! \brief Talk generator, generates .wav and .talk files out of a list.
class TalkGenerator :public QObject
{
Q_OBJECT
public:
enum Status
{
eOK,
eWARNING,
eERROR
};
struct TalkEntry
{
QString toSpeak;
QString wavfilename;
QString talkfilename;
QString target;
bool voiced;
bool encoded;
};
TalkGenerator(QObject* parent);
Status process(QList<TalkEntry>* list,int wavtrimth = -1);
QString correctString(QString s);
public slots:
void abort();
void setLang(QString name);
signals:
void done(bool);
void logItem(QString, int); //! set logger item
void logProgress(int, int); //! set progress bar.
private:
Status voiceList(QList<TalkEntry>* list,int wavetrimth);
Status encodeList(QList<TalkEntry>* list);
TTSBase* m_tts;
EncoderBase* m_enc;
QString m_lang;
struct CorrectionItems
{
QString search;
QString replace;
QString modifier;
};
QList<struct CorrectionItems> m_corrections;
bool m_abort;
};
#endif

View file

@ -0,0 +1,122 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "ttsbase.h"
#include "ttsfestival.h"
#include "ttssapi.h"
#include "ttssapi4.h"
#include "ttsmssp.h"
#include "ttsexes.h"
#include "ttsespeak.h"
#include "ttsespeakng.h"
#include "ttsflite.h"
#include "ttsmimic.h"
#include "ttsswift.h"
#if defined(Q_OS_MACX)
#include "ttscarbon.h"
#endif
// list of tts names and identifiers
QMap<QString,QString> TTSBase::ttsList;
TTSBase::TTSBase(QObject* parent): EncTtsSettingInterface(parent)
{
}
// static functions
void TTSBase::initTTSList()
{
#if !defined(Q_OS_WIN)
ttsList["espeak"] = tr("Espeak TTS Engine");
ttsList["espeakng"] = tr("Espeak-ng TTS Engine");
ttsList["mimic"] = tr("Mimic TTS Engine");
#endif
ttsList["flite"] = tr("Flite TTS Engine");
ttsList["swift"] = tr("Swift TTS Engine");
#if defined(Q_OS_WIN)
#if 0 /* SAPI4 has been disabled since long. Keep support for now. */
ttsList["sapi4"] = tr("SAPI4 TTS Engine");
#endif
ttsList["sapi"] = tr("SAPI5 TTS Engine");
ttsList["mssp"] = tr("MS Speech Platform");
#endif
#if defined(Q_OS_LINUX)
ttsList["festival"] = tr("Festival TTS Engine");
#endif
#if defined(Q_OS_MACX)
ttsList["carbon"] = tr("OS X System Engine");
#endif
}
// function to get a specific encoder
TTSBase* TTSBase::getTTS(QObject* parent,QString ttsName)
{
TTSBase* tts = nullptr;
#if defined(Q_OS_WIN)
if(ttsName == "sapi")
tts = new TTSSapi(parent);
else if (ttsName == "sapi4")
tts = new TTSSapi4(parent);
else if (ttsName == "mssp")
tts = new TTSMssp(parent);
else
#elif defined(Q_OS_LINUX)
if (ttsName == "festival")
tts = new TTSFestival(parent);
else
#elif defined(Q_OS_MACX)
if(ttsName == "carbon")
tts = new TTSCarbon(parent);
else
#endif
if(ttsName == "espeak")
tts = new TTSEspeak(parent);
else if(ttsName == "espeakng")
tts = new TTSEspeakNG(parent);
else if(ttsName == "mimic")
tts = new TTSMimic(parent);
else if(ttsName == "flite")
tts = new TTSFlite(parent);
else if(ttsName == "swift")
tts = new TTSSwift(parent);
else if(ttsName == "user")
tts = new TTSExes(parent);
return tts;
}
// get the list of encoders, nice names
QStringList TTSBase::getTTSList()
{
// init list if its empty
if(ttsList.count() == 0)
initTTSList();
return ttsList.keys();
}
// get nice name of a specific tts
QString TTSBase::getTTSName(QString tts)
{
if(ttsList.isEmpty())
initTTSList();
return ttsList.value(tts);
}

View file

@ -0,0 +1,70 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TTSBASE_H
#define TTSBASE_H
#include <QtCore>
#include "encttssettings.h"
enum TTSStatus{ FatalError, NoError, Warning };
class TTSBase : public EncTtsSettingInterface
{
Q_OBJECT
public:
enum Capability { None = 0, RunInParallel = 1, CanSpeak = 2 };
Q_DECLARE_FLAGS(Capabilities, Capability)
TTSBase(QObject *parent);
//! Child class should generate a clip
virtual TTSStatus voice(QString text,QString wavfile, QString* errStr) =0;
//! Child class should do startup
virtual bool start(QString *errStr) =0;
//! child class should stop
virtual bool stop() =0;
virtual QString voiceVendor(void) = 0;
// configuration
//! Child class should return true, when configuration is good
virtual bool configOk()=0;
//! Child class should generate and insertSetting(..) its settings
virtual void generateSettings() = 0;
//! Chlid class should commit the Settings to permanent storage
virtual void saveSettings() = 0;
virtual Capabilities capabilities() = 0;
// static functions
static TTSBase* getTTS(QObject* parent,QString ttsname);
static QStringList getTTSList();
static QString getTTSName(QString tts);
private:
//inits the tts List
static void initTTSList();
protected:
static QMap<QString,QString> ttsList;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(TTSBase::Capabilities)
#endif

View file

@ -0,0 +1,443 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2010 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "ttsbase.h"
#include "ttscarbon.h"
#include "encttssettings.h"
#include "rbsettings.h"
#include <CoreFoundation/CoreFoundation.h>
#include <ApplicationServices/ApplicationServices.h>
#include <Carbon/Carbon.h>
#include <unistd.h>
#include <sys/stat.h>
#include <inttypes.h>
#include "Logger.h"
TTSCarbon::TTSCarbon(QObject* parent) : TTSBase(parent)
{
}
TTSBase::Capabilities TTSCarbon::capabilities()
{
return TTSBase::CanSpeak;
}
bool TTSCarbon::configOk()
{
return true;
}
bool TTSCarbon::start(QString *errStr)
{
(void)errStr;
VoiceSpec vspec;
VoiceSpec* vspecref = NULL;
VoiceDescription vdesc;
OSErr error;
QString selectedVoice
= RbSettings::subValue("carbon", RbSettings::TtsVoice).toString();
SInt16 numVoices;
SInt16 voiceIndex;
error = CountVoices(&numVoices);
for(voiceIndex = 1; voiceIndex < numVoices; ++voiceIndex) {
error = GetIndVoice(voiceIndex, &vspec);
error = GetVoiceDescription(&vspec, &vdesc, sizeof(vdesc));
// name is pascal string, i.e. the first byte is the length.
QString name = QString::fromLocal8Bit((const char*)&vdesc.name[1],
vdesc.name[0]);
if(name == selectedVoice) {
vspecref = &vspec;
if(vdesc.script != -1)
m_voiceScript = (CFStringBuiltInEncodings)vdesc.script;
else
m_voiceScript = (CFStringBuiltInEncodings)vdesc.reserved[0];
break;
}
}
if(voiceIndex == numVoices) {
// voice not found. Add user notification here and proceed with
// system default voice.
LOG_WARNING() << "Selected voice not found, using system default!";
GetVoiceDescription(&vspec, &vdesc, sizeof(vdesc));
if(vdesc.script != -1)
m_voiceScript = (CFStringBuiltInEncodings)vdesc.script;
else
m_voiceScript = (CFStringBuiltInEncodings)vdesc.reserved[0];
}
error = NewSpeechChannel(vspecref, &m_channel);
//SetSpeechInfo(channel, soSpeechDoneCallBack, speechDone);
Fixed rate = (Fixed)(0x10000 * RbSettings::subValue("carbon",
RbSettings::TtsSpeed).toInt());
if(rate != 0)
SetSpeechRate(m_channel, rate);
Fixed pitch = (Fixed)(0x10000 * RbSettings::subValue("carbon",
RbSettings::TtsPitch).toInt());
if(pitch != 0)
SetSpeechPitch(m_channel, pitch);
return (error == 0) ? true : false;
}
bool TTSCarbon::stop(void)
{
DisposeSpeechChannel(m_channel);
return true;
}
void TTSCarbon::generateSettings(void)
{
QStringList voiceNames;
QString systemVoice;
SInt16 numVoices;
OSErr error;
VoiceSpec vspec;
VoiceDescription vdesc;
// get system voice
error = GetVoiceDescription(NULL, &vdesc, sizeof(vdesc));
systemVoice
= QString::fromLocal8Bit((const char*)&vdesc.name[1], vdesc.name[0]);
// get list of all voices
CountVoices(&numVoices);
for(SInt16 i = 1; i < numVoices; ++i) {
error = GetIndVoice(i, &vspec);
error = GetVoiceDescription(&vspec, &vdesc, sizeof(vdesc));
// name is pascal string, i.e. the first byte is the length.
QString name
= QString::fromLocal8Bit((const char*)&vdesc.name[1], vdesc.name[0]);
voiceNames.append(name.trimmed());
}
// voice
EncTtsSetting* setting;
QString voice
= RbSettings::subValue("carbon", RbSettings::TtsVoice).toString();
if(voice.isEmpty())
voice = systemVoice;
setting = new EncTtsSetting(this, EncTtsSetting::eSTRINGLIST,
tr("Voice:"), voice, voiceNames, EncTtsSetting::eNOBTN);
insertSetting(ConfigVoice, setting);
// speed
int speed = RbSettings::subValue("carbon", RbSettings::TtsSpeed).toInt();
setting = new EncTtsSetting(this, EncTtsSetting::eINT,
tr("Speed (words/min):"), speed, 80, 500,
EncTtsSetting::eNOBTN);
insertSetting(ConfigSpeed, setting);
// pitch
int pitch = RbSettings::subValue("carbon", RbSettings::TtsPitch).toInt();
setting = new EncTtsSetting(this, EncTtsSetting::eINT,
tr("Pitch (0 for default):"), pitch, 0, 65,
EncTtsSetting::eNOBTN);
insertSetting(ConfigPitch, setting);
}
void TTSCarbon::saveSettings(void)
{
// save settings in user config
RbSettings::setSubValue("carbon", RbSettings::TtsVoice,
getSetting(ConfigVoice)->current().toString());
RbSettings::setSubValue("carbon", RbSettings::TtsSpeed,
getSetting(ConfigSpeed)->current().toInt());
RbSettings::setSubValue("carbon", RbSettings::TtsPitch,
getSetting(ConfigPitch)->current().toInt());
RbSettings::sync();
}
/** @brief create wav file from text using the selected TTS voice.
*/
TTSStatus TTSCarbon::voice(QString text, QString wavfile, QString* errStr)
{
TTSStatus status = NoError;
OSErr error;
char* tmpfile = NULL;
if(!wavfile.isEmpty()) {
QString aifffile = wavfile + ".aiff";
// FIXME: find out why we need to do this.
// Create a local copy of the temporary file filename.
// Not doing so causes weird issues (path contains trailing spaces)
unsigned int len = aifffile.size() + 1;
tmpfile = (char*)malloc(len * sizeof(char));
strncpy(tmpfile, aifffile.toLocal8Bit().constData(), len);
CFStringRef tmpfileref = CFStringCreateWithCString(kCFAllocatorDefault,
tmpfile, kCFStringEncodingUTF8);
CFURLRef urlref = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
tmpfileref, kCFURLPOSIXPathStyle, false);
SetSpeechInfo(m_channel, soOutputToFileWithCFURL, urlref);
}
// speak it.
// Convert the string to the encoding requested by the voice. Do this
// via CFString, as this allows to directly use the destination encoding
// as CFString uses the same values as the voice.
// allocate enough space to allow storing the string in a 2 byte encoding
unsigned int textlen = 2 * text.length() + 1;
char* textbuf = (char*)calloc(textlen, sizeof(char));
char* utf8data = (char*)text.toUtf8().constData();
int utf8bytes = text.toUtf8().size();
CFStringRef cfstring = CFStringCreateWithBytes(kCFAllocatorDefault,
(UInt8*)utf8data, utf8bytes,
kCFStringEncodingUTF8, (Boolean)false);
CFIndex usedBuf = 0;
CFRange range;
range.location = 0; // character in string to start.
range.length = text.length(); // number of _characters_ in string
// FIXME: check if converting between encodings was lossless.
CFStringGetBytes(cfstring, range, m_voiceScript, ' ',
false, (UInt8*)textbuf, textlen, &usedBuf);
error = SpeakText(m_channel, textbuf, (unsigned long)usedBuf);
while(SpeechBusy()) {
// FIXME: add small delay here to make calls less frequent
QCoreApplication::processEvents();
}
if(error != 0) {
*errStr = tr("Could not voice string");
status = FatalError;
}
free(textbuf);
CFRelease(cfstring);
if(!wavfile.isEmpty()) {
// convert the temporary aiff file to wav
if(status == NoError
&& convertAiffToWav(tmpfile, wavfile.toLocal8Bit().constData()) != 0) {
*errStr = tr("Could not convert intermediate file");
status = FatalError;
}
// remove temporary aiff file
unlink(tmpfile);
free(tmpfile);
}
return status;
}
unsigned long TTSCarbon::be2u32(unsigned char* buf)
{
return (buf[0]&0xff)<<24 | (buf[1]&0xff)<<16 | (buf[2]&0xff)<<8 | (buf[3]&0xff);
}
unsigned long TTSCarbon::be2u16(unsigned char* buf)
{
return (buf[1]&0xff) | (buf[0]&0xff)<<8;
}
unsigned char* TTSCarbon::u32tobuf(unsigned char* buf, uint32_t val)
{
buf[0] = val & 0xff;
buf[1] = (val>> 8) & 0xff;
buf[2] = (val>>16) & 0xff;
buf[3] = (val>>24) & 0xff;
return buf;
}
unsigned char* TTSCarbon::u16tobuf(unsigned char* buf, uint16_t val)
{
buf[0] = val & 0xff;
buf[1] = (val>> 8) & 0xff;
return buf;
}
/** @brief convert 80 bit extended ("long double") to int.
* This is simplified to handle the usual audio sample rates. Everything else
* might break. If the value isn't supported it will return 0.
* Conversion taken from Rockbox aiff codec.
*/
unsigned int TTSCarbon::extended2int(unsigned char* buf)
{
unsigned int result = 0;
/* value negative? */
if(buf[0] & 0x80)
return 0;
/* check exponent. Int can handle up to 2^31. */
int exponent = buf[0] << 8 | buf[1];
if(exponent < 0x4000 || exponent > (0x4000 + 30))
return 0;
result = ((buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5]) + 1;
result >>= (16 + 14 - buf[1]);
return result;
}
/** @brief Convert aiff file to wav. Returns 0 on success.
*/
int TTSCarbon::convertAiffToWav(const char* aiff, const char* wav)
{
struct commchunk {
unsigned long chunksize;
unsigned short channels;
unsigned long frames;
unsigned short size;
int rate;
};
struct ssndchunk {
unsigned long chunksize;
unsigned long offset;
unsigned long blocksize;
};
FILE* in;
FILE* out;
unsigned char obuf[4];
unsigned char* buf;
/* minimum file size for a valid aiff file is 46 bytes:
* - FORM chunk: 12 bytes
* - COMM chunk: 18 bytes
* - SSND chunk: 16 bytes (with no actual data)
*/
struct stat filestat;
stat(aiff, &filestat);
if(filestat.st_size < 46)
return -1;
/* read input file into memory */
buf = (unsigned char*)malloc(filestat.st_size * sizeof(unsigned char));
if(!buf) /* error out if malloc() failed */
return -1;
in = fopen(aiff, "rb");
if(fread(buf, 1, filestat.st_size, in) < filestat.st_size) {
printf("could not read file: not enought bytes read\n");
fclose(in);
free(buf);
return -1;
}
fclose(in);
/* check input file format */
if(memcmp(buf, "FORM", 4) | memcmp(&buf[8], "AIFF", 4)) {
printf("No valid AIFF header found.\n");
free(buf);
return -1;
}
/* read COMM chunk */
unsigned char* commstart = &buf[12];
struct commchunk comm;
if(memcmp(commstart, "COMM", 4)) {
printf("COMM chunk not at beginning.\n");
free(buf);
return -1;
}
comm.chunksize = be2u32(&commstart[4]);
comm.channels = be2u16(&commstart[8]);
comm.frames = be2u32(&commstart[10]);
comm.size = be2u16(&commstart[14]);
comm.rate = extended2int(&commstart[16]);
/* find SSND as next chunk */
unsigned char* ssndstart = commstart + 8 + comm.chunksize;
while(memcmp(ssndstart, "SSND", 4) && ssndstart < (buf + filestat.st_size)) {
printf("Skipping chunk.\n");
ssndstart += be2u32(&ssndstart[4]) + 8;
}
if(ssndstart > (buf + filestat.st_size)) {
free(buf);
return -1;
}
struct ssndchunk ssnd;
ssnd.chunksize = be2u32(&ssndstart[4]);
ssnd.offset = be2u32(&ssndstart[8]);
ssnd.blocksize = be2u32(&ssndstart[12]);
/* Calculate the total length of the resulting RIFF chunk.
* The length is given by frames * samples * bytes/sample.
* We need to add:
* - 16 bytes: fmt chunk header
* - 8 bytes: data chunk header
* - 4 bytes: wave chunk identifier
*/
out = fopen(wav, "wb+");
/* write the wav header */
unsigned short blocksize = comm.channels * (comm.size >> 3);
unsigned long rifflen = blocksize * comm.frames + 28;
fwrite("RIFF", 1, 4, out);
fwrite(u32tobuf(obuf, rifflen), 1, 4, out);
fwrite("WAVE", 1, 4, out);
/* write the fmt chunk and chunk size (always 16) */
/* write fmt chunk header:
* header, size (always 0x10, format code (always 0x0001)
*/
fwrite("fmt \x10\x00\x00\x00\x01\x00", 1, 10, out);
/* number of channels (2 bytes) */
fwrite(u16tobuf(obuf, comm.channels), 1, 2, out);
/* sampling rate (4 bytes) */
fwrite(u32tobuf(obuf, comm.rate), 1, 4, out);
/* data rate, i.e. bytes/sec */
fwrite(u32tobuf(obuf, comm.rate * blocksize), 1, 4, out);
/* data block size */
fwrite(u16tobuf(obuf, blocksize), 1, 2, out);
/* bits per sample */
fwrite(u16tobuf(obuf, comm.size), 1, 2, out);
/* write the data chunk */
/* chunk id */
fwrite("data", 1, 4, out);
/* chunk size: 4 bytes. */
unsigned long cs = blocksize * comm.frames;
fwrite(u32tobuf(obuf, cs), 1, 4, out);
/* write data */
unsigned char* data = ssndstart;
unsigned long pos = ssnd.chunksize;
/* byteswap if samples are 16 bit */
if(comm.size == 16) {
while(pos) {
obuf[1] = *data++ & 0xff;
obuf[0] = *data++ & 0xff;
fwrite(obuf, 1, 2, out);
pos -= 2;
}
}
/* 8 bit samples have need no conversion so we can bulk copy.
* Everything that is not 16 bit is considered 8. */
else {
fwrite(data, 1, pos, out);
}
/* number of bytes has to be even, even if chunksize is not. */
if(cs % 2) {
fwrite(obuf, 1, 1, out);
}
fclose(out);
free(buf);
return 0;
}

View file

@ -0,0 +1,73 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2010 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TTSCARBON_H
#define TTSCARBON_H
#include <QtCore>
#include "ttsbase.h"
#include <Carbon/Carbon.h>
#include <inttypes.h>
class TTSCarbon : public TTSBase
{
Q_OBJECT
//! Enum to identify the settings
enum ConfigValuesCarbon
{
ConfigVoice,
ConfigSpeed,
ConfigPitch
};
public:
TTSCarbon(QObject *parent = NULL);
//! Child class should generate a clip
TTSStatus voice(QString text, QString wavfile, QString* errStr);
//! Child class should do startup
bool start(QString *errStr);
//! child class should stop
bool stop() ;
QString voiceVendor(void) { return QString(); }
// configuration
//! Child class should return true, when configuration is good
bool configOk();
//! Child class should generate and insertSetting(..) its settings
void generateSettings();
//! Child class should commit the Settings to permanent storage
void saveSettings();
Capabilities capabilities();
private:
SpeechChannel m_channel;
CFStringBuiltInEncodings m_voiceScript;
unsigned long be2u32(unsigned char* buf);
unsigned long be2u16(unsigned char* buf);
unsigned char* u32tobuf(unsigned char* buf, uint32_t val);
unsigned char* u16tobuf(unsigned char* buf, uint16_t val);
unsigned int extended2int(unsigned char* buf);
int convertAiffToWav(const char* aiff, const char* wav);
};
#endif // TTSCARBON_H

View file

@ -0,0 +1,42 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2012 by Dominik Riebeling
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TTSESPEAK_H
#define TTSESPEAK_H
#include <QtCore>
#include "ttsexes.h"
class TTSEspeak : public TTSExes
{
Q_OBJECT
public:
TTSEspeak(QObject* parent=nullptr) : TTSExes(parent)
{
m_name = "espeak";
/* default to espeak */
m_TTSTemplate = "\"%exe\" %options -w \"%wavfile\" -- \"%text\"";
m_TTSSpeakTemplate = "\"%exe\" %options -- \"%text\"";
m_capabilities = TTSBase::CanSpeak;
}
};
#endif

View file

@ -0,0 +1,41 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2020 by Solomon Peachy
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TTSESPEAKNG_H
#define TTSESPEAKNG_H
#include <QtCore>
#include "ttsexes.h"
class TTSEspeakNG : public TTSExes
{
Q_OBJECT
public:
TTSEspeakNG(QObject* parent=nullptr) : TTSExes(parent)
{
m_name = "espeak-ng";
m_TTSTemplate = "\"%exe\" %options -w \"%wavfile\" -- \"%text\"";
m_TTSSpeakTemplate = "\"%exe\" %options -- \"%text\"";
m_capabilities = TTSBase::CanSpeak;
}
};
#endif

View file

@ -0,0 +1,127 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "ttsexes.h"
#include "utils.h"
#include "rbsettings.h"
#include "Logger.h"
TTSExes::TTSExes(QObject* parent) : TTSBase(parent)
{
/* default to espeak */
m_name = "espeak";
m_capabilities = TTSBase::CanSpeak;
m_TTSTemplate = "\"%exe\" %options -w \"%wavfile\" -- \"%text\"";
m_TTSSpeakTemplate = "\"%exe\" %options -- \"%text\"";
}
TTSBase::Capabilities TTSExes::capabilities()
{
return m_capabilities;
}
void TTSExes::generateSettings()
{
loadSettings();
insertSetting(eEXEPATH, new EncTtsSetting(this, EncTtsSetting::eSTRING,
tr("Path to TTS engine:"), m_TTSexec, EncTtsSetting::eBROWSEBTN));
insertSetting(eOPTIONS, new EncTtsSetting(this, EncTtsSetting::eSTRING,
tr("TTS engine options:"), m_TTSOpts));
}
void TTSExes::saveSettings()
{
RbSettings::setSubValue(m_name, RbSettings::TtsPath,
getSetting(eEXEPATH)->current().toString());
RbSettings::setSubValue(m_name, RbSettings::TtsOptions,
getSetting(eOPTIONS)->current().toString());
RbSettings::sync();
}
void TTSExes::loadSettings(void)
{
m_TTSexec = RbSettings::subValue(m_name, RbSettings::TtsPath).toString();
if(m_TTSexec.isEmpty()) m_TTSexec = Utils::findExecutable(m_name);
m_TTSOpts = RbSettings::subValue(m_name, RbSettings::TtsOptions).toString();
}
bool TTSExes::start(QString *errStr)
{
loadSettings();
QFileInfo tts(m_TTSexec);
if(tts.exists())
{
return true;
}
else
{
*errStr = tr("TTS executable not found");
return false;
}
}
TTSStatus TTSExes::voice(QString text, QString wavfile, QString *errStr)
{
(void) errStr;
QString execstring;
if(wavfile.isEmpty() && m_capabilities & TTSBase::CanSpeak) {
if(m_TTSSpeakTemplate.isEmpty()) {
LOG_ERROR() << "internal error: TTS announces CanSpeak "
"but template empty!";
return FatalError;
}
execstring = m_TTSSpeakTemplate;
}
else if(wavfile.isEmpty()) {
LOG_ERROR() << "no output file passed to voice() "
"but TTS can't speak directly.";
return FatalError;
}
else {
execstring = m_TTSTemplate;
}
execstring.replace("%exe",m_TTSexec);
execstring.replace("%options",m_TTSOpts);
execstring.replace("%wavfile",wavfile);
execstring.replace("%text",text);
QProcess::execute(execstring);
if(!wavfile.isEmpty() && !QFileInfo(wavfile).isFile()) {
LOG_ERROR() << "output file does not exist:" << wavfile;
return FatalError;
}
return NoError;
}
bool TTSExes::configOk()
{
loadSettings();
if (QFileInfo::exists(m_TTSexec))
return true;
else
return false;
}

View file

@ -0,0 +1,61 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2009 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TTSEXES_H
#define TTSEXES_H
#include <QtCore>
#include "ttsbase.h"
class TTSExes : public TTSBase
{
enum ESettings
{
eEXEPATH,
eOPTIONS
};
Q_OBJECT
public:
TTSExes(QObject* parent=nullptr);
TTSStatus voice(QString text, QString wavfile, QString *errStr);
bool start(QString *errStr);
bool stop() {return true;}
QString voiceVendor(void) { return QString(); }
Capabilities capabilities();
// for settings
void generateSettings();
void saveSettings();
bool configOk();
private:
void loadSettings(void);
protected:
QString m_TTSTemplate;
QString m_TTSSpeakTemplate;
QString m_name;
QString m_TTSexec;
QString m_TTSOpts;
TTSBase::Capabilities m_capabilities;
};
#endif

View file

@ -0,0 +1,412 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include <QTcpSocket>
#include "ttsfestival.h"
#include "utils.h"
#include "rbsettings.h"
#include "Logger.h"
TTSFestival::~TTSFestival()
{
LOG_INFO() << "Destroying instance";
stop();
}
TTSBase::Capabilities TTSFestival::capabilities()
{
return RunInParallel;
}
void TTSFestival::generateSettings()
{
// server path
QString exepath = RbSettings::subValue("festival-server",
RbSettings::TtsPath).toString();
if(exepath == "" ) exepath = Utils::findExecutable("festival");
insertSetting(eSERVERPATH,new EncTtsSetting(this,
EncTtsSetting::eSTRING, "Path to Festival server:",
exepath,EncTtsSetting::eBROWSEBTN));
// client path
QString clientpath = RbSettings::subValue("festival-client",
RbSettings::TtsPath).toString();
if(clientpath == "" ) clientpath = Utils::findExecutable("festival_client");
insertSetting(eCLIENTPATH,new EncTtsSetting(this,EncTtsSetting::eSTRING,
tr("Path to Festival client:"),
clientpath,EncTtsSetting::eBROWSEBTN));
// voice
EncTtsSetting* setting = new EncTtsSetting(this,
EncTtsSetting::eSTRINGLIST, tr("Voice:"),
RbSettings::subValue("festival", RbSettings::TtsVoice),
getVoiceList(), EncTtsSetting::eREFRESHBTN);
connect(setting, &EncTtsSetting::refresh,
this, &TTSFestival::updateVoiceList);
connect(setting, &EncTtsSetting::dataChanged,
this, &TTSFestival::clearVoiceDescription);
insertSetting(eVOICE,setting);
//voice description
setting = new EncTtsSetting(this,EncTtsSetting::eREADONLYSTRING,
tr("Voice description:"),"",EncTtsSetting::eREFRESHBTN);
connect(setting, &EncTtsSetting::refresh,
this, &TTSFestival::updateVoiceDescription);
insertSetting(eVOICEDESC,setting);
}
void TTSFestival::saveSettings()
{
//save settings in user config
RbSettings::setSubValue("festival-server",
RbSettings::TtsPath,getSetting(eSERVERPATH)->current().toString());
RbSettings::setSubValue("festival-client",
RbSettings::TtsPath,getSetting(eCLIENTPATH)->current().toString());
RbSettings::setSubValue("festival",
RbSettings::TtsVoice,getSetting(eVOICE)->current().toString());
RbSettings::sync();
}
void TTSFestival::updateVoiceDescription()
{
// get voice Info with current voice and path
currentPath = getSetting(eSERVERPATH)->current().toString();
QString info = getVoiceInfo(getSetting(eVOICE)->current().toString());
currentPath = "";
getSetting(eVOICEDESC)->setCurrent(info);
}
void TTSFestival::clearVoiceDescription()
{
getSetting(eVOICEDESC)->setCurrent("");
}
void TTSFestival::updateVoiceList()
{
currentPath = getSetting(eSERVERPATH)->current().toString();
QStringList voiceList = getVoiceList();
currentPath = "";
getSetting(eVOICE)->setList(voiceList);
if(voiceList.size() > 0) getSetting(eVOICE)->setCurrent(voiceList.at(0));
else getSetting(eVOICE)->setCurrent("");
}
void TTSFestival::startServer()
{
if(!configOk())
return;
if(serverProcess.state() != QProcess::Running)
{
QString path;
/* currentPath is set by the GUI - if it's set, it is the currently set
path in the configuration GUI; if it's not set, use the saved path */
if (currentPath == "")
path = RbSettings::subValue("festival-server",RbSettings::TtsPath).toString();
else
path = currentPath;
serverProcess.start(QString("%1 --server").arg(path));
serverProcess.waitForStarted();
/* A friendlier version of a spinlock */
while (serverProcess.processId() == 0 && serverProcess.state() != QProcess::Running)
QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
if(serverProcess.state() == QProcess::Running)
LOG_INFO() << "Server is up and running";
else
LOG_ERROR() << "Server failed to start, state:"
<< serverProcess.state();
}
}
bool TTSFestival::ensureServerRunning()
{
if(serverProcess.state() != QProcess::Running)
{
startServer();
}
return serverProcess.state() == QProcess::Running;
}
bool TTSFestival::start(QString* errStr)
{
LOG_INFO() << "Starting server with voice"
<< RbSettings::subValue("festival", RbSettings::TtsVoice).toString();
bool running = ensureServerRunning();
if (!RbSettings::subValue("festival",RbSettings::TtsVoice).toString().isEmpty())
{
/* There's no harm in using both methods to set the voice .. */
QString voiceSelect = QString("(voice.select '%1)\n")
.arg(RbSettings::subValue("festival", RbSettings::TtsVoice).toString());
queryServer(voiceSelect, 3000);
if(prologFile.open())
{
prologFile.write(voiceSelect.toLatin1());
prologFile.close();
prologPath = QFileInfo(prologFile).absoluteFilePath();
LOG_INFO() << "Prolog created at" << prologPath;
}
}
if (!running)
(*errStr) = "Festival could not be started";
return running;
}
bool TTSFestival::stop()
{
serverProcess.terminate();
serverProcess.kill();
return true;
}
TTSStatus TTSFestival::voice(QString text, QString wavfile, QString* errStr)
{
LOG_INFO() << "Voicing" << text << "->" << wavfile;
QString path = RbSettings::subValue("festival-client",
RbSettings::TtsPath).toString();
QString cmd = QString("%1 --server localhost --otype riff --ttw --withlisp"
" --output \"%2\" --prolog \"%3\" - ").arg(path).arg(wavfile).arg(prologPath);
LOG_INFO() << "Client cmd:" << cmd;
QProcess clientProcess;
clientProcess.start(cmd);
clientProcess.write(QString("%1.\n").arg(text).toLatin1());
clientProcess.waitForBytesWritten();
clientProcess.closeWriteChannel();
clientProcess.waitForReadyRead();
QString response = clientProcess.readAll();
response = response.trimmed();
if(!response.contains("Utterance"))
{
LOG_WARNING() << "Could not voice string: " << response;
*errStr = tr("engine could not voice string");
return Warning;
/* do not stop the voicing process because of a single string
TODO: needs proper settings */
}
clientProcess.closeReadChannel(QProcess::StandardError);
clientProcess.closeReadChannel(QProcess::StandardOutput);
clientProcess.terminate();
clientProcess.kill();
return NoError;
}
bool TTSFestival::configOk()
{
bool ret;
if (currentPath == "")
{
QString serverPath = RbSettings::subValue("festival-server",
RbSettings::TtsPath).toString();
QString clientPath = RbSettings::subValue("festival-client",
RbSettings::TtsPath).toString();
ret = QFileInfo(serverPath).isExecutable() &&
QFileInfo(clientPath).isExecutable();
if(RbSettings::subValue("festival",RbSettings::TtsVoice).toString().size() > 0
&& voices.size() > 0)
ret = ret && (voices.indexOf(RbSettings::subValue("festival",
RbSettings::TtsVoice).toString()) != -1);
}
else /* If we're currently configuring the server, we need to know that
the entered path is valid */
ret = QFileInfo(currentPath).isExecutable();
return ret;
}
QStringList TTSFestival::getVoiceList()
{
if(!configOk())
return QStringList();
if(voices.size() > 0)
{
LOG_INFO() << "Using voice cache";
return voices;
}
QString response = queryServer("(voice.list)", 10000);
// get the 2nd line. It should be (<voice_name>, <voice_name>)
response = response.mid(response.indexOf('\n') + 1, -1);
response = response.left(response.indexOf('\n')).trimmed();
voices = response.mid(1, response.size()-2).split(' ');
voices.sort();
if (voices.size() == 1 && voices[0].size() == 0)
voices.removeAt(0);
if (voices.size() > 0)
LOG_INFO() << "Voices:" << voices;
else
LOG_WARNING() << "No voices. Response was:" << response;
return voices;
}
QString TTSFestival::getVoiceInfo(QString voice)
{
if(!configOk())
return "";
if(!getVoiceList().contains(voice))
return "";
if(voiceDescriptions.contains(voice))
return voiceDescriptions[voice];
QString response = queryServer(QString("(voice.description '%1)").arg(voice),
10000);
if (response == "")
{
voiceDescriptions[voice]=tr("No description available");
}
else
{
response = response.remove(QRegExp("(description \"*\")",
Qt::CaseInsensitive, QRegExp::Wildcard));
LOG_INFO() << "voiceInfo w/o descr:" << response;
response = response.remove(')');
#if QT_VERSION >= 0x050e00
QStringList responseLines = response.split('(', Qt::SkipEmptyParts);
#else
QStringList responseLines = response.split('(', QString::SkipEmptyParts);
#endif
responseLines.removeAt(0); // the voice name itself
QString description;
foreach(QString line, responseLines)
{
line = line.remove('(');
line = line.simplified();
line[0] = line[0].toUpper(); // capitalize the key
int firstSpace = line.indexOf(' ');
if (firstSpace > 0)
{
// add a colon between the key and the value
line = line.insert(firstSpace, ':');
// capitalize the value
line[firstSpace+2] = line[firstSpace+2].toUpper();
}
description += line + "\n";
}
voiceDescriptions[voice] = description.trimmed();
}
return voiceDescriptions[voice];
}
QString TTSFestival::queryServer(QString query, int timeout)
{
if(!configOk())
return "";
// this operation could take some time
emit busy();
LOG_INFO() << "queryServer with" << query;
if (!ensureServerRunning())
{
LOG_ERROR() << "queryServer: ensureServerRunning failed";
emit busyEnd();
return "";
}
QString response;
QDateTime endTime;
if(timeout > 0)
endTime = QDateTime::currentDateTime().addMSecs(timeout);
/* Festival is *extremely* unreliable. Although at this
* point we are sure that SIOD is accepting commands,
* we might end up with an empty response. Hence, the loop.
*/
while(true)
{
QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
QTcpSocket socket;
socket.connectToHost("localhost", 1314);
socket.waitForConnected();
if(socket.state() == QAbstractSocket::ConnectedState)
{
socket.write(QString("%1\n").arg(query).toLatin1());
socket.waitForBytesWritten();
socket.waitForReadyRead();
response = socket.readAll().trimmed();
if (response != "LP" && response != "")
break;
}
socket.abort();
socket.disconnectFromHost();
if(timeout > 0 && QDateTime::currentDateTime() >= endTime)
{
emit busyEnd();
return "";
}
/* make sure we wait a little as we don't want to flood the server
* with requests */
QDateTime tmpEndTime = QDateTime::currentDateTime().addMSecs(500);
while(QDateTime::currentDateTime() < tmpEndTime)
QCoreApplication::processEvents(QEventLoop::AllEvents);
}
if(response == "nil")
{
emit busyEnd();
return "";
}
QStringList lines = response.split('\n');
if(lines.size() > 2)
{
lines.removeFirst(); /* should be LP */
lines.removeLast(); /* should be ft_StUfF_keyOK */
}
else
LOG_ERROR() << "Response too short:" << response;
emit busyEnd();
return lines.join("\n");
}

View file

@ -0,0 +1,72 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2009 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TTSFESTIVAL_H
#define TTSFESTIVAL_H
#include <QTemporaryFile>
#include "ttsbase.h"
class TTSFestival : public TTSBase
{
enum ESettings
{
eSERVERPATH,
eCLIENTPATH,
eVOICE,
eVOICEDESC
};
Q_OBJECT
public:
TTSFestival(QObject* parent=nullptr) : TTSBase(parent) {}
~TTSFestival();
bool start(QString *errStr);
bool stop();
TTSStatus voice(QString text,QString wavfile, QString *errStr);
QString voiceVendor(void) { return QString(); }
Capabilities capabilities();
// for settings
bool configOk();
void generateSettings();
void saveSettings();
private slots:
void updateVoiceList();
void updateVoiceDescription();
void clearVoiceDescription();
private:
QTemporaryFile prologFile;
QString prologPath;
QString currentPath;
QStringList getVoiceList();
QString getVoiceInfo(QString voice);
inline void startServer();
inline bool ensureServerRunning();
QString queryServer(QString query, int timeout = -1);
QProcess serverProcess;
QStringList voices;
QMap<QString, QString> voiceDescriptions;
};
#endif

View file

@ -0,0 +1,43 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2012 by Dominik Riebeling
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TTSFLITE_H
#define TTSFLITE_H
#include <QtCore>
#include "ttsexes.h"
class TTSFlite : public TTSExes
{
Q_OBJECT
public:
TTSFlite(QObject* parent=nullptr) : TTSExes(parent)
{
m_name = "flite";
/* default to espeak */
m_TTSTemplate = "\"%exe\" %options -o \"%wavfile\" -t \"%text\"";
m_TTSSpeakTemplate = "";
m_capabilities = TTSBase::None;
}
};
#endif

View file

@ -0,0 +1,41 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2020 by Solomon Peachy
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TTSMIMIC_H
#define TTSMIMIC_H
#include <QtCore>
#include "ttsexes.h"
class TTSMimic : public TTSExes
{
Q_OBJECT
public:
TTSMimic(QObject* parent=nullptr) : TTSExes(parent)
{
m_name = "mimic";
m_TTSTemplate = "\"%exe\" %options -o \"%wavfile\" -t \"%text\"";
m_TTSSpeakTemplate = "\"%exe\" %options -t \"%text\"";
m_capabilities = TTSBase::CanSpeak;
}
};
#endif

View file

@ -0,0 +1,43 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2012 by Dominik Riebeling
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TTSMSSP_H
#define TTSMSSP_H
#include "ttsbase.h"
#include "ttssapi.h"
class TTSMssp: public TTSSapi
{
Q_OBJECT
public:
TTSMssp(QObject* parent=nullptr) : TTSSapi(parent)
{
m_TTSTemplate = "cscript //nologo \"%exe\" "
"/language:%lang /voice:\"%voice\" "
"/speed:%speed \"%options\" /mssp";
m_TTSVoiceTemplate = "cscript //nologo \"%exe\" "
"/language:%lang /listvoices /mssp";
m_TTSType = "mssp";
}
};
#endif

View file

@ -0,0 +1,274 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "ttssapi.h"
#include "utils.h"
#include "rbsettings.h"
#include "playerbuildinfo.h"
#include "Logger.h"
TTSSapi::TTSSapi(QObject* parent) : TTSBase(parent)
{
m_TTSTemplate = "cscript //nologo \"%exe\" /language:%lang "
"/voice:\"%voice\" /speed:%speed \"%options\"";
m_TTSVoiceTemplate = "cscript //nologo \"%exe\" /language:%lang /listvoices";
m_TTSType = "sapi";
defaultLanguage = "english";
m_started = false;
}
TTSBase::Capabilities TTSSapi::capabilities()
{
return None;
}
void TTSSapi::generateSettings()
{
// language
QMap<QString, QVariant> langmap = PlayerBuildInfo::instance()->value(
PlayerBuildInfo::LanguageList).toMap();
EncTtsSetting* setting = new EncTtsSetting(this,
EncTtsSetting::eSTRINGLIST, tr("Language:"),
RbSettings::subValue(m_TTSType, RbSettings::TtsLanguage),
langmap.keys());
connect(setting, &EncTtsSetting::dataChanged, this, &TTSSapi::updateVoiceList);
insertSetting(eLANGUAGE,setting);
// voice
setting = new EncTtsSetting(this,
EncTtsSetting::eSTRINGLIST, tr("Voice:"),
RbSettings::subValue(m_TTSType, RbSettings::TtsVoice),
getVoiceList(RbSettings::subValue(m_TTSType,
RbSettings::TtsLanguage).toString()),
EncTtsSetting::eREFRESHBTN);
connect(setting, &EncTtsSetting::refresh, this, &TTSSapi::updateVoiceList);
insertSetting(eVOICE,setting);
//speed
int speed = RbSettings::subValue(m_TTSType, RbSettings::TtsSpeed).toInt();
if(speed > 10 || speed < -10)
speed = 0;
insertSetting(eSPEED, new EncTtsSetting(this,
EncTtsSetting::eINT, tr("Speed:"), speed, -10, 10));
// options
insertSetting(eOPTIONS, new EncTtsSetting(this,
EncTtsSetting::eSTRING, tr("Options:"),
RbSettings::subValue(m_TTSType, RbSettings::TtsOptions)));
}
void TTSSapi::saveSettings()
{
//save settings in user config
RbSettings::setSubValue(m_TTSType, RbSettings::TtsLanguage,
getSetting(eLANGUAGE)->current().toString());
RbSettings::setSubValue(m_TTSType, RbSettings::TtsVoice,
getSetting(eVOICE)->current().toString());
RbSettings::setSubValue(m_TTSType, RbSettings::TtsSpeed,
getSetting(eSPEED)->current().toInt());
RbSettings::setSubValue(m_TTSType, RbSettings::TtsOptions,
getSetting(eOPTIONS)->current().toString());
RbSettings::sync();
}
void TTSSapi::updateVoiceList()
{
LOG_INFO() << "updating voicelist";
QStringList voiceList = getVoiceList(getSetting(eLANGUAGE)->current().toString());
getSetting(eVOICE)->setList(voiceList);
if(voiceList.size() > 0) getSetting(eVOICE)->setCurrent(voiceList.at(0));
else getSetting(eVOICE)->setCurrent("");
}
bool TTSSapi::start(QString *errStr)
{
m_TTSOpts = RbSettings::subValue(m_TTSType, RbSettings::TtsOptions).toString();
m_TTSLanguage =RbSettings::subValue(m_TTSType, RbSettings::TtsLanguage).toString();
m_TTSVoice=RbSettings::subValue(m_TTSType, RbSettings::TtsVoice).toString();
m_TTSSpeed=RbSettings::subValue(m_TTSType, RbSettings::TtsSpeed).toString();
QFile::remove(QDir::tempPath() +"/sapi_voice.vbs");
QFile::copy(":/builtin/sapi_voice.vbs",QDir::tempPath() + "/sapi_voice.vbs");
m_TTSexec = QDir::tempPath() +"/sapi_voice.vbs";
QFileInfo tts(m_TTSexec);
if(!tts.exists())
{
*errStr = tr("Could not copy the SAPI script");
return false;
}
// create the voice process
QString execstring = m_TTSTemplate;
execstring.replace("%exe",m_TTSexec);
execstring.replace("%options",m_TTSOpts);
execstring.replace("%lang",m_TTSLanguage);
execstring.replace("%voice",m_TTSVoice);
execstring.replace("%speed",m_TTSSpeed);
LOG_INFO() << "Start:" << execstring;
voicescript = new QProcess(nullptr);
//connect(voicescript,SIGNAL(readyReadStandardError()),this,SLOT(error()));
voicescript->start(execstring);
LOG_INFO() << "wait for process";
if(!voicescript->waitForStarted())
{
*errStr = tr("Could not start SAPI process");
LOG_ERROR() << "starting process timed out!";
return false;
}
if(!voicescript->waitForReadyRead(300))
{
*errStr = voicescript->readAllStandardError();
if(*errStr != "")
return false;
}
voicestream = new QTextStream(voicescript);
#if QT_VERSION < 0x060000
voicestream->setCodec("UTF16-LE");
#else
voicestream->setEncoding(QStringConverter::Utf16LE);
#endif
m_started = true;
return true;
}
QString TTSSapi::voiceVendor(void)
{
bool keeprunning = m_started;
QString vendor;
if(!m_started) {
QString error;
start(&error);
}
*voicestream << "QUERY\tVENDOR\r\n";
voicestream->flush();
while((vendor = voicestream->readLine()).isEmpty())
QCoreApplication::processEvents();
LOG_INFO() << "TTS vendor:" << vendor;
if(!keeprunning) {
stop();
}
return vendor;
}
QStringList TTSSapi::getVoiceList(QString language)
{
QStringList result;
QFile::copy(":/builtin/sapi_voice.vbs",QDir::tempPath() + "/sapi_voice.vbs");
m_TTSexec = QDir::tempPath() +"/sapi_voice.vbs";
QFileInfo tts(m_TTSexec);
if(!tts.exists())
return result;
// create the voice process
QString execstring = m_TTSVoiceTemplate;
execstring.replace("%exe",m_TTSexec);
execstring.replace("%lang",language);
LOG_INFO() << "Start:" << execstring;
voicescript = new QProcess(nullptr);
voicescript->start(execstring);
LOG_INFO() << "wait for process";
if(!voicescript->waitForStarted()) {
LOG_INFO() << "process startup timed out!";
return result;
}
voicescript->closeWriteChannel();
voicescript->waitForReadyRead();
QString dataRaw = voicescript->readAllStandardError().data();
if(dataRaw.startsWith("Error")) {
LOG_INFO() << "Error:" << dataRaw;
}
#if QT_VERSION >= 0x050e00
result = dataRaw.split(";", Qt::SkipEmptyParts);
#else
result = dataRaw.split(";", QString::SkipEmptyParts);
#endif
if(result.size() > 0)
{
result.sort();
result.removeFirst();
for(int i = 0; i< result.size();i++)
{
result[i] = result.at(i).simplified();
}
}
delete voicescript;
QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs",
QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
| QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
| QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup
| QFile::ReadOther | QFile::WriteOther | QFile::ExeOther );
QFile::remove(QDir::tempPath() +"/sapi_voice.vbs");
return result;
}
TTSStatus TTSSapi::voice(QString text,QString wavfile, QString *errStr)
{
(void) errStr;
QString query = "SPEAK\t"+wavfile+"\t"+text;
LOG_INFO() << "voicing" << query;
// append newline to query. Done now to keep debug output more readable.
query.append("\r\n");
*voicestream << query;
*voicestream << "SYNC\tbla\r\n";
voicestream->flush();
// do NOT poll the output with readLine(), this causes sync issues!
voicescript->waitForReadyRead();
if(!QFileInfo(wavfile).isFile()) {
LOG_ERROR() << "output file does not exist:" << wavfile;
return FatalError;
}
return NoError;
}
bool TTSSapi::stop()
{
*voicestream << "QUIT\r\n";
voicestream->flush();
voicescript->waitForFinished();
delete voicestream;
delete voicescript;
QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs",
QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
| QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
| QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup
| QFile::ReadOther | QFile::WriteOther | QFile::ExeOther );
QFile::remove(QDir::tempPath() +"/sapi_voice.vbs");
m_started = false;
return true;
}
bool TTSSapi::configOk()
{
if(RbSettings::subValue(m_TTSType, RbSettings::TtsVoice).toString().isEmpty())
return false;
return true;
}

View file

@ -0,0 +1,77 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2009 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TTSSAPI_H
#define TTSSAPI_H
#include "ttsbase.h"
class TTSSapi : public TTSBase
{
//! Enum to identify the settings
enum ESettings
{
eLANGUAGE,
eVOICE,
eSPEED,
eOPTIONS
};
Q_OBJECT
public:
TTSSapi(QObject* parent=nullptr);
TTSStatus voice(QString text,QString wavfile, QString *errStr);
bool start(QString *errStr);
bool stop();
QString voiceVendor(void);
Capabilities capabilities();
// for settings
bool configOk();
void generateSettings();
void saveSettings();
private slots:
void updateVoiceList();
private:
QStringList getVoiceList(QString language);
QProcess* voicescript;
QTextStream* voicestream;
QString defaultLanguage;
QString m_TTSexec;
QString m_TTSOpts;
QString m_TTSLanguage;
QString m_TTSVoice;
QString m_TTSSpeed;
bool m_started;
protected:
QString m_TTSTemplate;
QString m_TTSVoiceTemplate;
QString m_TTSType;
};
#endif

View file

@ -0,0 +1,43 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2012 by Dominik Riebeling
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TTSSAPI4_H
#define TTSSAPI4_H
#include "ttsbase.h"
#include "ttssapi.h"
class TTSSapi4: public TTSSapi
{
Q_OBJECT
public:
TTSSapi4(QObject* parent=nullptr) : TTSSapi(parent)
{
m_TTSTemplate = "cscript //nologo \"%exe\" "
"/language:%lang /voice:\"%voice\" "
"/speed:%speed \"%options\" /sapi4";
m_TTSVoiceTemplate = "cscript //nologo \"%exe\" "
"/language:%lang /listvoices /sapi4";
m_TTSType = "sapi4";
}
};
#endif

View file

@ -0,0 +1,40 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2012 by Dominik Riebeling
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TTSSWIFT_H
#define TTSSWIFT_H
#include <QtCore>
#include "ttsexes.h"
class TTSSwift : public TTSExes
{
Q_OBJECT
public:
TTSSwift(QObject* parent=nullptr) : TTSExes(parent)
{
m_name = "swift";
m_TTSTemplate = "\"%exe\" %options -o \"%wavfile\" -- \"%text\"";
m_TTSSpeakTemplate = "";
m_capabilities = TTSBase::None;
}
};
#endif

View file

@ -0,0 +1,126 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "uninstall.h"
#include "utils.h"
#include "Logger.h"
Uninstaller::Uninstaller(QObject* parent,QString mountpoint): QObject(parent)
{
m_mountpoint = mountpoint;
}
void Uninstaller::deleteAll(void)
{
QString rbdir(m_mountpoint + ".rockbox/");
emit logItem(tr("Starting Uninstallation"), LOGINFO);
emit logProgress(0, 0);
Utils::recursiveRmdir(rbdir);
emit logProgress(1, 1);
emit logItem(tr("Finished Uninstallation"), LOGOK);
emit logFinished();
}
void Uninstaller::uninstall(void)
{
emit logProgress(0, 0);
emit logItem(tr("Starting Uninstallation"), LOGINFO);
QSettings installlog(m_mountpoint + "/.rockbox/rbutil.log", QSettings::IniFormat, this);
for(int i=0; i< uninstallSections.size() ; i++)
{
emit logItem(tr("Uninstalling %1...").arg(uninstallSections.at(i)), LOGINFO);
QCoreApplication::processEvents();
// create list of all other install sections
QStringList sections = installlog.childGroups();
sections.removeAt(sections.indexOf(uninstallSections.at(i)));
installlog.beginGroup(uninstallSections.at(i));
QStringList toDeleteList = installlog.allKeys();
QStringList dirList;
installlog.endGroup();
// iterate over all entries
for(int j =0; j < toDeleteList.size(); j++ )
{
// check if current file is in use by another section
bool deleteFile = true;
for(int s = 0; s < sections.size(); s++)
{
installlog.beginGroup(sections.at(s));
if(installlog.contains(toDeleteList.at(j)))
{
deleteFile = false;
LOG_INFO() << "file still in use:" << toDeleteList.at(j);
}
installlog.endGroup();
}
installlog.beginGroup(uninstallSections.at(i));
QFileInfo toDelete(m_mountpoint + "/" + toDeleteList.at(j));
if(toDelete.isFile()) // if it is a file remove it
{
if(deleteFile && !QFile::remove(toDelete.filePath()))
emit logItem(tr("Could not delete %1")
.arg(toDelete.filePath()), LOGWARNING);
installlog.remove(toDeleteList.at(j));
LOG_INFO() << "deleted:" << toDelete.filePath();
}
else // if it is a dir, remember it for later deletion
{
// no need to keep track on folders still in use -- only empty
// folders will be rm'ed.
dirList << toDeleteList.at(j);
}
installlog.endGroup();
QCoreApplication::processEvents();
}
// delete the dirs
installlog.beginGroup(uninstallSections.at(i));
for(int j=0; j < dirList.size(); j++ )
{
installlog.remove(dirList.at(j));
QDir dir(m_mountpoint);
dir.rmdir(dirList.at(j)); // rm works only on empty folders
}
installlog.endGroup();
//installlog.removeGroup(uninstallSections.at(i))
}
uninstallSections.clear();
installlog.sync();
emit logProgress(1, 1);
emit logItem(tr("Uninstallation finished"), LOGOK);
emit logFinished();
}
QStringList Uninstaller::getAllSections()
{
QSettings installlog(m_mountpoint + "/.rockbox/rbutil.log", QSettings::IniFormat, nullptr);
QStringList allSections = installlog.childGroups();
allSections.removeAt(allSections.lastIndexOf("Bootloader"));
return allSections;
}
bool Uninstaller::uninstallPossible()
{
return QFileInfo::exists(m_mountpoint +"/.rockbox/rbutil.log");
}

View file

@ -0,0 +1,63 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef UNINSTALL_H
#define UNINSTALL_H
#include <QtCore>
#include "progressloggerinterface.h"
class Uninstaller : public QObject
{
Q_OBJECT
public:
Uninstaller(QObject* parent,QString mountpoint) ;
~Uninstaller(){}
void deleteAll(void);
void uninstall(void);
bool uninstallPossible();
QStringList getAllSections();
void setSections(QStringList sections) {uninstallSections = sections;}
signals:
void logItem(QString, int); //! set logger item
void logProgress(int, int); //! set progress bar.
void logFinished(void);
private slots:
private:
QString m_mountpoint;
QStringList uninstallSections;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,64 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef UTILS_H
#define UTILS_H
#include <QtCore/QObject>
#include <QString>
#include <QUrl>
class Utils : public QObject
{
public:
enum Size {
FilesystemTotal,
FilesystemFree,
FilesystemClusterSize,
};
enum MountpointsFilter {
MountpointsAll,
MountpointsSupported,
};
static bool recursiveRmdir(const QString &dirName);
static QString resolvePathCase(QString path);
static qulonglong filesystemFree(QString path);
static qulonglong filesystemTotal(QString path);
static qulonglong filesystemSize(QString path, enum Size type);
static QString filesystemType(QString path);
static QString findExecutable(QString name);
static QString checkEnvironment(bool permission);
static int compareVersionStrings(QString s1, QString s2);
static QString trimVersionString(QString s);
static QString filesystemName(QString path);
static QStringList mountpoints(enum MountpointsFilter type = MountpointsAll);
static QString resolveDevicename(QString path);
static QString resolveMountPoint(QString device);
static QMap<QString, QList<int> > findRunningProcess(QStringList names);
static QList<int> suspendProcess(QList<int> pidlist, bool suspend);
static bool ejectDevice(QString device);
static qint64 recursiveFolderSize(QString path);
};
#endif

View file

@ -0,0 +1,362 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "voicefile.h"
#include "utils.h"
#include "rockboxinfo.h"
#include "rbsettings.h"
#include "playerbuildinfo.h"
#include "ziputil.h"
#include "Logger.h"
VoiceFileCreator::VoiceFileCreator(QObject* parent) :QObject(parent)
{
m_wavtrimThreshold=500;
}
void VoiceFileCreator::abort()
{
m_abort = true;
emit aborted();
}
bool VoiceFileCreator::createVoiceFile()
{
m_talkList.clear();
m_abort = false;
emit logItem(tr("Starting Voicefile generation"),LOGINFO);
// test if tempdir exists
if(!QDir(QDir::tempPath()+"/rbvoice/").exists())
{
QDir(QDir::tempPath()).mkdir("rbvoice");
}
m_path = QDir::tempPath() + "/rbvoice/";
// read rockbox-info.txt
RockboxInfo info(m_mountpoint);
if(!info.success())
{
emit logItem(tr("could not find rockbox-info.txt"),LOGERROR);
emit done(true);
return false;
}
QString target = info.target();
QString features = info.features();
m_targetid = info.targetID().toInt();
m_versionstring = info.version();
m_voiceformat = info.voicefmt();
QString version = m_versionstring.left(m_versionstring.indexOf("-")).remove("r");
// check if voicefile is present on target
QString fn = m_mountpoint + "/.rockbox/langs/voicestrings.zip";
LOG_INFO() << "searching for zipped voicestrings at" << fn;
if(QFileInfo(fn).isFile()) {
// search for binary voice strings file in archive
ZipUtil z(this);
if(z.open(fn)) {
QStringList contents = z.files();
int index;
for(index = 0; index < contents.size(); ++index) {
// strip any path, we don't know the structure in the zip
if(QFileInfo(contents.at(index)).baseName() == m_lang) {
break;
}
}
if(index < contents.size()) {
LOG_INFO() << "extracting strings file from zip";
// extract strings
QTemporaryFile stringsfile;
stringsfile.open();
QString sfn = stringsfile.fileName();
// ZipUtil::extractArchive() only compares the filename.
if(z.extractArchive(sfn, QFileInfo(contents.at(index)).fileName())) {
emit logItem(tr("Extracted voice strings from installation"), LOGINFO);
stringsfile.seek(0);
QByteArray data = stringsfile.readAll();
const char* buf = data.constData();
// check file header
// header (4 bytes): cookie = 9a, version = 06, targetid, options
// subheader for each user. Only "core" for now.
// subheader (6 bytes): count (2bytes), size (2bytes), offset (2bytes)
if(buf[0] != (char)0x9a || buf[1] != 0x06 || buf[2] != m_targetid) {
emit logItem(tr("Extracted voice strings incompatible"), LOGINFO);
}
else {
QMap<int, QString> voicestrings;
/* skip header */
int idx = 10;
do {
unsigned int id = ((unsigned char)buf[idx])<<8
| ((unsigned char)buf[idx+1]);
// need to use strlen here, since QString::size()
// returns number of characters, not bytes.
int len = strlen(&buf[idx + 2]);
voicestrings[id] = QString::fromUtf8(&buf[idx+2]);
idx += 2 + len + 1;
} while(idx < data.size());
stringsfile.close();
// create input file suitable for voicefont from strings.
QTemporaryFile voicefontlist;
voicefontlist.open();
m_filename = voicefontlist.fileName();
for(int i = 0; i < voicestrings.size(); ++i) {
QByteArray qba;
qba = QString("id: %1_%2\n")
.arg(voicestrings.keys().at(i) < 0x8000 ? "LANG" : "VOICE")
.arg(voicestrings.keys().at(i)).toUtf8();
voicefontlist.write(qba);
qba = QString("voice: \"%1\"\n").arg(
voicestrings[voicestrings.keys().at(i)]).toUtf8();
voicefontlist.write(qba);
}
voicefontlist.close();
// everything successful, now create the actual voice file.
create();
return true;
}
}
}
}
}
emit logItem(tr("Could not retrieve strings from installation, downloading"), LOGINFO);
// if either no zip with voice strings is found or something went wrong
// retrieving the necessary files we'll end up here, trying to get the
// genlang output as previously from the webserver.
// prepare download url
QString genlang = PlayerBuildInfo::instance()->value(
PlayerBuildInfo::GenlangUrl).toString();
genlang.replace("%LANG%", m_lang);
genlang.replace("%TARGET%", target);
genlang.replace("%REVISION%", version);
genlang.replace("%FEATURES%", features);
QUrl genlangUrl(genlang);
LOG_INFO() << "downloading" << genlangUrl;
//download the correct genlang output
QTemporaryFile *downloadFile = new QTemporaryFile(this);
downloadFile->open();
m_filename = downloadFile->fileName();
downloadFile->close();
// get the real file.
getter = new HttpGet(this);
getter->setFile(downloadFile);
connect(getter, &HttpGet::done, this, &VoiceFileCreator::downloadDone);
connect(getter, &HttpGet::dataReadProgress, this, &VoiceFileCreator::logProgress);
connect(this, &VoiceFileCreator::aborted, getter, &HttpGet::abort);
emit logItem(tr("Downloading voice info..."),LOGINFO);
getter->getFile(genlangUrl);
return true;
}
void VoiceFileCreator::downloadDone(bool error)
{
LOG_INFO() << "download done, error:" << error;
// update progress bar
emit logProgress(1,1);
if(getter->httpResponse() != 200 && !getter->isCached()) {
emit logItem(tr("Download error: received HTTP error %1.")
.arg(getter->httpResponse()),LOGERROR);
emit done(true);
return;
}
if(getter->isCached())
emit logItem(tr("Cached file used."), LOGINFO);
if(error)
{
emit logItem(tr("Download error: %1").arg(getter->errorString()),LOGERROR);
emit done(true);
return;
}
else
emit logItem(tr("Download finished."),LOGINFO);
QCoreApplication::processEvents();
create();
}
void VoiceFileCreator::create(void)
{
//open downloaded file
QFile genlang(m_filename);
if(!genlang.open(QIODevice::ReadOnly))
{
emit logItem(tr("failed to open downloaded file"),LOGERROR);
emit done(true);
return;
}
//read in downloaded file
emit logItem(tr("Reading strings..."),LOGINFO);
QTextStream in(&genlang);
#if QT_VERSION < 0x060000
in.setCodec("UTF-8");
#else
in.setEncoding(QStringConverter::Utf8);
#endif
QString id, voice;
bool idfound = false;
bool voicefound=false;
bool useCorrection = RbSettings::value(RbSettings::UseTtsCorrections).toBool();
while (!in.atEnd())
{
QString line = in.readLine();
if(line.contains("id:")) //ID found
{
id = line.remove("id:").remove('"').trimmed();
idfound = true;
}
else if(line.contains("voice:")) // voice found
{
voice = line.remove("voice:").remove('"').trimmed();
voice = voice.remove("<").remove(">");
voicefound=true;
}
if(idfound && voicefound)
{
TalkGenerator::TalkEntry entry;
entry.toSpeak = voice;
entry.wavfilename = m_path + "/" + id + ".wav";
//voicefont wants them with .mp3 extension
entry.talkfilename = m_path + "/" + id + ".mp3";
entry.voiced = false;
entry.encoded = false;
if(id == "VOICE_PAUSE")
{
QFile::copy(":/builtin/VOICE_PAUSE.wav",m_path + "/VOICE_PAUSE.wav");
entry.wavfilename = m_path + "/VOICE_PAUSE.wav";
entry.voiced = true;
m_talkList.append(entry);
}
else if(entry.toSpeak.isEmpty()) {
LOG_WARNING() << "Empty voice string for ID" << id;
}
else {
m_talkList.append(entry);
}
idfound=false;
voicefound=false;
}
}
genlang.close();
// check for empty list
if(m_talkList.size() == 0)
{
emit logItem(tr("The downloaded file was empty!"),LOGERROR);
emit done(true);
return;
}
// generate files
{
TalkGenerator generator(this);
// set language for string correction. If not set no correction will be made.
if(useCorrection)
generator.setLang(m_lang);
connect(&generator, &TalkGenerator::done, this, &VoiceFileCreator::done);
connect(&generator, &TalkGenerator::logItem, this, &VoiceFileCreator::logItem);
connect(&generator, &TalkGenerator::logProgress, this, &VoiceFileCreator::logProgress);
connect(this, &VoiceFileCreator::aborted, &generator, &TalkGenerator::abort);
if(generator.process(&m_talkList, m_wavtrimThreshold) == TalkGenerator::eERROR)
{
cleanup();
emit logProgress(0,1);
emit done(true);
return;
}
}
//make voicefile
emit logItem(tr("Creating voicefiles..."),LOGINFO);
FILE* ids2 = fopen(m_filename.toLocal8Bit(), "r");
if (ids2 == nullptr)
{
cleanup();
emit logItem(tr("Error opening downloaded file"),LOGERROR);
emit done(true);
return;
}
FILE* output = fopen(QString(m_mountpoint + "/.rockbox/langs/" + m_lang
+ ".voice").toLocal8Bit(), "wb");
if (output == nullptr)
{
cleanup();
fclose(ids2);
emit logItem(tr("Error opening output file"),LOGERROR);
emit done(true);
return;
}
LOG_INFO() << "Running voicefont, format" << m_voiceformat;
voicefont(ids2,m_targetid,m_path.toLocal8Bit().data(), output, m_voiceformat);
// ids2 and output are closed by voicefont().
//cleanup
cleanup();
// Add Voice file to the install log
QSettings installlog(m_mountpoint + "/.rockbox/rbutil.log", QSettings::IniFormat, nullptr);
installlog.beginGroup(QString("Voice (self created, %1)").arg(m_lang));
installlog.setValue("/.rockbox/langs/" + m_lang + ".voice", m_versionstring);
installlog.endGroup();
installlog.sync();
emit logProgress(1,1);
emit logItem(tr("successfully created."),LOGOK);
emit done(false);
}
//! \brief Cleans up Files potentially left in the temp dir
//!
void VoiceFileCreator::cleanup()
{
emit logItem(tr("Cleaning up..."),LOGINFO);
for(int i=0; i < m_talkList.size(); i++)
{
if(QFile::exists(m_talkList[i].wavfilename))
QFile::remove(m_talkList[i].wavfilename);
if(QFile::exists(m_talkList[i].talkfilename))
QFile::remove(m_talkList[i].talkfilename);
QCoreApplication::processEvents();
}
emit logItem(tr("Finished"),LOGINFO);
return;
}

View file

@ -0,0 +1,77 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef VOICEFILE_H
#define VOICEFILE_H
#include <QtCore>
#include "progressloggerinterface.h"
#include "httpget.h"
#include "voicefont.h"
#include "talkgenerator.h"
class VoiceFileCreator :public QObject
{
Q_OBJECT
public:
VoiceFileCreator(QObject* parent);
//start creation
bool createVoiceFile();
void setMountPoint(QString mountpoint) {m_mountpoint =mountpoint; }
void setLang(QString name) { m_lang = name; }
void setWavtrimThreshold(int th){m_wavtrimThreshold = th;}
public slots:
void abort();
signals:
void done(bool);
void aborted();
void logItem(QString, int); //! set logger item
void logProgress(int, int); //! set progress bar.
private slots:
void downloadDone(bool error);
private:
void create(void);
void cleanup();
HttpGet *getter;
QString m_filename; //the temporary file
QString m_mountpoint; //mountpoint of the device
QString m_path; //path where the wav and mp3 files are stored to
int m_targetid; //the target id
QString m_lang; // the language which will be spoken
QString m_versionstring; // version string to be used for logging
int m_wavtrimThreshold;
int m_voiceformat;
bool m_abort;
QList<TalkGenerator::TalkEntry> m_talkList;
};
#endif

View file

@ -0,0 +1,204 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include "zipinstaller.h"
#include "utils.h"
#include "ziputil.h"
#include "Logger.h"
ZipInstaller::ZipInstaller(QObject* parent) :
QObject(parent),
m_unzip(true), m_usecache(false), m_getter(nullptr)
{
}
void ZipInstaller::install()
{
LOG_INFO() << "initializing installation";
m_runner = 0;
connect(this, &ZipInstaller::cont, this, &ZipInstaller::installContinue);
m_url = m_urllist.at(m_runner);
m_logsection = m_loglist.at(m_runner);
m_logver = m_verlist.at(m_runner);
installStart();
}
void ZipInstaller::abort()
{
LOG_INFO() << "Aborted";
emit internalAborted();
}
void ZipInstaller::installContinue()
{
LOG_INFO() << "continuing installation";
m_runner++; // this gets called when a install finished, so increase first.
LOG_INFO() << "runner done:" << m_runner << "/" << m_urllist.size();
if(m_runner < m_urllist.size()) {
emit logItem(tr("done."), LOGOK);
m_url = m_urllist.at(m_runner);
m_logsection = m_loglist.at(m_runner);
if(m_runner < m_verlist.size()) m_logver = m_verlist.at(m_runner);
else m_logver = "";
installStart();
}
else {
emit logItem(tr("Package installation finished successfully."), LOGOK);
emit done(false);
return;
}
}
void ZipInstaller::installStart()
{
LOG_INFO() << "starting installation";
emit logItem(tr("Downloading file %1.%2").arg(QFileInfo(m_url).baseName(),
QFileInfo(m_url).completeSuffix()),LOGINFO);
// temporary file needs to be opened to get the filename
// make sure to get a fresh one on each run.
// making this a parent of the temporary file ensures the file gets deleted
// after the class object gets destroyed.
m_downloadFile = new QTemporaryFile(this);
m_downloadFile->open();
m_file = m_downloadFile->fileName();
m_downloadFile->close();
// get the real file.
if(m_getter != nullptr) m_getter->deleteLater();
m_getter = new HttpGet(this);
if(m_usecache) {
m_getter->setCache(true);
}
m_getter->setFile(m_downloadFile);
connect(m_getter, &HttpGet::done, this, &ZipInstaller::downloadDone);
connect(m_getter, &HttpGet::dataReadProgress, this, &ZipInstaller::logProgress);
connect(this, &ZipInstaller::internalAborted, m_getter, &HttpGet::abort);
m_getter->getFile(QUrl(m_url));
}
void ZipInstaller::downloadDone(bool error)
{
LOG_INFO() << "download done, error:" << error;
QStringList zipContents; // needed later
// update progress bar
emit logProgress(1, 1);
if(m_getter->httpResponse() != 200 && !m_getter->isCached()) {
emit logItem(tr("Download error: received HTTP error %1\n%2")
.arg(m_getter->httpResponse()).arg(m_getter->errorString()),
LOGERROR);
emit done(true);
return;
}
if(m_getter->isCached())
emit logItem(tr("Cached file used."), LOGINFO);
if(error) {
emit logItem(tr("Download error: %1").arg(m_getter->errorString()), LOGERROR);
emit done(true);
return;
}
else emit logItem(tr("Download finished."),LOGOK);
QCoreApplication::processEvents();
if(m_unzip) {
// unzip downloaded file
LOG_INFO() << "about to unzip" << m_file << "to" << m_mountpoint;
emit logItem(tr("Extracting file."), LOGINFO);
QCoreApplication::processEvents();
ZipUtil zip(this);
connect(&zip, &ZipUtil::logProgress, this, &ZipInstaller::logProgress);
connect(&zip, &ZipUtil::logItem, this, &ZipInstaller::logItem);
zip.open(m_file, QuaZip::mdUnzip);
// check for free space. Make sure after installation will still be
// some room for operating (also includes calculation mistakes due to
// cluster sizes on the player).
if((qint64)Utils::filesystemFree(m_mountpoint)
< (zip.totalUncompressedSize(
Utils::filesystemSize(m_mountpoint, Utils::FilesystemClusterSize))
+ 1000000)) {
emit logItem(tr("Not enough disk space! Aborting."), LOGERROR);
emit logProgress(1, 1);
emit done(true);
return;
}
zipContents = zip.files();
if(!zip.extractArchive(m_mountpoint)) {
emit logItem(tr("Extraction failed!"), LOGERROR);
emit logProgress(1, 1);
emit done(true);
return;
}
zip.close();
}
else {
if (m_target.isEmpty())
m_target = QUrl(m_url).fileName();
QString destfile = m_mountpoint + "/" + m_target;
// only copy the downloaded file to the output location / name
emit logItem(tr("Installing file."), LOGINFO);
LOG_INFO() << "saving downloaded file (no extraction) to" << destfile;
m_downloadFile->open(); // copy fails if file is not opened (filename issue?)
// make sure the required path is existing
QString path = QFileInfo(destfile).absolutePath();
QDir p;
p.mkpath(path);
// QFile::copy() doesn't overwrite files, so remove old one first
// TODO: compare old and new file and fail if those are different.
QFile(destfile).remove();
if(!m_downloadFile->copy(destfile)) {
emit logItem(tr("Installing file failed."), LOGERROR);
emit done(true);
return;
}
// add file to log
zipContents.append(m_target);
}
if(m_logver.isEmpty()) {
// if no version info is set use the timestamp of the server file.
m_logver = m_getter->timestamp().toString(Qt::ISODate);
}
emit logItem(tr("Creating installation log"),LOGINFO);
QSettings installlog(m_mountpoint + "/.rockbox/rbutil.log", QSettings::IniFormat, nullptr);
installlog.beginGroup(m_logsection);
for(int i = 0; i < zipContents.size(); i++)
{
installlog.setValue(zipContents.at(i), m_logver);
}
installlog.endGroup();
installlog.sync();
emit cont();
}

View file

@ -0,0 +1,88 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef ZIPINSTALLER_H
#define ZIPINSTALLER_H
#include <QtCore>
#include "progressloggerinterface.h"
#include "httpget.h"
#include "Logger.h"
/** Install a file or zip.
* Downloads file(s) from a given URL, and installs by either extracting or
* copying it to the target path set by setMountpoint().
*/
class ZipInstaller : public QObject
{
Q_OBJECT
public:
ZipInstaller(QObject* parent);
~ZipInstaller(){}
void install(void);
void setMountPoint(QString mountpoint) {m_mountpoint = mountpoint;}
void setUrl(QString url){m_urllist = QStringList(url);}
void setUrl(QStringList url) { m_urllist = url; }
void setLogSection(QString name) {m_loglist = QStringList(name);}
void setLogSection(QStringList name) { m_loglist = name; }
void setLogVersion(QString v = "")
{ m_verlist = QStringList(v); LOG_INFO() << m_verlist;}
void setLogVersion(QStringList v)
{ m_verlist = v; LOG_INFO() << m_verlist;}
/** Change between copy and unzip mode. */
void setUnzip(bool i) { m_unzip = i; }
/** Set target filename for copy mode.
* If not set the filename part of the download URL is used. */
void setTarget(QString t) { m_target = t; }
void setCache(bool c) { m_usecache = c; }
public slots:
void abort(void);
private slots:
void downloadDone(bool);
void installStart(void);
void installContinue(void);
signals:
void done(bool error);
void cont();
void logItem(QString, int); //! set logger item
void logProgress(int, int); //! set progress bar.
void internalAborted(void);
private:
QString m_url, m_file, m_mountpoint, m_logsection, m_logver;
QStringList m_urllist, m_loglist, m_verlist;
bool m_unzip;
QString m_target;
int m_runner;
bool m_usecache;
HttpGet *m_getter;
QTemporaryFile *m_downloadFile;
};
#endif

View file

@ -0,0 +1,302 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2011 Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QtCore>
#include <QDebug>
#include "ziputil.h"
#include "progressloggerinterface.h"
#include "Logger.h"
#include "quazip/quazip.h"
#include "quazip/quazipfile.h"
#include "quazip/quazipfileinfo.h"
ZipUtil::ZipUtil(QObject* parent) : ArchiveUtil(parent)
{
m_zip = nullptr;
}
ZipUtil::~ZipUtil()
{
if(m_zip) {
delete m_zip;
}
}
//! @brief open zip file.
//! @param zipfile path to zip file
//! @param mode open mode (see QuaZip::Mode)
//! @return true on success, false otherwise
bool ZipUtil::open(QString& zipfile, QuaZip::Mode mode)
{
m_zip = new QuaZip(zipfile);
return m_zip->open(mode);
}
//! @brief close zip file.
//! @return true on success, false otherwise
bool ZipUtil::close(void)
{
if(!m_zip) {
return false;
}
int error = UNZ_OK;
if(m_zip->isOpen()) {
m_zip->close();
error = m_zip->getZipError();
}
delete m_zip;
m_zip = nullptr;
return (error == UNZ_OK) ? true : false;
}
//! @brief extract currently opened archive
//! @brief dest path to extract archive to, can be filename when extracting a
//! single file.
//! @brief file file to extract from archive, full archive if empty.
//! @return true on success, false otherwise
bool ZipUtil::extractArchive(const QString& dest, QString file)
{
LOG_INFO() << "extractArchive" << dest << file;
bool result = true;
if(!m_zip) {
return false;
}
QuaZipFile *currentFile = new QuaZipFile(m_zip);
int entries = m_zip->getEntriesCount();
int current = 0;
// construct the filename when extracting a single file from an archive.
// if the given destination is a full path use it as output name,
// otherwise use it as path to place the file as named in the archive.
QString singleoutfile;
if(!file.isEmpty() && QFileInfo(dest).isDir()) {
singleoutfile = dest + "/" + file;
}
else if(!file.isEmpty()){
singleoutfile = dest;
}
for(bool more = m_zip->goToFirstFile(); more; more = m_zip->goToNextFile())
{
++current;
// if the entry is a path ignore it. Path existence is ensured separately.
if(m_zip->getCurrentFileName().split("/").last() == "")
continue;
// some tools set the MS-DOS file attributes. Check those for D flag,
// since in some cases a folder entry does not end with a /
QuaZipFileInfo fi;
currentFile->getFileInfo(&fi);
if(fi.externalAttr & 0x10) // FAT entry bit 4 indicating directory
continue;
QString outfilename;
if(!singleoutfile.isEmpty()
&& QFileInfo(m_zip->getCurrentFileName()).fileName() == file) {
outfilename = singleoutfile;
}
else if(singleoutfile.isEmpty()) {
outfilename = dest + "/" + m_zip->getCurrentFileName();
}
if(outfilename.isEmpty())
continue;
QFile outputFile(outfilename);
// make sure the output path exists
if(!QDir().mkpath(QFileInfo(outfilename).absolutePath())) {
result = false;
emit logItem(tr("Creating output path failed"), LOGERROR);
LOG_INFO() << "creating output path failed for:"
<< outfilename;
break;
}
if(!outputFile.open(QFile::WriteOnly)) {
result = false;
emit logItem(tr("Creating output file failed"), LOGERROR);
LOG_INFO() << "creating output file failed:"
<< outfilename;
break;
}
currentFile->open(QIODevice::ReadOnly);
outputFile.write(currentFile->readAll());
if(currentFile->getZipError() != UNZ_OK) {
result = false;
emit logItem(tr("Error during Zip operation"), LOGERROR);
LOG_INFO() << "QuaZip error:" << currentFile->getZipError()
<< "on file" << currentFile->getFileName();
break;
}
currentFile->close();
outputFile.close();
emit logProgress(current, entries);
}
delete currentFile;
emit logProgress(1, 1);
return result;
}
//! @brief append a folder to current archive
//! @param source source folder
//! @param basedir base folder for archive. Will get stripped from zip paths.
//! @return true on success, false otherwise
bool ZipUtil::appendDirToArchive(QString& source, QString& basedir)
{
bool result = true;
if(!m_zip || !m_zip->isOpen()) {
LOG_INFO() << "Zip file not open!";
return false;
}
// get a list of all files and folders. Needed for progress info and avoids
// recursive calls.
QDirIterator iterator(source, QDirIterator::Subdirectories);
QStringList fileList;
while(iterator.hasNext()) {
iterator.next();
// skip folders, we can't add them.
if(!QFileInfo(iterator.filePath()).isDir()) {
fileList.append(iterator.filePath());
}
}
LOG_INFO() << "Adding" << fileList.size() << "files to archive";
int max = fileList.size();
for(int i = 0; i < max; i++) {
QString current = fileList.at(i);
if(!appendFileToArchive(current, basedir)) {
LOG_ERROR() << "Error appending file" << current
<< "to archive" << m_zip->getZipName();
result = false;
break;
}
emit logProgress(i, max);
}
return result;
}
//! @brief append a single file to current archive
//!
bool ZipUtil::appendFileToArchive(QString& file, QString& basedir)
{
bool result = true;
if(!m_zip || !m_zip->isOpen()) {
LOG_ERROR() << "Zip file not open!";
return false;
}
// skip folders, we can't add them.
QFileInfo fileinfo(file);
if(fileinfo.isDir()) {
return false;
}
QString infile = fileinfo.canonicalFilePath();
QString newfile = infile;
newfile.remove(QDir(basedir).canonicalPath() + "/");
QuaZipFile fout(m_zip);
QFile fin(file);
if(!fin.open(QFile::ReadOnly)) {
LOG_ERROR() << "Could not open file for reading:" << file;
return false;
}
if(!fout.open(QIODevice::WriteOnly, QuaZipNewInfo(newfile, infile))) {
fin.close();
LOG_ERROR() << "Could not open file for writing:" << newfile;
return false;
}
result = (fout.write(fin.readAll()) < 0) ? false : true;
fin.close();
fout.close();
return result;
}
//! @brief calculate total size of extracted files
qint64 ZipUtil::totalUncompressedSize(unsigned int clustersize)
{
qint64 uncompressed = 0;
QList<QuaZipFileInfo> items = contentProperties();
if(items.size() == 0) {
return -1;
}
int max = items.size();
if(clustersize > 0) {
for(int i = 0; i < max; ++i) {
qint64 item = items.at(i).uncompressedSize;
uncompressed += (item + clustersize - (item % clustersize));
}
}
else {
for(int i = 0; i < max; ++i) {
uncompressed += items.at(i).uncompressedSize;
}
}
if(clustersize > 0) {
LOG_INFO() << "calculation rounded to cluster size for each file:"
<< clustersize;
}
LOG_INFO() << "size of archive files uncompressed:"
<< uncompressed;
return uncompressed;
}
QStringList ZipUtil::files(void)
{
QList<QuaZipFileInfo> items = contentProperties();
QStringList fileList;
if(items.size() == 0) {
return fileList;
}
int max = items.size();
for(int i = 0; i < max; ++i) {
fileList.append(items.at(i).name);
}
return fileList;
}
QList<QuaZipFileInfo> ZipUtil::contentProperties()
{
QList<QuaZipFileInfo> items;
if(!m_zip || !m_zip->isOpen()) {
LOG_ERROR() << "Zip file not open!";
return items;
}
QuaZipFileInfo info;
QuaZipFile currentFile(m_zip);
for(bool more = m_zip->goToFirstFile(); more; more = m_zip->goToNextFile())
{
currentFile.getFileInfo(&info);
if(currentFile.getZipError() != UNZ_OK) {
LOG_ERROR() << "QuaZip error:" << currentFile.getZipError()
<< "on file" << currentFile.getFileName();
return QList<QuaZipFileInfo>();
}
items.append(info);
}
return items;
}

View file

@ -0,0 +1,53 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2011 Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef ZIPUTIL_H
#define ZIPUTIL_H
#include <QtCore>
#include "archiveutil.h"
#include "quazip/quazip.h"
#include "quazip/quazipfile.h"
#include "quazip/quazipfileinfo.h"
class ZipUtil : public ArchiveUtil
{
Q_OBJECT
public:
ZipUtil(QObject* parent);
~ZipUtil();
bool open(QString& zipfile, QuaZip::Mode mode = QuaZip::mdUnzip);
virtual bool close(void);
virtual bool extractArchive(const QString& dest, QString file = "");
bool appendDirToArchive(QString& source, QString& basedir);
bool appendFileToArchive(QString& file, QString& basedir);
qint64 totalUncompressedSize(unsigned int clustersize = 0);
virtual QStringList files(void);
signals:
void logProgress(int, int);
void logItem(QString, int);
private:
QList<QuaZipFileInfo> contentProperties();
QuaZip* m_zip;
};
#endif

View file

@ -0,0 +1,42 @@
# Rockbox Utility changelog.
# This file is parsed by Rockbox Utility. Format:
# - Lines starting with # are comments and ignored.
# - A version starts with the string "Version" followed by the number.
# - After the version individual entries follow. Those start with a *.
# - After the entries an empty line has to follow.
# - After that the next version can start.
Version 1.4
* Rework player detection functionality to provide better results.
* Limit mountpoints ("Select your device in the filesystem") in configuration dialog to usable ones.
* Change encoder volume configuration to allow more sensible values.
* Save proxy password differently in configuration file (better solution for FS#12166).
* Add support for building Rockbox Utility with Qt5.
* Add support for extracting Original Firmware files compressed with CAB (G#418).
* Add support for Creative Zen X-Fi3 (G#419).
* Add Changelog window.
* Rework System Trace functionality.
* Add support for Iriver H300 v1.31K firmware.
* Add support for Sandisk Sansa Clip Zip v01.01.21 firmware.
* Fix manual link for Archos Recorder V2.
Version 1.4.1
* Fix crash on detecting player in MTP mode (FS#12989).
* Extend hint when uninstallation requires reinstalling the Original Firmware.
* Improve update check information dialog.
* Correct USB IDs for Sandisk Sansa c200v2.
* Add support for iPod Classic 6G bootloader.
Version 1.5.0
* Add support for xDuoo X3, X3ii, X20.
* Add support for AGPTek Rocker.
* Add support for AIGO Eros Q and various clones (AIGO Eros K, AGPTek H3, HIFI Walker H2, Surfans F20)
* Add support for showing "retired" target status.
* Update Iriver H100 / H300 series bootloader.
* Rename Game Files installation to Plugin Data; now installs files for further games (Duke3D, Wolfenstein3D, etc.) as well as other plugins (MIDI patchset)
* Support installing daily builds.
* Move Manual / prerendered voice file installation to main dialog.
* Support installing voice files in other languages than english (available languages depends on build).
* Improved HiDPI support.
* Drop support for Qt4, now requires Qt5.
* Update various external libraries.

View file

@ -0,0 +1,984 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QMessageBox>
#include <QProgressDialog>
#include <QFileDialog>
#include <QUrl>
#ifdef QT_MULTIMEDIA_LIB
#include <QSound>
#endif
#include "version.h"
#include "configure.h"
#include "autodetection.h"
#include "ui_configurefrm.h"
#include "encoderbase.h"
#include "ttsbase.h"
#include "system.h"
#include "encttscfggui.h"
#include "rbsettings.h"
#include "playerbuildinfo.h"
#include "utils.h"
#include "comboboxviewdelegate.h"
#if defined(Q_OS_WIN32)
#if defined(UNICODE)
#define _UNICODE
#endif
#include <tchar.h>
#include <windows.h>
#endif
#include "rbutilqt.h"
#include "systrace.h"
#include "Logger.h"
#define DEFAULT_LANG "English (en)"
#define DEFAULT_LANG_CODE "en"
Config::Config(QWidget *parent,int index) : QDialog(parent)
{
programPath = qApp->applicationDirPath() + "/";
ui.setupUi(this);
ui.tabConfiguration->setCurrentIndex(index);
ui.radioManualProxy->setChecked(true);
// build language list and sort alphabetically
QStringList langs = findLanguageFiles();
for(int i = 0; i < langs.size(); ++i)
lang.insert(languageName(langs.at(i))
+ QString(" (%1)").arg(langs.at(i)), langs.at(i));
lang.insert(DEFAULT_LANG, DEFAULT_LANG_CODE);
QMap<QString, QString>::const_iterator i = lang.constBegin();
while (i != lang.constEnd()) {
ui.listLanguages->addItem(i.key());
i++;
}
ComboBoxViewDelegate *delegate = new ComboBoxViewDelegate(this);
ui.mountPoint->setItemDelegate(delegate);
#if !defined(DBG)
ui.mountPoint->setEditable(false);
#endif
ui.listLanguages->setSelectionMode(QAbstractItemView::SingleSelection);
ui.proxyPass->setEchoMode(QLineEdit::Password);
ui.treeDevices->setAlternatingRowColors(true);
ui.listLanguages->setAlternatingRowColors(true);
/* Explicitly set some widgets to have left-to-right layout */
ui.treeDevices->setLayoutDirection(Qt::LeftToRight);
ui.mountPoint->setLayoutDirection(Qt::LeftToRight);
ui.proxyHost->setLayoutDirection(Qt::LeftToRight);
ui.proxyPort->setLayoutDirection(Qt::LeftToRight);
ui.proxyUser->setLayoutDirection(Qt::LeftToRight);
ui.proxyPass->setLayoutDirection(Qt::LeftToRight);
ui.listLanguages->setLayoutDirection(Qt::LeftToRight);
ui.cachePath->setLayoutDirection(Qt::LeftToRight);
ui.comboTts->setLayoutDirection(Qt::LeftToRight);
this->setModal(true);
connect(ui.buttonOk, SIGNAL(clicked()), this, SLOT(accept()));
connect(ui.buttonCancel, SIGNAL(clicked()), this, SLOT(abort()));
connect(ui.radioNoProxy, SIGNAL(toggled(bool)), this, SLOT(setNoProxy(bool)));
connect(ui.radioSystemProxy, SIGNAL(toggled(bool)), this, SLOT(setSystemProxy(bool)));
connect(ui.refreshMountPoint, SIGNAL(clicked()), this, SLOT(refreshMountpoint()));
connect(ui.buttonAutodetect,SIGNAL(clicked()),this,SLOT(autodetect()));
connect(ui.buttonCacheBrowse, SIGNAL(clicked()), this, SLOT(browseCache()));
connect(ui.buttonCacheClear, SIGNAL(clicked()), this, SLOT(cacheClear()));
connect(ui.configTts, SIGNAL(clicked()), this, SLOT(configTts()));
connect(ui.configEncoder, SIGNAL(clicked()), this, SLOT(configEnc()));
connect(ui.comboTts, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTtsState(int)));
connect(ui.treeDevices, SIGNAL(itemSelectionChanged()), this, SLOT(updateEncState()));
connect(ui.testTTS,SIGNAL(clicked()),this,SLOT(testTts()));
connect(ui.showDisabled, SIGNAL(toggled(bool)), this, SLOT(showDisabled(bool)));
connect(ui.mountPoint, SIGNAL(editTextChanged(QString)), this, SLOT(updateMountpoint(QString)));
connect(ui.mountPoint, SIGNAL(currentIndexChanged(int)), this, SLOT(updateMountpoint(int)));
connect(ui.checkShowProxyPassword, SIGNAL(toggled(bool)), this, SLOT(showProxyPassword(bool)));
// delete this dialog after it finished automatically.
connect(this, SIGNAL(finished(int)), this, SLOT(deleteLater()));
setUserSettings();
setDevices();
}
void Config::accept()
{
LOG_INFO() << "checking configuration";
QString errormsg = tr("The following errors occurred:") + "<ul>";
bool error = false;
// proxy: save entered proxy values, not displayed.
if(ui.radioManualProxy->isChecked()) {
proxy.setScheme("http");
proxy.setUserName(ui.proxyUser->text());
proxy.setPassword(ui.proxyPass->text());
proxy.setHost(ui.proxyHost->text());
proxy.setPort(ui.proxyPort->value());
}
// Encode the password using base64 before storing it to the configuration
// file.
// There are two reasons for doing this:
// - QUrl::toEncoded() has problems with some characters like the colon and
// @. Those are not percent encoded, causing the string getting parsed
// wrongly when reading it back (see FS#12166).
// - The password is cleartext in the configuration file.
// While using base64 doesn't provide any real security either it's at
// least better than plaintext.
// Since this program is open source any fixed mechanism to obfuscate /
// encrypt the password isn't much help either since anyone interested in
// the password can look at the sources. The best way would be to
// eventually use host OS functionality to store the password.
QUrl p = proxy;
p.setPassword(proxy.password().toUtf8().toBase64());
RbSettings::setValue(RbSettings::Proxy, p.toString());
LOG_INFO() << "setting proxy to:" << proxy.toString(QUrl::RemovePassword);
// proxy type
QString proxyType;
if(ui.radioNoProxy->isChecked()) proxyType = "none";
else if(ui.radioSystemProxy->isChecked()) proxyType = "system";
else proxyType = "manual";
RbSettings::setValue(RbSettings::ProxyType, proxyType);
RbSettings::setValue(RbSettings::Language, language);
// make sure mountpoint is read from dropdown box
if(mountpoint.isEmpty()) {
updateMountpoint(ui.mountPoint->currentIndex());
}
// mountpoint
if(mountpoint.isEmpty()) {
errormsg += "<li>" + tr("No mountpoint given") + "</li>";
error = true;
}
else if(!QFileInfo::exists(mountpoint)) {
errormsg += "<li>" + tr("Mountpoint does not exist") + "</li>";
error = true;
}
else if(!QFileInfo(mountpoint).isDir()) {
errormsg += "<li>" + tr("Mountpoint is not a directory.") + "</li>";
error = true;
}
else if(!QFileInfo(mountpoint).isWritable()) {
errormsg += "<li>" + tr("Mountpoint is not writeable") + "</li>";
error = true;
}
else {
RbSettings::setValue(RbSettings::Mountpoint,
QDir::fromNativeSeparators(mountpoint));
}
// platform
QString nplat;
if(ui.treeDevices->selectedItems().size() != 0) {
nplat = ui.treeDevices->selectedItems().at(0)->data(0, Qt::UserRole).toString();
RbSettings::setValue(RbSettings::Platform, nplat);
}
else {
errormsg += "<li>" + tr("No player selected") + "</li>";
error = true;
}
// cache settings
if(QFileInfo(ui.cachePath->text()).isDir()) {
if(!QFileInfo(ui.cachePath->text()).isWritable()) {
errormsg += "<li>" + tr("Cache path not writeable. Leave path empty "
"to default to systems temporary path.") + "</li>";
error = true;
}
else
RbSettings::setValue(RbSettings::CachePath, ui.cachePath->text());
}
else // default to system temp path
RbSettings::setValue(RbSettings::CachePath, QDir::tempPath());
RbSettings::setValue(RbSettings::CacheDisabled, ui.cacheDisable->isChecked());
// tts settings
RbSettings::setValue(RbSettings::UseTtsCorrections, ui.ttsCorrections->isChecked());
int i = ui.comboTts->currentIndex();
RbSettings::setValue(RbSettings::Tts, ui.comboTts->itemData(i).toString());
RbSettings::setValue(RbSettings::RbutilVersion, PUREVERSION);
errormsg += "</ul>";
errormsg += tr("You need to fix the above errors before you can continue.");
if(error) {
QMessageBox::critical(this, tr("Configuration error"), errormsg);
}
else {
// sync settings
RbSettings::sync();
this->close();
emit settingsUpdated();
}
}
void Config::abort()
{
LOG_INFO() << "aborted.";
this->close();
}
void Config::setUserSettings()
{
// set proxy
proxy.setUrl(RbSettings::value(RbSettings::Proxy).toString(),
QUrl::StrictMode);
// password is base64 encoded in configuration.
QByteArray pw = QByteArray::fromBase64(proxy.password().toUtf8());
proxy.setPassword(pw);
ui.proxyPort->setValue(proxy.port());
ui.proxyHost->setText(proxy.host());
ui.proxyUser->setText(proxy.userName());
ui.proxyPass->setText(proxy.password());
QString proxyType = RbSettings::value(RbSettings::ProxyType).toString();
if(proxyType == "manual") ui.radioManualProxy->setChecked(true);
else if(proxyType == "system") ui.radioSystemProxy->setChecked(true);
else ui.radioNoProxy->setChecked(true);
// set language selection
QList<QListWidgetItem*> a;
QString b;
// find key for lang value
QMap<QString, QString>::const_iterator i = lang.constBegin();
QString l = RbSettings::value(RbSettings::Language).toString();
if(l.isEmpty())
l = QLocale::system().name();
while (i != lang.constEnd()) {
if(i.value() == l) {
b = i.key();
break;
}
else if(l.startsWith(i.value(), Qt::CaseInsensitive)) {
// check if there is a base language (en -> en_US, etc.)
b = i.key();
break;
}
i++;
}
a = ui.listLanguages->findItems(b, Qt::MatchExactly);
if(a.size() > 0)
ui.listLanguages->setCurrentItem(a.at(0));
// don't connect before language list has been set up to prevent
// triggering the signal by selecting the saved language.
connect(ui.listLanguages, SIGNAL(itemSelectionChanged()), this, SLOT(updateLanguage()));
// devices tab
refreshMountpoint();
mountpoint = QDir::toNativeSeparators(RbSettings::value(RbSettings::Mountpoint).toString());
setMountpoint(mountpoint);
// cache tab
if(!QFileInfo(RbSettings::value(RbSettings::CachePath).toString()).isDir())
RbSettings::setValue(RbSettings::CachePath, QDir::tempPath());
ui.cachePath->setText(QDir::toNativeSeparators(RbSettings::value(RbSettings::CachePath).toString()));
ui.cacheDisable->setChecked(RbSettings::value(RbSettings::CacheDisabled).toBool());
updateCacheInfo(RbSettings::value(RbSettings::CachePath).toString());
// TTS tab
ui.ttsCorrections->setChecked(RbSettings::value(RbSettings::UseTtsCorrections).toBool());
}
void Config::updateCacheInfo(QString path)
{
qint64 sz = Utils::recursiveFolderSize(path + "/rbutil-cache");
ui.cacheSize->setText(tr("Current cache size is %L1 kiB.")
.arg(sz/1024));
}
void Config::showProxyPassword(bool show)
{
if(show)
ui.proxyPass->setEchoMode(QLineEdit::Normal);
else
ui.proxyPass->setEchoMode(QLineEdit::Password);
}
void Config::showDisabled(bool show)
{
LOG_INFO() << "disabled targets shown:" << show;
if(show)
QMessageBox::warning(this, tr("Showing disabled targets"),
tr("You just enabled showing targets that are marked disabled. "
"Disabled targets are not recommended to end users. Please "
"use this option only if you know what you are doing."));
setDevices();
}
void Config::setDevices()
{
// setup devices table
LOG_INFO() << "setting up devices list";
QStringList targets;
if(ui.showDisabled->isChecked())
targets = PlayerBuildInfo::instance()->value(
PlayerBuildInfo::TargetNamesAll).toStringList();
else
targets = PlayerBuildInfo::instance()->value(
PlayerBuildInfo::TargetNamesEnabled).toStringList();
QMultiMap <QString, QString> manuf;
for(int it = 0; it < targets.size(); it++)
{
QString curbrand = PlayerBuildInfo::instance()->value(
PlayerBuildInfo::Brand, targets.at(it)).toString();
manuf.insert(curbrand, targets.at(it));
}
// set up devices table
ui.treeDevices->header()->hide();
ui.treeDevices->expandAll();
ui.treeDevices->setColumnCount(1);
QList<QTreeWidgetItem *> items;
// get manufacturers
QStringList brands = manuf.uniqueKeys();
QTreeWidgetItem *w;
QTreeWidgetItem *w2;
QTreeWidgetItem *w3 = nullptr;
QString selected = RbSettings::value(RbSettings::Platform).toString();
for(int c = 0; c < brands.size(); c++) {
w = new QTreeWidgetItem();
w->setFlags(Qt::ItemIsEnabled);
w->setText(0, brands.at(c));
items.append(w);
// go through platforms and add all players matching the current brand
for(int it = 0; it < targets.size(); it++) {
// skip if not current brand
if(!manuf.values(brands.at(c)).contains(targets.at(it)))
continue;
// construct display name
QString curname = QString("%1 (%2)").arg(
PlayerBuildInfo::instance()->value(PlayerBuildInfo::DisplayName,
targets.at(it)).toString(),
PlayerBuildInfo::instance()->statusAsString(targets.at(it)));
LOG_INFO() << "add supported device:" << brands.at(c) << curname;
w2 = new QTreeWidgetItem(w, QStringList(curname));
w2->setData(0, Qt::UserRole, targets.at(it));
if(targets.at(it) == selected) {
w2->setSelected(true);
w->setExpanded(true);
w3 = w2; // save pointer to hilight old selection
}
items.append(w2);
}
}
// remove any old items in list
QTreeWidgetItem* widgetitem;
do {
widgetitem = ui.treeDevices->takeTopLevelItem(0);
delete widgetitem;
}
while(widgetitem);
// add new items
ui.treeDevices->insertTopLevelItems(0, items);
if(w3 != nullptr) {
ui.treeDevices->setCurrentItem(w3); // hilight old selection
ui.treeDevices->scrollToItem(w3);
}
// tts / encoder tab
//encoders
updateEncState();
//tts
QStringList ttslist = TTSBase::getTTSList();
for(int a = 0; a < ttslist.size(); a++)
ui.comboTts->addItem(TTSBase::getTTSName(ttslist.at(a)), ttslist.at(a));
//update index of combobox
int index = ui.comboTts->findData(RbSettings::value(RbSettings::Tts).toString());
if(index < 0) index = 0;
ui.comboTts->setCurrentIndex(index);
updateTtsState(index);
}
void Config::updateTtsState(int index)
{
QString ttsName = ui.comboTts->itemData(index).toString();
TTSBase* tts = TTSBase::getTTS(this,ttsName);
if(!tts)
{
QMessageBox::critical(this, tr("TTS error"),
tr("The selected TTS failed to initialize. You can't use this TTS."));
return;
}
if(tts->configOk())
{
ui.configTTSstatus->setText(tr("Configuration OK"));
ui.configTTSstatusimg->setPixmap(QPixmap(QString::fromUtf8(":/icons/go-next.svg")));
#ifdef QT_MULTIMEDIA_LIB
ui.testTTS->setEnabled(true);
#else
ui.testTTS->setEnabled(false);
#endif
}
else
{
ui.configTTSstatus->setText(tr("Configuration INVALID"));
ui.configTTSstatusimg->setPixmap(QPixmap(QString::fromUtf8(":/icons/dialog-error.svg")));
ui.testTTS->setEnabled(false);
}
delete tts; /* Config objects are never deleted (in fact, they are leaked..), so we can't rely on QObject,
since that would delete the TTSBase instance on application exit*/
}
void Config::updateEncState()
{
if(ui.treeDevices->selectedItems().size() == 0)
return;
QString devname = ui.treeDevices->selectedItems().at(0)->data(0, Qt::UserRole).toString();
QString encoder = PlayerBuildInfo::instance()->value(
PlayerBuildInfo::Encoder, devname).toString();
ui.encoderName->setText(EncoderBase::getEncoderName(
PlayerBuildInfo::instance()->value(PlayerBuildInfo::Encoder, devname).toString()));
EncoderBase* enc = EncoderBase::getEncoder(this,encoder);
if(enc->configOk())
{
ui.configEncstatus->setText(tr("Configuration OK"));
ui.configEncstatusimg->setPixmap(QPixmap(QString::fromUtf8(":/icons/go-next.svg")));
}
else
{
ui.configEncstatus->setText(tr("Configuration INVALID"));
ui.configEncstatusimg->setPixmap(QPixmap(QString::fromUtf8(":/icons/dialog-error.svg")));
}
}
void Config::setNoProxy(bool checked)
{
ui.proxyPort->setEnabled(!checked);
ui.proxyHost->setEnabled(!checked);
ui.proxyUser->setEnabled(!checked);
ui.proxyPass->setEnabled(!checked);
ui.checkShowProxyPassword->setEnabled(!checked);
ui.checkShowProxyPassword->setChecked(false);
showProxyPassword(false);
}
void Config::setSystemProxy(bool checked)
{
setNoProxy(checked);
if(checked) {
// save values in input box
proxy.setScheme("http");
proxy.setUserName(ui.proxyUser->text());
proxy.setPassword(ui.proxyPass->text());
proxy.setHost(ui.proxyHost->text());
proxy.setPort(ui.proxyPort->value());
// show system values in input box
QUrl envproxy = System::systemProxy();
LOG_INFO() << "setting system proxy" << envproxy;
ui.proxyHost->setText(envproxy.host());
ui.proxyPort->setValue(envproxy.port());
ui.proxyUser->setText(envproxy.userName());
ui.proxyPass->setText(envproxy.password());
if(envproxy.host().isEmpty() || envproxy.port() == -1) {
LOG_WARNING() << "system proxy is invalid.";
QMessageBox::warning(this, tr("Proxy Detection"),
tr("The System Proxy settings are invalid!\n"
"Rockbox Utility can't work with this proxy settings. "
"Make sure the system proxy is set correctly. Note that "
"\"proxy auto-config (PAC)\" scripts are not supported by "
"Rockbox Utility. If your system uses this you need "
"to use manual proxy settings."),
QMessageBox::Ok ,QMessageBox::Ok);
// the current proxy settings are invalid. Check the saved proxy
// type again.
if(RbSettings::value(RbSettings::ProxyType).toString() == "manual")
ui.radioManualProxy->setChecked(true);
else
ui.radioNoProxy->setChecked(true);
}
}
else {
ui.proxyHost->setText(proxy.host());
ui.proxyPort->setValue(proxy.port());
ui.proxyUser->setText(proxy.userName());
ui.proxyPass->setText(proxy.password());
}
}
QStringList Config::findLanguageFiles()
{
QDir dir(programPath);
QStringList fileNames;
QStringList langs;
fileNames = dir.entryList(QStringList("*.qm"), QDir::Files, QDir::Name);
QDir resDir(":/lang");
fileNames += resDir.entryList(QStringList("*.qm"), QDir::Files, QDir::Name);
QRegExp exp("^rbutil_(.*)\\.qm");
for(int i = 0; i < fileNames.size(); i++) {
QString a = fileNames.at(i);
a.replace(exp, "\\1");
langs.append(a);
}
langs.sort();
LOG_INFO() << "available lang files:" << langs;
return langs;
}
QString Config::languageName(const QString &qmFile)
{
QTranslator translator;
QString file = "rbutil_" + qmFile;
if(!translator.load(file, programPath))
translator.load(file, ":/lang");
return translator.translate("Configure", "English",
"This is the localized language name, i.e. your language.");
}
void Config::updateLanguage()
{
LOG_INFO() << "update selected language";
// remove all old translators
for(int i = 0; i < RbUtilQt::translators.size(); ++i) {
qApp->removeTranslator(RbUtilQt::translators.at(i));
// do not delete old translators, this confuses Qt.
}
RbUtilQt::translators.clear();
QList<QListWidgetItem*> a = ui.listLanguages->selectedItems();
if(a.size() > 0)
language = lang.value(a.at(0)->text());
LOG_INFO() << "new language:" << language;
QTranslator *translator = new QTranslator(qApp);
QTranslator *qttrans = new QTranslator(qApp);
QString absolutePath = QCoreApplication::instance()->applicationDirPath();
if(!translator->load("rbutil_" + language, absolutePath))
translator->load("rbutil_" + language, ":/lang");
if(!qttrans->load("qt_" + language,
QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
qttrans->load("qt_" + language, ":/lang");
qApp->installTranslator(translator);
qApp->installTranslator(qttrans);
//: This string is used to indicate the writing direction. Translate it
//: to "RTL" (without quotes) for RTL languages. Anything else will get
//: treated as LTR language.
if(QObject::tr("LTR") == "RTL")
qApp->setLayoutDirection(Qt::RightToLeft);
else
qApp->setLayoutDirection(Qt::LeftToRight);
RbUtilQt::translators.append(translator);
RbUtilQt::translators.append(qttrans);
QLocale::setDefault(QLocale(language));
}
void Config::browseCache()
{
QString old = ui.cachePath->text();
if(!QFileInfo(old).isDir())
old = QDir::tempPath();
QString c = QFileDialog::getExistingDirectory(this, tr("Set Cache Path"), old);
if(c.isEmpty())
c = old;
else if(!QFileInfo(c).isDir())
c = QDir::tempPath();
ui.cachePath->setText(QDir::toNativeSeparators(c));
updateCacheInfo(c);
}
void Config::refreshMountpoint()
{
// avoid QComboBox to send signals during rebuild to avoid changing to an
// unwanted item.
ui.mountPoint->blockSignals(true);
ui.mountPoint->clear();
QStringList mps = Utils::mountpoints(Utils::MountpointsSupported);
for(int i = 0; i < mps.size(); ++i) {
// add mountpoint as user data so we can change the displayed string
// later (to include volume label or similar)
// Skip unwritable mountpoints, they are not useable for us.
if(QFileInfo(mps.at(i)).isWritable()) {
QString description = tr("%1 (%2 GiB of %3 GiB free)")
.arg(Utils::filesystemName(mps.at(i)))
.arg((double)Utils::filesystemFree(mps.at(i))/(1<<30), 0, 'f', 2)
.arg((double)Utils::filesystemTotal(mps.at(i))/(1<<30), 0, 'f', 2);
ui.mountPoint->addItem(QDir::toNativeSeparators(mps.at(i)), description);
}
else {
LOG_WARNING() << "mountpoint not writable, skipping:" << mps.at(i);
}
}
if(!mountpoint.isEmpty()) {
setMountpoint(mountpoint);
}
ui.mountPoint->blockSignals(false);
}
void Config::updateMountpoint(QString m)
{
if(!m.isEmpty()) {
mountpoint = QDir::fromNativeSeparators(m);
LOG_INFO() << "Mountpoint set to" << mountpoint;
}
}
void Config::updateMountpoint(int idx)
{
if(idx == -1) {
return;
}
QString mp = ui.mountPoint->itemText(idx);
if(!mp.isEmpty()) {
mountpoint = QDir::fromNativeSeparators(mp);
LOG_INFO() << "Mountpoint set to" << mountpoint;
}
}
void Config::setMountpoint(QString m)
{
if(m.isEmpty()) {
return;
}
int index = ui.mountPoint->findText(QDir::toNativeSeparators(m));
if(index != -1) {
ui.mountPoint->setCurrentIndex(index);
}
else {
// keep a mountpoint that is not in the list for convenience (to allow
// easier development)
ui.mountPoint->addItem(QDir::toNativeSeparators(m));
ui.mountPoint->setCurrentIndex(ui.mountPoint->findText(m));
}
LOG_INFO() << "Mountpoint set to" << mountpoint;
}
void Config::autodetect()
{
Autodetection detector(this);
// disable tree during detection as "working" feedback.
// TODO: replace the tree view with a splash screen during this time.
ui.treeDevices->setEnabled(false);
this->setCursor(Qt::WaitCursor);
QCoreApplication::processEvents();
detector.detect();
QList<struct Autodetection::Detected> detected;
detected = detector.detected();
this->unsetCursor();
if(detected.size() > 1) {
// FIXME: handle multiple found players.
QString msg;
msg = tr("Multiple devices have been detected. Please disconnect "
"all players but one and try again.");
msg += "<br/>";
msg += tr("Detected devices:");
msg += "<ul>";
for(int i = 0; i < detected.size(); ++i) {
QString mp = detected.at(i).mountpoint;
if(mp.isEmpty()) {
mp = tr("(unknown)");
}
msg += QString("<li>%1</li>").arg(tr("%1 at %2").arg(
PlayerBuildInfo::instance()->value(
PlayerBuildInfo::DisplayName,
detected.at(i).device).toString(),
QDir::toNativeSeparators(mp)));
}
msg += "</ul>";
msg += tr("Note: detecting connected devices might be ambiguous. "
"You might have less devices connected than listed. "
"In this case it might not be possible to detect your "
"player unambiguously.");
QMessageBox::information(this, tr("Device Detection"), msg);
ui.treeDevices->setEnabled(true);
}
else if(detected.size() == 0) {
QMessageBox::warning(this, tr("Device Detection"),
tr("Could not detect a device.\n"
"Select your device and Mountpoint manually."),
QMessageBox::Ok ,QMessageBox::Ok);
ui.treeDevices->setEnabled(true);
}
else if(detected.at(0).status != Autodetection::PlayerOk
&& detected.at(0).status != Autodetection::PlayerAmbiguous) {
QString msg;
switch(detected.at(0).status) {
case Autodetection::PlayerIncompatible:
msg += tr("Detected an unsupported player:\n%1\n"
"Sorry, Rockbox doesn't run on your player.")
.arg(PlayerBuildInfo::instance()->value(
PlayerBuildInfo::DisplayName,
detected.at(0).device).toString());
break;
case Autodetection::PlayerMtpMode:
msg = tr("%1 in MTP mode found!\n"
"You need to change your player to MSC mode for installation. ")
.arg(PlayerBuildInfo::instance()->value(
PlayerBuildInfo::DisplayName,
detected.at(0).device).toString());
break;
case Autodetection::PlayerWrongFilesystem:
if(PlayerBuildInfo::instance()->value(
PlayerBuildInfo::BootloaderMethod, detected.at(0).device) == "ipod") {
msg = tr("%1 \"MacPod\" found!\n"
"Rockbox needs a FAT formatted Ipod (so-called \"WinPod\") "
"to run. ").arg(PlayerBuildInfo::instance()->value(
PlayerBuildInfo::DisplayName,
detected.at(0).device).toString());
}
else {
msg = tr("The player contains an incompatible filesystem.\n"
"Make sure you selected the correct mountpoint and "
"the player is set up to use a filesystem compatible "
"with Rockbox.");
}
break;
case Autodetection::PlayerError:
default:
msg += tr("An unknown error occured during player detection.");
break;
}
QMessageBox::information(this, tr("Device Detection"), msg);
ui.treeDevices->setEnabled(true);
}
else {
selectDevice(detected.at(0).device, detected.at(0).mountpoint);
}
}
void Config::selectDevice(QString device, QString mountpoint)
{
// collapse all items
for(int a = 0; a < ui.treeDevices->topLevelItemCount(); a++)
ui.treeDevices->topLevelItem(a)->setExpanded(false);
// deselect the selected item(s)
for(int a = 0; a < ui.treeDevices->selectedItems().size(); a++)
ui.treeDevices->selectedItems().at(a)->setSelected(false);
// find the new item
// enumerate all platform items
QList<QTreeWidgetItem*> itmList
= ui.treeDevices->findItems("*",Qt::MatchWildcard);
for(int i=0; i< itmList.size();i++)
{
//enumerate device items
for(int j=0;j < itmList.at(i)->childCount();j++)
{
QString data = itmList.at(i)->child(j)->data(0, Qt::UserRole).toString();
// unset bold flag
QFont f = itmList.at(i)->child(j)->font(0);
f.setBold(false);
itmList.at(i)->child(j)->setFont(0, f);
if(device == data) // item found
{
f.setBold(true);
itmList.at(i)->child(j)->setFont(0, f);
itmList.at(i)->child(j)->setSelected(true); //select the item
itmList.at(i)->setExpanded(true); //expand the platform item
//ui.treeDevices->indexOfTopLevelItem(itmList.at(i)->child(j));
ui.treeDevices->scrollToItem(itmList.at(i)->child(j));
break;
}
}
}
this->unsetCursor();
if(!mountpoint.isEmpty())
{
setMountpoint(mountpoint);
}
else
{
QMessageBox::warning(this, tr("Autodetection"),
tr("Could not detect a Mountpoint.\n"
"Select your Mountpoint manually."),
QMessageBox::Ok, QMessageBox::Ok);
}
ui.treeDevices->setEnabled(true);
}
void Config::cacheClear()
{
if(QMessageBox::critical(this, tr("Really delete cache?"),
tr("Do you really want to delete the cache? "
"Make absolutely sure this setting is correct as it will "
"remove <b>all</b> files in this folder!"),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
return;
QString cache = ui.cachePath->text() + "/rbutil-cache/";
if(!QFileInfo(cache).isDir()) {
QMessageBox::critical(this, tr("Path wrong!"),
tr("The cache path is invalid. Aborting."), QMessageBox::Ok);
return;
}
QDir dir(cache);
dir.removeRecursively();
updateCacheInfo(RbSettings::value(RbSettings::CachePath).toString());
}
void Config::configTts()
{
int index = ui.comboTts->currentIndex();
TTSBase* tts = TTSBase::getTTS(this,ui.comboTts->itemData(index).toString());
EncTtsCfgGui gui(this,tts,TTSBase::getTTSName(ui.comboTts->itemData(index).toString()));
gui.exec();
updateTtsState(ui.comboTts->currentIndex());
delete tts; /* Config objects are never deleted (in fact, they are
leaked..), so we can't rely on QObject, since that would
delete the TTSBase instance on application exit */
}
void Config::testTts()
{
#ifdef QT_MULTIMEDIA_LIB
QString errstr;
int index = ui.comboTts->currentIndex();
TTSBase* tts;
tts = TTSBase::getTTS(this,ui.comboTts->itemData(index).toString());
if(!tts)
{
QMessageBox::critical(this, tr("TTS error"),
tr("The selected TTS failed to initialize. You can't use this TTS."));
return;
}
ui.testTTS->setEnabled(false);
if(!tts->configOk())
{
QMessageBox::warning(this,tr("TTS configuration invalid"),
tr("TTS configuration invalid. \n Please configure TTS engine."));
return;
}
if(!tts->start(&errstr))
{
QMessageBox::warning(this,tr("Could not start TTS engine."),
tr("Could not start TTS engine.\n") + errstr
+ tr("\nPlease configure TTS engine."));
ui.testTTS->setEnabled(true);
return;
}
QString filename;
QTemporaryFile file(this);
// keep filename empty if the TTS can do speaking for itself.
if(!(tts->capabilities() & TTSBase::CanSpeak)) {
file.open();
filename = file.fileName();
file.close();
}
if(tts->voice(tr("Rockbox Utility Voice Test"),filename,&errstr) == FatalError)
{
tts->stop();
QMessageBox::warning(this,tr("Could not voice test string."),
tr("Could not voice test string.\n") + errstr
+ tr("\nPlease configure TTS engine."));
ui.testTTS->setEnabled(false);
return;
}
tts->stop();
if(!filename.isEmpty()) {
QSound::play(filename);
}
ui.testTTS->setEnabled(true);
delete tts; /* Config objects are never deleted (in fact, they are
leaked..), so we can't rely on QObject, since that would
delete the TTSBase instance on application exit */
#endif
}
void Config::configEnc()
{
if(ui.treeDevices->selectedItems().size() == 0)
return;
QString devname = ui.treeDevices->selectedItems().at(0)->data(0, Qt::UserRole).toString();
QString encoder = PlayerBuildInfo::instance()->value(
PlayerBuildInfo::Encoder, devname).toString();
ui.encoderName->setText(
EncoderBase::getEncoderName(PlayerBuildInfo::instance()->value(
PlayerBuildInfo::Encoder, devname).toString()));
EncoderBase* enc = EncoderBase::getEncoder(this,encoder);
EncTtsCfgGui gui(this,enc,EncoderBase::getEncoderName(encoder));
gui.exec();
updateEncState();
}
void Config::changeEvent(QEvent *e)
{
if(e->type() == QEvent::LanguageChange) {
ui.retranslateUi(this);
updateCacheInfo(ui.cachePath->text());
} else {
QWidget::changeEvent(e);
}
}

View file

@ -0,0 +1,79 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Riebeling
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef CONFIGURE_H
#define CONFIGURE_H
#include "ui_configurefrm.h"
#include <QDialog>
#include <QWidget>
#include <QUrl>
class Config : public QDialog
{
Q_OBJECT
public:
Config(QWidget *parent = nullptr,int index=0);
signals:
void settingsUpdated(void);
public slots:
void accept(void);
void abort(void);
private:
void setUserSettings();
void setDevices();
Ui::ConfigForm ui;
QStringList findLanguageFiles(void);
QString languageName(const QString&);
QMap<QString, QString> lang;
QString language;
QString programPath;
QUrl proxy;
QString mountpoint;
void updateCacheInfo(QString);
void changeEvent(QEvent *event);
void selectDevice(QString device, QString mountpoint);
private slots:
void showProxyPassword(bool show);
void setNoProxy(bool);
void setSystemProxy(bool);
void updateLanguage(void);
void refreshMountpoint(void);
void browseCache(void);
void autodetect(void);
void setMountpoint(QString);
void updateMountpoint(QString);
void updateMountpoint(int);
void cacheClear(void);
void configTts(void);
void configEnc(void);
void updateTtsState(int);
void updateEncState();
void testTts();
void showDisabled(bool);
};
#endif

View file

@ -0,0 +1,572 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigForm</class>
<widget class="QDialog" name="ConfigForm">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>465</height>
</rect>
</property>
<property name="windowTitle">
<string>Configuration</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="labelTitle">
<property name="text">
<string>Configure Rockbox Utility</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QTabWidget" name="tabConfiguration">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tabDevice">
<attribute name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/multimedia-player.svg</normaloff>:/icons/multimedia-player.svg</iconset>
</attribute>
<attribute name="title">
<string>&amp;Device</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="labelMountPoint">
<property name="text">
<string>Select your device in the &amp;filesystem</string>
</property>
<property name="buddy">
<cstring>mountPoint</cstring>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QComboBox" name="mountPoint">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="refreshMountPoint">
<property name="text">
<string>&amp;Refresh</string>
</property>
<property name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/view-refresh.svg</normaloff>:/icons/view-refresh.svg</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelPlayer">
<property name="text">
<string>&amp;Select your audio player</string>
</property>
<property name="buddy">
<cstring>treeDevices</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>118</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="2" colspan="2">
<widget class="QCheckBox" name="showDisabled">
<property name="text">
<string>Show disabled targets</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="4">
<widget class="QTreeWidget" name="treeDevices">
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item row="4" column="0" colspan="4">
<widget class="QPushButton" name="buttonAutodetect">
<property name="text">
<string>&amp;Autodetect</string>
</property>
<property name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/edit-find.svg</normaloff>:/icons/edit-find.svg</iconset>
</property>
<property name="default">
<bool>true</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabProxy">
<attribute name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/network-idle.svg</normaloff>:/icons/network-idle.svg</iconset>
</attribute>
<attribute name="title">
<string>&amp;Proxy</string>
</attribute>
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QRadioButton" name="radioNoProxy">
<property name="text">
<string>&amp;No Proxy</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="radioSystemProxy">
<property name="text">
<string>Use S&amp;ystem values</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QRadioButton" name="radioManualProxy">
<property name="text">
<string>&amp;Manual Proxy settings</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Proxy Values</string>
</property>
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>&amp;Host:</string>
</property>
<property name="buddy">
<cstring>proxyHost</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Port:</string>
</property>
<property name="buddy">
<cstring>proxyPort</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="proxyPass">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>&amp;Username</string>
</property>
<property name="buddy">
<cstring>proxyUser</cstring>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Pass&amp;word</string>
</property>
<property name="buddy">
<cstring>proxyPass</cstring>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QCheckBox" name="checkShowProxyPassword">
<property name="text">
<string>Show</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QLineEdit" name="proxyUser"/>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="proxyHost">
<property name="frame">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QSpinBox" name="proxyPort">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0">
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabLanguage">
<attribute name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/preferences-desktop-locale.svg</normaloff>:/icons/preferences-desktop-locale.svg</iconset>
</attribute>
<attribute name="title">
<string>&amp;Language</string>
</attribute>
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QListWidget" name="listLanguages"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabCache">
<attribute name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/package-x-generic.svg</normaloff>:/icons/package-x-generic.svg</iconset>
</attribute>
<attribute name="title">
<string>Cac&amp;he</string>
</attribute>
<attribute name="toolTip">
<string>Download cache settings</string>
</attribute>
<layout class="QGridLayout">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="cacheDescription">
<property name="text">
<string>Rockbox Utility uses a local download cache to save network traffic. You can change the path to the cache and use it as local repository by enabling Offline mode.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="cacheSize">
<property name="text">
<string>Current cache size is %1</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>P&amp;ath</string>
</property>
<property name="buddy">
<cstring>cachePath</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="cachePath">
<property name="toolTip">
<string>Entering an invalid folder will reset the path to the systems temporary path.</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCacheBrowse">
<property name="text">
<string>&amp;Browse</string>
</property>
<property name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/edit-find.svg</normaloff>:/icons/edit-find.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" colspan="2">
<layout class="QVBoxLayout">
<item>
<widget class="QCheckBox" name="cacheDisable">
<property name="text">
<string>Disable local &amp;download cache</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="1">
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>61</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="0">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="1">
<widget class="QPushButton" name="buttonCacheClear">
<property name="text">
<string>Clean cache &amp;now</string>
</property>
<property name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/user-trash-full.svg</normaloff>:/icons/user-trash-full.svg</iconset>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabTts">
<attribute name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/audio-input-microphone.svg</normaloff>:/icons/audio-input-microphone.svg</iconset>
</attribute>
<attribute name="title">
<string>&amp;TTS &amp;&amp; Encoder</string>
</attribute>
<layout class="QVBoxLayout">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>TTS Engine</string>
</property>
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QLabel" name="labelTts">
<property name="text">
<string>&amp;Select TTS Engine</string>
</property>
<property name="buddy">
<cstring>comboTts</cstring>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="comboTts"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelTtsExecutable">
<property name="text">
<string>Configure TTS Engine</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="configTTSstatus">
<property name="text">
<string>Configuration invalid!</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="configTTSstatusimg">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="rbutilqt.qrc">:/icons/dialog-error.svg</pixmap>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="configTts">
<property name="text">
<string>Configure &amp;TTS</string>
</property>
<property name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/edit-find.svg</normaloff>:/icons/edit-find.svg</iconset>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="testTTS">
<property name="text">
<string>Test TTS</string>
</property>
<property name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/audio-volume-high.svg</normaloff>:/icons/audio-volume-high.svg</iconset>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QCheckBox" name="ttsCorrections">
<property name="text">
<string>&amp;Use string corrections for TTS</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Encoder Engine</string>
</property>
<layout class="QGridLayout">
<item row="0" column="1">
<widget class="QLabel" name="configEncstatus">
<property name="text">
<string>Configuration invalid!</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="configEncstatusimg">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="rbutilqt.qrc">:/icons/process-stop.svg</pixmap>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="configEncoder">
<property name="text">
<string>Configure &amp;Enc</string>
</property>
<property name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/edit-find.svg</normaloff>:/icons/edit-find.svg</iconset>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="encoderName">
<property name="text">
<string>encoder name</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>458</width>
<height>131</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item row="2" column="0">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="buttonOk">
<property name="text">
<string>&amp;Ok</string>
</property>
<property name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/go-next.svg</normaloff>:/icons/go-next.svg</iconset>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="buttonCancel">
<property name="text">
<string>&amp;Cancel</string>
</property>
<property name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/process-stop.svg</normaloff>:/icons/process-stop.svg</iconset>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="rbutilqt.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -0,0 +1,217 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0" >
<class>CreateVoiceFrm</class>
<widget class="QDialog" name="CreateVoiceFrm" >
<property name="windowModality" >
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>588</width>
<height>448</height>
</rect>
</property>
<property name="windowTitle" >
<string>Create Voice File</string>
</property>
<layout class="QGridLayout" name="gridLayout" >
<item row="0" column="0" rowspan="5" >
<widget class="QLabel" name="label" >
<property name="text" >
<string/>
</property>
<property name="pixmap" >
<pixmap resource="rbutilqt.qrc" >:/icons/wizard.jpg</pixmap>
</property>
<property name="alignment" >
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3" >
<widget class="QLabel" name="label_2" >
<property name="sizePolicy" >
<sizepolicy hsizetype="Minimum" vsizetype="Minimum" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>Select the Language you want to generate a voicefile for:</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="3" >
<widget class="QGroupBox" name="groupBox_2" >
<property name="title" >
<string>Generation settings</string>
</property>
<layout class="QGridLayout" >
<item row="0" column="0" >
<widget class="QLabel" name="labelTtsProfile" >
<property name="text" >
<string>TTS:</string>
</property>
</widget>
</item>
<item row="0" column="2" >
<widget class="QPushButton" name="change" >
<property name="sizePolicy" >
<sizepolicy hsizetype="Minimum" vsizetype="Minimum" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="icon">
<iconset resource="rbutilqt.qrc">
<normaloff>:/icons/preferences-system.svg</normaloff>:/icons/preferences-system.svg</iconset>
</property>
<property name="text" >
<string>Change</string>
</property>
</widget>
</item>
<item row="0" column="1" >
<spacer name="horizontalSpacer" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="2" >
<widget class="QSpinBox" name="wavtrimthreshold" >
<property name="accessibleName" >
<string>Wavtrim Threshold</string>
</property>
<property name="maximum" >
<number>5000</number>
</property>
<property name="value" >
<number>500</number>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<string>Silence threshold</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="1" >
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>20</width>
<height>201</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="2" >
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="3" >
<layout class="QHBoxLayout" >
<item>
<widget class="QPushButton" name="buttonOk" >
<property name="text" >
<string>&amp;Install</string>
</property>
<property name="icon" >
<iconset resource="rbutilqt.qrc" >
<normaloff>:/icons/go-next.svg</normaloff>:/icons/go-next.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCancel" >
<property name="text" >
<string>&amp;Cancel</string>
</property>
<property name="icon" >
<iconset resource="rbutilqt.qrc" >
<normaloff>:/icons/process-stop.svg</normaloff>:/icons/process-stop.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1" colspan="3" >
<widget class="QComboBox" name="comboLanguage" >
<property name="accessibleName" >
<string>Language</string>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>buttonOk</tabstop>
<tabstop>buttonCancel</tabstop>
<tabstop>comboLanguage</tabstop>
<tabstop>change</tabstop>
<tabstop>wavtrimthreshold</tabstop>
</tabstops>
<resources>
<include location="rbutilqt.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonOk</sender>
<signal>clicked()</signal>
<receiver>CreateVoiceFrm</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<x>472</x>
<y>418</y>
</hint>
<hint type="destinationlabel" >
<x>382</x>
<y>328</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonCancel</sender>
<signal>clicked()</signal>
<receiver>CreateVoiceFrm</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel" >
<x>561</x>
<y>428</y>
</hint>
<hint type="destinationlabel" >
<x>522</x>
<y>332</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -0,0 +1,144 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Riebeling
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QMessageBox>
#include "createvoicewindow.h"
#include "ui_createvoicefrm.h"
#include "configure.h"
#include "rbsettings.h"
#include "playerbuildinfo.h"
#include "Logger.h"
CreateVoiceWindow::CreateVoiceWindow(QWidget *parent) : QDialog(parent)
{
ui.setupUi(this);
voicecreator = new VoiceFileCreator(this);
updateSettings();
connect(ui.change,SIGNAL(clicked()),this,SLOT(change()));
}
void CreateVoiceWindow::change()
{
// save window settings
saveSettings();
// call configuration dialog
Config *cw = new Config(this,4);
connect(cw, SIGNAL(settingsUpdated()), this, SLOT(updateSettings()));
cw->show();
}
void CreateVoiceWindow::accept()
{
logger = new ProgressLoggerGui(this);
connect(logger,SIGNAL(closed()),this,SLOT(close()));
logger->show();
saveSettings();
//configure voicecreator
voicecreator->setMountPoint(RbSettings::value(RbSettings::Mountpoint).toString());
voicecreator->setLang(ui.comboLanguage->itemData(ui.comboLanguage->currentIndex()).toString());
voicecreator->setWavtrimThreshold(ui.wavtrimthreshold->value());
//start creating
connect(voicecreator, SIGNAL(done(bool)), logger, SLOT(setFinished()));
connect(voicecreator, SIGNAL(logItem(QString, int)), logger, SLOT(addItem(QString, int)));
connect(voicecreator, SIGNAL(logProgress(int, int)), logger, SLOT(setProgress(int, int)));
connect(logger,SIGNAL(aborted()),voicecreator,SLOT(abort()));
voicecreator->createVoiceFile();
}
/** @brief update displayed settings
*/
void CreateVoiceWindow::updateSettings(void)
{
// fill in language combobox. Map has QString as value, but is stored as QVariant.
QMap<QString, QVariant> langs
= PlayerBuildInfo::instance()->value(PlayerBuildInfo::LanguageList).toMap();
for(auto it = langs.begin(); it != langs.end(); it++) {
ui.comboLanguage->addItem(it.value().toString(), it.key());
}
// set saved lang
int sel = ui.comboLanguage->findData(
RbSettings::value(RbSettings::VoiceLanguage).toString());
// if no saved language is found try to figure the language from the UI lang
if(sel == -1) {
// the UI language is stored as ISO 631-1 code. Try to resolve it to the
// Rockbox language string.
QString uilang = RbSettings::value(RbSettings::Language).toString();
// default to english if no language is set.
if(uilang.isEmpty()) {
// FIXME: we try to set the UI language from the environment, but
// don't store it unless changed. Falling back to en is only valid
// if the system is actually english.
uilang = "en";
}
QString l = PlayerBuildInfo::instance()->value(
PlayerBuildInfo::LanguageInfo, uilang).toStringList().at(0);
if(!l.isEmpty()) {
sel = ui.comboLanguage->findData(l);
}
}
ui.comboLanguage->setCurrentIndex(sel);
QString ttsName = RbSettings::value(RbSettings::Tts).toString();
TTSBase* tts = TTSBase::getTTS(this,ttsName);
if(!tts)
{
QMessageBox::critical(this, tr("TTS error"),
tr("The selected TTS failed to initialize. You can't use this TTS."));
return;
}
if(tts->configOk())
ui.labelTtsProfile->setText(tr("Engine: <b>%1</b>")
.arg(TTSBase::getTTSName(ttsName)));
else
ui.labelTtsProfile->setText(tr("Engine: <b>%1</b>")
.arg("Invalid TTS configuration!"));
ui.wavtrimthreshold->setValue(RbSettings::value(RbSettings::WavtrimThreshold).toInt());
emit settingsUpdated();
}
/** @brief save options
*/
void CreateVoiceWindow::saveSettings(void)
{
// save selected language
RbSettings::setValue(RbSettings::VoiceLanguage,
ui.comboLanguage->itemData(ui.comboLanguage->currentIndex()).toString());
// save wavtrim threshold value
RbSettings::setValue(RbSettings::WavtrimThreshold,
ui.wavtrimthreshold->value());
RbSettings::sync();
}
void CreateVoiceWindow::changeEvent(QEvent *e)
{
if(e->type() == QEvent::LanguageChange) {
ui.retranslateUi(this);
updateSettings();
} else {
QWidget::changeEvent(e);
}
}

View file

@ -0,0 +1,56 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef CREATEVOICEWINDOW_H
#define CREATEVOICEWINDOW_H
#include <QDialog>
#include <QUrl>
#include "ui_createvoicefrm.h"
#include "progressloggergui.h"
#include "voicefile.h"
class CreateVoiceWindow : public QDialog
{
Q_OBJECT
public:
CreateVoiceWindow(QWidget *parent);
void setProxy(QUrl proxy){m_proxy = proxy;}
public slots:
void accept(void);
void change(void);
void updateSettings(void);
void saveSettings(void);
signals:
void settingsUpdated(void);
private:
void changeEvent(QEvent *event);
VoiceFileCreator* voicecreator;
Ui::CreateVoiceFrm ui;
ProgressLoggerGui* logger;
QUrl m_proxy;
};
#endif

View file

@ -0,0 +1,9 @@
# Configuration for creating a dmg with dmgbuild
# (https://github.com/al45tair/dmgbuild)
import os
files = [ 'RockboxUtility.app' ]
background = '#c6d6f5'
icon = os.path.join(defines['basepath'], 'rbutilqt/icons/rbutilqt.icns')

View file

@ -0,0 +1,384 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
* $Id: encoders.h 17902 2008-06-30 22:09:45Z bluebrother $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QSpacerItem>
#include <QPushButton>
#include <QHBoxLayout>
#include <QDoubleSpinBox>
#include <QSpinBox>
#include <QLineEdit>
#include <QFileDialog>
#include <QComboBox>
#include <QGroupBox>
#include <QLabel>
#include <QCheckBox>
#include <QProgressDialog>
#include "encttscfggui.h"
#include "Logger.h"
EncTtsCfgGui::EncTtsCfgGui(QDialog* parent, EncTtsSettingInterface* iface, QString name)
: QDialog(parent)
{
m_settingInterface = iface;
m_busyCnt=0;
// create a busy Dialog
m_busyDlg= new QProgressDialog("", "", 0, 0,this);
m_busyDlg->setWindowTitle(tr("Waiting for engine..."));
m_busyDlg->setModal(true);
m_busyDlg->setLabel(nullptr);
m_busyDlg->setCancelButton(nullptr);
m_busyDlg->hide();
connect(iface,SIGNAL(busy()),this,SLOT(showBusy()));
connect(iface,SIGNAL(busyEnd()),this,SLOT(hideBusy()));
//setup the window
setWindowTitle(name);
setUpWindow();
}
void EncTtsCfgGui::setUpWindow()
{
m_settingsList = m_settingInterface->getSettings();
// layout
QVBoxLayout *mainLayout = new QVBoxLayout;
// groupbox
QGroupBox *groupBox = new QGroupBox(this);
QGridLayout *gridLayout = new QGridLayout(groupBox);
// setting widgets
for(int i = 0; i < m_settingsList.size(); i++)
{
QLabel *label = new QLabel(m_settingsList.at(i)->name());
gridLayout->addWidget(label, i, 0);
QWidget *widget = createWidgets(m_settingsList.at(i));
gridLayout->addWidget(widget, i, 1);
widget->setLayoutDirection(Qt::LeftToRight);
QWidget *btn = createButton(m_settingsList.at(i));
if(btn != nullptr)
{
gridLayout->addWidget(btn, i, 2);
}
}
// add hidden spacers to make the dialog scale properly
QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
gridLayout->addItem(spacer, m_settingsList.size(), 0);
spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
gridLayout->addItem(spacer, m_settingsList.size(), 1);
groupBox->setLayout(gridLayout);
mainLayout->addWidget(groupBox);
// connect browse btn
connect(&m_browseBtnMap,SIGNAL(mapped(QObject*)),this,SLOT(browse(QObject*)));
// ok - cancel buttons
QPushButton* okBtn = new QPushButton(tr("Ok"),this);
okBtn->setDefault(true);
okBtn->setIcon(QIcon(":icons/go-next.svg"));
QPushButton* cancelBtn = new QPushButton(tr("Cancel"),this);
cancelBtn->setIcon(QIcon(":icons/process-stop.svg"));
connect(okBtn,SIGNAL(clicked()),this,SLOT(accept()));
connect(cancelBtn,SIGNAL(clicked()),this,SLOT(reject()));
QHBoxLayout *btnbox = new QHBoxLayout;
btnbox->addWidget(okBtn);
btnbox->addWidget(cancelBtn);
btnbox->insertStretch(0,1);
mainLayout->addLayout(btnbox);
this->setLayout(mainLayout);
}
QWidget* EncTtsCfgGui::createWidgets(EncTtsSetting* setting)
{
// value display
QWidget* value = nullptr;
switch(setting->type())
{
case EncTtsSetting::eDOUBLE:
{
QDoubleSpinBox *spinBox = new QDoubleSpinBox(this);
spinBox->setAccessibleName(setting->name());
spinBox->setMinimum(setting->min().toDouble());
spinBox->setMaximum(setting->max().toDouble());
spinBox->setSingleStep(0.01);
spinBox->setValue(setting->current().toDouble());
connect(spinBox,SIGNAL(valueChanged(double)),this,SLOT(updateSetting()));
value = spinBox;
break;
}
case EncTtsSetting::eINT:
{
QSpinBox *spinBox = new QSpinBox(this);
spinBox->setAccessibleName(setting->name());
spinBox->setMinimum(setting->min().toInt());
spinBox->setMaximum(setting->max().toInt());
spinBox->setValue(setting->current().toInt());
connect(spinBox,SIGNAL(valueChanged(int)),this,SLOT(updateSetting()));
value = spinBox;
break;
}
case EncTtsSetting::eSTRING:
{
QLineEdit *lineEdit = new QLineEdit(this);
lineEdit->setAccessibleName(setting->name());
lineEdit->setText(setting->current().toString());
connect(lineEdit,SIGNAL(textChanged(QString)),this,SLOT(updateSetting()));
value = lineEdit;
break;
}
case EncTtsSetting::eREADONLYSTRING:
{
value = new QLabel(setting->current().toString(),this);
break;
}
case EncTtsSetting::eSTRINGLIST:
{
QComboBox *comboBox = new QComboBox(this);
comboBox->setAccessibleName(setting->name());
comboBox->addItems(setting->list());
int index = comboBox->findText(setting->current().toString());
comboBox->setCurrentIndex(index);
connect(comboBox,SIGNAL(currentIndexChanged(QString)),this,SLOT(updateSetting()));
value = comboBox;
break;
}
case EncTtsSetting::eBOOL:
{
QCheckBox *checkbox = new QCheckBox(this);
checkbox->setAccessibleName(setting->name());
checkbox->setCheckState(setting->current().toBool() == true ? Qt::Checked : Qt::Unchecked);
connect(checkbox,SIGNAL(stateChanged(int)),this,SLOT(updateSetting()));
value = checkbox;
break;
}
default:
{
LOG_WARNING() << "Warning: unknown EncTTsSetting type" << setting->type();
break;
}
}
// remember widget
if(value != nullptr)
{
m_settingsWidgetsMap.insert(setting,value);
connect(setting,SIGNAL(updateGui()),this,SLOT(updateWidget()));
}
return value;
}
QWidget* EncTtsCfgGui::createButton(EncTtsSetting* setting)
{
if(setting->button() == EncTtsSetting::eBROWSEBTN)
{
QPushButton* browsebtn = new QPushButton(tr("Browse"),this);
browsebtn->setIcon(QIcon(":/icons/system-search.svg"));
m_browseBtnMap.setMapping(browsebtn,setting);
connect(browsebtn,SIGNAL(clicked()),&m_browseBtnMap,SLOT(map()));
return browsebtn;
}
else if(setting->button() == EncTtsSetting::eREFRESHBTN)
{
QPushButton* refreshbtn = new QPushButton(tr("Refresh"),this);
refreshbtn->setIcon(QIcon(":/icons/view-refresh.svg"));
connect(refreshbtn,SIGNAL(clicked()),setting,SIGNAL(refresh()));
return refreshbtn;
}
else
return nullptr;
}
void EncTtsCfgGui::updateSetting()
{
//cast and get the sender widget
QWidget* widget = qobject_cast<QWidget*>(QObject::sender());
if(widget == nullptr) return;
// get the corresponding setting
EncTtsSetting* setting = m_settingsWidgetsMap.key(widget);
// update widget based on setting type
switch(setting->type())
{
case EncTtsSetting::eDOUBLE:
{
setting->setCurrent(((QDoubleSpinBox*)widget)->value(),false);
break;
}
case EncTtsSetting::eINT:
{
setting->setCurrent(((QSpinBox*)widget)->value(),false);
break;
}
case EncTtsSetting::eSTRING:
{
setting->setCurrent(((QLineEdit*)widget)->text(),false);
break;
}
case EncTtsSetting::eREADONLYSTRING:
{
setting->setCurrent(((QLabel*)widget)->text(),false);
break;
}
case EncTtsSetting::eSTRINGLIST:
{
setting->setCurrent(((QComboBox*)widget)->currentText(),false);
break;
}
case EncTtsSetting::eBOOL:
{
setting->setCurrent(((QCheckBox*)widget)->isChecked(),false);
break;
}
default:
{
LOG_WARNING() << "unknown setting type!";
break;
}
}
}
void EncTtsCfgGui::updateWidget()
{
// get sender setting
EncTtsSetting* setting = qobject_cast<EncTtsSetting*>(QObject::sender());
if(setting == nullptr) return;
// get corresponding widget
QWidget* widget = m_settingsWidgetsMap.value(setting);
// update Widget based on setting type
switch(setting->type())
{
case EncTtsSetting::eDOUBLE:
{
QDoubleSpinBox* spinbox = (QDoubleSpinBox*) widget;
spinbox->setMinimum(setting->min().toDouble());
spinbox->setMaximum(setting->max().toDouble());
spinbox->blockSignals(true);
spinbox->setValue(setting->current().toDouble());
spinbox->blockSignals(false);
break;
}
case EncTtsSetting::eINT:
{
QSpinBox* spinbox = (QSpinBox*) widget;
spinbox->setMinimum(setting->min().toInt());
spinbox->setMaximum(setting->max().toInt());
spinbox->blockSignals(true);
spinbox->setValue(setting->current().toInt());
spinbox->blockSignals(false);
break;
}
case EncTtsSetting::eSTRING:
{
QLineEdit* lineedit = (QLineEdit*) widget;
lineedit->blockSignals(true);
lineedit->setText(setting->current().toString());
lineedit->blockSignals(false);
break;
}
case EncTtsSetting::eREADONLYSTRING:
{
QLabel* label = (QLabel*) widget;
label->blockSignals(true);
label->setText(setting->current().toString());
label->blockSignals(false);
break;
}
case EncTtsSetting::eSTRINGLIST:
{
QComboBox* combobox = (QComboBox*) widget;
combobox->blockSignals(true);
combobox->clear();
combobox->addItems(setting->list());
int index = combobox->findText(setting->current().toString());
combobox->setCurrentIndex(index);
combobox->blockSignals(false);
break;
}
case EncTtsSetting::eBOOL:
{
QCheckBox* checkbox = (QCheckBox*) widget;
checkbox->blockSignals(true);
checkbox->setCheckState(setting->current().toBool() == true ? Qt::Checked : Qt::Unchecked);
checkbox->blockSignals(false);
break;
}
default:
{
LOG_WARNING() << "unknown EncTTsSetting";
break;
}
}
}
void EncTtsCfgGui::showBusy()
{
if(m_busyCnt == 0) m_busyDlg->show();
m_busyCnt++;
}
void EncTtsCfgGui::hideBusy()
{
m_busyCnt--;
if(m_busyCnt == 0) m_busyDlg->hide();
}
void EncTtsCfgGui::accept(void)
{
m_settingInterface->saveSettings();
this->done(0);
}
void EncTtsCfgGui::reject(void)
{
this->done(0);
}
//! takes a QObject because of QsignalMapper
void EncTtsCfgGui::browse(QObject* settingObj)
{
// cast top setting
EncTtsSetting* setting= qobject_cast<EncTtsSetting*>(settingObj);
if(setting == nullptr) return;
//current path
QString curPath = setting->current().toString();
// show file dialog
QString exe = QFileDialog::getOpenFileName(this, tr("Select executable"), curPath, "*");
if(!QFileInfo(exe).isExecutable())
return;
// set new value, gui will update automatically
setting->setCurrent(exe);
}

Some files were not shown because too many files have changed in this diff Show more