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:
parent
6c6f0757d7
commit
c876d3bbef
494 changed files with 13 additions and 13 deletions
63
utils/rbutilqt/INSTALL
Normal file
63
utils/rbutilqt/INSTALL
Normal 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
24
utils/rbutilqt/Info.plist
Normal 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>
|
||||
79
utils/rbutilqt/Makefile.libs
Normal file
79
utils/rbutilqt/Makefile.libs
Normal 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)
|
||||
12
utils/rbutilqt/RockboxUtility.desktop
Normal file
12
utils/rbutilqt/RockboxUtility.desktop
Normal 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
198
utils/rbutilqt/aboutbox.ui
Normal 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.<br/>© The Rockbox Team.<br/>Released under the GNU General Public License v2.<br/>Uses icons by the <a href="http://tango.freedesktop.org/">Tango Project</a>.<br/><center><a href="http://www.rockbox.org">http://www.rockbox.org</a></center></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>&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>&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&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>&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>
|
||||
30
utils/rbutilqt/base/archiveutil.cpp
Normal file
30
utils/rbutilqt/base/archiveutil.cpp
Normal 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()
|
||||
{
|
||||
}
|
||||
41
utils/rbutilqt/base/archiveutil.h
Normal file
41
utils/rbutilqt/base/archiveutil.h
Normal 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
|
||||
|
||||
|
||||
376
utils/rbutilqt/base/autodetection.cpp
Normal file
376
utils/rbutilqt/base/autodetection.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
72
utils/rbutilqt/base/autodetection.h
Normal file
72
utils/rbutilqt/base/autodetection.h
Normal 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_*/
|
||||
|
||||
201
utils/rbutilqt/base/bootloaderinstallams.cpp
Normal file
201
utils/rbutilqt/base/bootloaderinstallams.cpp
Normal 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);
|
||||
}
|
||||
|
||||
42
utils/rbutilqt/base/bootloaderinstallams.h
Normal file
42
utils/rbutilqt/base/bootloaderinstallams.h
Normal 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
|
||||
302
utils/rbutilqt/base/bootloaderinstallbase.cpp
Normal file
302
utils/rbutilqt/base/bootloaderinstallbase.cpp
Normal 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;
|
||||
}
|
||||
|
||||
118
utils/rbutilqt/base/bootloaderinstallbase.h
Normal file
118
utils/rbutilqt/base/bootloaderinstallbase.h
Normal 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
|
||||
|
||||
178
utils/rbutilqt/base/bootloaderinstallbspatch.cpp
Normal file
178
utils/rbutilqt/base/bootloaderinstallbspatch.cpp
Normal 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);
|
||||
}
|
||||
47
utils/rbutilqt/base/bootloaderinstallbspatch.h
Normal file
47
utils/rbutilqt/base/bootloaderinstallbspatch.h
Normal 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
|
||||
133
utils/rbutilqt/base/bootloaderinstallchinachip.cpp
Normal file
133
utils/rbutilqt/base/bootloaderinstallchinachip.cpp
Normal 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);
|
||||
}
|
||||
41
utils/rbutilqt/base/bootloaderinstallchinachip.h
Normal file
41
utils/rbutilqt/base/bootloaderinstallchinachip.h
Normal 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
|
||||
159
utils/rbutilqt/base/bootloaderinstallfile.cpp
Normal file
159
utils/rbutilqt/base/bootloaderinstallfile.cpp
Normal 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;
|
||||
}
|
||||
|
||||
48
utils/rbutilqt/base/bootloaderinstallfile.h
Normal file
48
utils/rbutilqt/base/bootloaderinstallfile.h
Normal 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
|
||||
|
||||
140
utils/rbutilqt/base/bootloaderinstallhelper.cpp
Normal file
140
utils/rbutilqt/base/bootloaderinstallhelper.cpp
Normal 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();
|
||||
}
|
||||
36
utils/rbutilqt/base/bootloaderinstallhelper.h
Normal file
36
utils/rbutilqt/base/bootloaderinstallhelper.h
Normal 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
|
||||
|
||||
271
utils/rbutilqt/base/bootloaderinstallhex.cpp
Normal file
271
utils/rbutilqt/base/bootloaderinstallhex.cpp
Normal 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;
|
||||
}
|
||||
|
||||
53
utils/rbutilqt/base/bootloaderinstallhex.h
Normal file
53
utils/rbutilqt/base/bootloaderinstallhex.h
Normal 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
|
||||
|
||||
193
utils/rbutilqt/base/bootloaderinstallimx.cpp
Normal file
193
utils/rbutilqt/base/bootloaderinstallimx.cpp
Normal 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);
|
||||
}
|
||||
|
||||
47
utils/rbutilqt/base/bootloaderinstallimx.h
Normal file
47
utils/rbutilqt/base/bootloaderinstallimx.h
Normal 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
|
||||
272
utils/rbutilqt/base/bootloaderinstallipod.cpp
Normal file
272
utils/rbutilqt/base/bootloaderinstallipod.cpp
Normal 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;
|
||||
}
|
||||
|
||||
51
utils/rbutilqt/base/bootloaderinstallipod.h
Normal file
51
utils/rbutilqt/base/bootloaderinstallipod.h
Normal 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
|
||||
|
||||
162
utils/rbutilqt/base/bootloaderinstallmi4.cpp
Normal file
162
utils/rbutilqt/base/bootloaderinstallmi4.cpp
Normal 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;
|
||||
}
|
||||
|
||||
48
utils/rbutilqt/base/bootloaderinstallmi4.h
Normal file
48
utils/rbutilqt/base/bootloaderinstallmi4.h
Normal 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
|
||||
|
||||
143
utils/rbutilqt/base/bootloaderinstallmpio.cpp
Normal file
143
utils/rbutilqt/base/bootloaderinstallmpio.cpp
Normal 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);
|
||||
}
|
||||
|
||||
43
utils/rbutilqt/base/bootloaderinstallmpio.h
Normal file
43
utils/rbutilqt/base/bootloaderinstallmpio.h
Normal 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
|
||||
437
utils/rbutilqt/base/bootloaderinstalls5l.cpp
Normal file
437
utils/rbutilqt/base/bootloaderinstalls5l.cpp
Normal 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);
|
||||
}
|
||||
71
utils/rbutilqt/base/bootloaderinstalls5l.h
Normal file
71
utils/rbutilqt/base/bootloaderinstalls5l.h
Normal 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
|
||||
286
utils/rbutilqt/base/bootloaderinstallsansa.cpp
Normal file
286
utils/rbutilqt/base/bootloaderinstallsansa.cpp
Normal 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);
|
||||
}
|
||||
|
||||
51
utils/rbutilqt/base/bootloaderinstallsansa.h
Normal file
51
utils/rbutilqt/base/bootloaderinstallsansa.h
Normal 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
|
||||
|
||||
165
utils/rbutilqt/base/bootloaderinstalltcc.cpp
Normal file
165
utils/rbutilqt/base/bootloaderinstalltcc.cpp
Normal 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);
|
||||
}
|
||||
44
utils/rbutilqt/base/bootloaderinstalltcc.h
Normal file
44
utils/rbutilqt/base/bootloaderinstalltcc.h
Normal 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
|
||||
86
utils/rbutilqt/base/encoderbase.cpp
Normal file
86
utils/rbutilqt/base/encoderbase.cpp
Normal 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();
|
||||
}
|
||||
|
||||
63
utils/rbutilqt/base/encoderbase.h
Normal file
63
utils/rbutilqt/base/encoderbase.h
Normal 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
|
||||
|
||||
78
utils/rbutilqt/base/encoderexe.cpp
Normal file
78
utils/rbutilqt/base/encoderexe.cpp
Normal 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);
|
||||
}
|
||||
|
||||
53
utils/rbutilqt/base/encoderexe.h
Normal file
53
utils/rbutilqt/base/encoderexe.h
Normal 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
|
||||
|
||||
312
utils/rbutilqt/base/encoderlame.cpp
Normal file
312
utils/rbutilqt/base/encoderlame.cpp
Normal 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;
|
||||
}
|
||||
|
||||
72
utils/rbutilqt/base/encoderlame.h
Normal file
72
utils/rbutilqt/base/encoderlame.h
Normal 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
|
||||
|
||||
119
utils/rbutilqt/base/encoderrbspeex.cpp
Normal file
119
utils/rbutilqt/base/encoderrbspeex.cpp
Normal 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;
|
||||
}
|
||||
|
||||
61
utils/rbutilqt/base/encoderrbspeex.h
Normal file
61
utils/rbutilqt/base/encoderrbspeex.h
Normal 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
|
||||
|
||||
70
utils/rbutilqt/base/encttssettings.cpp
Normal file
70
utils/rbutilqt/base/encttssettings.cpp
Normal 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);
|
||||
}
|
||||
128
utils/rbutilqt/base/encttssettings.h
Normal file
128
utils/rbutilqt/base/encttssettings.h
Normal 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
|
||||
256
utils/rbutilqt/base/httpget.cpp
Normal file
256
utils/rbutilqt/base/httpget.cpp
Normal 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;
|
||||
}
|
||||
|
||||
111
utils/rbutilqt/base/httpget.h
Normal file
111
utils/rbutilqt/base/httpget.h
Normal 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
|
||||
|
||||
164
utils/rbutilqt/base/mspackutil.cpp
Normal file
164
utils/rbutilqt/base/mspackutil.cpp
Normal 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";
|
||||
}
|
||||
}
|
||||
51
utils/rbutilqt/base/mspackutil.h
Normal file
51
utils/rbutilqt/base/mspackutil.h
Normal 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
|
||||
|
||||
|
||||
362
utils/rbutilqt/base/playerbuildinfo.cpp
Normal file
362
utils/rbutilqt/base/playerbuildinfo.cpp
Normal 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;
|
||||
}
|
||||
|
||||
123
utils/rbutilqt/base/playerbuildinfo.h
Normal file
123
utils/rbutilqt/base/playerbuildinfo.h
Normal 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
|
||||
60
utils/rbutilqt/base/progressloggerinterface.h
Normal file
60
utils/rbutilqt/base/progressloggerinterface.h
Normal 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
|
||||
|
||||
207
utils/rbutilqt/base/rbsettings.cpp
Normal file
207
utils/rbutilqt/base/rbsettings.cpp
Normal 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;
|
||||
}
|
||||
|
||||
105
utils/rbutilqt/base/rbsettings.h
Normal file
105
utils/rbutilqt/base/rbsettings.h
Normal 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
|
||||
|
||||
83
utils/rbutilqt/base/rockboxinfo.cpp
Normal file
83
utils/rbutilqt/base/rockboxinfo.cpp
Normal 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;
|
||||
}
|
||||
|
||||
54
utils/rbutilqt/base/rockboxinfo.h
Normal file
54
utils/rbutilqt/base/rockboxinfo.h
Normal 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
|
||||
|
||||
519
utils/rbutilqt/base/system.cpp
Normal file
519
utils/rbutilqt/base/system.cpp
Normal 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
|
||||
}
|
||||
|
||||
|
||||
53
utils/rbutilqt/base/system.h
Normal file
53
utils/rbutilqt/base/system.h
Normal 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
|
||||
|
||||
300
utils/rbutilqt/base/talkfile.cpp
Normal file
300
utils/rbutilqt/base/talkfile.cpp
Normal 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();
|
||||
}
|
||||
|
||||
83
utils/rbutilqt/base/talkfile.h
Normal file
83
utils/rbutilqt/base/talkfile.h
Normal 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
|
||||
|
||||
337
utils/rbutilqt/base/talkgenerator.cpp
Normal file
337
utils/rbutilqt/base/talkgenerator.cpp
Normal 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();
|
||||
}
|
||||
91
utils/rbutilqt/base/talkgenerator.h
Normal file
91
utils/rbutilqt/base/talkgenerator.h
Normal 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
|
||||
|
||||
122
utils/rbutilqt/base/ttsbase.cpp
Normal file
122
utils/rbutilqt/base/ttsbase.cpp
Normal 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);
|
||||
}
|
||||
70
utils/rbutilqt/base/ttsbase.h
Normal file
70
utils/rbutilqt/base/ttsbase.h
Normal 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
|
||||
443
utils/rbutilqt/base/ttscarbon.cpp
Normal file
443
utils/rbutilqt/base/ttscarbon.cpp
Normal 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;
|
||||
}
|
||||
|
||||
73
utils/rbutilqt/base/ttscarbon.h
Normal file
73
utils/rbutilqt/base/ttscarbon.h
Normal 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
|
||||
|
||||
42
utils/rbutilqt/base/ttsespeak.h
Normal file
42
utils/rbutilqt/base/ttsespeak.h
Normal 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
|
||||
41
utils/rbutilqt/base/ttsespeakng.h
Normal file
41
utils/rbutilqt/base/ttsespeakng.h
Normal 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
|
||||
127
utils/rbutilqt/base/ttsexes.cpp
Normal file
127
utils/rbutilqt/base/ttsexes.cpp
Normal 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;
|
||||
}
|
||||
|
||||
61
utils/rbutilqt/base/ttsexes.h
Normal file
61
utils/rbutilqt/base/ttsexes.h
Normal 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
|
||||
412
utils/rbutilqt/base/ttsfestival.cpp
Normal file
412
utils/rbutilqt/base/ttsfestival.cpp
Normal 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");
|
||||
|
||||
}
|
||||
|
||||
72
utils/rbutilqt/base/ttsfestival.h
Normal file
72
utils/rbutilqt/base/ttsfestival.h
Normal 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
|
||||
43
utils/rbutilqt/base/ttsflite.h
Normal file
43
utils/rbutilqt/base/ttsflite.h
Normal 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
|
||||
41
utils/rbutilqt/base/ttsmimic.h
Normal file
41
utils/rbutilqt/base/ttsmimic.h
Normal 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
|
||||
43
utils/rbutilqt/base/ttsmssp.h
Normal file
43
utils/rbutilqt/base/ttsmssp.h
Normal 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
|
||||
274
utils/rbutilqt/base/ttssapi.cpp
Normal file
274
utils/rbutilqt/base/ttssapi.cpp
Normal 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;
|
||||
}
|
||||
|
||||
77
utils/rbutilqt/base/ttssapi.h
Normal file
77
utils/rbutilqt/base/ttssapi.h
Normal 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
|
||||
43
utils/rbutilqt/base/ttssapi4.h
Normal file
43
utils/rbutilqt/base/ttssapi4.h
Normal 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
|
||||
40
utils/rbutilqt/base/ttsswift.h
Normal file
40
utils/rbutilqt/base/ttsswift.h
Normal 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
|
||||
126
utils/rbutilqt/base/uninstall.cpp
Normal file
126
utils/rbutilqt/base/uninstall.cpp
Normal 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");
|
||||
}
|
||||
|
||||
63
utils/rbutilqt/base/uninstall.h
Normal file
63
utils/rbutilqt/base/uninstall.h
Normal 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
|
||||
|
||||
1062
utils/rbutilqt/base/utils.cpp
Normal file
1062
utils/rbutilqt/base/utils.cpp
Normal file
File diff suppressed because it is too large
Load diff
64
utils/rbutilqt/base/utils.h
Normal file
64
utils/rbutilqt/base/utils.h
Normal 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
|
||||
|
||||
362
utils/rbutilqt/base/voicefile.cpp
Normal file
362
utils/rbutilqt/base/voicefile.cpp
Normal 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;
|
||||
}
|
||||
|
||||
77
utils/rbutilqt/base/voicefile.h
Normal file
77
utils/rbutilqt/base/voicefile.h
Normal 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
|
||||
|
||||
204
utils/rbutilqt/base/zipinstaller.cpp
Normal file
204
utils/rbutilqt/base/zipinstaller.cpp
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
88
utils/rbutilqt/base/zipinstaller.h
Normal file
88
utils/rbutilqt/base/zipinstaller.h
Normal 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
|
||||
|
||||
302
utils/rbutilqt/base/ziputil.cpp
Normal file
302
utils/rbutilqt/base/ziputil.cpp
Normal 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;
|
||||
}
|
||||
|
||||
53
utils/rbutilqt/base/ziputil.h
Normal file
53
utils/rbutilqt/base/ziputil.h
Normal 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
|
||||
|
||||
42
utils/rbutilqt/changelog.txt
Normal file
42
utils/rbutilqt/changelog.txt
Normal 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.
|
||||
984
utils/rbutilqt/configure.cpp
Normal file
984
utils/rbutilqt/configure.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
79
utils/rbutilqt/configure.h
Normal file
79
utils/rbutilqt/configure.h
Normal 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
|
||||
572
utils/rbutilqt/configurefrm.ui
Normal file
572
utils/rbutilqt/configurefrm.ui
Normal 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>&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 &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>&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>&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>&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>&Proxy</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QRadioButton" name="radioNoProxy">
|
||||
<property name="text">
|
||||
<string>&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&ystem values</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QRadioButton" name="radioManualProxy">
|
||||
<property name="text">
|
||||
<string>&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>&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>&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>&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&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>&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&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&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>&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 &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 &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>&TTS && 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>&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 &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>&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 &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>&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>&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>
|
||||
217
utils/rbutilqt/createvoicefrm.ui
Normal file
217
utils/rbutilqt/createvoicefrm.ui
Normal 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>&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>&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>
|
||||
144
utils/rbutilqt/createvoicewindow.cpp
Normal file
144
utils/rbutilqt/createvoicewindow.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
56
utils/rbutilqt/createvoicewindow.h
Normal file
56
utils/rbutilqt/createvoicewindow.h
Normal 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
|
||||
9
utils/rbutilqt/dmgbuild.cfg
Normal file
9
utils/rbutilqt/dmgbuild.cfg
Normal 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')
|
||||
|
||||
384
utils/rbutilqt/encttscfggui.cpp
Normal file
384
utils/rbutilqt/encttscfggui.cpp
Normal 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
Loading…
Add table
Add a link
Reference in a new issue