1
0
Fork 0
forked from len0rd/rockbox

rbutil: Merge rbutil with utils folder.

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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