Reorganized project to avoid the ".pro not found" by having all the

pro files in the same directory. With this change the android library
now builds properly.

This also fixes the parallel compilation problem that happened when
building using +8 cores.
This commit is contained in:
codestation
2015-03-21 14:48:25 -04:30
parent c223016725
commit 9f790dc788
106 changed files with 13921 additions and 0 deletions

35
android/android.pro Normal file
View File

@@ -0,0 +1,35 @@
include(../config.pri)
include(../common/defines.pri)
TARGET = qcma_android
TEMPLATE=app
LIBS += -L../common -lqcma_common
# this library needs to link statically their deps but Qt doesn't pass --static to PKGCONFIG
QMAKE_CXXFLAGS += $$system(pkg-config --static --cflags libvitamtp libavformat libavcodec libavutil libswscale)
LIBS += $$system(pkg-config --static --libs libvitamtp libavformat libavcodec libavutil libswscale)
SOURCES += \
main_android.cpp
# headlessmanager.cpp
#HEADERS += \
# headlessmanager.h
DISTFILES += \
../android-src/gradle/wrapper/gradle-wrapper.jar \
../android-src/AndroidManifest.xml \
../android-src/gradlew.bat \
../android-src/res/values/libs.xml \
../android-src/build.gradle \
../android-src/gradle/wrapper/gradle-wrapper.properties \
../android-src/gradlew \
android-src/gradle/wrapper/gradle-wrapper.jar \
android-src/AndroidManifest.xml \
android-src/gradlew.bat \
android-src/res/values/libs.xml \
android-src/build.gradle \
android-src/gradle/wrapper/gradle-wrapper.properties \
android-src/gradlew
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android-src

191
android/headlessmanager.cpp Normal file
View File

@@ -0,0 +1,191 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cmaclient.h"
#include "cmautils.h"
#include "sqlitedb.h"
#include "qlistdb.h"
#include "headlessmanager.h"
#include <QCoreApplication>
#include <QSettings>
#include <QTextStream>
#include <sys/socket.h>
#include <unistd.h>
#include <vitamtp.h>
int HeadlessManager::sighup_fd[2];
int HeadlessManager::sigterm_fd[2];
HeadlessManager::HeadlessManager(QObject *obj_parent) :
QObject(obj_parent)
{
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sighup_fd))
qFatal("Couldn't create HUP socketpair");
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigterm_fd))
qFatal("Couldn't create TERM socketpair");
sn_hup = new QSocketNotifier(sighup_fd[1], QSocketNotifier::Read, this);
connect(sn_hup, SIGNAL(activated(int)), this, SLOT(handleSigHup()));
sn_term = new QSocketNotifier(sigterm_fd[1], QSocketNotifier::Read, this);
connect(sn_term, SIGNAL(activated(int)), this, SLOT(handleSigTerm()));
}
HeadlessManager::~HeadlessManager()
{
VitaMTP_Cleanup();
delete m_db;
}
void HeadlessManager::refreshDatabase()
{
if(m_db->load()) {
return;
}
QTextStream(stdout) << "Database scan has started" << endl;
if(!m_db->rescan()) {
qWarning("No PS Vita system has been registered");
}
}
void HeadlessManager::start()
{
if(VitaMTP_Init() < 0) {
qCritical("Cannot initialize VitaMTP library");
return;
}
if(QSettings().value("useMemoryStorage", true).toBool()) {
m_db = new QListDB();
} else {
m_db = new SQLiteDB();
}
// initializing database for the first use
refreshDatabase();
// send a signal when the update is finished
connect(m_db, SIGNAL(updated(int)), this, SIGNAL(databaseUpdated(int)));
thread_count = 0;
qDebug("Starting cma threads");
CmaClient *client;
QSettings settings;
if(!settings.value("disableUSB", false).toBool()) {
if(!belongsToGroup("vitamtp"))
qCritical() << tr("This user doesn't belong to the vitamtp group, there could be a problem while reading the USB bus.");
usb_thread = new QThread();
client = new CmaClient(m_db);
usb_thread->setObjectName("usb_thread");
connect(usb_thread, SIGNAL(started()), client, SLOT(connectUsb()));
connect(client, SIGNAL(finished()), usb_thread, SLOT(quit()), Qt::DirectConnection);
connect(usb_thread, SIGNAL(finished()), usb_thread, SLOT(deleteLater()));
connect(usb_thread, SIGNAL(finished()), this, SLOT(threadStopped()));
connect(usb_thread, SIGNAL(finished()), client, SLOT(deleteLater()));
connect(client, SIGNAL(refreshDatabase()), this, SLOT(refreshDatabase()));
client->moveToThread(usb_thread);
usb_thread->start();
thread_count++;
}
if(!settings.value("disableWireless", false).toBool()) {
CmaBroadcast *broadcast = new CmaBroadcast(this);
wireless_thread = new QThread();
client = new CmaClient(m_db, broadcast);
wireless_thread->setObjectName("wireless_thread");
connect(wireless_thread, SIGNAL(started()), client, SLOT(connectWireless()));
connect(client, SIGNAL(finished()), wireless_thread, SLOT(quit()), Qt::DirectConnection);
connect(wireless_thread, SIGNAL(finished()), wireless_thread, SLOT(deleteLater()));
connect(wireless_thread, SIGNAL(finished()), this, SLOT(threadStopped()));
connect(wireless_thread, SIGNAL(finished()), client, SLOT(deleteLater()));
connect(client, SIGNAL(refreshDatabase()), this, SLOT(refreshDatabase()));
client->moveToThread(wireless_thread);
wireless_thread->start();
thread_count++;
}
if(thread_count == 0) {
qCritical("You must enable at least USB or Wireless monitoring");
}
}
void HeadlessManager::receiveMessage(QString message)
{
QTextStream(stdout) << message << endl;
}
void HeadlessManager::stop()
{
if(CmaClient::stop() < 0) {
QCoreApplication::quit();
}
}
void HeadlessManager::threadStopped()
{
mutex.lock();
if(--thread_count == 0) {
QCoreApplication::quit();
}
mutex.unlock();
}
void HeadlessManager::hupSignalHandler(int)
{
char a = 1;
::write(sighup_fd[0], &a, sizeof(a));
}
void HeadlessManager::termSignalHandler(int)
{
char a = 1;
::write(sigterm_fd[0], &a, sizeof(a));
}
void HeadlessManager::handleSigTerm()
{
sn_term->setEnabled(false);
char tmp;
::read(sigterm_fd[1], &tmp, sizeof(tmp));
stop();
sn_term->setEnabled(true);
}
void HeadlessManager::handleSigHup()
{
sn_hup->setEnabled(false);
char tmp;
::read(sighup_fd[1], &tmp, sizeof(tmp));
refreshDatabase();
sn_hup->setEnabled(true);
}

76
android/headlessmanager.h Normal file
View File

@@ -0,0 +1,76 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HEADLESSMANAGER_H
#define HEADLESSMANAGER_H
#include "database.h"
#include <QObject>
#include <QThread>
#include <QSocketNotifier>
class HeadlessManager : public QObject
{
Q_OBJECT
public:
explicit HeadlessManager(QObject *parent = 0);
~HeadlessManager();
void start();
// unix signal handlers
static void hupSignalHandler(int);
static void termSignalHandler(int);
private:
int thread_count;
QMutex mutex;
Database *m_db;
QThread *usb_thread;
QThread *wireless_thread;
// signal handling
static int sighup_fd[2];
static int sigterm_fd[2];
QSocketNotifier *sn_hup;
QSocketNotifier *sn_term;
signals:
void stopped();
void databaseUpdated(int count);
public slots:
void refreshDatabase();
void stop();
// Qt signal handlers
void handleSigHup();
void handleSigTerm();
private slots:
void threadStopped();
void receiveMessage(QString message);
};
#endif // HEADLESSMANAGER_H

122
android/main_android.cpp Normal file
View File

@@ -0,0 +1,122 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QCoreApplication>
#include <QDebug>
#include <QLibraryInfo>
#include <QLocale>
#include <QStringList>
#include <QTextCodec>
#include <QThread>
#include <QTranslator>
#include <inttypes.h>
#include <vitamtp.h>
#include "headlessmanager.h"
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
static void noDebugOutput(QtMsgType type, const QMessageLogContext &, const QString & msg)
{
QByteArray localMsg = msg.toLocal8Bit();
const char *message = localMsg.constData();
#else
static void noDebugOutput(QtMsgType type, const char *message)
{
#endif
switch (type) {
case QtDebugMsg:
break;
case QtWarningMsg:
fprintf(stderr, "Warning: %s\n", message);
break;
case QtCriticalMsg:
fprintf(stderr, "Critical: %s\n", message);
break;
case QtFatalMsg:
fprintf(stderr, "Fatal: %s\n", message);
abort();
}
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
// FIXME: libmtp sends SIGPIPE if a socket write fails crashing the whole app
// the proper fix is to libmtp to handle the cancel properly or ignoring
// SIGPIPE on the socket
signal(SIGPIPE, SIG_IGN);
if(app.arguments().contains("--with-debug")) {
VitaMTP_Set_Logging(VitaMTP_DEBUG);
} else if(app.arguments().contains("--verbose")) {
VitaMTP_Set_Logging(VitaMTP_VERBOSE);
} else {
VitaMTP_Set_Logging(VitaMTP_NONE);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
qInstallMessageHandler(noDebugOutput);
#else
qInstallMsgHandler(noDebugOutput);
#endif
}
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
#endif
QTextStream(stdout) << "Starting Qcma " << QCMA_VER << endl;
QTranslator translator;
QString locale = QLocale().system().name();
qDebug() << "Current locale:" << locale;
if(app.arguments().contains("--set-locale")) {
int index = app.arguments().indexOf("--set-locale");
if(index + 1 < app.arguments().length()) {
qDebug("Enforcing locale: %s", app.arguments().at(index + 1).toUtf8().data());
locale = app.arguments().at(index + 1);
}
}
if(translator.load("qcma_" + locale, ":/resources/translations")) {
app.installTranslator(&translator);
} else {
qWarning() << "Cannot load translation for locale:" << locale;
}
QTranslator system_translator;
system_translator.load("qt_" + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
app.installTranslator(&system_translator);
qDebug("Starting main thread: 0x%016" PRIxPTR, (uintptr_t)QThread::currentThreadId());
// set the organization/application for QSettings to work properly
app.setOrganizationName("codestation");
app.setApplicationName("qcma");
//HeadlessManager manager;
// receive the message from another process
//QObject::connect(&app, SIGNAL(messageAvailable(QString)), &manager, SLOT(receiveMessage(QString)));
//manager.start();
return app.exec();
}

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,36 @@
include(../config.pri)
include(../gui/defines.pri)
TARGET = qcma_appindicator
TEMPLATE = lib
CONFIG += plugin link_pkgconfig
DEFINES += QCMA_TRAYINDICATOR_LIBRARY
QT += gui widgets
QT_CONFIG -= no-pkg-config
PKGCONFIG = appindicator-0.1 libnotify
SOURCES += \
unityindicator.cpp
HEADERS += \
trayindicator.h \
unityindicator.h
CXXFLAGS_WARNINGS = -Wall -Wextra -Wshadow -Wcast-align -Wctor-dtor-privacy \
-Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations \
-Wmissing-include-dirs -Woverloaded-virtual -Wredundant-decls \
-Wstrict-overflow=5 -Wundef -Wno-unused -Wno-missing-field-initializers \
-Wno-format-nonliteral
QMAKE_CXXFLAGS += -Wno-write-strings -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS $$CXXFLAGS_WARNINGS
DATADIR = $$PREFIX/share
actions64.path = $$DATADIR/icons/hicolor/64x64/actions
actions64.files += resources/images/qcma_on.png
actions64.files += resources/images/qcma_off.png
target.path = /usr/lib/qcma
INSTALLS += target actions64

View File

@@ -0,0 +1 @@
../gui/trayindicator.h

View File

@@ -0,0 +1,201 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2014 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "unityindicator.h"
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <QStandardPaths>
#else
#include <QDesktopServices>
#define QStandardPaths QDesktopServices
#define writableLocation storageLocation
#endif
#include <QDebug>
#include <QDir>
#include <QVector>
#undef signals
extern "C" {
#include <libnotify/notify.h>
void optionsIndicator(GtkMenu *menu, gpointer data);
void reloadIndicator(GtkMenu *menu, gpointer data);
void backupIndicator(GtkMenu *menu, gpointer data);
void aboutIndicator(GtkMenu *menu, gpointer data);
void aboutQtIndicator(GtkMenu *menu, gpointer data);
void quitIndicator(GtkMenu *menu, gpointer data);
}
#define signals public
UnityIndicator::UnityIndicator(QWidget *obj_parent) :
TrayIndicator(obj_parent)
{
notify_init("qcma");
}
UnityIndicator::~UnityIndicator()
{
notify_uninit();
for(QVector<QPair<gpointer, gulong> >::iterator it = m_handlers.begin(); it != m_handlers.end(); ++it)
{
g_signal_handler_disconnect(it->first, it->second);
}
}
void optionsIndicator(GtkMenu *, gpointer data)
{
UnityIndicator *m_this = reinterpret_cast<UnityIndicator *>(data);
emit m_this->openConfig();
}
void reloadIndicator(GtkMenu *, gpointer data)
{
UnityIndicator *m_this = reinterpret_cast<UnityIndicator *>(data);
emit m_this->refreshDatabase();
}
void backupIndicator(GtkMenu *, gpointer data)
{
UnityIndicator *m_this = reinterpret_cast<UnityIndicator *>(data);
emit m_this->openManager();
}
void aboutIndicator(GtkMenu *, gpointer data)
{
UnityIndicator *m_this = reinterpret_cast<UnityIndicator *>(data);
emit m_this->showAboutDialog();
}
void aboutQtIndicator(GtkMenu *, gpointer data)
{
UnityIndicator *m_this = reinterpret_cast<UnityIndicator *>(data);
emit m_this->showAboutQt();
}
void quitIndicator(GtkMenu *, gpointer data)
{
UnityIndicator *m_this = reinterpret_cast<UnityIndicator *>(data);
emit m_this->stopServer();
}
void UnityIndicator::init()
{
GtkWidget *menu = gtk_menu_new();
GtkWidget *options = gtk_menu_item_new_with_label(qPrintable(tr("Settings")));
GtkWidget *reload = gtk_menu_item_new_with_label(qPrintable(tr("Refresh database")));
GtkWidget *backup = gtk_menu_item_new_with_label(qPrintable(tr("Backup Manager")));
GtkWidget *separator1 = gtk_separator_menu_item_new();
GtkWidget *about = gtk_menu_item_new_with_label(qPrintable(tr("About QCMA")));
GtkWidget *about_qt = gtk_menu_item_new_with_label(qPrintable(tr("About Qt")));
GtkWidget *separator2 = gtk_separator_menu_item_new();
GtkWidget *quit = gtk_menu_item_new_with_label(qPrintable(tr("Quit")));
gtk_menu_shell_append(GTK_MENU_SHELL(menu), options);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), reload);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), backup);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator1);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), about);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), about_qt);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator2);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), quit);
gulong gobject_handler;
gobject_handler = g_signal_connect(options, "activate", G_CALLBACK(optionsIndicator), this);
m_handlers.append(QPair<gpointer, gulong>(options, gobject_handler));
gobject_handler = g_signal_connect(reload, "activate", G_CALLBACK(reloadIndicator), this);
m_handlers.append(QPair<gpointer, gulong>(reload, gobject_handler));
gobject_handler = g_signal_connect(backup, "activate", G_CALLBACK(backupIndicator), this);
m_handlers.append(QPair<gpointer, gulong>(backup, gobject_handler));
gobject_handler = g_signal_connect(about, "activate", G_CALLBACK(aboutIndicator), this);
m_handlers.append(QPair<gpointer, gulong>(about, gobject_handler));
gobject_handler = g_signal_connect(about_qt, "activate", G_CALLBACK(aboutQtIndicator), this);
m_handlers.append(QPair<gpointer, gulong>(about_qt, gobject_handler));
gobject_handler = g_signal_connect(quit, "activate", G_CALLBACK(quitIndicator), this);
m_handlers.append(QPair<gpointer, gulong>(quit, gobject_handler));
gtk_widget_show(options);
gtk_widget_show(reload);
gtk_widget_show(backup);
gtk_widget_show(separator1);
gtk_widget_show(about);
gtk_widget_show(about_qt);
gtk_widget_show(separator2);
gtk_widget_show(quit);
m_indicator = app_indicator_new(
"qcma-appindicator",
"qcma-messages",
APP_INDICATOR_CATEGORY_APPLICATION_STATUS
);
QString icon_path;
QString icon_name = "share/icons/hicolor/64x64/actions/qcma_on.png";
if(QFile("/usr/" + icon_name).exists())
icon_path = QFileInfo("/usr/" + icon_name).absolutePath();
else if(QFile("/usr/local" + icon_name).exists())
icon_path = QFileInfo("/usr/" + icon_name).absolutePath();
if(!icon_path.isEmpty())
{
qDebug() << "Using " << icon_path << " as icon theme path";
app_indicator_set_icon_theme_path(m_indicator, qPrintable(icon_path));
}
else
qWarning() << "Cannot find qcma icons.";
app_indicator_set_status(m_indicator, APP_INDICATOR_STATUS_ACTIVE);
app_indicator_set_menu(m_indicator, GTK_MENU(menu));
}
bool UnityIndicator::isVisible()
{
return true;
}
void UnityIndicator::setIcon(const QString &icon)
{
app_indicator_set_icon_full(m_indicator, qPrintable(icon), "icon");
}
void UnityIndicator::show()
{
}
void UnityIndicator::hide()
{
}
void UnityIndicator::showMessage(const QString &title, const QString &message)
{
NotifyNotification *notif = notify_notification_new(qPrintable(title), qPrintable(message), "dialog-information");
notify_notification_show(notif, NULL);
g_object_unref(G_OBJECT(notif));
}
// exported library function
TrayIndicator *createTrayIndicator(QWidget *parent)
{
return new UnityIndicator(parent);
}

View File

@@ -0,0 +1,54 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2014 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UNITYINDICATOR_H
#define UNITYINDICATOR_H
#include "trayindicator.h"
#include <QVector>
#undef signals
extern "C" {
#include <libappindicator/app-indicator.h>
#include <gtk/gtk.h>
}
#define signals public
class UnityIndicator : public TrayIndicator
{
Q_OBJECT
public:
explicit UnityIndicator(QWidget *parent = 0);
~UnityIndicator();
void init();
void setIcon(const QString &icon);
bool isVisible();
void show();
void hide();
void showMessage(const QString &title, const QString &message);
private:
AppIndicator *m_indicator;
QVector<QPair<gpointer, gulong> > m_handlers;
};
#endif // UNITYINDICATOR_H

30
cli/cli.pro Normal file
View File

@@ -0,0 +1,30 @@
include(../config.pri)
include(../common/defines.pri)
TARGET = qcma_cli
TEMPLATE += app
QT += network sql
LIBS += -L../common -lqcma_common
SOURCES += \
main_cli.cpp \
singlecoreapplication.cpp \
headlessmanager.cpp
HEADERS += \
singlecoreapplication.h \
headlessmanager.h
# find packages using pkg-config
QT_CONFIG -= no-pkg-config
CONFIG += link_pkgconfig
PKGCONFIG = libvitamtp libavformat libavcodec libavutil libswscale
# Linux-only config
unix:!macx {
man_cli.files = qcma_cli.1
man_cli.path = $$MANDIR
target.path = $$BINDIR
INSTALLS += target man_cli
}

191
cli/headlessmanager.cpp Normal file
View File

@@ -0,0 +1,191 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cmaclient.h"
#include "cmautils.h"
#include "sqlitedb.h"
#include "qlistdb.h"
#include "headlessmanager.h"
#include <QCoreApplication>
#include <QSettings>
#include <QTextStream>
#include <sys/socket.h>
#include <unistd.h>
#include <vitamtp.h>
int HeadlessManager::sighup_fd[2];
int HeadlessManager::sigterm_fd[2];
HeadlessManager::HeadlessManager(QObject *obj_parent) :
QObject(obj_parent)
{
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sighup_fd))
qFatal("Couldn't create HUP socketpair");
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigterm_fd))
qFatal("Couldn't create TERM socketpair");
sn_hup = new QSocketNotifier(sighup_fd[1], QSocketNotifier::Read, this);
connect(sn_hup, SIGNAL(activated(int)), this, SLOT(handleSigHup()));
sn_term = new QSocketNotifier(sigterm_fd[1], QSocketNotifier::Read, this);
connect(sn_term, SIGNAL(activated(int)), this, SLOT(handleSigTerm()));
}
HeadlessManager::~HeadlessManager()
{
VitaMTP_Cleanup();
delete m_db;
}
void HeadlessManager::refreshDatabase()
{
if(m_db->load()) {
return;
}
QTextStream(stdout) << "Database scan has started" << endl;
if(!m_db->rescan()) {
qWarning("No PS Vita system has been registered");
}
}
void HeadlessManager::start()
{
if(VitaMTP_Init() < 0) {
qCritical("Cannot initialize VitaMTP library");
return;
}
if(QSettings().value("useMemoryStorage", true).toBool()) {
m_db = new QListDB();
} else {
m_db = new SQLiteDB();
}
// initializing database for the first use
refreshDatabase();
// send a signal when the update is finished
connect(m_db, SIGNAL(updated(int)), this, SIGNAL(databaseUpdated(int)));
thread_count = 0;
qDebug("Starting cma threads");
CmaClient *client;
QSettings settings;
if(!settings.value("disableUSB", false).toBool()) {
if(!belongsToGroup("vitamtp"))
qCritical() << tr("This user doesn't belong to the vitamtp group, there could be a problem while reading the USB bus.");
usb_thread = new QThread();
client = new CmaClient(m_db);
usb_thread->setObjectName("usb_thread");
connect(usb_thread, SIGNAL(started()), client, SLOT(connectUsb()));
connect(client, SIGNAL(finished()), usb_thread, SLOT(quit()), Qt::DirectConnection);
connect(usb_thread, SIGNAL(finished()), usb_thread, SLOT(deleteLater()));
connect(usb_thread, SIGNAL(finished()), this, SLOT(threadStopped()));
connect(usb_thread, SIGNAL(finished()), client, SLOT(deleteLater()));
connect(client, SIGNAL(refreshDatabase()), this, SLOT(refreshDatabase()));
client->moveToThread(usb_thread);
usb_thread->start();
thread_count++;
}
if(!settings.value("disableWireless", false).toBool()) {
CmaBroadcast *broadcast = new CmaBroadcast(this);
wireless_thread = new QThread();
client = new CmaClient(m_db, broadcast);
wireless_thread->setObjectName("wireless_thread");
connect(wireless_thread, SIGNAL(started()), client, SLOT(connectWireless()));
connect(client, SIGNAL(finished()), wireless_thread, SLOT(quit()), Qt::DirectConnection);
connect(wireless_thread, SIGNAL(finished()), wireless_thread, SLOT(deleteLater()));
connect(wireless_thread, SIGNAL(finished()), this, SLOT(threadStopped()));
connect(wireless_thread, SIGNAL(finished()), client, SLOT(deleteLater()));
connect(client, SIGNAL(refreshDatabase()), this, SLOT(refreshDatabase()));
client->moveToThread(wireless_thread);
wireless_thread->start();
thread_count++;
}
if(thread_count == 0) {
qCritical("You must enable at least USB or Wireless monitoring");
}
}
void HeadlessManager::receiveMessage(QString message)
{
QTextStream(stdout) << message << endl;
}
void HeadlessManager::stop()
{
if(CmaClient::stop() < 0) {
QCoreApplication::quit();
}
}
void HeadlessManager::threadStopped()
{
mutex.lock();
if(--thread_count == 0) {
QCoreApplication::quit();
}
mutex.unlock();
}
void HeadlessManager::hupSignalHandler(int)
{
char a = 1;
::write(sighup_fd[0], &a, sizeof(a));
}
void HeadlessManager::termSignalHandler(int)
{
char a = 1;
::write(sigterm_fd[0], &a, sizeof(a));
}
void HeadlessManager::handleSigTerm()
{
sn_term->setEnabled(false);
char tmp;
::read(sigterm_fd[1], &tmp, sizeof(tmp));
stop();
sn_term->setEnabled(true);
}
void HeadlessManager::handleSigHup()
{
sn_hup->setEnabled(false);
char tmp;
::read(sighup_fd[1], &tmp, sizeof(tmp));
refreshDatabase();
sn_hup->setEnabled(true);
}

76
cli/headlessmanager.h Normal file
View File

@@ -0,0 +1,76 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HEADLESSMANAGER_H
#define HEADLESSMANAGER_H
#include "database.h"
#include <QObject>
#include <QThread>
#include <QSocketNotifier>
class HeadlessManager : public QObject
{
Q_OBJECT
public:
explicit HeadlessManager(QObject *parent = 0);
~HeadlessManager();
void start();
// unix signal handlers
static void hupSignalHandler(int);
static void termSignalHandler(int);
private:
int thread_count;
QMutex mutex;
Database *m_db;
QThread *usb_thread;
QThread *wireless_thread;
// signal handling
static int sighup_fd[2];
static int sigterm_fd[2];
QSocketNotifier *sn_hup;
QSocketNotifier *sn_term;
signals:
void stopped();
void databaseUpdated(int count);
public slots:
void refreshDatabase();
void stop();
// Qt signal handlers
void handleSigHup();
void handleSigTerm();
private slots:
void threadStopped();
void receiveMessage(QString message);
};
#endif // HEADLESSMANAGER_H

165
cli/main_cli.cpp Normal file
View File

@@ -0,0 +1,165 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef Q_OS_WIN32
#include <signal.h>
#endif
#include <QDebug>
#include <QLibraryInfo>
#include <QLocale>
#include <QStringList>
#include <QTextCodec>
#include <QThread>
#include <QTranslator>
#include <inttypes.h>
#include <vitamtp.h>
#include "singlecoreapplication.h"
#include "headlessmanager.h"
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
static void noDebugOutput(QtMsgType type, const QMessageLogContext &, const QString & msg)
{
QByteArray localMsg = msg.toLocal8Bit();
const char *message = localMsg.constData();
#else
static void noDebugOutput(QtMsgType type, const char *message)
{
#endif
switch (type) {
case QtDebugMsg:
break;
case QtWarningMsg:
fprintf(stderr, "Warning: %s\n", message);
break;
case QtCriticalMsg:
fprintf(stderr, "Critical: %s\n", message);
break;
case QtFatalMsg:
fprintf(stderr, "Fatal: %s\n", message);
abort();
}
}
static bool setup_handlers()
{
struct sigaction hup, term;
hup.sa_handler = HeadlessManager::hupSignalHandler;
sigemptyset(&hup.sa_mask);
hup.sa_flags = 0;
hup.sa_flags |= SA_RESTART;
if (sigaction(SIGHUP, &hup, NULL) != 0) {
qCritical("SIGHUP signal handle failed");
return false;
}
term.sa_handler = HeadlessManager::termSignalHandler;
sigemptyset(&term.sa_mask);
term.sa_flags |= SA_RESTART;
if (sigaction(SIGTERM, &term, NULL) != 0) {
qCritical("SIGTERM signal handle failed");
return false;
}
if (sigaction(SIGINT, &term, NULL) != 0) {
qCritical("SIGINT signal handle failed");
return false;
}
return true;
}
int main(int argc, char *argv[])
{
if(SingleCoreApplication::sendMessage("Another instance of Qcma tried to start")) {
QTextStream(stdout) << "An instance of Qcma is already running" << endl;
return 0;
}
SingleCoreApplication app(argc, argv);
// FIXME: libmtp sends SIGPIPE if a socket write fails crashing the whole app
// the proper fix is to libmtp to handle the cancel properly or ignoring
// SIGPIPE on the socket
signal(SIGPIPE, SIG_IGN);
if(!setup_handlers())
return 1;
if(app.arguments().contains("--with-debug")) {
VitaMTP_Set_Logging(VitaMTP_DEBUG);
} else if(app.arguments().contains("--verbose")) {
VitaMTP_Set_Logging(VitaMTP_VERBOSE);
} else {
VitaMTP_Set_Logging(VitaMTP_NONE);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
qInstallMessageHandler(noDebugOutput);
#else
qInstallMsgHandler(noDebugOutput);
#endif
}
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
#endif
QTextStream(stdout) << "Starting Qcma " << QCMA_VER << endl;
QTranslator translator;
QString locale = QLocale().system().name();
qDebug() << "Current locale:" << locale;
if(app.arguments().contains("--set-locale")) {
int index = app.arguments().indexOf("--set-locale");
if(index + 1 < app.arguments().length()) {
qDebug("Enforcing locale: %s", app.arguments().at(index + 1).toUtf8().data());
locale = app.arguments().at(index + 1);
}
}
if(translator.load("qcma_" + locale, ":/resources/translations")) {
app.installTranslator(&translator);
} else {
qWarning() << "Cannot load translation for locale:" << locale;
}
QTranslator system_translator;
system_translator.load("qt_" + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
app.installTranslator(&system_translator);
qDebug("Starting main thread: 0x%016" PRIxPTR, (uintptr_t)QThread::currentThreadId());
// set the organization/application for QSettings to work properly
app.setOrganizationName("codestation");
app.setApplicationName("qcma");
HeadlessManager manager;
// receive the message from another process
QObject::connect(&app, SIGNAL(messageAvailable(QString)), &manager, SLOT(receiveMessage(QString)));
manager.start();
return app.exec();
}

33
cli/qcma_cli.1 Normal file
View File

@@ -0,0 +1,33 @@
.TH QCMA-CLI 1 "MARCH 2015"
.SH NAME
qcma-cli \- Content manager for the PlayStation Vita
.SH SYNOPSIS
.B qcma_cli \fR[options]
.TP
.B \-\-verbose
print detailed output
.TP
.B \-\-with-debug
print very verbose output (including hexdump transfers)
.SH DESCRIPTION
\fBqcma_cli\fR is a cross-platform application to provide an Open Source implementation
of the original Content Manager Assistant that comes with the PS Vita.
.TP
Qcma will let you backup your games, songs, photos & videos via USB or wireless.
.SH SIGNALS
You can control \fBqcma_cli\fR using signals, i.e. using the \fBkill\fR command to send a signal to the process.
.TP
.B SIGHUP
Refreshes the database.
.TP
.B SIGTERM or SIGINT
Shuts down the process but waits until the current event is finished.
.SH "SEE ALSO"
.I /usr/share/doc/qcma/README.md

View File

@@ -0,0 +1,69 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "singlecoreapplication.h"
#include <QDebug>
const int SingleCoreApplication::timeout = 500;
const QString SingleCoreApplication::SHARED_KEY = "QCMA_KEY";
SingleCoreApplication::SingleCoreApplication(int &argc, char **argv) :
QCoreApplication(argc, argv)
{
server = new QLocalServer(this);
connect(server, SIGNAL(newConnection()), this, SLOT(receiveMessage()));
QLocalServer::removeServer(SHARED_KEY);
server->listen(SHARED_KEY);
}
void SingleCoreApplication::receiveMessage()
{
QLocalSocket *socket = server->nextPendingConnection();
if(!socket->waitForReadyRead(timeout)) {
qDebug() << socket->errorString();
return;
}
QByteArray byteArray = socket->readAll();
QString message = QString::fromUtf8(byteArray.constData());
emit messageAvailable(message);
socket->disconnectFromServer();
}
bool SingleCoreApplication::sendMessage(const QString &message)
{
QLocalSocket socket;
socket.connectToServer(SHARED_KEY, QIODevice::WriteOnly);
if(!socket.waitForConnected(timeout)) {
return false;
}
socket.write(message.toUtf8());
if(!socket.waitForBytesWritten(timeout)) {
qDebug() << socket.errorString();
return false;
}
socket.disconnectFromServer();
return true;
}

View File

@@ -0,0 +1,50 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SINGLECOREAPPLICATION_H
#define SINGLECOREAPPLICATION_H
#include <QCoreApplication>
#include <QLocalSocket>
#include <QLocalServer>
#include <QSharedMemory>
class SingleCoreApplication : public QCoreApplication
{
Q_OBJECT
public:
explicit SingleCoreApplication(int &argc, char **argv);
static bool sendMessage(const QString &message);
private:
QLocalServer *server;
static const int timeout;
static const QString SHARED_KEY;
signals:
void messageAvailable(QString message);
public slots:
void receiveMessage();
};
#endif // SINGLECOREAPPLICATION_H

267
common/avdecoder.cpp Normal file
View File

@@ -0,0 +1,267 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cmautils.h"
#include "avdecoder.h"
#include "database.h"
#include <QBuffer>
#include <QFile>
#include <QImage>
#include <QSettings>
AVDecoder::AvInit init;
AVDecoder::AVDecoder() :
pFormatCtx(NULL), pCodecCtx(NULL), av_stream(NULL), av_codec(NULL), stream_index(-1)
{
}
AVDecoder::~AVDecoder()
{
if(pCodecCtx) {
avcodec_close(pCodecCtx);
}
if(pFormatCtx) {
avformat_close_input(&pFormatCtx);
}
}
bool AVDecoder::open(const QString filename)
{
if(avformat_open_input(&pFormatCtx, QFile::encodeName(filename).constData(), NULL, NULL) != 0) {
return false;
}
if(avformat_find_stream_info(pFormatCtx, NULL) < 0) {
avformat_close_input(&pFormatCtx);
return false;
}
return true;
}
bool AVDecoder::loadCodec(codec_type codec)
{
AVDictionary *opts = NULL;
if((stream_index = av_find_best_stream(pFormatCtx, (AVMediaType)codec, -1, -1, &av_codec, 0)) < 0) {
return false;
}
av_stream = pFormatCtx->streams[stream_index];
pCodecCtx = av_stream->codec;
if(avcodec_open2(pCodecCtx, av_codec, &opts) < 0) {
return false;
}
return true;
}
const char *AVDecoder::getMetadataEntry(const char *key, const char *default_value)
{
AVDictionaryEntry *entry;
if((entry = av_dict_get(pFormatCtx->metadata, key, NULL, 0)) != NULL) {
return entry->value;
}
return default_value;
}
void AVDecoder::getAudioMetadata(metadata_t &metadata)
{
metadata.data.music.artist = strdup(getMetadataEntry("artist", ""));
metadata.data.music.album = strdup(getMetadataEntry("album", ""));
metadata.data.music.title = strdup(getMetadataEntry("title", ""));
if(loadCodec(CODEC_AUDIO)) {
metadata.data.music.tracks->data.track_audio.bitrate = pCodecCtx->bit_rate;
} else {
metadata.data.music.tracks->data.track_audio.bitrate = pFormatCtx->bit_rate;
}
}
void AVDecoder::getVideoMetadata(metadata_t &metadata)
{
metadata.data.video.copyright = strdup(getMetadataEntry("copyright", ""));
metadata.data.video.explanation = strdup(getMetadataEntry("comments", ""));
metadata.data.video.title = strdup(getMetadataEntry("title", metadata.name));
if(loadCodec(CODEC_VIDEO)) {
metadata.data.video.tracks->data.track_video.width = pCodecCtx->width;
metadata.data.video.tracks->data.track_video.height = pCodecCtx->height;
metadata.data.video.tracks->data.track_video.bitrate = pCodecCtx->bit_rate;
metadata.data.video.tracks->data.track_video.duration = pFormatCtx->duration / 1000;
if(strcmp(av_codec->name, "h264") == 0) {
metadata.data.video.tracks->data.track_video.codecType = CODEC_TYPE_AVC;
} else if(strcmp(av_codec->name, "mpeg4") == 0) {
metadata.data.video.tracks->data.track_video.codecType = CODEC_TYPE_MPEG4;
} else {
metadata.data.video.tracks->data.track_video.codecType = 0;
}
}
}
QByteArray AVDecoder::getAudioThumbnail(int width, int height)
{
QByteArray data;
if(!loadCodec(CODEC_VIDEO)) {
return data;
}
AVPacket pkt;
if(av_read_frame(pFormatCtx, &pkt) >= 0) {
// first frame == first thumbnail (hopefully)
QBuffer imgbuffer(&data);
imgbuffer.open(QIODevice::WriteOnly);
QImage img = QImage::fromData(QByteArray((const char *)pkt.data, pkt.size));
QImage result = img.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation);
result.save(&imgbuffer, "JPEG");
}
return data;
}
AVFrame *AVDecoder::getDecodedFrame(AVCodecContext *codec_ctx, int frame_stream_index)
{
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,28,1)
AVFrame *pFrame = av_frame_alloc();
#else
AVFrame *pFrame = avcodec_alloc_frame();
#endif
AVPacket packet;
int frame_finished = 0;
while(!frame_finished && av_read_frame(pFormatCtx, &packet)>=0) {
if(packet.stream_index == frame_stream_index) {
avcodec_decode_video2(codec_ctx, pFrame, &frame_finished, &packet);
}
av_free_packet(&packet);
}
if(frame_finished) {
return pFrame;
} else {
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,28,1)
av_frame_free(&pFrame);
#else
av_free(pFrame);
#endif
return NULL;
}
}
QByteArray AVDecoder::getVideoThumbnail(int width, int height)
{
QByteArray data;
AVFrame *pFrame;
int percentage = QSettings().value("videoThumbnailSeekPercentage", 30).toInt();
if(!loadCodec(CODEC_VIDEO)) {
return data;
}
qint64 seek_pos = pFormatCtx->duration * percentage / (AV_TIME_BASE * 100);
qint64 frame = av_rescale(seek_pos, av_stream->time_base.den, av_stream->time_base.num);
if(avformat_seek_file(pFormatCtx, stream_index, 0, frame, frame, AVSEEK_FLAG_FRAME) < 0) {
avcodec_close(pCodecCtx);
return data;
}
if((pFrame = getDecodedFrame(pCodecCtx, stream_index)) == NULL) {
avcodec_close(pCodecCtx);
return data;
}
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,28,1)
AVFrame *pFrameRGB = av_frame_alloc();
#else
AVFrame *pFrameRGB = avcodec_alloc_frame();
#endif
int numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
uint8_t *buffer = (uint8_t *)av_malloc(numBytes);
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
SwsContext *sws_ctx = sws_getContext(
pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
pCodecCtx->width,
pCodecCtx->height,
PIX_FMT_RGB24,
SWS_BICUBIC,
NULL,
NULL,
NULL
);
if(!sws_ctx) {
avcodec_close(pCodecCtx);
return data;
}
sws_scale(
sws_ctx,
pFrame->data,
pFrame->linesize,
0,
pCodecCtx->height,
pFrameRGB->data,
pFrameRGB->linesize
);
QImage image(pCodecCtx->width, pCodecCtx->height, QImage::Format_RGB888);
for(int y = 0, h = pCodecCtx->height, w = pCodecCtx->width; y < h; y++) {
memcpy(image.scanLine(y), pFrameRGB->data[0] + y * pFrameRGB->linesize[0], w * 3);
}
QBuffer imgbuffer(&data);
imgbuffer.open(QIODevice::WriteOnly);
QImage result = image.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation);
result.save(&imgbuffer, "JPEG");
av_free(buffer);
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,28,1)
av_frame_free(&pFrameRGB);
av_frame_free(&pFrame);
#else
av_free(pFrameRGB);
av_free(pFrame);
#endif
avcodec_close(pCodecCtx);
return data;
}
void AVDecoder::close()
{
avformat_close_input(&pFormatCtx);
pFormatCtx = NULL;
}

91
common/avdecoder.h Normal file
View File

@@ -0,0 +1,91 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef AVDECODER_H
#define AVDECODER_H
#include <QString>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/mathematics.h>
}
#include <vitamtp.h>
class AVDecoder
{
public:
AVDecoder();
~AVDecoder();
enum codec_type {CODEC_VIDEO = AVMEDIA_TYPE_VIDEO, CODEC_AUDIO = AVMEDIA_TYPE_AUDIO};
bool open(const QString filename);
void close();
bool loadCodec(codec_type codec);
QByteArray getAudioThumbnail(int width, int height);
QByteArray getVideoThumbnail(int width, int height);
void getAudioMetadata(metadata_t &metadata);
void getVideoMetadata(metadata_t &metadata);
const char *getMetadataEntry(const char *key, const char *default_value = NULL);
inline int getWidth() {
return pCodecCtx->width;
}
inline int getHeight() {
return pCodecCtx->height;
}
inline int getDuration() {
return pFormatCtx->duration / 1000;
}
inline int getBitrate() {
return pCodecCtx->bit_rate;
}
// simulate a static constructor to initialize libav only once
class AvInit
{
public:
AvInit() {
av_register_all();
// hide warning logs
av_log_set_level(AV_LOG_ERROR);
}
};
static AvInit init;
private:
AVFrame *getDecodedFrame(AVCodecContext *pCodecCtx, int stream_index);
AVFormatContext *pFormatCtx;
AVCodecContext *pCodecCtx;
AVStream *av_stream;
AVCodec *av_codec;
int stream_index;
};
#endif // AVDECODER_H

132
common/capability.cpp Normal file
View File

@@ -0,0 +1,132 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "capability.h"
#include "cmautils.h"
#include <QDebug>
#include <QHostInfo>
#include <QSettings>
DeviceCapability::DeviceCapability() :
vita_info()
{
}
bool DeviceCapability::exchangeInfo(vita_device_t *device)
{
if(VitaMTP_GetVitaInfo(device, &vita_info) != PTP_RC_OK) {
qWarning("Cannot retreve device information.");
return false;
}
if(vita_info.protocolVersion > VITAMTP_PROTOCOL_MAX_VERSION) {
qWarning("Vita wants protocol version %08d while we only support %08d. Attempting to continue.",
vita_info.protocolVersion, VITAMTP_PROTOCOL_MAX_VERSION);
}
QString hostname = QHostInfo::localHostName();
int protocol_version = ::getVitaProtocolVersion();
qDebug() << "Sending Qcma protocol version:" << protocol_version;
const initiator_info_t *pc_info = VitaMTP_Data_Initiator_New(hostname.toUtf8().data(), protocol_version);
// Next, we send the client's (this program) info (discard the const here)
if(VitaMTP_SendInitiatorInfo(device, (initiator_info_t *)pc_info) != PTP_RC_OK) {
qWarning("Cannot send host information.");
return false;
}
if(vita_info.protocolVersion >= VITAMTP_PROTOCOL_FW_2_10) {
// Get the device's capabilities
capability_info_t *vita_capabilities;
if(VitaMTP_GetVitaCapabilityInfo(device, &vita_capabilities) != PTP_RC_OK) {
qWarning("Failed to get capability information from Vita.");
return false;
}
// TODO: vitamtp needs to send the full metadata info to know the expected format
// of thumbnails, for example. Until then lets discard the received info.
VitaMTP_Data_Free_Capability(vita_capabilities);
// Send the host's capabilities
capability_info_t *pc_capabilities = generate_pc_capability_info();
if(VitaMTP_SendPCCapabilityInfo(device, pc_capabilities) != PTP_RC_OK) {
qWarning("Failed to send capability information to Vita.");
free_pc_capability_info(pc_capabilities);
return false;
}
free_pc_capability_info(pc_capabilities);
}
// Finally, we tell the Vita we are connected
if(VitaMTP_SendHostStatus(device, VITA_HOST_STATUS_Connected) != PTP_RC_OK) {
qWarning("Cannot send host status.");
return false;
}
VitaMTP_Data_Free_Initiator(pc_info);
return true;
}
void DeviceCapability::free_pc_capability_info(capability_info_t *info)
{
delete[] &info->functions.formats.next_item[-1];
delete[] &info->functions.next_item[-1];
delete info;
}
capability_info_t *DeviceCapability::generate_pc_capability_info()
{
typedef capability_info::capability_info_function tfunction;
typedef tfunction::capability_info_format tformat;
// TODO: Actually do this based on QCMA capabilities
capability_info_t *pc_capabilities = new capability_info_t;
pc_capabilities->version = "1.0";
tfunction *functions = new tfunction[3]();
tformat *game_formats = new tformat[5]();
game_formats[0].contentType = "vitaApp";
game_formats[0].next_item = &game_formats[1];
game_formats[1].contentType = "PSPGame";
game_formats[1].next_item = &game_formats[2];
game_formats[2].contentType = "PSPSaveData";
game_formats[2].next_item = &game_formats[3];
game_formats[3].contentType = "PSGame";
game_formats[3].next_item = &game_formats[4];
game_formats[4].contentType = "PSMApp";
functions[0].type = "game";
functions[0].formats = game_formats[0];
functions[0].next_item = &functions[1];
functions[1].type = "backup";
functions[1].next_item = &functions[2];
functions[2].type = "systemUpdate";
pc_capabilities->functions = functions[0];
return pc_capabilities;
}
DeviceCapability::~DeviceCapability()
{
VitaMTP_Data_Free_VitaInfo(&vita_info);
}

52
common/capability.h Normal file
View File

@@ -0,0 +1,52 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CAPABILITY_H
#define CAPABILITY_H
#include <vitamtp.h>
class DeviceCapability
{
public:
explicit DeviceCapability();
~DeviceCapability();
bool exchangeInfo(vita_device_t *device);
const char *getVersion() {
return vita_info.responderVersion;
}
int getProtocolVersion() {
return vita_info.protocolVersion;
}
const char *getOnlineId() {
return vita_info.onlineId;
}
const char *getModelInfo() {
return vita_info.modelInfo;
}
private:
capability_info_t *generate_pc_capability_info();
void free_pc_capability_info(capability_info_t *info);
vita_info_t vita_info;
};
#endif // CAPABILITY_H

122
common/cmabroadcast.cpp Normal file
View File

@@ -0,0 +1,122 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cmabroadcast.h"
#include "cmautils.h"
#include <QDebug>
#include <QHostAddress>
#include <QHostInfo>
#include <QMutexLocker>
#include <QSettings>
#include <QUuid>
#include <vitamtp.h>
const QString CmaBroadcast::broadcast_reply =
"%1\r\n"
"host-id:%2\r\n"
"host-type:%3\r\n"
"host-name:%4\r\n"
"host-mtp-protocol-version:%5\r\n"
"host-request-port:%6\r\n"
"host-wireless-protocol-version:%7\r\n"
"host-supported-device:PS Vita, PS Vita TV\r\n";
const char *CmaBroadcast::broadcast_query_start = "SRCH";
const char *CmaBroadcast::broadcast_query_end = " * HTTP/1.1\r\n";
const char *CmaBroadcast::broadcast_ok = "HTTP/1.1 200 OK";
const char *CmaBroadcast::broadcast_unavailable = "HTTP/1.1 503 NG";
CmaBroadcast::CmaBroadcast(QObject *obj_parent) :
QObject(obj_parent)
{
QSettings settings;
// generate a GUID if doesn't exist yet in settings
uuid = settings.value("guid").toString();
if(uuid.isEmpty()) {
uuid = QUuid::createUuid().toString().mid(1,36);
settings.setValue("guid", uuid);
}
hostname = QHostInfo::localHostName();
setAvailable();
socket = new QUdpSocket(this);
connect(socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
QHostAddress host_address(QHostAddress::Any);
#else
QHostAddress host_address(QHostAddress::AnyIPv4);
#endif
if(!socket->bind(host_address, QCMA_REQUEST_PORT, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint)) {
qCritical() << "Failed to bind address for UDP broadcast";
}
}
void CmaBroadcast::readPendingDatagrams()
{
if(socket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(socket->pendingDatagramSize());
QHostAddress cma_sender;
quint16 senderPort;
socket->readDatagram(datagram.data(), datagram.size(), &cma_sender, &senderPort);
if(datagram.startsWith(broadcast_query_start) && datagram.contains(broadcast_query_end)) {
QMutexLocker locker(&mutex);
socket->writeDatagram(reply, cma_sender, senderPort);
} else {
qWarning("Unknown request: %.*s\n", datagram.length(), datagram.constData());
}
}
}
void CmaBroadcast::setAvailable()
{
QMutexLocker locker(&mutex);
int protocol_version = ::getVitaProtocolVersion();
reply.clear();
reply.insert(0, broadcast_reply
.arg(broadcast_ok, uuid, "win", hostname)
.arg(protocol_version, 8, 10, QChar('0'))
.arg(QCMA_REQUEST_PORT)
.arg(VITAMTP_WIRELESS_MAX_VERSION, 8, 10, QChar('0')));
reply.append('\0');
}
void CmaBroadcast::setUnavailable()
{
QMutexLocker locker(&mutex);
int protocol_version = ::getVitaProtocolVersion();
reply.clear();
reply.insert(0, broadcast_reply
.arg(broadcast_unavailable, uuid, "win", hostname)
.arg(protocol_version, 8, 10, QChar('0'))
.arg(QCMA_REQUEST_PORT)
.arg(VITAMTP_WIRELESS_MAX_VERSION, 8, 10, QChar('0')));
reply.append('\0');
}

58
common/cmabroadcast.h Normal file
View File

@@ -0,0 +1,58 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMABROADCAST_H
#define CMABROADCAST_H
#include <QMutex>
#include <QObject>
#include <QUdpSocket>
#define QCMA_REQUEST_PORT 9309
class CmaBroadcast : public QObject
{
Q_OBJECT
public:
explicit CmaBroadcast(QObject *parent = 0);
private:
void replyBroadcast(const QByteArray &datagram);
QMutex mutex;
QString uuid;
QByteArray reply;
QString hostname;
QUdpSocket *socket;
static const QString broadcast_reply;
static const char *broadcast_query_start;
static const char *broadcast_query_end;
static const char *broadcast_ok;
static const char *broadcast_unavailable;
public slots:
void setAvailable();
void setUnavailable();
private slots:
void readPendingDatagrams();
};
#endif // CMABROADCAST_H

284
common/cmaclient.cpp Normal file
View File

@@ -0,0 +1,284 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cmaclient.h"
#include "capability.h"
#include "avdecoder.h"
#include "cmaevent.h"
#include "cmautils.h"
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QImage>
#include <QTextStream>
#include <QTime>
#include <QSettings>
#include <QUrl>
QMutex CmaClient::mutex;
QMutex CmaClient::runner;
QWaitCondition CmaClient::usbcondition;
QMutex CmaClient::usbwait;
QSemaphore CmaClient::sema;
QString CmaClient::tempOnlineId = QString();
bool CmaClient::is_active = false;
bool CmaClient::in_progress = false;
CmaClient *CmaClient::this_object = NULL;
CmaClient::CmaClient(Database *db, QObject *obj_parent) :
QObject(obj_parent), m_db(db), m_broadcast(NULL)
{
this_object = this;
}
CmaClient::CmaClient(Database *db, CmaBroadcast *broadcast, QObject *obj_parent) :
QObject(obj_parent), m_db(db), m_broadcast(broadcast)
{
this_object = this;
}
void CmaClient::connectUsb()
{
vita_device_t *vita;
qDebug("Starting usb_thread: 0x%016" PRIxPTR, (uintptr_t)QThread::currentThreadId());
setActive(true);
usbwait.lock();
do {
if((vita = VitaMTP_Get_First_USB_Vita()) != NULL) {
qDebug("Starting new USB connection");
processNewConnection(vita);
} else {
//TODO: replace this with an event-driven setup
usbcondition.wait(&usbwait, 2000);
mutex.lock();
if(in_progress) {
sema.acquire();
}
mutex.unlock();
}
} while(isActive());
usbwait.unlock();
qDebug("Finishing usb_thread");
emit finished();
}
void CmaClient::connectWireless()
{
vita_device_t *vita;
wireless_host_info_t host = {NULL, NULL, NULL, QCMA_REQUEST_PORT};
typedef CmaClient CC;
QTime now = QTime::currentTime();
qsrand(now.msec());
qDebug("Starting wireless_thread: 0x%016" PRIxPTR, (uintptr_t)QThread::currentThreadId());
setActive(true);
do {
qDebug("Waiting for wireless connection");
if((vita = VitaMTP_Get_First_Wireless_Vita(&host, 0, CC::deviceRegistered, CC::generatePin, CC::registrationComplete)) != NULL) {
qDebug("Starting new wireless connection");
m_broadcast->setUnavailable();
processNewConnection(vita);
m_broadcast->setAvailable();
} else {
mutex.lock();
if(in_progress) {
sema.acquire();
}
mutex.unlock();
// if is active then something happened while setting the socket, wait a little and try again
if(isActive()) {
qDebug("Error getting wireless connection");
Sleeper::sleep(2000);
} else {
qDebug("Wireless connection cancelled by the user");
}
}
} while(isActive());
qDebug("Finishing wireless_thread");
emit finished();
}
void CmaClient::processNewConnection(vita_device_t *device)
{
QMutexLocker locker(&mutex);
in_progress = true;
QTextStream(stdout) << "Vita connected, id: " << VitaMTP_Get_Identification(device) << endl;
DeviceCapability vita_info;
if(!vita_info.exchangeInfo(device)) {
qCritical("Error while exchanging info with the vita");
if(VitaMTP_Get_Device_Type(device) == VitaDeviceUSB) {
// reset the device
VitaMTP_USB_Reset(device);
}
} else {
QSettings settings;
// Conection successful, inform the user
if(vita_info.getOnlineId() != NULL) {
settings.setValue("lastOnlineId", vita_info.getOnlineId());
emit deviceConnected(QString(tr("Connected to %1 (PS Vita)")).arg(vita_info.getOnlineId()));
} else {
QString onlineId = settings.value("lastOnlineId", "default").toString();
emit deviceConnected(QString(tr("Connected to %1 (PS Vita)")).arg(onlineId));
}
enterEventLoop(device);
}
VitaMTP_SendHostStatus(device, VITA_HOST_STATUS_EndConnection);
qDebug("Releasing device...");
VitaMTP_Release_Device(device);
emit deviceDisconnected();
in_progress = false;
sema.release();
}
void CmaClient::registrationComplete()
{
QSettings settings;
qDebug("Registration completed");
settings.setValue("lastOnlineId", tempOnlineId);
emit this_object->pinComplete();
}
int CmaClient::deviceRegistered(const char *deviceid)
{
qDebug("Got connection request from %s", deviceid);
// TODO: check the device to see if is already registered
return 1;
}
int CmaClient::generatePin(wireless_vita_info_t *info, int *p_err)
{
// save the device name in a temporal variable, just in case the pin is rejected
tempOnlineId = QString(info->name);
qDebug("Registration request from %s (MAC: %s)", info->name, info->mac_addr);
int pin = rand() % 10000 * 10000 | rand() % 10000;
QTextStream out(stdout);
out << "Your registration PIN for " << info->name << " is: ";
out.setFieldWidth(8);
out.setPadChar('0');
out << pin << endl;
*p_err = 0;
emit this_object->receivedPin(info->name, pin);
return pin;
}
void CmaClient::enterEventLoop(vita_device_t *device)
{
vita_event_t obj_event;
qDebug("Starting event loop");
CmaEvent eventLoop(m_db, device);
QThread obj_thread;
obj_thread.setObjectName("event_thread");
eventLoop.moveToThread(&obj_thread);
connect(&obj_thread, SIGNAL(started()), &eventLoop, SLOT(process()));
connect(&eventLoop, SIGNAL(refreshDatabase()), this, SIGNAL(refreshDatabase()), Qt::DirectConnection);
connect(&eventLoop, SIGNAL(finishedEventLoop()), &obj_thread, SLOT(quit()), Qt::DirectConnection);
connect(&eventLoop, SIGNAL(messageSent(QString)), this, SIGNAL(messageSent(QString)), Qt::DirectConnection);
obj_thread.start();
while(isActive()) {
if(VitaMTP_Read_Event(device, &obj_event) < 0) {
qWarning("Error reading event from Vita.");
break;
}
// do not create a event for this since there aren't more events to read
if(obj_event.Code == PTP_EC_VITA_RequestTerminate) {
qDebug("Terminating event thread");
break;
// this one shuold be processed inmediately
} else if(obj_event.Code == PTP_EC_VITA_RequestCancelTask) {
eventLoop.vitaEventCancelTask(&obj_event, obj_event.Param1);
qDebug("Ended event, code: 0x%x, id: %d", obj_event.Code, obj_event.Param1);
continue;
}
// the events are processed synchronously except for cancel/terminate
qDebug("Sending new event");
eventLoop.setEvent(obj_event);
}
eventLoop.stop();
obj_thread.wait();
qDebug("Finishing event loop");
}
int CmaClient::stop()
{
QTextStream(stdout) << "Stopping Qcma" << endl;
if(!isActive()) {
return -1;
}
CmaClient::setActive(false);
qDebug("Cancelling wireless server thread");
VitaMTP_Cancel_Get_Wireless_Vita();
usbcondition.wakeAll();
return 0;
}
bool CmaClient::isActive()
{
QMutexLocker locker(&runner);
return is_active;
}
void CmaClient::setActive(bool state)
{
QMutexLocker locker(&runner);
is_active = state;
}
bool CmaClient::isRunning()
{
bool ret;
if(mutex.tryLock()) {
ret = in_progress;
mutex.unlock();
} else {
ret = true;
}
return ret;
}

91
common/cmaclient.h Normal file
View File

@@ -0,0 +1,91 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMACLIENT_H
#define CMACLIENT_H
#include "qlistdb.h"
#include "cmaevent.h"
#include "cmabroadcast.h"
#include <QObject>
#include <QSemaphore>
#include <QString>
#include <QWaitCondition>
#include <vitamtp.h>
class CmaClient : public QObject
{
Q_OBJECT
public:
explicit CmaClient(Database *db, QObject *obj_parent = 0);
explicit CmaClient(Database *db, CmaBroadcast *broadcast, QObject *obj_parent = 0);
static bool isRunning();
void launch();
private:
static bool isActive();
static void setActive(bool state);
static bool isEventLoopEnabled();
static void setEventLoop(bool state);
void enterEventLoop(vita_device_t *device);
void processNewConnection(vita_device_t *device);
static int deviceRegistered(const char *deviceid);
static int generatePin(wireless_vita_info_t *info, int *p_err);
static int cancelCallback();
static void registrationComplete();
Database *m_db;
CmaBroadcast *m_broadcast;
static QString tempOnlineId;
//TODO: move all the control variables to the client manager class
static bool is_active;
static bool in_progress;
static CmaClient *this_object;
static QMutex mutex;
static QMutex runner;
static QMutex usbwait;
static QWaitCondition usbcondition;
static QSemaphore sema;
signals:
void newEvent(vita_event_t event);
void receivedPin(QString, int);
void pinComplete();
void deviceDetected();
void deviceConnected(QString);
void messageSent(QString);
void deviceDisconnected();
void refreshDatabase();
void finished();
public slots:
static int stop();
private slots:
void connectUsb();
void connectWireless();
};
#endif // CMACLIENT_H

1009
common/cmaevent.cpp Normal file

File diff suppressed because it is too large Load Diff

93
common/cmaevent.h Normal file
View File

@@ -0,0 +1,93 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMAEVENT_H
#define CMAEVENT_H
#include "database.h"
#include "httpdownloader.h"
#include <QFile>
#include <QNetworkReply>
#include <QObject>
#include <QSemaphore>
#include <vitamtp.h>
class CmaEvent : public QObject
{
Q_OBJECT
public:
explicit CmaEvent(Database *db, vita_device_t *s_device);
void vitaEventCancelTask(vita_event_t *event, int eventId);
private:
uint16_t processAllObjects(metadata_t &metadata, uint32_t handle);
void vitaEventSendObject(vita_event_t *event, int eventId);
void vitaEventSendObjectMetadata(vita_event_t *event, int eventId);
void vitaEventSendNumOfObject(vita_event_t *event, int eventId);
void vitaEventSendHttpObjectFromURL(vita_event_t *event, int eventId);
void vitaEventUnimplementated(vita_event_t *event, int eventId);
void vitaEventSendObjectStatus(vita_event_t *event, int eventId);
void vitaEventSendObjectThumb(vita_event_t *event, int eventId);
void vitaEventDeleteObject(vita_event_t *event, int eventId);
void vitaEventGetSettingInfo(vita_event_t *event, int eventId);
void vitaEventSendHttpObjectPropFromURL(vita_event_t *event, int eventId);
void vitaEventSendPartOfObject(vita_event_t *event, int eventId);
void vitaEventOperateObject(vita_event_t *event, int eventId);
void vitaEventGetPartOfObject(vita_event_t *event, int eventId);
void vitaEventSendStorageSize(vita_event_t *event, int eventId);
void vitaEventCheckExistance(vita_event_t *event, int eventId);
void vitaEventGetTreatObject(vita_event_t *event, int eventId);
void vitaEventSendCopyConfirmationInfo(vita_event_t *event, int eventId);
void vitaEventSendObjectMetadataItems(vita_event_t *event, int eventId);
void vitaEventSendNPAccountInfo(vita_event_t *event, int eventId);
static int readCallback(unsigned char *data, unsigned long wantlen, unsigned long *gotlen);
static int writeCallback(const unsigned char *data, unsigned long size, unsigned long *written);
void processEvent();
bool isActive();
void setDevice(vita_device_t *m_device);
vita_device_t *m_device;
vita_event_t m_event;
Database *m_db;
// control variables
bool is_active;
QMutex mutex;
QMutex active;
QSemaphore sema;
static QFile *m_file;
signals:
void finishedEventLoop();
void refreshDatabase();
void messageSent(QString);
public slots:
void process();
void setEvent(vita_event_t event);
void stop();
};
#endif // CMAEVENT_H

242
common/cmaobject.cpp Normal file
View File

@@ -0,0 +1,242 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cmaobject.h"
#include "sforeader.h"
#include "avdecoder.h"
#include "database.h"
#include "cmautils.h"
#include <QDir>
#include <QDateTime>
#include <QDebug>
#include <QSettings>
int CMAObject::ohfi_count = OHFI_OFFSET;
CMAObject::CMAObject(CMAObject *obj_parent) :
parent(obj_parent), metadata()
{
}
CMAObject::~CMAObject()
{
free(metadata.name);
free(metadata.path);
if(MASK_SET(metadata.dataType, SaveData | Folder)) {
free(metadata.data.saveData.title);
free(metadata.data.saveData.detail);
free(metadata.data.saveData.dirName);
free(metadata.data.saveData.savedataTitle);
} else if(MASK_SET(metadata.dataType, Photo | File)) {
free(metadata.data.photo.title);
free(metadata.data.photo.fileName);
delete metadata.data.photo.tracks;
} else if(MASK_SET(metadata.dataType, Music | File)) {
free(metadata.data.music.title);
free(metadata.data.music.fileName);
free(metadata.data.music.album);
free(metadata.data.music.artist);
delete metadata.data.music.tracks;
} else if(MASK_SET(metadata.dataType, Video | File)) {
free(metadata.data.video.title);
free(metadata.data.video.explanation);
free(metadata.data.video.fileName);
free(metadata.data.video.copyright);
delete metadata.data.video.tracks;
}
}
void CMAObject::loadSfoMetadata(const QString &path)
{
QString sfo = QDir(path).absoluteFilePath("PARAM.SFO");
bool skipMetadata = QSettings().value("skipMetadata", false).toBool();
SfoReader reader;
if(!skipMetadata && reader.load(sfo)) {
metadata.data.saveData.title = strdup(reader.value("TITLE", ""));
//FIXME: disable savedata detail for now
//QString detail(reader.value("SAVEDATA_DETAIL", ""));
// libxml follow the spec and normalizes the newlines (and others) but
// the PS Vita chokes on contiguous codes and crashes the CMA app on
// the device. Of course, the "fix" would be that libxml doesn't
// normalize newlines but that is aganist the XML spec that the PS Vita
// doesn't respect >_>
// convert DOS to UNIX newlines
//detail.replace("\r\n", "\n");
// separate newlines from quotes
//detail.replace("\n\"", "\n \"");
//detail.replace("\"\n", "\" \n");
// merge consecutive newlines
//detail.replace("\n\n", "\n");
//while(detail.endsWith('\n')) {
// detail.chop(1);
//}
//metadata.data.saveData.detail = strdup(detail.toStdString().c_str());
metadata.data.saveData.detail = strdup("");
// remove newlines from savedata title
QString title(reader.value("SAVEDATA_TITLE", ""));
while(title.endsWith('\n')) {
title.chop(1);
}
metadata.data.saveData.savedataTitle = strdup(title.toStdString().c_str());
metadata.data.saveData.dateTimeUpdated = QFileInfo(sfo).created().toUTC().toTime_t();
} else {
metadata.data.saveData.title = strdup(metadata.name);
metadata.data.saveData.detail = strdup("");
metadata.data.saveData.savedataTitle = strdup("");
metadata.data.saveData.dateTimeUpdated = 0;
}
}
void CMAObject::initObject(const QFileInfo &file, int obj_file_type)
{
metadata.name = strdup(file.fileName().toUtf8().data());
metadata.ohfiParent = parent->metadata.ohfi;
metadata.ohfi = ohfi_count++;
metadata.type = VITA_DIR_TYPE_MASK_REGULAR; // ignored for files
metadata.dateTimeCreated = file.created().toUTC().toTime_t();
metadata.size = 0;
DataType type = file.isFile() ? File : Folder;
metadata.dataType = (DataType)(type | (parent->metadata.dataType & ~Folder));
// create additional metadata
if(MASK_SET(metadata.dataType, SaveData | Folder)) {
metadata.data.saveData.dirName = strdup(metadata.name);
metadata.data.saveData.statusType = 1;
loadSfoMetadata(file.absoluteFilePath());
} else if(MASK_SET(metadata.dataType, Music | File)) {
if(obj_file_type < 0) {
qWarning("Invalid file type for music: %i, setting it to zero", obj_file_type);
obj_file_type = 0;
}
metadata.data.music.fileName = strdup(metadata.name);
metadata.data.music.fileFormatType = audio_list[obj_file_type].file_format;
metadata.data.music.statusType = 1;
metadata.data.music.numTracks = 1;
metadata.data.music.tracks = new media_track();
metadata.data.music.tracks->type = VITA_TRACK_TYPE_AUDIO;
metadata.data.music.tracks->data.track_photo.codecType = audio_list[obj_file_type].file_codec;
Database::loadMusicMetadata(file.absoluteFilePath(), metadata);
} else if(MASK_SET(metadata.dataType, Video | File)) {
metadata.data.video.fileName = strdup(metadata.name);
metadata.data.video.dateTimeUpdated = file.created().toUTC().toTime_t();
metadata.data.video.statusType = 1;
metadata.data.video.fileFormatType = FILE_FORMAT_MP4;
metadata.data.video.parentalLevel = 0;
metadata.data.video.numTracks = 1;
metadata.data.video.tracks = new media_track();
metadata.data.video.tracks->type = VITA_TRACK_TYPE_VIDEO;
Database::loadVideoMetadata(file.absoluteFilePath(), metadata);
} else if(MASK_SET(metadata.dataType, Photo | File)) {
if(obj_file_type < 0) {
qWarning("Invalid file type for photos: %i, setting it to zero", obj_file_type);
obj_file_type = 0;
}
metadata.data.photo.fileName = strdup(metadata.name);
metadata.data.photo.fileFormatType = photo_list[obj_file_type].file_format;
metadata.data.photo.statusType = 1;
metadata.data.photo.dateTimeOriginal = file.created().toUTC().toTime_t();
metadata.data.photo.numTracks = 1;
metadata.data.photo.tracks = new media_track();
metadata.data.photo.tracks->type = VITA_TRACK_TYPE_PHOTO;
metadata.data.photo.tracks->data.track_photo.codecType = photo_list[obj_file_type].file_codec;
Database::loadPhotoMetadata(file.absoluteFilePath(), metadata);
}
m_path = file.absoluteFilePath();
if(parent->metadata.path == NULL) {
metadata.path = strdup(metadata.name);
} else {
QString newpath = QString(parent->metadata.path) + "/" + metadata.name;
metadata.path = strdup(newpath.toUtf8().data());
}
updateObjectSize(file.size());
}
void CMAObject::updateObjectSize(qint64 size)
{
if(parent) {
parent->updateObjectSize(size);
}
metadata.size += size;
}
void CMAObject::rename(const QString &newname)
{
free(metadata.name);
metadata.name = strdup(newname.toUtf8().data());
if(metadata.path) {
QStringList metadata_path(QString(metadata.path).split("/"));
metadata_path.replace(metadata_path.count() - 1, newname);
free(metadata.path);
metadata.path = strdup(metadata_path.join("/").toUtf8().data());
}
m_path = QFileInfo(m_path).absoluteDir().path() + "/" + newname;
}
void CMAObject::refreshPath()
{
if(parent) {
free(metadata.path);
QString newpath(QString(parent->metadata.path) + "/" + metadata.name);
metadata.path = strdup(newpath.toUtf8().data());
m_path = parent->m_path + "/" + metadata.name;
}
}
bool CMAObject::hasParent(const CMAObject *obj)
{
if(parent) {
if(metadata.ohfiParent == obj->metadata.ohfi) {
return true;
} else {
return parent->hasParent(obj);
}
}
return false;
}
bool CMAObject::operator==(const CMAObject &obj)
{
return metadata.ohfi == obj.metadata.ohfi;
}
bool CMAObject::operator!=(const CMAObject &obj)
{
return metadata.ohfi != obj.metadata.ohfi;
}
bool CMAObject::operator<(const CMAObject &obj)
{
return metadata.ohfi < obj.metadata.ohfi;
}

65
common/cmaobject.h Normal file
View File

@@ -0,0 +1,65 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMAOBJECT_H
#define CMAOBJECT_H
#include <QFileInfo>
#include <QString>
#include <vitamtp.h>
#define OHFI_OFFSET 256
class CMAObject
{
public:
explicit CMAObject(CMAObject *parent = 0);
~CMAObject();
void refreshPath();
void rename(const QString &name);
void updateObjectSize(qint64 size);
bool hasParent(const CMAObject *obj);
void initObject(const QFileInfo &file, int file_type = -1);
bool operator==(const CMAObject &obj);
bool operator!=(const CMAObject &obj);
bool operator<(const CMAObject &obj);
inline void setOhfi(int ohfi) {
metadata.ohfi = ohfi;
}
inline static void resetOhfiCounter() {
ohfi_count = OHFI_OFFSET;
}
QString m_path;
CMAObject *parent;
metadata_t metadata;
protected:
static int ohfi_count;
private:
void loadSfoMetadata(const QString &path);
};
#endif // CMAOBJECT_H

154
common/cmarootobject.cpp Normal file
View File

@@ -0,0 +1,154 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cmarootobject.h"
#include <QDir>
QString CMARootObject::uuid = "ffffffffffffffff";
CMARootObject::CMARootObject(int ohfi) :
num_filters(0), filters(NULL), root_ohfi(ohfi)
{
}
void CMARootObject::initObject(const QString &path)
{
metadata.ohfi = root_ohfi;
metadata.type = VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_REGULAR;
switch(root_ohfi) {
case VITA_OHFI_MUSIC:
metadata.dataType = Music;
m_path = path;
num_filters = 5;
filters = new metadata_t[5];
createFilter(&filters[0], "Artists", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ARTISTS);
createFilter(&filters[1], "Albums", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ALBUMS);
createFilter(&filters[2], "Songs", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_SONGS);
createFilter(&filters[3], "Genres", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_GENRES);
createFilter(&filters[4], "Playlists", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_PLAYLISTS);
break;
case VITA_OHFI_PHOTO:
metadata.dataType = Photo;
m_path = path;
num_filters = 3;
filters = new metadata_t[3];
createFilter(&filters[0], "All", VITA_DIR_TYPE_MASK_PHOTO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ALL);
createFilter(&filters[1], "Month", VITA_DIR_TYPE_MASK_PHOTO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_MONTH);
createFilter(&filters[2], "Folders", VITA_DIR_TYPE_MASK_PHOTO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_REGULAR);
break;
case VITA_OHFI_VIDEO:
metadata.dataType = Video;
m_path = path;
num_filters = 2;
filters = new metadata_t[2];
createFilter(&filters[0], "All", VITA_DIR_TYPE_MASK_VIDEO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ALL);
createFilter(&filters[1], "Folders", VITA_DIR_TYPE_MASK_VIDEO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_REGULAR);
break;
case VITA_OHFI_VITAAPP:
metadata.dataType = App;
m_path = QDir(QDir(path).absoluteFilePath("APP")).absoluteFilePath(uuid);
num_filters = 0;
break;
case VITA_OHFI_PSPAPP:
metadata.dataType = App;
m_path = QDir(QDir(path).absoluteFilePath("PGAME")).absoluteFilePath(uuid);
num_filters = 0;
break;
case VITA_OHFI_PSPSAVE:
metadata.dataType = SaveData;
m_path = QDir(QDir(path).absoluteFilePath("PSAVEDATA")).absoluteFilePath(uuid);
num_filters = 0;
break;
case VITA_OHFI_PSXAPP:
metadata.dataType = App;
m_path = QDir(QDir(path).absoluteFilePath("PSGAME")).absoluteFilePath(uuid);
num_filters = 0;
break;
case VITA_OHFI_PSMAPP:
metadata.dataType = App;
m_path = QDir(QDir(path).absoluteFilePath("PSM")).absoluteFilePath(uuid);
num_filters = 0;
break;
case VITA_OHFI_BACKUP:
metadata.dataType = App;
m_path = QDir(QDir(path).absoluteFilePath("SYSTEM")).absoluteFilePath(uuid);
num_filters = 0;
break;
case VITA_OHFI_PACKAGE:
metadata.dataType = Package;
m_path = path;
num_filters = 0;
}
// create the backup directories
QDir dir(m_path);
dir.mkpath(dir.absolutePath());
}
CMARootObject::~CMARootObject()
{
for(int i = 0; i < num_filters; i++) {
free(filters[i].name);
free(filters[i].path);
}
delete[] filters;
}
void CMARootObject::createFilter(metadata_t *filter, const char *name, int type)
{
filter->ohfiParent = metadata.ohfi;
filter->ohfi = ohfi_count++;
filter->name = strdup(name);
filter->path = strdup(metadata.path ? metadata.path : "");
filter->type = type;
filter->dateTimeCreated = 0;
filter->size = 0;
filter->dataType = static_cast<DataType>(Folder | Special);
filter->next_metadata = NULL;
//qDebug("Added filter %s to database with OHFI %d (%s)", name, filter->ohfi, metadata.name);
}
int CMARootObject::getFilters(metadata_t **p_head)
{
int numObjects = num_filters;
for(int i = 0; i < numObjects; i++) {
filters[i].next_metadata = &filters[i + 1];
}
filters[numObjects - 1].next_metadata = NULL;
if(p_head != NULL) {
*p_head = filters;
}
return numObjects;
}

49
common/cmarootobject.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMAROOTOBJECT_H
#define CMAROOTOBJECT_H
#include "cmaobject.h"
#include <QList>
#include <vitamtp.h>
class CMARootObject : public CMAObject
{
public:
explicit CMARootObject(int ohfi);
~CMARootObject();
void initObject(const QString &path);
void remove(const CMAObject *obj);
int getFilters(metadata_t **p_head);
int num_filters;
metadata_t *filters;
static QString uuid;
private:
void createFilter(metadata_t *filter, const char *name, int type);
int root_ohfi;
};
#endif // CMAROOTOBJECT_H

305
common/cmautils.cpp Normal file
View File

@@ -0,0 +1,305 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cmautils.h"
#include "avdecoder.h"
#include <QBuffer>
#include <QDebug>
#include <QDir>
#include <QImage>
#include <QSettings>
#ifdef Q_OS_WIN32
#include <windows.h>
#elif defined Q_OS_ANDROID
#include <sys/vfs.h>
#define statvfs statfs
#else
#include <sys/statvfs.h>
#endif
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>
#include <unistd.h>
#endif
bool getDiskSpace(const QString &dir, quint64 *free, quint64 *total)
{
#ifdef Q_OS_WIN32
if(GetDiskFreeSpaceEx(dir.toStdWString().c_str(), (ULARGE_INTEGER *)free, (ULARGE_INTEGER *)total, NULL) != 0) {
return true;
}
#else
struct statvfs stat;
if(statvfs(dir.toUtf8().data(), &stat) == 0) {
*total = stat.f_frsize * stat.f_blocks;
*free = stat.f_frsize * stat.f_bfree;
return true;
}
#endif
return false;
}
bool removeRecursively(const QString &path)
{
QFileInfo file_info(path);
if(file_info.isDir()) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
return QDir(path).removeRecursively();
#else
bool result = false;
QDir dir(path);
QDir::Filters filter = QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files;
if(dir.exists(path)) {
foreach(QFileInfo info, dir.entryInfoList(filter, QDir::DirsFirst)) {
if(info.isDir()) {
result = removeRecursively(info.absoluteFilePath());
} else {
result = QFile::remove(info.absoluteFilePath());
}
if(!result) {
return result;
}
}
result = dir.rmdir(path);
}
return result;
#endif
} else {
return QFile::remove(path);
}
}
static QByteArray findFolderAlbumArt(const QString path, metadata_t *metadata)
{
QByteArray data;
QDir folder(path);
QStringList files = folder.entryList(QDir::Files | QDir::Readable);
const QStringList cover_list = QStringList() << "album" << "cover" << "front";
const QStringList ext_list = QStringList() << "jpg" << "jpeg" << "png" << "gif";
foreach(const QString &file, files) {
foreach(const QString &cover, cover_list) {
foreach(const QString &ext, ext_list) {
if(file.compare(QString("%1.%2").arg(cover, ext), Qt::CaseInsensitive) == 0) {
qDebug() << "Trying to load album art from" << folder.absoluteFilePath(file);
QImage img;
if(img.load(folder.absoluteFilePath(file))) {
QBuffer buffer(&data);
buffer.open(QIODevice::WriteOnly);
QImage result = img.scaled(256, 250, Qt::KeepAspectRatio, Qt::SmoothTransformation);
result.save(&buffer, "JPEG");
metadata->data.thumbnail.width = result.width();
metadata->data.thumbnail.height = result.height();
}
// only try with the first match
break;
}
}
}
}
return data;
}
QByteArray getThumbnail(const QString &path, DataType type, metadata_t *metadata)
{
QByteArray data;
if(MASK_SET(type, SaveData)) {
QFile file(QDir(path).absoluteFilePath("ICON0.PNG"));
if(file.open(QIODevice::ReadOnly)) {
data = file.readAll();
}
} else if(MASK_SET(type, Photo)) {
QImage img;
if(img.load(path)) {
QBuffer buffer(&data);
buffer.open(QIODevice::WriteOnly);
QImage result = img.scaled(240, 136, Qt::KeepAspectRatio, Qt::FastTransformation);
result.save(&buffer, "JPEG");
metadata->data.thumbnail.width = result.width();
metadata->data.thumbnail.height = result.height();
}
} else if(MASK_SET(type, Music)) {
if(MASK_SET(type, Folder)) {
// TODO: try to load an album cover from one of the audio files.
data = findFolderAlbumArt(path, metadata);
} else {
AVDecoder decoder;
if(decoder.open(path)) {
data = decoder.getAudioThumbnail(256, 256);
metadata->data.thumbnail.width = 256;
metadata->data.thumbnail.height = 256;
}
}
} else if(MASK_SET(type, Video)) {
AVDecoder decoder;
if(decoder.open(path)) {
data = decoder.getVideoThumbnail(256, 256);
metadata->data.thumbnail.width = 256;
metadata->data.thumbnail.height = 256;
}
}
return data;
}
QString readable_size(qint64 size, bool use_gib)
{
QStringList list;
list << "KiB" << "MiB";
if(use_gib) {
list << "GiB";
}
QStringListIterator i(list);
QString unit("bytes");
float size_f = size;
while(size_f >= 1024.0 && i.hasNext()) {
unit = i.next();
size_f /= 1024.0;
}
return QString().setNum(size_f,'f',2) + " " + unit;
}
int getVitaProtocolVersion()
{
QString protocol_mode = QSettings().value("protocolMode", "automatic").toString();
qDebug() << "Using protocol mode: " << protocol_mode;
int protocol;
if(protocol_mode == "manual")
{
int index = QSettings().value("protocolIndex").toInt();
switch(index)
{
case 0:
protocol = VITAMTP_PROTOCOL_FW_3_30;
break;
case 1:
protocol = VITAMTP_PROTOCOL_FW_3_10;
break;
case 2:
protocol = VITAMTP_PROTOCOL_FW_3_00;
break;
case 3:
protocol = VITAMTP_PROTOCOL_FW_2_60;
break;
case 4:
protocol = VITAMTP_PROTOCOL_FW_2_10;
break;
case 5:
protocol = VITAMTP_PROTOCOL_FW_2_00;
break;
case 6:
protocol = VITAMTP_PROTOCOL_FW_1_80;
break;
case 7:
protocol = VITAMTP_PROTOCOL_FW_1_60;
break;
case 8:
protocol = VITAMTP_PROTOCOL_FW_1_50;
break;
case 9:
protocol = VITAMTP_PROTOCOL_FW_1_00;
break;
default:
protocol = VITAMTP_PROTOCOL_MAX_VERSION;
break;
}
}
else if(protocol_mode == "custom")
{
bool ok;
protocol = QSettings().value("protocolVersion").toInt(&ok);
if(!ok || protocol <= 0)
protocol = VITAMTP_PROTOCOL_MAX_VERSION;
}
else
protocol = VITAMTP_PROTOCOL_MAX_VERSION;
return protocol;
}
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
bool belongsToGroup(const char *groupname)
{
int size_max = sysconf(_SC_GETGR_R_SIZE_MAX);
if(size_max == -1)
size_max = 1024;
QByteArray buf(size_max, Qt::Uninitialized);
group *result = NULL;
group entry;
getgrnam_r(groupname, &entry, buf.data(), buf.size(), &result);
if(result != NULL && *result->gr_mem != NULL) {
char **user_list = result->gr_mem;
int user_size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
if(user_size_max == -1)
user_size_max = 1024;
std::vector<char> user_buf(user_size_max);
uid_t user_id = getuid();
while(*user_list != NULL) {
char *user_name = *user_list;
passwd *pw = NULL;
passwd entry;
getpwnam_r(user_name, &entry, user_buf.data(), user_buf.size(), &pw);
if(pw != NULL && pw->pw_uid == user_id) {
return true;
}
user_list++;
}
}
return false;
}
#endif

59
common/cmautils.h Normal file
View File

@@ -0,0 +1,59 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UTILS_H
#define UTILS_H
#include <QByteArray>
#include <QString>
#include <QThread>
#include <vitamtp.h>
// Qt4 doesn't have public methods for Thread::*sleep
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
typedef QThread Sleeper;
#else
class Sleeper : QThread
{
public:
static void sleep(unsigned long secs) {
QThread::sleep(secs);
}
static void msleep(unsigned long msecs) {
QThread::msleep(msecs);
}
static void usleep(unsigned long usecs) {
QThread::usleep(usecs);
}
};
#endif
bool removeRecursively(const QString &path);
QString readable_size(qint64 size, bool use_gib = false);
bool getDiskSpace(const QString &dir, quint64 *free, quint64 *total);
QByteArray getThumbnail(const QString &path, DataType type, metadata_t *metadata);
int getVitaProtocolVersion();
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
bool belongsToGroup(const char *groupname);
#endif
#endif // UTILS_H

50
common/common.pro Normal file
View File

@@ -0,0 +1,50 @@
include(../config.pri)
TARGET = qcma_common
QT += core network sql
TEMPLATE = lib
CONFIG += staticlib
SOURCES += \
capability.cpp \
cmaobject.cpp \
cmarootobject.cpp \
cmautils.cpp \
sforeader.cpp \
cmaclient.cpp \
cmabroadcast.cpp \
avdecoder.cpp \
cmaevent.cpp \
dds.cpp \
sqlitedb.cpp \
httpdownloader.cpp \
qlistdb.cpp \
database.cpp
HEADERS += \
capability.h \
cmaobject.h \
cmarootobject.h \
cmautils.h \
sforeader.h \
cmaclient.h \
cmabroadcast.h \
avdecoder.h \
cmaevent.h \
dds.h \
sqlitedb.h \
httpdownloader.h \
qlistdb.h \
database.h
OTHER_FILES += \
resources/xml/psp2-updatelist.xml
TRANSLATIONS += \
resources/translations/qcma_es.ts \
resources/translations/qcma_fr.ts \
resources/translations/qcma_ja.ts
RESOURCES += common.qrc translations.qrc
PKGCONFIG = libvitamtp libavformat libavcodec libavutil libswscale

5
common/common.qrc Normal file
View File

@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/main">
<file>resources/xml/psp2-updatelist.xml</file>
</qresource>
</RCC>

152
common/database.cpp Normal file
View File

@@ -0,0 +1,152 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "database.h"
#include "avdecoder.h"
#include <QDebug>
#include <QImage>
#include <QSettings>
#include <QTextStream>
#include <QThread>
const file_type audio_list[] = {
{"mp3", FILE_FORMAT_MP3, CODEC_TYPE_MP3},
{"mp4", FILE_FORMAT_MP4, CODEC_TYPE_AAC},
{"wav", FILE_FORMAT_WAV, CODEC_TYPE_PCM}
};
const file_type photo_list[] = {
{"jpg", FILE_FORMAT_JPG, CODEC_TYPE_JPG},
{"jpeg", FILE_FORMAT_JPG, CODEC_TYPE_JPG},
{"png", FILE_FORMAT_PNG, CODEC_TYPE_PNG},
{"tif", FILE_FORMAT_TIF, CODEC_TYPE_TIF},
{"tiff", FILE_FORMAT_TIF, CODEC_TYPE_TIF},
{"bmp", FILE_FORMAT_BMP, CODEC_TYPE_BMP},
{"gif", FILE_FORMAT_GIF, CODEC_TYPE_GIF},
};
const file_type video_list[] = {
{"mp4", FILE_FORMAT_MP4, 0}
};
Database::Database(QObject *obj_parent) :
QObject(obj_parent),
mutex(QMutex::Recursive)
{
}
void Database::process()
{
qDebug("Starting database_thread: 0x%016" PRIxPTR, (uintptr_t)QThread::currentThreadId());
clear();
cancel_operation = false;
int count = create();
cancel_operation = false;
QTextStream(stdout) << "Total entries added to the database: " << count << endl;
if(count < 0) {
clear();
}
emit updated(count);
mutex.unlock();
}
void Database::cancelOperation()
{
QMutexLocker locker(&cancel);
cancel_operation = true;
}
bool Database::continueOperation()
{
QMutexLocker locker(&cancel);
return !cancel_operation;
}
int Database::checkFileType(const QString path, int ohfi_root)
{
switch(ohfi_root) {
case VITA_OHFI_MUSIC:
for(int i = 0, max = sizeof(audio_list) / sizeof(file_type); i < max; i++) {
if(path.endsWith(audio_list[i].file_ext, Qt::CaseInsensitive)) {
return i;
}
}
break;
case VITA_OHFI_PHOTO:
for(int i = 0, max = sizeof(photo_list) / sizeof(file_type); i< max; i++) {
if(path.endsWith(photo_list[i].file_ext, Qt::CaseInsensitive)) {
return i;
}
}
break;
case VITA_OHFI_VIDEO:
for(int i = 0, max = sizeof(video_list) / sizeof(file_type); i< max; i++) {
if(path.endsWith(video_list[i].file_ext, Qt::CaseInsensitive)) {
return i;
}
}
break;
default:
return 0;
}
return -1;
}
void Database::loadMusicMetadata(const QString &path, metadata_t &metadata)
{
AVDecoder decoder;
bool skipMetadata = QSettings().value("skipMetadata", false).toBool();
if(!skipMetadata && decoder.open(path)) {
decoder.getAudioMetadata(metadata);
} else {
metadata.data.music.album = strdup("");
metadata.data.music.artist = strdup("");
metadata.data.music.title = strdup(metadata.name);
}
}
void Database::loadVideoMetadata(const QString &path, metadata_t &metadata)
{
AVDecoder decoder;
bool skipMetadata = QSettings().value("skipMetadata", false).toBool();
if(!skipMetadata && decoder.open(path)) {
decoder.getVideoMetadata(metadata);
} else {
metadata.data.video.title = strdup(metadata.name);
metadata.data.video.explanation = strdup("");
metadata.data.video.copyright = strdup("");
// default to H264 video codec
metadata.data.video.tracks->data.track_video.codecType = CODEC_TYPE_AVC;
}
}
void Database::loadPhotoMetadata(const QString &path, metadata_t &metadata)
{
QImage img;
bool skipMetadata = QSettings().value("skipMetadata", false).toBool();
if(!skipMetadata && img.load(path)) {
metadata.data.photo.tracks->data.track_photo.width = img.width();
metadata.data.photo.tracks->data.track_photo.height = img.height();
}
metadata.data.photo.title = strdup(metadata.name);
}

117
common/database.h Normal file
View File

@@ -0,0 +1,117 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DATABASE_H
#define DATABASE_H
#include <QMutex>
#include <QObject>
#include <vitamtp.h>
typedef struct {
const char *file_ext;
int file_format;
int file_codec;
} file_type;
#define OHFI_BASE_VALUE 256
#define FILE_FORMAT_MP4 1
#define FILE_FORMAT_WAV 2
#define FILE_FORMAT_MP3 3
#define FILE_FORMAT_JPG 4
#define FILE_FORMAT_PNG 5
#define FILE_FORMAT_GIF 6
#define FILE_FORMAT_BMP 7
#define FILE_FORMAT_TIF 8
#define CODEC_TYPE_MPEG4 2
#define CODEC_TYPE_AVC 3
#define CODEC_TYPE_MP3 12
#define CODEC_TYPE_AAC 13
#define CODEC_TYPE_PCM 15
#define CODEC_TYPE_JPG 17
#define CODEC_TYPE_PNG 18
#define CODEC_TYPE_TIF 19
#define CODEC_TYPE_BMP 20
#define CODEC_TYPE_GIF 21
extern const file_type audio_list[3];
extern const file_type photo_list[7];
extern const file_type video_list[1];
class Database : public QObject
{
Q_OBJECT
public:
explicit Database(QObject *parent = 0);
virtual bool load() = 0;
virtual bool rescan() = 0;
virtual void setUUID(const QString &uuid) = 0;
virtual int childObjectCount(int parent_ohfi) = 0;
virtual bool deleteEntry(int ohfi, int root_ohfi = 0) = 0;
virtual QString getAbsolutePath(int ohfi) = 0;
virtual QString getRelativePath(int ohfi) = 0;
virtual bool getObjectList(int ohfi, metadata_t **metadata) = 0;
virtual bool getObjectMetadata(int ohfi, metadata_t &metadata) = 0;
virtual int getObjectMetadatas(int parent_ohfi, metadata_t **metadata, int index = 0, int max_number = 0) = 0;
virtual qint64 getObjectSize(int ohfi) = 0;
virtual int getPathId(const char *name, int ohfi) = 0;
virtual int insertObjectEntry(const QString &path, const QString &name, int parent_ohfi) = 0;
virtual bool renameObject(int ohfi, const QString &name) = 0;
virtual void setObjectSize(int ohfi, qint64 size) = 0;
virtual int getParentId(int ohfi) = 0;
virtual int getRootId(int ohfi) = 0;
virtual void freeMetadata(metadata_t *metadata) = 0;
static int checkFileType(const QString path, int ohfi_root);
static void loadMusicMetadata(const QString &path, metadata_t &metadata);
static void loadPhotoMetadata(const QString &path, metadata_t &metadata);
static void loadVideoMetadata(const QString &path, metadata_t &metadata);
QMutex mutex;
protected:
bool continueOperation();
private:
virtual void clear() = 0;
virtual int create() = 0;
// control variables
QMutex cancel;
bool cancel_operation;
signals:
void fileAdded(QString);
void directoryAdded(QString);
void updated(int);
protected slots:
void process();
public slots:
void cancelOperation();
};
#endif // DATABASE_H

1032
common/dds.cpp Normal file

File diff suppressed because it is too large Load Diff

30
common/dds.h Normal file
View File

@@ -0,0 +1,30 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KIMG_DDS_H
#define KIMG_DDS_H
#include <QImage>
#include <QString>
bool loadDDS(const QString &filename, QImage *image);
#endif

2
common/defines.pri Normal file
View File

@@ -0,0 +1,2 @@
INCLUDEPATH += $$IN_PWD
DEPENDPATH += $$IN_PWD

136
common/httpdownloader.cpp Normal file
View File

@@ -0,0 +1,136 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2014 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "httpdownloader.h"
#include <QDebug>
#include <QThread>
#include <vitamtp.h>
QNetworkReply *HTTPDownloader::reply = NULL;
volatile qint64 HTTPDownloader::m_contentLength = -1;
QMutex HTTPDownloader::dataAvailable;
QMutex HTTPDownloader::dataRead;
QByteArray HTTPDownloader::buffer;
bool HTTPDownloader::bufferReady = false;
qint64 HTTPDownloader::downloadLeft = 0;
HTTPDownloader::HTTPDownloader(const QString &url, QObject *obj_parent) :
QObject(obj_parent), remote_url(url), firstRead(true)
{
lengthMutex.lock();
}
HTTPDownloader::~HTTPDownloader()
{
lengthMutex.unlock();
dataAvailable.unlock();
}
void HTTPDownloader::downloadFile()
{
dataAvailable.lock();
qDebug("Starting http_thread: 0x%016" PRIxPTR, (uintptr_t)QThread::currentThreadId());
request = new QNetworkAccessManager(this);
reply = request->get(QNetworkRequest(QUrl(remote_url)));
connect(reply, SIGNAL(metaDataChanged()), this, SLOT(metadataChanged()));
connect(reply, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(error(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater()));
connect(reply, SIGNAL(finished()), request, SLOT(deleteLater()));
}
void HTTPDownloader::metadataChanged()
{
QVariant len = reply->header(QNetworkRequest::ContentLengthHeader);
if(len.isValid()) {
m_contentLength = len.toInt();
buffer.resize(8);
*(uint64_t *)buffer.data() = m_contentLength;
downloadLeft = m_contentLength;
} else {
m_contentLength = -1;
}
lengthMutex.unlock();
}
qint64 HTTPDownloader::getFileSize()
{
lengthMutex.lock();
return m_contentLength;
}
void HTTPDownloader::readyRead()
{
QMutexLocker locker(&dataRead);
downloadLeft -= reply->bytesAvailable();
buffer.append(reply->readAll());
if(buffer.size() >= 16 * 1024 || downloadLeft == 0) {
dataAvailable.unlock();
}
if(downloadLeft == 0)
qDebug() << "remote download complete";
}
int HTTPDownloader::readCallback(unsigned char *data, unsigned long wantlen, unsigned long *gotlen)
{
if(downloadLeft && !dataAvailable.tryLock(30000)) {
qWarning("Connection timeout while receiving data from network, aborting");
return -1;
}
QMutexLocker locker(&dataRead);
if(buffer.size() == 0)
return -1;
int read_size = wantlen > (unsigned long)buffer.size() ? buffer.size() : wantlen;
memcpy(data, buffer.data(), read_size);
buffer.remove(0, read_size);
qDebug() << "sending data: " << read_size << ", left in buffer: " << buffer.size();
*gotlen = read_size;
return PTP_RC_OK;
}
void HTTPDownloader::error(QNetworkReply::NetworkError errorCode)
{
Q_UNUSED(errorCode);
QString str_error = reply->errorString();
qWarning() << "Network error:" << str_error;
emit messageSent(tr("Network error: %1").arg(str_error));
lengthMutex.unlock();
// clear the buffer so a read callback can be aborted
QMutexLocker locker(&dataRead);
buffer.clear();
}

66
common/httpdownloader.h Normal file
View File

@@ -0,0 +1,66 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2014 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HTTPDOWNLOADER_H
#define HTTPDOWNLOADER_H
#include <QByteArray>
#include <QMutex>
#include <QNetworkReply>
#include <QObject>
#include <QWaitCondition>
#include <inttypes.h>
class HTTPDownloader : public QObject
{
Q_OBJECT
public:
explicit HTTPDownloader(const QString &url, QObject *parent = 0);
~HTTPDownloader();
qint64 getFileSize();
signals:
void messageSent(QString);
public slots:
void downloadFile();
static int readCallback(unsigned char *data, unsigned long wantlen, unsigned long *gotlen);
void metadataChanged();
void readyRead();
void error(QNetworkReply::NetworkError);
private:
QString remote_url;
QNetworkAccessManager *request;
QMutex lengthMutex;
bool firstRead;
static QMutex dataAvailable;
static QMutex dataRead;
static QNetworkReply *reply;
volatile static qint64 m_contentLength;
static QByteArray buffer;
static bool bufferReady;
static qint64 downloadLeft;
};
#endif // HTTPDOWNLOADER_H

610
common/qlistdb.cpp Normal file
View File

@@ -0,0 +1,610 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cmautils.h"
#include "qlistdb.h"
#include "cmaobject.h"
#include <QDir>
#include <QDirIterator>
#include <QSettings>
#include <QTextStream>
#include <QThread>
#include <QDebug>
QListDB::QListDB(QObject *obj_parent) :
Database(obj_parent)
{
QString uuid = QSettings().value("lastAccountId", "ffffffffffffffff").toString();
CMARootObject::uuid = uuid;
thread = new QThread();
moveToThread(thread);
timer = new QTimer();
thread->start();
timer->setInterval(0);
timer->setSingleShot(true);
connect(timer, SIGNAL(timeout()), this, SLOT(process()));
}
QListDB::~QListDB()
{
clear();
timer->stop();
delete timer;
thread->quit();
thread->wait();
delete thread;
}
void QListDB::setUUID(const QString &uuid)
{
CMARootObject::uuid = uuid;
QSettings().setValue("lastAccountId", uuid);
}
bool QListDB::load()
{
// not implemented
return false;
}
bool QListDB::rescan()
{
if(mutex.tryLock(1000)) {
if(CMARootObject::uuid != "ffffffffffffffff") {
timer->start();
return true;
} else {
mutex.unlock();
return false;
}
}
return false;
}
void QListDB::clear()
{
for(map_list::iterator root = object_list.begin(); root != object_list.end(); ++root) {
CMARootObject *first = static_cast<CMARootObject *>((*root).takeFirst());
delete first;
qDeleteAll(*root);
}
object_list.clear();
}
int QListDB::create()
{
int total_objects = 0;
//QMutexLocker locker(&mutex);
const int ohfi_array[] = { VITA_OHFI_MUSIC, VITA_OHFI_PHOTO, VITA_OHFI_VIDEO,
VITA_OHFI_PACKAGE, VITA_OHFI_BACKUP, VITA_OHFI_VITAAPP,
VITA_OHFI_PSPAPP, VITA_OHFI_PSPSAVE, VITA_OHFI_PSXAPP,
VITA_OHFI_PSMAPP
};
CMAObject::resetOhfiCounter();
QSettings settings;
for(int i = 0, max = sizeof(ohfi_array) / sizeof(int); i < max; i++) {
CMARootObject *obj = new CMARootObject(ohfi_array[i]);
bool skipCurrent = false;
int dir_count;
switch(ohfi_array[i]) {
case VITA_OHFI_MUSIC:
obj->initObject(settings.value("musicPath").toString());
skipCurrent = settings.value("musicSkip", false).toBool();
break;
case VITA_OHFI_PHOTO:
obj->initObject(settings.value("photoPath").toString());
skipCurrent = settings.value("photoSkip", false).toBool();
break;
case VITA_OHFI_VIDEO:
obj->initObject(settings.value("videoPath").toString());
skipCurrent = settings.value("videoSkip", false).toBool();
break;
case VITA_OHFI_BACKUP:
case VITA_OHFI_VITAAPP:
case VITA_OHFI_PSPAPP:
case VITA_OHFI_PSPSAVE:
case VITA_OHFI_PSXAPP:
case VITA_OHFI_PSMAPP:
obj->initObject(settings.value("appsPath").toString());
break;
case VITA_OHFI_PACKAGE:
obj->initObject(settings.value("pkgPath").toString());
}
root_list list;
list << obj;
emit directoryAdded(obj->m_path);
if(!skipCurrent) {
dir_count = recursiveScanRootDirectory(list, obj, ohfi_array[i]);
} else {
dir_count = 0;
}
if(dir_count < 0) {
return -1;
}
qDebug("Added objects for OHFI 0x%02X: %i", ohfi_array[i], dir_count);
total_objects += dir_count;
object_list[ohfi_array[i]] = list;
}
return total_objects;
}
CMAObject *QListDB::getParent(CMAObject *last_dir, const QString &current_path)
{
while(last_dir && current_path != last_dir->m_path) {
last_dir = last_dir->parent;
}
return last_dir;
}
int QListDB::scanRootDirectory(root_list &list, int ohfi_type)
{
int obj_file_type = -1;
int total_objects = 0;
CMAObject *last_dir = list.first();
QDir dir(last_dir->m_path);
dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
QDirIterator it(dir, QDirIterator::Subdirectories);
while(it.hasNext()) {
if(!continueOperation()) {
return -1;
}
it.next();
QFileInfo info = it.fileInfo();
if(info.isFile()) {
if((obj_file_type = checkFileType(info.absoluteFilePath(), ohfi_type)) < 0) {
//qDebug("Excluding %s from database", info.absoluteFilePath().toStdString().c_str());
continue;
}
}
CMAObject *obj = new CMAObject(getParent(last_dir, info.path()));
obj->initObject(info, obj_file_type);
//qDebug("Added %s to database with OHFI %d", obj->metadata.name, obj->metadata.ohfi);
list << obj;
if(obj->metadata.dataType & Folder) {
last_dir = obj;
} else {
total_objects++;
}
}
return total_objects;
}
int QListDB::recursiveScanRootDirectory(root_list &list, CMAObject *obj_parent, int ohfi_type)
{
int obj_file_type = -1;
int total_objects = 0;
QDir dir(obj_parent->m_path);
dir.setSorting(QDir::Name | QDir::DirsFirst);
QFileInfoList qsl = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot, QDir::Time);
foreach(const QFileInfo &info, qsl) {
if(!continueOperation()) {
return -1;
}
if(info.isFile() && (obj_file_type = checkFileType(info.absoluteFilePath(), ohfi_type)) < 0) {
//qDebug("Excluding %s from database", info.absoluteFilePath().toStdString().c_str());>
} else {
CMAObject *obj = new CMAObject(obj_parent);
obj->initObject(info, obj_file_type);
emit fileAdded(obj->metadata.name);
//qDebug("Added %s to database with OHFI %d", obj->metadata.name, obj->metadata.ohfi);
list << obj;
if(info.isDir()) {
emit directoryAdded(obj->m_path);
total_objects += recursiveScanRootDirectory(list, obj, ohfi_type);
} else {
total_objects++;
}
}
}
return total_objects;
}
bool QListDB::removeInternal(root_list &list, int ohfi)
{
bool found = false;
QList<CMAObject *>::iterator it = list.begin();
while(it != list.end()) {
if(!found && (*it)->metadata.ohfi == ohfi) {
// update the size of the parent objects
(*it)->updateObjectSize(-(*it)->metadata.size);
it = list.erase(it);
found = true;
} else if(found && (*it)->metadata.ohfiParent == ohfi) {
it = list.erase(it);
} else {
++it;
}
}
return found;
}
bool QListDB::lessThanComparator(const CMAObject *a, const CMAObject *b)
{
return a->metadata.ohfi < b->metadata.ohfi;
}
bool QListDB::hasFilter(const CMARootObject *object,int ohfi)
{
for(int i = 0; i < object->num_filters; i++) {
if(object->filters[i].ohfi == ohfi) {
return true;
}
}
return false;
}
bool QListDB::findInternal(const root_list &list, int ohfi, find_data &data)
{
if(hasFilter(static_cast<CMARootObject *>(list.first()), ohfi)) {
data.it = list.begin();
} else {
CMAObject obj;
obj.setOhfi(ohfi);
data.it = qBinaryFind(list.begin(), list.end(), &obj, QListDB::lessThanComparator);
}
data.end = list.end();
return data.it != data.end;
}
bool QListDB::find(int ohfi, QListDB::find_data &data)
{
for(map_list::iterator root = object_list.begin(); root != object_list.end(); ++root) {
if(findInternal(*root, ohfi, data)) {
return true;
}
}
return false;
}
CMAObject *QListDB::ohfiToObject(int ohfi)
{
find_data data;
return find(ohfi, data) ? *data.it : NULL;
}
CMAObject *QListDB::pathToObjectInternal(const root_list &list, const char *path)
{
// skip the first element since is the root element
root_list::const_iterator skipped_first = ++list.begin();
for(root_list::const_iterator obj = skipped_first; obj != list.end(); ++obj) {
if(strcasecmp(path, (*obj)->metadata.path) == 0) {
return (*obj);
}
}
return NULL;
}
int QListDB::acceptFilteredObject(const CMAObject *obj_parent, const CMAObject *current, int type)
{
QMutexLocker locker(&mutex);
int result = 0;
if(MASK_SET(type, VITA_DIR_TYPE_MASK_PHOTO)) {
result = (current->metadata.dataType & Photo);
} else if(MASK_SET(type, VITA_DIR_TYPE_MASK_VIDEO)) {
result = (current->metadata.dataType & Video);
} else if(MASK_SET(type, VITA_DIR_TYPE_MASK_MUSIC)) {
result = (current->metadata.dataType & Music);
}
if(MASK_SET(type, VITA_DIR_TYPE_MASK_ARTISTS)) {
// unimplemented
return 0;
} else if(MASK_SET(type, VITA_DIR_TYPE_MASK_GENRES)) {
// unimplemented
return 0;
} else if(MASK_SET(type, VITA_DIR_TYPE_MASK_PLAYLISTS)) {
// unimplemented
return 0;
} else if(MASK_SET(type, VITA_DIR_TYPE_MASK_MONTH)) {
// unimplemented
return 0;
} else if(type & (VITA_DIR_TYPE_MASK_ALL | VITA_DIR_TYPE_MASK_SONGS)) {
result = result && (current->metadata.dataType & File);
} else if(type & (VITA_DIR_TYPE_MASK_REGULAR)) {
result = (obj_parent->metadata.ohfi == current->metadata.ohfiParent);
}
// TODO: Support other filter types
return result;
}
void QListDB::dumpMetadataList(const metadata_t *p_head)
{
QMutexLocker locker(&mutex);
while(p_head) {
qDebug("Metadata: %s with OHFI %d", p_head->name, p_head->ohfi);
p_head = p_head->next_metadata;
}
}
bool QListDB::getObjectMetadata(int ohfi, metadata_t &metadata)
{
QMutexLocker locker(&mutex);
CMAObject *obj = ohfiToObject(ohfi);
if(obj) {
//TODO: return the pointer instead of copying the struct
metadata = obj->metadata;
return true;
}
return false;
}
int QListDB::childObjectCount(int parent_ohfi)
{
return getObjectMetadatas(parent_ohfi, NULL);
}
bool QListDB::deleteEntry(int ohfi, int root_ohfi)
{
QMutexLocker locker(&mutex);
if(root_ohfi) {
return removeInternal(object_list[root_ohfi], ohfi);
} else {
for(map_list::iterator root = object_list.begin(); root != object_list.end(); ++root) {
if(removeInternal(*root, ohfi)) {
return true;
}
}
}
return false;
}
bool QListDB::getObjectList(int ohfi, metadata_t **metadata)
{
find_data iters;
if(find(ohfi, iters)) {
CMAObject *object;
do {
object = *iters.it;
*metadata = &object->metadata;
metadata = &(*metadata)->next_metadata;
*metadata = NULL;
object = *++iters.it;
} while(iters.it != iters.end && object->metadata.ohfiParent >= OHFI_OFFSET);
return true;
}
return false;
}
int QListDB::getObjectMetadatas(int parent_ohfi, metadata_t **metadata, int index, int max_number)
{
QMutexLocker locker(&mutex);
CMARootObject *obj_parent = static_cast<CMARootObject *>(ohfiToObject(parent_ohfi));
if(obj_parent == NULL) {
return 0;
}
if(obj_parent->metadata.dataType & File) {
*metadata = &obj_parent->metadata;
return 1;
}
int type = obj_parent->metadata.type;
if(obj_parent->metadata.ohfi < OHFI_OFFSET && obj_parent->filters) { // if we have filters
if(parent_ohfi == obj_parent->metadata.ohfi) { // if we are looking at root
return obj_parent->getFilters(metadata);
} else { // we are looking at a filter
for(int j = 0; j < obj_parent->num_filters; j++) {
if(obj_parent->filters[j].ohfi == parent_ohfi) {
type = obj_parent->filters[j].type;
break;
}
}
}
}
int offset = 0;
int numObjects = 0;
metadata_t temp = metadata_t();
metadata_t *tail = &temp;
for(map_list::iterator root = object_list.begin(); root != object_list.end(); ++root) {
for(root_list::iterator object = (*root).begin(); object != (*root).end(); ++object) {
if(acceptFilteredObject(obj_parent, *object, type)) {
if(offset++ >= index) {
tail->next_metadata = &(*object)->metadata;
tail = tail->next_metadata;
numObjects++;
}
if(max_number > 0 && numObjects >= max_number) {
break;
}
}
}
if(numObjects > 0) {
break;
}
}
tail->next_metadata = NULL;
if(metadata != NULL) {
*metadata = temp.next_metadata;
}
return numObjects;
}
qint64 QListDB::getObjectSize(int ohfi)
{
QMutexLocker locker(&mutex);
CMAObject *obj = ohfiToObject(ohfi);
return obj ? obj->metadata.size : -1;
}
int QListDB::getPathId(const char *name, int ohfi)
{
QMutexLocker locker(&mutex);
for(map_list::iterator root = object_list.begin(); root != object_list.end(); ++root) {
if(ohfi && (*root).first()->metadata.ohfi != ohfi) {
continue;
}
CMAObject *obj = pathToObjectInternal(*root, name);
if(obj) {
return obj->metadata.ohfi;
}
}
return 0;
}
int QListDB::insertObjectEntry(const QString &path, const QString &name, int parent_ohfi)
{
QMutexLocker locker(&mutex);
CMAObject *parent_obj = ohfiToObject(parent_ohfi);
for(map_list::iterator root = object_list.begin(); root != object_list.end(); ++root) {
root_list *cat_list = &(*root);
root_list::const_iterator it = qBinaryFind(cat_list->begin(), cat_list->end(), parent_obj, QListDB::lessThanComparator);
if(it != cat_list->end()) {
CMAObject *newobj = new CMAObject(parent_obj);
// get the root object
while(parent_obj->parent) {
parent_obj = parent_obj->parent;
}
QFileInfo info(path, name);
newobj->initObject(info, parent_obj->metadata.dataType);
cat_list->append(newobj);
return newobj->metadata.ohfi;
}
}
return 0;
}
QString QListDB::getAbsolutePath(int ohfi)
{
QMutexLocker locker(&mutex);
CMAObject *obj = ohfiToObject(ohfi);
return obj ? obj->m_path : NULL;
}
QString QListDB::getRelativePath(int ohfi)
{
QMutexLocker locker(&mutex);
CMAObject *obj = ohfiToObject(ohfi);
return obj ? obj->metadata.path : NULL;
}
bool QListDB::renameObject(int ohfi, const QString &name)
{
QMutexLocker locker(&mutex);
CMAObject *root = ohfiToObject(ohfi);
if(!root) {
return false;
}
//rename the current object
root->rename(name);
QListDB::find_data iters;
find(root->metadata.ohfi, iters);
// rename the rest of the list only if has the renamed parent in some part of the chain
while(iters.it != iters.end) {
CMAObject *obj = *iters.it++;
if(obj->hasParent(root)) {
obj->refreshPath();
}
}
return true;
}
void QListDB::setObjectSize(int ohfi, qint64 size)
{
QMutexLocker locker(&mutex);
CMAObject *obj = ohfiToObject(ohfi);
if(obj) {
obj->updateObjectSize(size);
}
}
int QListDB::getRootId(int ohfi)
{
QMutexLocker locker(&mutex);
CMAObject *obj = ohfiToObject(ohfi);
if(!obj) {
return 0;
}
while(obj->parent) {
obj = obj->parent;
}
return obj->metadata.ohfi;
}
int QListDB::getParentId(int ohfi)
{
QMutexLocker locker(&mutex);
CMAObject *obj = ohfiToObject(ohfi);
return obj ? obj->metadata.ohfiParent : 0;
}

95
common/qlistdb.h Normal file
View File

@@ -0,0 +1,95 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QLISTDB_H
#define QLISTDB_H
#include "database.h"
#include "cmarootobject.h"
#include <QList>
#include <QMap>
#include <QMutex>
#include <QObject>
#include <QTimer>
#include <vitamtp.h>
class QListDB : public Database
{
Q_OBJECT
public:
explicit QListDB(QObject *parent = 0);
~QListDB();
bool load();
bool rescan();
void close();
void clear();
bool reload(bool &prepared);
void setUUID(const QString &uuid);
int childObjectCount(int parent_ohfi);
bool deleteEntry(int ohfi, int root_ohfi = 0);
QString getAbsolutePath(int ohfi);
bool getObjectList(int ohfi, metadata_t **metadata);
bool getObjectMetadata(int ohfi, metadata_t &metadata);
int getObjectMetadatas(int parent_ohfi, metadata_t **metadata, int index = 0, int max_number = 0);
qint64 getObjectSize(int ohfi);
int getParentId(int ohfi);
int getPathId(const char *name, int ohfi);
QString getRelativePath(int ohfi);
int getRootId(int ohfi);
int insertObjectEntry(const QString &path, const QString &name, int parent_ohfi);
bool renameObject(int ohfi, const QString &name);
void setObjectSize(int ohfi, qint64 size);
void freeMetadata(metadata_t *metadata) {
Q_UNUSED(metadata);
}
private:
typedef struct {
QList<CMAObject *>::const_iterator it;
QList<CMAObject *>::const_iterator end;
} find_data;
typedef QList<CMAObject *> root_list;
typedef QMap<int, root_list> map_list;
int create();
int scanRootDirectory(root_list &list,int ohfi_type);
int recursiveScanRootDirectory(root_list &list, CMAObject *parent, int ohfi_type);
bool hasFilter(const CMARootObject *object,int ohfi);
bool removeInternal(root_list &list, int ohfi);
bool findInternal(const root_list &list, int ohfi, find_data &data);
CMAObject *getParent(CMAObject *last_dir, const QString &current_path);
CMAObject *pathToObjectInternal(const root_list &list, const char *path);
static bool lessThanComparator(const CMAObject *a, const CMAObject *b);
void dumpMetadataList(const metadata_t *p_head);
bool find(int ohfi, find_data &data);
int acceptFilteredObject(const CMAObject *parent, const CMAObject *current, int type);
CMAObject *ohfiToObject(int ohfi);
QTimer *timer;
QThread *thread;
map_list object_list;
};
#endif // QLISTDB_H

View File

@@ -0,0 +1,530 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="es_ES">
<context>
<name>BackupItem</name>
<message>
<source>Delete entry</source>
<translation type="vanished">Borrar entrada</translation>
</message>
<message>
<source>Open folder</source>
<translation type="vanished">Abrir directorio</translation>
</message>
</context>
<context>
<name>BackupManagerForm</name>
<message>
<source>Backup Manager</source>
<translation type="vanished">Gestor de Respaldos</translation>
</message>
<message>
<source>Online ID / Username</source>
<translation type="vanished">ID Online / Nombre de usuario</translation>
</message>
<message>
<source>Backup Type</source>
<translation type="vanished">Tipo de respaldo</translation>
</message>
<message>
<source>PS Vita Games</source>
<translation type="vanished">Juegos PS Vita</translation>
</message>
<message>
<source>PSP Games</source>
<translation type="vanished">Juegos PSP</translation>
</message>
<message>
<source>PSM Games</source>
<translation type="vanished">Juegos PSM</translation>
</message>
<message>
<source>PSOne Games</source>
<translation type="vanished">Juegos PSOne</translation>
</message>
<message>
<source>PSP Savedatas</source>
<translation type="vanished">Salvados PSP</translation>
</message>
<message>
<source>Backups</source>
<translation type="vanished">Respaldos</translation>
</message>
<message>
<source>Backup disk usage</source>
<translation type="vanished">Uso de disco en respaldos</translation>
</message>
<message>
<source>Filter</source>
<translation type="vanished">Filtro</translation>
</message>
<message>
<source>Default account</source>
<translation type="vanished">Cuenta por defecto</translation>
</message>
<message>
<source>Are you sure to remove the backup of the following entry?</source>
<translation type="vanished">¿Estas seguro de borrar la siguiente entrada?</translation>
</message>
<message>
<source>Backup disk usage: %1</source>
<translation type="vanished">Uso de disco en respaldos: %1</translation>
</message>
<message>
<source> [GAME]</source>
<translation type="vanished"> [JUEGO]</translation>
</message>
<message>
<source> [SAVE]</source>
<translation type="vanished"> [SALVADO]</translation>
</message>
<message>
<source> [UPDATE]</source>
<translation type="vanished"> [ACTUALIZACIÓN]</translation>
</message>
<message>
<source> [DLC]</source>
<translation type="vanished"> [DLC]</translation>
</message>
</context>
<context>
<name>ClientManager</name>
<message>
<source>Added %1 items to the database</source>
<translation type="vanished">Agregadas %1 entradas a la base de datos</translation>
</message>
<message>
<source>Database indexing aborted by user</source>
<translation type="vanished">Actualización de la base de datos cancelada por el usuario</translation>
</message>
<message>
<source>Cannot initialize VitaMTP library</source>
<translation type="vanished">No se pudo inicializar VitaMTP</translation>
</message>
<message>
<source>This user doesn&apos;t belong to the vitamtp group, there could be a problem while reading the USB bus.</source>
<translation type="vanished">Este usuario no pertenece al grupo vitamtp, puede haber problemas al leer el bus USB.</translation>
</message>
<message>
<source>You must enable at least USB or Wireless monitoring</source>
<translation type="vanished">Debe habilitar por lo menos el monitoreo USB o inalámbrico</translation>
</message>
<message>
<source>No PS Vita system has been registered</source>
<translation type="vanished">Nigún sistema PS Vita ha sido registrado</translation>
</message>
</context>
<context>
<name>CmaClient</name>
<message>
<location filename="../../cmaclient.cpp" line="151"/>
<location filename="../../cmaclient.cpp" line="154"/>
<source>Connected to %1 (PS Vita)</source>
<translation>Conectado a %1 (PS Vita)</translation>
</message>
</context>
<context>
<name>ConfigWidget</name>
<message>
<source>QCMA Settings</source>
<translation type="vanished">Ajustes QCMA</translation>
</message>
<message>
<source>Folders</source>
<translation type="vanished">Directorios</translation>
</message>
<message>
<source>Specify the folders that the PS Vita will access for each content type.</source>
<translation type="vanished">Especificar los directorios que el sistema PS Vita accederá para cada tipo de contenido.</translation>
</message>
<message>
<source>This is the location your Screenshots and Pictures are Saved to/Imported from.</source>
<translation type="vanished">Esta es la ubicación donde tus capturas de pantalla e imágenes serán almacenadas o importadas.</translation>
</message>
<message>
<source>Photo Folder</source>
<translation type="vanished">Directorio de Fotos</translation>
</message>
<message>
<source>Browse...</source>
<translation type="vanished">Buscar...</translation>
</message>
<message>
<source>This is the location your Videos are Saved to/Imported from.</source>
<translation type="vanished">Esta es la ubicación donde tus videos serán almacenados o importados.</translation>
</message>
<message>
<source>Video Folder</source>
<translation type="vanished">Directorio de Videos</translation>
</message>
<message>
<source>This is the location your Music is Saved to/Imported from.</source>
<translation type="vanished">Esta es la ubicación donde tu música será almacenada o importada.</translation>
</message>
<message>
<source>Music Folder</source>
<translation type="vanished">Directorio de Música</translation>
</message>
<message>
<source>This is the location your Games, Apps, Savegames, and System Backups are Saved to/Imported from.</source>
<translation type="vanished">Esta es la ubicación donde tus juegos, aplicaciones, partidas salvadas y respaldos del sistema serán almacenados o importados.</translation>
</message>
<message>
<source>Applications / Backups</source>
<translation type="vanished">Aplicaciones / Juegos / Respaldos</translation>
</message>
<message>
<source>This is the location your Software Updates and Browser Data is Saved to/Imported from.</source>
<translation type="vanished">Esta es la ubicación donde el sistema PS Vita leerá los contenidos que intente descargar.</translation>
</message>
<message>
<source>Updates / Web content</source>
<translation type="vanished">Actualizaciones / Contenido Web</translation>
</message>
<message>
<source>This is the location your PS Vita system will read all the content that it tries to download.</source>
<translation type="vanished">Esta es la ubicación donde el sistema PS Vita leerá los contenidos que intente descargar.</translation>
</message>
<message>
<source>Packages</source>
<translation type="vanished">Archivos PKG</translation>
</message>
<message>
<source>Other</source>
<translation type="vanished">Otros</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:14pt; font-weight:600;&quot;&gt;Advanced settings&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="vanished">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:14pt; font-weight:600;&quot;&gt;Ajustes Avanzados&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source>Offline Mode</source>
<translation type="vanished">Modo desconectado</translation>
</message>
<message>
<source>Skip metadata extraction</source>
<translation type="vanished">Saltar la extracción de metadatos</translation>
</message>
<message>
<source>Update database automatically when files on the PC are changed</source>
<translation type="vanished">Actualizar la base de datos automaticamente</translation>
</message>
<message>
<source>SQLite</source>
<translation type="vanished">SQLite</translation>
</message>
<message>
<source>Skip photo scanning</source>
<translation type="vanished">Omitir el escaneo de fotos</translation>
</message>
<message>
<source>Skip video scanning</source>
<translation type="vanished">Omitir el escaneo de vídeos</translation>
</message>
<message>
<source>Skip music scanning</source>
<translation type="vanished">Omitir el escaneo de música</translation>
</message>
<message>
<source>CMA protocol version</source>
<translation type="vanished">Versión de protocolo CMA</translation>
</message>
<message>
<source>CMA protocol selection</source>
<translation type="vanished">Uso de protocolo CMA</translation>
</message>
<message>
<source>Latest</source>
<translation type="vanished">Último</translation>
</message>
<message>
<source>Manual</source>
<translation type="vanished">Manual</translation>
</message>
<message>
<source>Custom</source>
<translation type="vanished">Personalizado</translation>
</message>
<message>
<source>CMA custom version</source>
<translation type="vanished">Versión personalizada CMA</translation>
</message>
<message>
<source>Disable USB monitoring</source>
<translation type="vanished">Deshabilitar monitoreo USB</translation>
</message>
<message>
<source>Disable Wi-Fi monitoring</source>
<translation type="vanished">Deshabilitar monitoreo WiFi</translation>
</message>
<message>
<source>Database backend</source>
<translation type="vanished">Almacenaje de base de datos</translation>
</message>
<message>
<source>In Memory</source>
<translation type="vanished">En memoria</translation>
</message>
<message>
<source>Select the folder to be used as a photo source</source>
<translation type="vanished">Seleccione el directorio a ser utilizado como origen de fotos</translation>
</message>
<message>
<source>Select the folder to be used as a music source</source>
<translation type="vanished">Seleccione el directorio a ser utilizado como origen de música</translation>
</message>
<message>
<source>Select the folder to be used as a video source</source>
<translation type="vanished">Seleccione el directorio a ser utilizado como origen de videos</translation>
</message>
<message>
<source>Select the folder to be used to save PS Vita games and backups</source>
<translation type="vanished">Seleccione el directorio a ser utilizado para guardar juegos y respaldos</translation>
</message>
<message>
<source>Select the folder to be used to fetch software updates</source>
<translation type="vanished">Seleccione el directorio a ser utilizado para extraer actualizaciones de software</translation>
</message>
<message>
<source>Select the folder to be used to software packages</source>
<translation type="vanished">Seleccione el directorio a ser utilizado para almacenar archivos pkg</translation>
</message>
</context>
<context>
<name>ConfirmDialog</name>
<message>
<source>Confirmation Message</source>
<translation type="vanished">Mensaje de confirmación</translation>
</message>
</context>
<context>
<name>FilterLineEdit</name>
<message>
<source>Filter</source>
<translation type="vanished">Filtro</translation>
</message>
</context>
<context>
<name>HTTPDownloader</name>
<message>
<location filename="../../httpdownloader.cpp" line="129"/>
<source>Network error: %1</source>
<translation>Error de red: %1</translation>
</message>
</context>
<context>
<name>HeadlessManager</name>
<message>
<source>This user doesn&apos;t belong to the vitamtp group, there could be a problem while reading the USB bus.</source>
<translatorcomment>Este usuario no pertenece al grupo vitamtp, puede haber problemas al leer el bus USB.</translatorcomment>
<translation type="vanished">Este usuario no pertenece al grupo vitamtp, puede haber problemas al leer el bus USB.</translation>
</message>
</context>
<context>
<name>KDENotifierTray</name>
<message>
<source>Settings</source>
<translation type="vanished">Ajustes</translation>
</message>
<message>
<source>Refresh database</source>
<translation type="vanished">Refrescar base de datos</translation>
</message>
<message>
<source>Backup Manager</source>
<translation type="vanished">Gestor de Respaldos</translation>
</message>
<message>
<source>About QCMA</source>
<translation type="vanished">Acerca de QCMA</translation>
</message>
<message>
<source>About Qt</source>
<translation type="vanished">Acerca de Qt</translation>
</message>
<message>
<source>Quit</source>
<translation type="vanished">Salir</translation>
</message>
<message>
<source>Qcma status</source>
<translation type="vanished">Estado de Qcma</translation>
</message>
<message>
<source>Disconnected</source>
<translation type="vanished">Desconectado</translation>
</message>
</context>
<context>
<name>MainWidget</name>
<message>
<source>Shutting down...</source>
<translation type="vanished">Cerrando...</translation>
</message>
<message>
<source>Stopping QCMA (disconnect your PS Vita)</source>
<translation type="vanished">Deteniendo QCMA (desconecte su PS Vita)</translation>
</message>
<message>
<source>Disconnected</source>
<translation type="vanished">Desconectado</translation>
</message>
<message>
<source>The device has been disconnected</source>
<translation type="vanished">El dispositivo se ha desconectado</translation>
</message>
<message>
<source>About Qcma</source>
<translation type="vanished">Acerca de Qcma</translation>
</message>
<message>
<source>Copyright (C) 2015 Codestation</source>
<translation type="vanished">Copyright (C) 2015 Codestation</translation>
</message>
<message>
<source>Copyright (C) 2015 Codestation
build hash: %1
build branch: %2</source>
<translation type="vanished">Copyright (C) 2015 Codestation
Hash de compilación: %1
Rama de compilación: %2</translation>
</message>
<message>
<source>Information</source>
<translation type="vanished">Información</translation>
</message>
</context>
<context>
<name>PinForm</name>
<message>
<source>Device pairing</source>
<translation type="vanished">Emparejamiento de dispositivo</translation>
</message>
<message>
<source>An unregistered PS Vita system is connecting with QCMA via Wi-Fi</source>
<translation type="vanished">Un sistema PS Vita no registrado se intenta conectar con QCMA mediante Wi-Fi</translation>
</message>
<message>
<source>Device: PS Vita</source>
<translation type="vanished">Dispositivo: PS Vita</translation>
</message>
<message>
<source>Input the following number in the PS Vita system to register it with QCMA</source>
<translation type="vanished">Introduce el siguiente número en el sistema PS Vita para registrarlo con QCMA</translation>
</message>
<message>
<source>Time remaining: 300 seconds</source>
<translation type="vanished">Tiempo restante: 300 segundos</translation>
</message>
<message>
<source>Cancel</source>
<translation type="vanished">Cancelar</translation>
</message>
<message>
<source>Device: %1 (PS Vita)</source>
<translation type="vanished">Dispositivo: %1 (PS Vita)</translation>
</message>
<message>
<source>Time remaining: %1 seconds</source>
<translation type="vanished">Tiempo restante: %1 segundos</translation>
</message>
</context>
<context>
<name>ProgressForm</name>
<message>
<source>Refreshing database...</source>
<translation type="vanished">Actualizando base de datos...</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Reading directory:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="vanished">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Leyendo directorio:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source>directory name</source>
<translation type="vanished">Cargando nombre de directorio</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Processing file:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="vanished">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Procesando archivo:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source>file name</source>
<translation type="vanished">Cargando nombre de archivo</translation>
</message>
<message>
<source>Cancel</source>
<translation type="vanished">Cancelar</translation>
</message>
<message>
<source>Database indexing in progress</source>
<translation type="vanished">Actualización de base de datos en progreso</translation>
</message>
<message>
<source>Are you sure to cancel the database indexing?</source>
<translation type="vanished">¿Estas seguro de cancelar la actualización a la base de datos?</translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<source>An instance of Qcma is already running</source>
<translation type="vanished">Otra instancia de Qcma ya se encuentra en ejecución</translation>
</message>
</context>
<context>
<name>QTrayIcon</name>
<message>
<source>Settings</source>
<translation type="vanished">Ajustes</translation>
</message>
<message>
<source>Refresh database</source>
<translation type="vanished">Refrescar base de datos</translation>
</message>
<message>
<source>Backup Manager</source>
<translation type="vanished">Gestor de Respaldos</translation>
</message>
<message>
<source>About QCMA</source>
<translation type="vanished">Acerca de QCMA</translation>
</message>
<message>
<source>About Qt</source>
<translation type="vanished">Acerca de Qt</translation>
</message>
<message>
<source>Quit</source>
<translation type="vanished">Salir</translation>
</message>
</context>
<context>
<name>UnityIndicator</name>
<message>
<source>Settings</source>
<translation type="vanished">Ajustes</translation>
</message>
<message>
<source>Refresh database</source>
<translation type="vanished">Refrescar base de datos</translation>
</message>
<message>
<source>Backup Manager</source>
<translation type="vanished">Gestor de Respaldos</translation>
</message>
<message>
<source>About QCMA</source>
<translation type="vanished">Acerca de QCMA</translation>
</message>
<message>
<source>About Qt</source>
<translation type="vanished">Acerca de Qt</translation>
</message>
<message>
<source>Quit</source>
<translation type="vanished">Salir</translation>
</message>
</context>
</TS>

View File

@@ -0,0 +1,508 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="fr_FR">
<context>
<name>BackupItem</name>
<message>
<source>Delete entry</source>
<translation type="vanished">Supprimer l&apos;entrée</translation>
</message>
<message>
<source>Open folder</source>
<translation type="vanished">Ouvrir le répertoire</translation>
</message>
</context>
<context>
<name>BackupManagerForm</name>
<message>
<source>Backup Manager</source>
<translation type="vanished">Gestonaire de Sauvegardes</translation>
</message>
<message>
<source>Online ID / Username</source>
<translation type="vanished">ID Online / Nom d&apos;utilisateur</translation>
</message>
<message>
<source>Backup Type</source>
<translation type="vanished">Type de sauvegarde</translation>
</message>
<message>
<source>PS Vita Games</source>
<translation type="vanished">Jeux PS Vita</translation>
</message>
<message>
<source>PSP Games</source>
<translation type="vanished">Jeux PSP</translation>
</message>
<message>
<source>PSM Games</source>
<translation type="vanished">Jeux PSM</translation>
</message>
<message>
<source>PSOne Games</source>
<translation type="vanished">Jeux PSOne</translation>
</message>
<message>
<source>PSP Savedatas</source>
<translation type="vanished">Sauvegardes PSP</translation>
</message>
<message>
<source>Backups</source>
<translation type="vanished">Sauvegardes</translation>
</message>
<message>
<source>Backup disk usage</source>
<translation type="vanished">Utilisation du disque par les sauvegardes</translation>
</message>
<message>
<source>Filter</source>
<translation type="vanished">Filtrer</translation>
</message>
<message>
<source>Default account</source>
<translation type="vanished">Compte par défaut</translation>
</message>
<message>
<source>Are you sure to remove the backup of the following entry?</source>
<translation type="vanished">Êtes-vous sur de voulloir supprimer cette sauvegarde?</translation>
</message>
<message>
<source>Backup disk usage: %1</source>
<translation type="vanished">Utilisation du disque de sauvegarde: %1</translation>
</message>
<message>
<source> [GAME]</source>
<translation type="vanished"> [JEU]</translation>
</message>
<message>
<source> [SAVE]</source>
<translation type="vanished"> [SAUVEGARDE]</translation>
</message>
<message>
<source> [UPDATE]</source>
<translation type="vanished"> [MISE_A_JOUR]</translation>
</message>
<message>
<source> [DLC]</source>
<translation type="vanished"> [DLC]</translation>
</message>
</context>
<context>
<name>ClientManager</name>
<message>
<source>Added %1 items to the database</source>
<translation type="vanished">%1 éléments ont é rajoutés à la base de données</translation>
</message>
<message>
<source>Database indexing aborted by user</source>
<translation type="vanished">Mise à jour de la base de données annulée par l&apos;utilisateur</translation>
</message>
<message>
<source>Cannot initialize VitaMTP library</source>
<translation type="vanished">Impossible d&apos;initaliser la librairie VitaMTP</translation>
</message>
<message>
<source>You must enable at least USB or Wireless monitoring</source>
<translation type="vanished">Vous devez soit activer la connexion par Wifi, soit activer celle par USB</translation>
</message>
<message>
<source>No PS Vita system has been registered</source>
<translation type="vanished">Aucunne PS Vita reconnue</translation>
</message>
</context>
<context>
<name>CmaClient</name>
<message>
<location filename="../../cmaclient.cpp" line="151"/>
<location filename="../../cmaclient.cpp" line="154"/>
<source>Connected to %1 (PS Vita)</source>
<translation>Connecté à %1 (PS Vita)</translation>
</message>
</context>
<context>
<name>ConfigWidget</name>
<message>
<source>QCMA Settings</source>
<translation type="vanished">Configuration de QCMA</translation>
</message>
<message>
<source>Folders</source>
<translation type="vanished">Répertoires</translation>
</message>
<message>
<source>Specify the folders that the PS Vita will access for each content type.</source>
<translation type="vanished">Choisissez les répertoires que la PS Vita va acceder pour chaque type de contenu.</translation>
</message>
<message>
<source>This is the location your Screenshots and Pictures are Saved to/Imported from.</source>
<translation type="vanished">Emplacement pour Copier/Importer les Copies d&apos;écrans et les Photos.</translation>
</message>
<message>
<source>Photo Folder</source>
<translation type="vanished">Répertoire des Photos</translation>
</message>
<message>
<source>Browse...</source>
<translation type="vanished">Selectionner...</translation>
</message>
<message>
<source>This is the location your Videos are Saved to/Imported from.</source>
<translation type="vanished">Emplacement pour Copier/Importer les Vidéos.</translation>
</message>
<message>
<source>Video Folder</source>
<translation type="vanished">Répertoire des Vidéos</translation>
</message>
<message>
<source>This is the location your Music is Saved to/Imported from.</source>
<translation type="vanished">Emplacement pour Copier/Importer les Musiques.</translation>
</message>
<message>
<source>Music Folder</source>
<translation type="vanished">Répertoire des Musiques</translation>
</message>
<message>
<source>This is the location your Games, Apps, Savegames, and System Backups are Saved to/Imported from.</source>
<translation type="vanished">Emplacement pour Copier/Importer les Jeux, Applications &amp; Sauvegardes</translation>
</message>
<message>
<source>Applications / Backups</source>
<translation type="vanished">Applications / Jeux / Sauvegardes</translation>
</message>
<message>
<source>This is the location your Software Updates and Browser Data is Saved to/Imported from.</source>
<translation type="vanished">Emplacement pour Copier/Importer les mises à jours du logiciel de la Vita</translation>
</message>
<message>
<source>Updates / Web content</source>
<translation type="vanished">Mise à jours / Contenu Web</translation>
</message>
<message>
<source>This is the location your PS Vita system will read all the content that it tries to download.</source>
<translation type="vanished">Emplacement pour Copier/Importer les contenus téléchargés.</translation>
</message>
<message>
<source>Packages</source>
<translation type="vanished">Archive des Packages</translation>
</message>
<message>
<source>Other</source>
<translation type="vanished">Autres</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:14pt; font-weight:600;&quot;&gt;Advanced settings&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="vanished">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:14pt; font-weight:600;&quot;&gt;Options Avancées&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source>Offline Mode</source>
<translation type="vanished">Mode déconnecté</translation>
</message>
<message>
<source>Skip metadata extraction</source>
<translation type="vanished">Passer l&apos;extracion de métadonnées</translation>
</message>
<message>
<source>Update database automatically when files on the PC are changed</source>
<translation type="vanished">Mise à jour automatique de la base de données</translation>
</message>
<message>
<source>SQLite</source>
<translation type="vanished">SQLite</translation>
</message>
<message>
<source>Skip photo scanning</source>
<translation type="vanished">Passer le scan des photos</translation>
</message>
<message>
<source>Skip video scanning</source>
<translation type="vanished">Passer le scan des videos</translation>
</message>
<message>
<source>Skip music scanning</source>
<translation type="vanished">Passer le scan des musiques</translation>
</message>
<message>
<source>CMA protocol version</source>
<translation type="vanished">Version du protocol CMA</translation>
</message>
<message>
<source>CMA protocol selection</source>
<translation type="vanished">Utilisation du protocolo CMA</translation>
</message>
<message>
<source>Latest</source>
<translation type="vanished">Dernier</translation>
</message>
<message>
<source>Manual</source>
<translation type="vanished">Manuel</translation>
</message>
<message>
<source>Custom</source>
<translation type="vanished">Personalisé</translation>
</message>
<message>
<source>CMA custom version</source>
<translation type="vanished">Version CMA personalisée</translation>
</message>
<message>
<source>Disable USB monitoring</source>
<translation type="vanished">Désactiver la surveillance USB</translation>
</message>
<message>
<source>Disable Wi-Fi monitoring</source>
<translation type="vanished">Désactiver la surveillance WiFi</translation>
</message>
<message>
<source>Database backend</source>
<translation type="vanished">Moteur de base de données</translation>
</message>
<message>
<source>In Memory</source>
<translation type="vanished">En memoire</translation>
</message>
<message>
<source>Select the folder to be used as a photo source</source>
<translation type="vanished">Choisissez le répertoire à utiliser comme source de photos</translation>
</message>
<message>
<source>Select the folder to be used as a music source</source>
<translation type="vanished">Choisissez le répertoire à utiliser comme source de musiques</translation>
</message>
<message>
<source>Select the folder to be used as a video source</source>
<translation type="vanished">Choisissez le répertoire à utiliser comme source de videos</translation>
</message>
<message>
<source>Select the folder to be used to save PS Vita games and backups</source>
<translation type="vanished">Choisissez le répertoire à utiliser pour sauvegarder les jeux PS Vita</translation>
</message>
<message>
<source>Select the folder to be used to fetch software updates</source>
<translation type="vanished">Choisissez le répertoire à utiliser pour recevoir les mise à jour</translation>
</message>
<message>
<source>Select the folder to be used to software packages</source>
<translation type="vanished">Choisissez le répertoire à utiliser pour ranger les packages de software</translation>
</message>
</context>
<context>
<name>ConfirmDialog</name>
<message>
<source>Confirmation Message</source>
<translation type="vanished">Message de confirmation</translation>
</message>
</context>
<context>
<name>FilterLineEdit</name>
<message>
<source>Filter</source>
<translation type="vanished">Filtre</translation>
</message>
</context>
<context>
<name>HTTPDownloader</name>
<message>
<location filename="../../httpdownloader.cpp" line="129"/>
<source>Network error: %1</source>
<translation>Erreur réseau: %1</translation>
</message>
</context>
<context>
<name>KDENotifierTray</name>
<message>
<source>Settings</source>
<translation type="vanished">Paramètres</translation>
</message>
<message>
<source>Refresh database</source>
<translation type="vanished">Mise à jour de la DB</translation>
</message>
<message>
<source>Backup Manager</source>
<translation type="vanished">Gestonaire de Sauvegards</translation>
</message>
<message>
<source>About QCMA</source>
<translation type="vanished">À propos de QCMA</translation>
</message>
<message>
<source>About Qt</source>
<translation type="vanished">À propos de Qt</translation>
</message>
<message>
<source>Quit</source>
<translation type="vanished">Quitter</translation>
</message>
<message>
<source>Qcma status</source>
<translation type="vanished">État de Qcma</translation>
</message>
<message>
<source>Disconnected</source>
<translation type="vanished">Déconnecté</translation>
</message>
</context>
<context>
<name>MainWidget</name>
<message>
<source>Shutting down...</source>
<translation type="vanished">Fin du programme...</translation>
</message>
<message>
<source>Stopping QCMA (disconnect your PS Vita)</source>
<translation type="vanished">Arrêter QCMA (déconnecte votre PS Vita)</translation>
</message>
<message>
<source>Disconnected</source>
<translation type="vanished">Déconnecté</translation>
</message>
<message>
<source>The device has been disconnected</source>
<translation type="vanished">L&apos;appareil a é déconnecté</translation>
</message>
<message>
<source>Copyright (C) 2015 Codestation</source>
<translation type="obsolete">Copyright (C) 2014 Codestation {2015 ?}</translation>
</message>
<message>
<source>Copyright (C) 2015 Codestation
build hash: %1
build branch: %2</source>
<translation type="obsolete">Copyright (C) 2014 Codestation
Hash de compilación: %1
Rama de compilación: %2 {2015 ?} {1
?}</translation>
</message>
<message>
<source>Information</source>
<translation type="vanished">Information</translation>
</message>
</context>
<context>
<name>PinForm</name>
<message>
<source>Device pairing</source>
<translation type="vanished">Pairage d&apos;appareil</translation>
</message>
<message>
<source>An unregistered PS Vita system is connecting with QCMA via Wi-Fi</source>
<translation type="vanished">Un systême PS Vita inconnu essaye de se connecter à QCMA par Wi-Fi</translation>
</message>
<message>
<source>Device: PS Vita</source>
<translation type="vanished">Appareil: PS Vita</translation>
</message>
<message>
<source>Input the following number in the PS Vita system to register it with QCMA</source>
<translation type="vanished">Introduisez ce numero d&apos;identification dans le systême PS Vita pour l&apos;enregistrer avec QCMA</translation>
</message>
<message>
<source>Time remaining: 300 seconds</source>
<translation type="vanished">Temps restant: 300 secondes</translation>
</message>
<message>
<source>Cancel</source>
<translation type="vanished">Annuler</translation>
</message>
<message>
<source>Device: %1 (PS Vita)</source>
<translation type="vanished">Appareil: %1 (PS Vita)</translation>
</message>
<message>
<source>Time remaining: %1 seconds</source>
<translation type="vanished">Temps restant: %1 secondes</translation>
</message>
</context>
<context>
<name>ProgressForm</name>
<message>
<source>Refreshing database...</source>
<translation type="vanished">Mise à jour de la base de données...</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Reading directory:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="vanished">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Lecture du répertoire:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source>directory name</source>
<translation type="vanished">Nom du répertoire</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Processing file:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="vanished">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Traitement du fichier:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source>file name</source>
<translation type="vanished">Nom du fichier</translation>
</message>
<message>
<source>Cancel</source>
<translation type="vanished">Annuler</translation>
</message>
<message>
<source>Database indexing in progress</source>
<translation type="vanished">Mise à jour de la base de donnée en cours</translation>
</message>
<message>
<source>Are you sure to cancel the database indexing?</source>
<translation type="vanished">Êtes-vous sur de vouloir annuler la mise à jour de la base de données?</translation>
</message>
</context>
<context>
<name>QTrayIcon</name>
<message>
<source>Settings</source>
<translation type="vanished">Paramètres</translation>
</message>
<message>
<source>Refresh database</source>
<translation type="vanished">Rafraichir la base de données</translation>
</message>
<message>
<source>Backup Manager</source>
<translation type="vanished">Gestonaire de Sauvegardes</translation>
</message>
<message>
<source>About QCMA</source>
<translation type="vanished">À propos de QCMA</translation>
</message>
<message>
<source>About Qt</source>
<translation type="vanished">À propos de Qt</translation>
</message>
<message>
<source>Quit</source>
<translation type="vanished">Quitter</translation>
</message>
</context>
<context>
<name>UnityIndicator</name>
<message>
<source>Settings</source>
<translation type="vanished">Paramètres</translation>
</message>
<message>
<source>Refresh database</source>
<translation type="vanished">Mise à jour de la base de données</translation>
</message>
<message>
<source>Backup Manager</source>
<translation type="vanished">Gestionaire de Sauvegardes</translation>
</message>
<message>
<source>About QCMA</source>
<translation type="vanished">À propos de QCMA</translation>
</message>
<message>
<source>About Qt</source>
<translation type="vanished">À propos de Qt</translation>
</message>
<message>
<source>Quit</source>
<translation type="vanished">Quitter</translation>
</message>
</context>
</TS>

View File

@@ -0,0 +1,490 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ja_JP">
<context>
<name>BackupItem</name>
<message>
<source>Delete entry</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Open folder</source>
<translation type="vanished"></translation>
</message>
</context>
<context>
<name>BackupManagerForm</name>
<message>
<source>Backup Manager</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Online ID / Username</source>
<translation type="vanished">ID / </translation>
</message>
<message>
<source>Backup Type</source>
<translation type="vanished"></translation>
</message>
<message>
<source>PS Vita Games</source>
<translation type="vanished">PS Vitaゲーム</translation>
</message>
<message>
<source>PSP Games</source>
<translation type="vanished">PSPゲーム</translation>
</message>
<message>
<source>PSM Games</source>
<translation type="vanished">PSMゲーム</translation>
</message>
<message>
<source>PSOne Games</source>
<translation type="vanished">PS1ゲーム</translation>
</message>
<message>
<source>PSP Savedatas</source>
<translation type="vanished">PSPセーブデータ</translation>
</message>
<message>
<source>Backups</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Backup disk usage</source>
<translation type="vanished">使</translation>
</message>
<message>
<source>Filter</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Default account</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Are you sure to remove the backup of the following entry?</source>
<translation type="vanished">?</translation>
</message>
<message>
<source>Backup disk usage: %1</source>
<translation type="vanished">使: %1</translation>
</message>
<message>
<source> [GAME]</source>
<translation type="vanished"> []</translation>
</message>
<message>
<source> [SAVE]</source>
<translation type="vanished"> []</translation>
</message>
<message>
<source> [UPDATE]</source>
<translation type="vanished"> []</translation>
</message>
<message>
<source> [DLC]</source>
<translation type="vanished"> [DLC]</translation>
</message>
</context>
<context>
<name>ClientManager</name>
<message>
<source>Added %1 items to the database</source>
<translation type="vanished">%1</translation>
</message>
<message>
<source>Database indexing aborted by user</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Cannot initialize VitaMTP library</source>
<translation type="vanished">VitaMTPライブラリを初期化できません</translation>
</message>
<message>
<source>You must enable at least USB or Wireless monitoring</source>
<translation type="vanished">USBと無線のどちらかの監視を有効にする必要があります</translation>
</message>
<message>
<source>No PS Vita system has been registered</source>
<translation type="vanished">PS Vitaが登録されていません</translation>
</message>
</context>
<context>
<name>CmaClient</name>
<message>
<location filename="../../cmaclient.cpp" line="151"/>
<location filename="../../cmaclient.cpp" line="154"/>
<source>Connected to %1 (PS Vita)</source>
<translation>%1 (PS Vita) </translation>
</message>
</context>
<context>
<name>ConfigWidget</name>
<message>
<source>QCMA Settings</source>
<translation type="vanished">QCMA設定</translation>
</message>
<message>
<source>Folders</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Specify the folders that the PS Vita will access for each content type.</source>
<translation type="vanished">PS Vitaがアクセスするフォルダーをコンテンツの種類ごとに指定してください</translation>
</message>
<message>
<source>This is the location your Screenshots and Pictures are Saved to/Imported from.</source>
<translation type="vanished">/</translation>
</message>
<message>
<source>Photo Folder</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Browse...</source>
<translation type="vanished">...</translation>
</message>
<message>
<source>This is the location your Videos are Saved to/Imported from.</source>
<translation type="vanished">/</translation>
</message>
<message>
<source>Video Folder</source>
<translation type="vanished"></translation>
</message>
<message>
<source>This is the location your Music is Saved to/Imported from.</source>
<translation type="vanished">/</translation>
</message>
<message>
<source>Music Folder</source>
<translation type="vanished"></translation>
</message>
<message>
<source>This is the location your Games, Apps, Savegames, and System Backups are Saved to/Imported from.</source>
<translation type="vanished">/</translation>
</message>
<message>
<source>Applications / Backups</source>
<translation type="vanished">/</translation>
</message>
<message>
<source>This is the location your Software Updates and Browser Data is Saved to/Imported from.</source>
<translation type="vanished">/</translation>
</message>
<message>
<source>Updates / Web content</source>
<translation type="vanished">/Webコンテンツ</translation>
</message>
<message>
<source>This is the location your PS Vita system will read all the content that it tries to download.</source>
<translation type="vanished">PS Vitaシステムが読み込み</translation>
</message>
<message>
<source>Packages</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Other</source>
<translation type="vanished"></translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:14pt; font-weight:600;&quot;&gt;Advanced settings&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="vanished">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:14pt; font-weight:600;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source>Offline Mode</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Skip metadata extraction</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Update database automatically when files on the PC are changed</source>
<translation type="vanished">PCのファイルが変更された際にデータベースを自動的に更新する</translation>
</message>
<message>
<source>SQLite</source>
<translation type="vanished">SQLite</translation>
</message>
<message>
<source>Skip photo scanning</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Skip video scanning</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Skip music scanning</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Disable USB monitoring</source>
<translation type="vanished">USBの監視を無効にする</translation>
</message>
<message>
<source>Disable Wi-Fi monitoring</source>
<translation type="vanished">Wi-Fiの監視を無効にする</translation>
</message>
<message>
<source>Database backend</source>
<translation type="vanished"></translation>
</message>
<message>
<source>In Memory</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Select the folder to be used as a photo source</source>
<translation type="vanished">使</translation>
</message>
<message>
<source>Select the folder to be used as a music source</source>
<translation type="vanished">使</translation>
</message>
<message>
<source>Select the folder to be used as a video source</source>
<translation type="vanished">使</translation>
</message>
<message>
<source>Select the folder to be used to save PS Vita games and backups</source>
<translation type="vanished">PS Vitaのゲームとバックアップの保存に使用されるフォルダを選択してください</translation>
</message>
<message>
<source>Select the folder to be used to fetch software updates</source>
<translation type="vanished">使</translation>
</message>
<message>
<source>Select the folder to be used to software packages</source>
<translation type="vanished">使</translation>
</message>
</context>
<context>
<name>ConfirmDialog</name>
<message>
<source>Confirmation Message</source>
<translation type="vanished"></translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Are you sure to delete the backup of the following game?&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt; font-weight:600;&quot;&gt;Game Name&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
</source>
<translation type="vanished">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;?&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt; font-weight:600;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
</translation>
</message>
</context>
<context>
<name>FilterLineEdit</name>
<message>
<source>Filter</source>
<translation type="vanished"></translation>
</message>
</context>
<context>
<name>HTTPDownloader</name>
<message>
<location filename="../../httpdownloader.cpp" line="129"/>
<source>Network error: %1</source>
<translation>: %1</translation>
</message>
</context>
<context>
<name>KDENotifierTray</name>
<message>
<source>Settings</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Refresh database</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Backup Manager</source>
<translation type="vanished"></translation>
</message>
<message>
<source>About QCMA</source>
<translation type="vanished">QCMAについて</translation>
</message>
<message>
<source>About Qt</source>
<translation type="vanished">Qtについて</translation>
</message>
<message>
<source>Quit</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Qcma status</source>
<translation type="vanished">QCMAステータス</translation>
</message>
<message>
<source>Disconnected</source>
<translation type="vanished"></translation>
</message>
</context>
<context>
<name>MainWidget</name>
<message>
<source>Shutting down...</source>
<translation type="vanished">...</translation>
</message>
<message>
<source>Stopping QCMA (disconnect your PS Vita)</source>
<translation type="vanished">QCMAを停止しています (PS Vitaとの接続を切断してください)</translation>
</message>
<message>
<source>Disconnected</source>
<translation type="vanished"></translation>
</message>
<message>
<source>The device has been disconnected</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Copyright (C) 2015 Codestation</source>
<translation type="obsolete">Copyright (C) 2014 Codestation {2015 ?}</translation>
</message>
<message>
<source>Copyright (C) 2015 Codestation
build hash: %1
build branch: %2</source>
<translation type="obsolete">Copyright (C) 2014 Codestation
: %1
: %2 {2015 ?} {1
?}</translation>
</message>
<message>
<source>Information</source>
<translation type="vanished"></translation>
</message>
</context>
<context>
<name>PinForm</name>
<message>
<source>Device pairing</source>
<translation type="vanished"></translation>
</message>
<message>
<source>An unregistered PS Vita system is connecting with QCMA via Wi-Fi</source>
<translation type="vanished">PS VitaがWi-Fi経由でQCMAで接続しています</translation>
</message>
<message>
<source>Device: PS Vita</source>
<translation type="vanished">端末: PS Vita</translation>
</message>
<message>
<source>Input the following number in the PS Vita system to register it with QCMA</source>
<translation type="vanished">QCMAでPS Vitaを登録するために次の番号をPS Vitaで入力してください</translation>
</message>
<message>
<source>Time remaining: 300 seconds</source>
<translation type="vanished">残り時間: 300秒</translation>
</message>
<message>
<source>Cancel</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Device: %1 (PS Vita)</source>
<translation type="vanished">: %1 (PS Vita)</translation>
</message>
<message>
<source>Time remaining: %1 seconds</source>
<translation type="vanished">: %1</translation>
</message>
</context>
<context>
<name>ProgressForm</name>
<message>
<source>Refreshing database...</source>
<translation type="vanished">...</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Reading directory:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="vanished">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Leyendo directorio:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source>directory name</source>
<translation type="vanished"></translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Processing file:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="vanished">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Procesando archivo:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source>file name</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Database indexing in progress</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Are you sure to cancel the database indexing?</source>
<translation type="vanished">?</translation>
</message>
</context>
<context>
<name>QTrayIcon</name>
<message>
<source>Settings</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Refresh database</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Backup Manager</source>
<translation type="vanished"></translation>
</message>
<message>
<source>About QCMA</source>
<translation type="vanished">QCMAについて</translation>
</message>
<message>
<source>About Qt</source>
<translation type="vanished">Qtについて</translation>
</message>
<message>
<source>Quit</source>
<translation type="vanished"></translation>
</message>
</context>
<context>
<name>UnityIndicator</name>
<message>
<source>Settings</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Refresh database</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Backup Manager</source>
<translation type="vanished"></translation>
</message>
<message>
<source>About QCMA</source>
<translation type="vanished">QCMAについて</translation>
</message>
<message>
<source>About Qt</source>
<translation type="vanished">Qtについて</translation>
</message>
<message>
<source>Quit</source>
<translation type="vanished"></translation>
</message>
</context>
</TS>

View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<update_data_list>
<region id="au">
<np level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<np_d level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<version system_version="00.000.000" label="0.00">
<update_data update_type="full">
<image size="0">http://www.example.com/PSP2UPDAT.PUP</image>
</update_data>
</version>
</region>
<region id="eu">
<np level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<np_d level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<version system_version="00.000.000" label="0.00">
<update_data update_type="full">
<image size="0">http://www.example.com/PSP2UPDAT.PUP</image>
</update_data>
</version>
</region>
<region id="jp">
<np level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<np_d level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<version system_version="00.000.000" label="0.00">
<update_data update_type="full">
<image size="0">http://www.example.com/PSP2UPDAT.PUP</image>
</update_data>
</version>
</region>
<region id="kr">
<np level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<np_d level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<version system_version="00.000.000" label="0.00">
<update_data update_type="full">
<image size="0">http://www.example.com/PSP2UPDAT.PUP</image>
</update_data>
</version>
</region>
<region id="mx">
<np level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<np_d level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<version system_version="00.000.000" label="0.00">
<update_data update_type="full">
<image size="0">http://www.example.com/PSP2UPDAT.PUP</image>
</update_data>
</version>
</region>
<region id="ru">
<np level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<np_d level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<version system_version="00.000.000" label="0.00">
<update_data update_type="full">
<image size="0">http://www.example.com/PSP2UPDAT.PUP</image>
</update_data>
</version>
</region>
<region id="tw">
<np level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<np_d level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<version system_version="00.000.000" label="0.00">
<update_data update_type="full">
<image size="0">http://www.example.com/PSP2UPDAT.PUP</image>
</update_data>
</version>
</region>
<region id="uk">
<np level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<np_d level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<version system_version="00.000.000" label="0.00">
<update_data update_type="full">
<image size="0">http://www.example.com/PSP2UPDAT.PUP</image>
</update_data>
</version>
</region>
<region id="us">
<np level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<np_d level0_system_version="00.000.000" level1_system_version="00.000.000" level2_system_version="00.000.000" map="00.000.000" />
<version system_version="00.000.000" label="0.00">
<update_data update_type="full">
<image size="0">http://www.example.com/PSP2UPDAT.PUP</image>
</update_data>
</version>
</region>
</update_data_list>

51
common/sforeader.cpp Normal file
View File

@@ -0,0 +1,51 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sforeader.h"
#include <QFile>
SfoReader::SfoReader()
{
}
bool SfoReader::load(const QString &path)
{
QFile file(path);
if(file.open(QIODevice::ReadOnly)) {
data = file.readAll();
key_offset = data.constData();
header = (sfo_header *)key_offset;
index = (sfo_index *)(key_offset + sizeof(sfo_header));
return true;
}
return false;
}
const char *SfoReader::value(const char *key, const char *defaultValue)
{
const char *base_key = key_offset + header->key_offset;
for(uint i = 0; i < header->pair_count; i++) {
const char *curr_key = base_key + index[i].key_offset;
if(strcmp(key, curr_key) == 0) {
return key_offset + header->value_offset + index[i].data_offset;
}
}
return defaultValue;
}

56
common/sforeader.h Normal file
View File

@@ -0,0 +1,56 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SFOREADER_H
#define SFOREADER_H
#include <QString>
class SfoReader
{
public:
SfoReader();
bool load(const QString &path);
const char *value(const char *key, const char *defaultValue);
private:
typedef struct {
quint16 key_offset;
uchar alignment;
uchar data_type;
quint32 value_size;
quint32 value_size_with_padding;
quint32 data_offset;
} __attribute__((packed)) sfo_index;
typedef struct {
char id[4];
quint32 version;
quint32 key_offset;
quint32 value_offset;
quint32 pair_count;
} __attribute__((packed)) sfo_header;
QByteArray data;
const char *key_offset;
const sfo_header *header;
const sfo_index *index;
};
#endif // SFOREADER_H

1318
common/sqlitedb.cpp Normal file

File diff suppressed because it is too large Load Diff

102
common/sqlitedb.h Normal file
View File

@@ -0,0 +1,102 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2014 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SQLITEDB_H
#define SQLITEDB_H
#include "database.h"
#include <vitamtp.h>
#include <QFileInfo>
#include <QObject>
#include <QSqlDatabase>
#include <QSqlError>
#include <QTimer>
class SQLiteDB : public Database
{
Q_OBJECT
public:
explicit SQLiteDB(QObject *parent = 0);
~SQLiteDB();
bool load();
bool rescan();
void close();
bool reload(bool &prepared);
void setUUID(const QString &m_uuid);
bool open();
int create();
void clear();
bool initialize();
QSqlError getLastError();
int childObjectCount(int parent_ohfi);
bool deleteEntry(int ohfi, int root_ohfi = 0);
QString getAbsolutePath(int ohfi);
bool getObjectList(int ohfi, metadata_t **metadata);
bool getObjectMetadata(int ohfi, metadata_t &metadata);
int getObjectMetadatas(int parent_ohfi, metadata_t **metadata, int index = 0, int max_number = 0);
qint64 getObjectSize(int ohfi);
int getParentId(int ohfi);
int getPathId(const char *name, int ohfi);
QString getRelativePath(int ohfi);
int getRootId(int ohfi);
int insertObjectEntry(const QString &path, const QString &name, int parent_ohfi);
bool renameObject(int ohfi, const QString &name);
void setObjectSize(int ohfi, qint64 size);
void freeMetadata(metadata_t *metadata);
int getPathId(const QString &path);
QString getPathFromId(int ohfi);
bool updateSize(int ohfi, quint64 size);
bool deleteEntry(int ohfi);
bool insertSourceEntry(uint object_id, const QString &path, const QString &name);
uint insertMusicEntry(const QString &path, const QString &name, int parent, int type);
uint insertVideoEntry(const QString &path, const QString &name, int parent, int type);
uint insertPhotoEntry(const QString &path, const QString &name, int parent, int type);
uint insertSavedataEntry(const QString &path, const QString &name, int parent, int type);
bool insertApplicationEntry(const QString &name, int ohfi, int app_type);
private:
int recursiveScanRootDirectory(const QString &base_path, const QString &rel_path, int parent_ohfi, int root_ohfi);
int insertObjectEntryInternal(const QString &path, const QString &name, int parent_ohfi, int type);
int insertDefaultEntry(const QString &path, const QString &name, const QString &title, int parent, int type);
int insertNodeEntry(const QString &title, int type, int data_type);
bool updateAdjacencyList(int ohfi, int parent);
QString getBasePath(int root_ohfi);
int getObjectType(int ohfi);
void fillMetadata(const QSqlQuery &query, metadata_t &metadata);
qint64 getChildenTotalSize(int ohfi);
bool updateObjectPath(int ohfi, const QString &name);
int getRootItems(int root_ohfi, metadata_t **metadata);
bool insertVirtualEntries();
bool insertVirtualEntry(int ohfi);
QTimer *timer;
QThread *thread;
QString m_uuid;
QSqlDatabase db;
};
#endif // SQLITEDB_H

7
common/translations.qrc Normal file
View File

@@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/">
<file>resources/translations/qcma_es.qm</file>
<file>resources/translations/qcma_fr.qm</file>
<file>resources/translations/qcma_ja.qm</file>
</qresource>
</RCC>

63
config.pri Normal file
View File

@@ -0,0 +1,63 @@
CXXFLAGS_WARNINGS = -Wall -Wextra -Wdisabled-optimization -Wformat=2 -Winit-self \
-Wmissing-include-dirs -Woverloaded-virtual -Wundef -Wno-unused \
-Wno-missing-field-initializers -Wno-format-nonliteral
# custom CXXFLAGS
QMAKE_CXXFLAGS += -Wno-write-strings -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS $$CXXFLAGS_WARNINGS
QT_CONFIG -= no-pkg-config
CONFIG += link_pkgconfig
CONFIG -= debug_and_release
VERSION = 0.3.10
#Linux-only config
unix:!macx {
# largefile support
DEFINES += _FILE_OFFSET_BITS=64 _LARGEFILE_SOURCE
# installation prefix
isEmpty(PREFIX) {
PREFIX = /usr/local
}
BINDIR = $$PREFIX/bin
DATADIR = $$PREFIX/share
MANDIR = $$DATADIR/man/man1
}
# Windows config
win32 {
# avoid alignment issues with newer mingw compiler
QMAKE_CXXFLAGS += -mno-ms-bitfields
}
# OS X config
macx {
# OS X icon
ICON = resources/images/qcma.icns
# re-enable pkg-config on OS X (brew installs pkg-config files)
QT_CONFIG -= no-pkg-config
}
# try to get the current git version + hash
QCMA_GIT_VERSION=$$system(git describe --tags)
#use the generic version if the above command fails (no git executable or metadata)
isEmpty(QCMA_GIT_VERSION) {
DEFINES += QCMA_VER=\\\"$$VERSION\\\"
} else {
DEFINES += QCMA_VER=\\\"$$QCMA_GIT_VERSION\\\"
}
GET_HASHES {
# try to get the current git commit and branch
QCMA_GIT_HASH=$$system(git rev-parse --short HEAD)
QCMA_GIT_BRANCH=$$system(git rev-parse --abbrev-ref HEAD)
# pass the current git commit hash
!isEmpty(QCMA_GIT_HASH):!isEmpty(QCMA_GIT_BRANCH) {
DEFINES += QCMA_BUILD_HASH=\\\"$$QCMA_GIT_HASH\\\"
DEFINES += QCMA_BUILD_BRANCH=\\\"$$QCMA_GIT_BRANCH\\\"
}
}

214
gui/clientmanager.cpp Normal file
View File

@@ -0,0 +1,214 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "clientmanager.h"
#include "cmaclient.h"
#include "cmautils.h"
#include "forms/progressform.h"
#include <QSettings>
#include <vitamtp.h>
#ifdef Q_OS_UNIX
#include <sys/socket.h>
#include <unistd.h>
int ClientManager::sighup_fd[2];
int ClientManager::sigterm_fd[2];
#endif
ClientManager::ClientManager(Database *db, QObject *obj_parent) :
QObject(obj_parent), m_db(db)
{
#ifdef Q_OS_UNIX
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sighup_fd))
qFatal("Couldn't create HUP socketpair");
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigterm_fd))
qFatal("Couldn't create TERM socketpair");
sn_hup = new QSocketNotifier(sighup_fd[1], QSocketNotifier::Read, this);
connect(sn_hup, SIGNAL(activated(int)), this, SLOT(handleSigHup()));
sn_term = new QSocketNotifier(sigterm_fd[1], QSocketNotifier::Read, this);
connect(sn_term, SIGNAL(activated(int)), this, SLOT(handleSigTerm()));
#endif
}
ClientManager::~ClientManager()
{
VitaMTP_Cleanup();
}
void ClientManager::databaseUpdated(int count)
{
progress.interruptShow();
progress.hide();
if(count >= 0) {
emit messageSent(tr("Added %1 items to the database").arg(count));
} else {
emit messageSent(tr("Database indexing aborted by user"));
}
emit updated(count);
}
void ClientManager::showPinDialog(QString name, int pin)
{
pinForm.setPin(name, pin);
pinForm.startCountdown();
}
void ClientManager::start()
{
if(VitaMTP_Init() < 0) {
emit messageSent(tr("Cannot initialize VitaMTP library"));
return;
}
// initializing database for the first use
refreshDatabase();
connect(m_db, SIGNAL(fileAdded(QString)), &progress, SLOT(setFileName(QString)));
connect(m_db, SIGNAL(directoryAdded(QString)), &progress, SLOT(setDirectoryName(QString)));
connect(m_db, SIGNAL(updated(int)), this, SLOT(databaseUpdated(int)));
connect(&progress, SIGNAL(canceled()), m_db, SLOT(cancelOperation()), Qt::DirectConnection);
thread_count = 0;
qDebug("Starting cma threads");
CmaClient *client;
QSettings settings;
if(!settings.value("disableUSB", false).toBool()) {
#ifdef Q_OS_LINUX
if(!belongsToGroup("vitamtp"))
emit messageSent(tr("This user doesn't belong to the vitamtp group, there could be a problem while reading the USB bus."));
#endif
usb_thread = new QThread();
client = new CmaClient(m_db);
usb_thread->setObjectName("usb_thread");
connect(usb_thread, SIGNAL(started()), client, SLOT(connectUsb()));
connect(client, SIGNAL(messageSent(QString)), this, SIGNAL(messageSent(QString)));
connect(client, SIGNAL(finished()), usb_thread, SLOT(quit()), Qt::DirectConnection);
connect(usb_thread, SIGNAL(finished()), usb_thread, SLOT(deleteLater()));
connect(usb_thread, SIGNAL(finished()), this, SLOT(threadStopped()));
connect(usb_thread, SIGNAL(finished()), client, SLOT(deleteLater()));
connect(client, SIGNAL(deviceConnected(QString)), this, SIGNAL(deviceConnected(QString)));
connect(client, SIGNAL(deviceDisconnected()), this, SIGNAL(deviceDisconnected()));
connect(client, SIGNAL(refreshDatabase()), this, SLOT(refreshDatabase()));
client->moveToThread(usb_thread);
usb_thread->start();
thread_count++;
}
if(!settings.value("disableWireless", false).toBool()) {
CmaBroadcast *broadcast = new CmaBroadcast(this);
wireless_thread = new QThread();
client = new CmaClient(m_db, broadcast);
wireless_thread->setObjectName("wireless_thread");
connect(wireless_thread, SIGNAL(started()), client, SLOT(connectWireless()));
connect(client, SIGNAL(messageSent(QString)), this, SIGNAL(messageSent(QString)));
connect(client, SIGNAL(receivedPin(QString,int)), this, SLOT(showPinDialog(QString,int)));
connect(client, SIGNAL(finished()), wireless_thread, SLOT(quit()), Qt::DirectConnection);
connect(wireless_thread, SIGNAL(finished()), wireless_thread, SLOT(deleteLater()));
connect(wireless_thread, SIGNAL(finished()), this, SLOT(threadStopped()));
connect(wireless_thread, SIGNAL(finished()), client, SLOT(deleteLater()));
connect(client, SIGNAL(pinComplete()), &pinForm, SLOT(hide()));
connect(client, SIGNAL(deviceConnected(QString)), this, SIGNAL(deviceConnected(QString)));
connect(client, SIGNAL(deviceDisconnected()), this, SIGNAL(deviceDisconnected()));
connect(client, SIGNAL(refreshDatabase()), this, SLOT(refreshDatabase()));
client->moveToThread(wireless_thread);
wireless_thread->start();
thread_count++;
}
if(thread_count == 0) {
emit messageSent(tr("You must enable at least USB or Wireless monitoring"));
}
}
void ClientManager::refreshDatabase()
{
if(m_db->load()) {
return;
}
if(!m_db->rescan()) {
emit messageSent(tr("No PS Vita system has been registered"));
} else {
progress.showDelayed(1000);
}
}
void ClientManager::stop()
{
if(CmaClient::stop() < 0) {
emit stopped();
}
}
void ClientManager::threadStopped()
{
mutex.lock();
if(--thread_count == 0) {
emit stopped();
}
mutex.unlock();
}
#ifdef Q_OS_UNIX
void ClientManager::hupSignalHandler(int)
{
char a = 1;
::write(sighup_fd[0], &a, sizeof(a));
}
void ClientManager::termSignalHandler(int)
{
char a = 1;
::write(sigterm_fd[0], &a, sizeof(a));
}
void ClientManager::handleSigTerm()
{
sn_term->setEnabled(false);
char tmp;
::read(sigterm_fd[1], &tmp, sizeof(tmp));
stop();
sn_term->setEnabled(true);
}
void ClientManager::handleSigHup()
{
sn_hup->setEnabled(false);
char tmp;
::read(sighup_fd[1], &tmp, sizeof(tmp));
refreshDatabase();
sn_hup->setEnabled(true);
}
#endif

94
gui/clientmanager.h Normal file
View File

@@ -0,0 +1,94 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CLIENTMANAGER_H
#define CLIENTMANAGER_H
#include "database.h"
#include "forms/pinform.h"
#include "forms/progressform.h"
#include <QObject>
#include <QThread>
#ifdef Q_OS_UNIX
#include <QSocketNotifier>
#endif
class ClientManager : public QObject
{
Q_OBJECT
public:
explicit ClientManager(Database *db, QObject *parent = 0);
~ClientManager();
void start();
void stop();
#ifdef Q_OS_UNIX
// unix signal handlers
static void hupSignalHandler(int);
static void termSignalHandler(int);
#endif
private:
int thread_count;
QMutex mutex;
Database *m_db;
PinForm pinForm;
ProgressForm progress;
QThread *usb_thread;
QThread *wireless_thread;
#ifdef Q_OS_UNIX
// signal handling
static int sighup_fd[2];
static int sigterm_fd[2];
QSocketNotifier *sn_hup;
QSocketNotifier *sn_term;
#endif
signals:
void updated(int);
void stopped();
void receivedPin(int);
void deviceDisconnected();
void messageSent(QString);
void deviceConnected(QString);
public slots:
void refreshDatabase();
#ifdef Q_OS_UNIX
// Qt signal handlers
void handleSigHup();
void handleSigTerm();
#endif
private slots:
void threadStopped();
void databaseUpdated(int count);
void showPinDialog(QString name, int pin);
};
#endif // CLIENTMANAGER_H

2
gui/defines.pri Normal file
View File

@@ -0,0 +1,2 @@
INCLUDEPATH += $$IN_PWD
DEPENDPATH += $$IN_PWD

89
gui/filterlineedit.cpp Normal file
View File

@@ -0,0 +1,89 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Xian Nox
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "filterlineedit.h"
#include <QIcon>
#include <QStyle>
FilterLineEdit::FilterLineEdit(QWidget *obj_parent) :
QLineEdit(obj_parent)
{
int frame_width = frameWidth();
clearButton = new QToolButton(this);
QIcon clearIcon(":/main/resources/images/edit-clear-locationbar-rtl.png");
clearButton->setIcon(clearIcon);
clearButton->setIconSize(QSize(sizeHint().height() - 4 * frame_width,
sizeHint().height() - 4 * frame_width));
clearButton->setCursor(Qt::ArrowCursor);
clearButton->setStyleSheet("QToolButton { border:none; padding:0px; }");
clearButton->hide();
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateCloseButton(const QString&)));
setStyleSheet(QString("LineEdit { color:black; font-style:normal; padding-right:%1px; }").arg(
clearButton->sizeHint().width() + frame_width + 1));
QSize min_size_hint = minimumSizeHint();
setMinimumSize(qMax(min_size_hint.width(), clearButton->sizeHint().height() + frame_width),
qMax(min_size_hint.height(), clearButton->sizeHint().height() + frame_width));
}
void FilterLineEdit::updateCloseButton(const QString& filter_text)
{
if(filter_text.isEmpty() || filter_text == tr("Filter")) {
clearButton->setVisible(false);
} else {
clearButton->setVisible(true);
}
}
void FilterLineEdit::focusInEvent(QFocusEvent *e)
{
if(this->styleSheet() == "FilterLineEdit { color:gray; font-style:italic; }") {
this->clear();
}
setStyleSheet(QString("FilterLineEdit { color:black; font-style:normal; padding-right:%1px; }").arg(clearButton->sizeHint().width() + frameWidth() + 1));
QLineEdit::focusInEvent(e);
}
void FilterLineEdit::focusOutEvent(QFocusEvent *e)
{
if(this->text().isEmpty()) {
this->setText(tr("Filter"));
this->setStyleSheet("FilterLineEdit { color:gray; font-style:italic; }");
}
QLineEdit::focusOutEvent(e);
}
int FilterLineEdit::frameWidth() const
{
return style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this);
}
void FilterLineEdit::resizeEvent(QResizeEvent *e)
{
QSize sz = clearButton->sizeHint();
clearButton->move(rect().right() - sz.width(), rect().bottom() - sz.height() + frameWidth());
QLineEdit::resizeEvent(e);
}

47
gui/filterlineedit.h Normal file
View File

@@ -0,0 +1,47 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Xian Nox
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FILTERLINEEDIT_H
#define FILTERLINEEDIT_H
#include <QLineEdit>
#include <QToolButton>
class FilterLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit FilterLineEdit(QWidget *parent = 0);
protected:
void focusInEvent(QFocusEvent *e);
void focusOutEvent(QFocusEvent *e);
void resizeEvent(QResizeEvent *e);
int frameWidth() const;
private:
QToolButton *clearButton;
private slots:
void updateCloseButton(const QString &text);
};
#endif // FILTERLINEEDIT_H

104
gui/forms/backupitem.cpp Normal file
View File

@@ -0,0 +1,104 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "backupitem.h"
#include "ui_backupitem.h"
#include "cmautils.h"
#include "dds.h"
#include <QDesktopServices>
#include <QUrl>
const QString BackupItem::gameTemplate = "<html><head/><body>"
"<p><span style=\" font-size:13pt; font-weight:600;\">%1</span></p>"
"</body></html>";
const QString BackupItem::sizeTemplate = "<html><head/><body>"
"<p><span style=\" font-size:10pt;\">%1</span></p>"
"</body></html>";
const QString BackupItem::infoTemplate = "<html><head/><body>"
"<p><span style=\" font-size:10pt;\">&nbsp;%1</span></p>"
"</body></html>";
BackupItem::BackupItem(QWidget *obj_parent) :
QWidget(obj_parent),
ui(new Ui::BackupItem)
{
ui->setupUi(this);
// connect the buttons
connect(ui->openButton, SIGNAL(clicked()), this, SLOT(openDirectory()));
connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(removeEntry()));
}
BackupItem::~BackupItem()
{
delete ui;
}
void BackupItem::openDirectory()
{
QDesktopServices::openUrl(QUrl("file:///" + m_path));
}
void BackupItem::removeEntry()
{
emit deleteEntry(this);
}
const QPixmap *BackupItem::getIconPixmap()
{
return ui->itemPicture->pixmap();
}
void BackupItem::setDirectory(const QString &path)
{
m_path = path;
}
void BackupItem::setItemInfo(const QString &name, const QString &item_size, const QString &extra)
{
ui->gameLabel->setText(gameTemplate.arg(name));
ui->sizeLabel->setText(sizeTemplate.arg(item_size));
ui->infoLabel->setText(infoTemplate.arg(extra));
}
int BackupItem::getIconWidth()
{
return ui->itemPicture->width();
}
void BackupItem::setItemIcon(const QString &path, int item_width, bool try_dds)
{
ui->itemPicture->setMinimumWidth(item_width);
QPixmap pixmap(path);
if((pixmap.width() <= 0 || pixmap.height() <= 0) && try_dds) {
QImage image;
if(loadDDS(path, &image)) {
pixmap = QPixmap::fromImage(image);
}
}
ui->itemPicture->setPixmap(pixmap);
}
bool BackupItem::lessThan(const BackupItem *s1, const BackupItem *s2)
{
return s1->title.compare(s2->title) < 0;
}

63
gui/forms/backupitem.h Normal file
View File

@@ -0,0 +1,63 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BACKUPITEM_H
#define BACKUPITEM_H
#include <QWidget>
namespace Ui {
class BackupItem;
}
class BackupItem : public QWidget
{
Q_OBJECT
public:
explicit BackupItem(QWidget *parent = 0);
~BackupItem();
void setItemInfo(const QString &name, const QString &size, const QString &extra);
void setItemIcon(const QString &m_path, int width = 48, bool try_dds = false);
void setDirectory(const QString &m_path);
const QPixmap *getIconPixmap();
int getIconWidth();
static bool lessThan(const BackupItem *s1, const BackupItem *s2);
int ohfi;
QString title;
private:
QString m_path;
Ui::BackupItem *ui;
static const QString gameTemplate;
static const QString sizeTemplate;
static const QString infoTemplate;
signals:
void deleteEntry(BackupItem *entry);
private slots:
void openDirectory();
void removeEntry();
};
#endif // BACKUPITEM_H

154
gui/forms/backupitem.ui Normal file
View File

@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BackupItem</class>
<widget class="QWidget" name="BackupItem">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>638</width>
<height>75</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="itemPicture">
<property name="minimumSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>0</width>
<height>48</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="rightMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="gameLabel">
<property name="text">
<string notr="true">&lt;p&gt;&lt;span style=&quot; font-size:12pt; font-weight:600;&quot;&gt;Game Name&lt;/span&gt;&lt;/p&gt;</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="sizeLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="text">
<string notr="true">&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;0.00 GiB&lt;/span&gt;&lt;/p&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="infoLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:9pt;&quot;&gt;[APP] [SAVE] [UPDATE] [DLC]&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="deleteButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>32</height>
</size>
</property>
<property name="text">
<string>Delete entry</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="openButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>32</height>
</size>
</property>
<property name="text">
<string>Open folder</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,257 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "backupmanagerform.h"
#include "ui_backupmanagerform.h"
#include "cmaobject.h"
#include "sforeader.h"
#include "confirmdialog.h"
#include "cmautils.h"
#include "filterlineedit.h"
#include <QDebug>
#include <QDialogButtonBox>
#include <QDir>
#include <QSettings>
#include <vitamtp.h>
BackupManagerForm::BackupManagerForm(Database *db, QWidget *obj_parent) :
QDialog(obj_parent), m_db(db),
ui(new Ui::BackupManagerForm)
{
ui->setupUi(this);
setupForm();
}
BackupManagerForm::~BackupManagerForm()
{
delete ui;
}
void BackupManagerForm::setupForm()
{
this->resize(800, 480);
connect(ui->backupComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(loadBackupListing(int)));
ui->tableWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
ui->tableWidget->horizontalHeader()->hide();
}
void BackupManagerForm::removeEntry(BackupItem *item)
{
ConfirmDialog msgBox;
msgBox.setMessageText(tr("Are you sure to remove the backup of the following entry?"), item->title);
msgBox.setMessagePixmap(*item->getIconPixmap(), item->getIconWidth());
if(msgBox.exec() == 0) {
return;
}
QMutexLocker locker(&m_db->mutex);
int parent_ohfi = m_db->getParentId(item->ohfi);
removeRecursively(m_db->getAbsolutePath(item->ohfi));
m_db->deleteEntry(item->ohfi);
for(int i = 0; i < ui->tableWidget->rowCount(); ++i) {
BackupItem *iter_item = static_cast<BackupItem *>(ui->tableWidget->cellWidget(i, 0));
if(iter_item == item) {
ui->tableWidget->removeRow(i);
break;
}
}
if(parent_ohfi > 0) {
setBackupUsage(m_db->getObjectSize(parent_ohfi));
}
}
void BackupManagerForm::setBackupUsage(qint64 usage_size)
{
ui->usageLabel->setText(tr("Backup disk usage: %1").arg(readable_size(usage_size, true)));
}
void BackupManagerForm::loadBackupListing(int index)
{
int ohfi;
bool sys_dir;
int img_width;
//TODO: load all the accounts in the combobox
ui->accountBox->clear();
ui->accountBox->addItem(QSettings().value("lastOnlineId", tr("Default account")).toString());
if(index < 0) {
index = ui->backupComboBox->currentIndex();
}
ui->tableWidget->clear();
switch(index) {
case 0:
ohfi = VITA_OHFI_VITAAPP;
img_width = 48;
sys_dir = true;
break;
case 1:
ohfi = VITA_OHFI_PSPAPP;
img_width = 80;
sys_dir = true;
break;
case 2:
ohfi = VITA_OHFI_PSMAPP;
img_width = 48;
sys_dir = true;
break;
case 3:
ohfi = VITA_OHFI_PSXAPP;
img_width = 48;
sys_dir = true;
break;
case 4:
ohfi = VITA_OHFI_PSPSAVE;
img_width = 80;
sys_dir = false;
break;
case 5:
ohfi = VITA_OHFI_BACKUP;
img_width = 48;
sys_dir = false;
break;
default:
ohfi = VITA_OHFI_VITAAPP;
img_width = 48;
sys_dir = true;
}
m_db->mutex.lock();
// get the item list
metadata_t *meta = NULL;
int row_count = m_db->getObjectMetadatas(ohfi, &meta);
ui->tableWidget->setRowCount(row_count);
// exit if there aren't any items
if(row_count == 0) {
setBackupUsage(0);
m_db->mutex.unlock();
return;
}
// adjust the table item width to fill all the widget
QHeaderView *vert_header = ui->tableWidget->verticalHeader();
QHeaderView *horiz_header = ui->tableWidget->horizontalHeader();
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
horiz_header->setSectionResizeMode(QHeaderView::Stretch);
#else
horiz_header->setResizeMode(QHeaderView::Stretch);
#endif
qint64 backup_size = m_db->getObjectSize(ohfi);
setBackupUsage(backup_size);
QString path = m_db->getAbsolutePath(ohfi);
QList<BackupItem *> item_list;
metadata_t *first = meta;
while(meta) {
QString base_path = path + QDir::separator() + meta->name;
QString parent_path = sys_dir ? base_path + QDir::separator() + "sce_sys" : base_path;
SfoReader reader;
QString game_name;
// retrieve the game name from the SFO
if(reader.load(QDir(parent_path).absoluteFilePath(sys_dir ? "param.sfo" : "PARAM.SFO"))) {
game_name = QString::fromUtf8(reader.value("TITLE", meta->name));
} else {
game_name = QString(meta->name);
}
BackupItem *item = new BackupItem();
// save the game title and ohfi for sorting/deleting
item->ohfi = meta->ohfi;
item->title = game_name;
connect(item, SIGNAL(deleteEntry(BackupItem*)), this, SLOT(removeEntry(BackupItem*)));
// show better size info for multi GiB backups
bool use_gb = ohfi == VITA_OHFI_BACKUP && meta->size > 1024*1024*1024;
QString read_size = readable_size(meta->size, use_gb);
QString info;
// check if is listing PS Vita games
if(index == 0) {
if(QDir(base_path + QDir::separator() + "app").exists()) {
info.append(tr(" [GAME]"));
}
if(QDir(base_path + QDir::separator() + "savedata").exists()) {
info.append(tr(" [SAVE]"));
}
if(QDir(base_path + QDir::separator() + "patch").exists()) {
info.append(tr(" [UPDATE]"));
}
if(QDir(base_path + QDir::separator() + "addcont").exists()) {
info.append(tr(" [DLC]"));
}
}
item->setItemInfo(game_name, read_size, info);
item->setItemIcon(QDir(parent_path).absoluteFilePath(sys_dir ? "icon0.png" : "ICON0.PNG"), img_width, ohfi == VITA_OHFI_PSMAPP);
item->setDirectory(path + QDir::separator() + meta->name);
item->resize(646, 68);
item_list << item;
meta = meta->next_metadata;
}
m_db->freeMetadata(first);
qSort(item_list.begin(), item_list.end(), BackupItem::lessThan);
int row;
QList<BackupItem *>::iterator it;
vert_header->setUpdatesEnabled(false);
// insert the sorted items into the table
for(it = item_list.begin(), row = 0; it != item_list.end(); ++it, ++row) {
ui->tableWidget->setCellWidget(row, 0, *it);
vert_header->resizeSection(row, 68);
}
vert_header->setUpdatesEnabled(true);
m_db->mutex.unlock();
// apply filter
this->on_filterLineEdit_textChanged(ui->filterLineEdit->text());
}
void BackupManagerForm::on_filterLineEdit_textChanged(const QString &arg1)
{
if(arg1 != tr("Filter")) {
for(int i = 0; i < ui->tableWidget->rowCount(); ++i) {
BackupItem *item = (BackupItem*) ui->tableWidget->cellWidget(i, 0);
if(item->title.contains(arg1, Qt::CaseInsensitive)) {
ui->tableWidget->setRowHidden(i, false);
} else {
ui->tableWidget->setRowHidden(i, true);
}
}
}
}

View File

@@ -0,0 +1,55 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BACKUPMANAGERFORM_H
#define BACKUPMANAGERFORM_H
#include "database.h"
#include "backupitem.h"
#include <QDialog>
namespace Ui {
class BackupManagerForm;
}
class BackupManagerForm : public QDialog
{
Q_OBJECT
public:
explicit BackupManagerForm(Database *db, QWidget *parent = 0);
~BackupManagerForm();
Database *m_db;
private:
void setupForm();
void setBackupUsage(qint64 size);
Ui::BackupManagerForm *ui;
public slots:
void loadBackupListing(int index);
void removeEntry(BackupItem *item);
private slots:
void on_filterLineEdit_textChanged(const QString &arg1);
};
#endif // BACKUPMANAGERFORM_H

View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BackupManagerForm</class>
<widget class="QWidget" name="BackupManagerForm">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>857</width>
<height>478</height>
</rect>
</property>
<property name="windowTitle">
<string>Backup Manager</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Online ID / Username</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="accountBox"/>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout_4">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Backup Type</string>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="backupComboBox">
<item>
<property name="text">
<string>PS Vita Games</string>
</property>
</item>
<item>
<property name="text">
<string>PSP Games</string>
</property>
</item>
<item>
<property name="text">
<string>PSM Games</string>
</property>
</item>
<item>
<property name="text">
<string>PSOne Games</string>
</property>
</item>
<item>
<property name="text">
<string>PSP Savedatas</string>
</property>
</item>
<item>
<property name="text">
<string>Backups</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QTableWidget" name="tableWidget">
<property name="columnCount">
<number>1</number>
</property>
<column/>
</widget>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="usageLabel">
<property name="text">
<string>Backup disk usage</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="FilterLineEdit" name="filterLineEdit">
<property name="styleSheet">
<string notr="true">FilterLineEdit { color:gray; font-style:italic; }</string>
</property>
<property name="text">
<string>Filter</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>FilterLineEdit</class>
<extends>QLineEdit</extends>
<header>filterlineedit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

249
gui/forms/configwidget.cpp Normal file
View File

@@ -0,0 +1,249 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "configwidget.h"
#include "ui_configwidget.h"
extern "C" {
#include <vitamtp.h>
}
#include <QFileDialog>
#include <QSettings>
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <QStandardPaths>
#else
#include <QDesktopServices>
#define QStandardPaths QDesktopServices
#define writableLocation storageLocation
#endif
ConfigWidget::ConfigWidget(QWidget *obj_parent) :
QDialog(obj_parent),
ui(new Ui::ConfigWidget)
{
ui->setupUi(this);
connectSignals();
setDefaultData();
}
void ConfigWidget::connectSignals()
{
QSignalMapper *mapper = new QSignalMapper(this);
mapper->setMapping(ui->photoBtn, BTN_PHOTO);
mapper->setMapping(ui->musicBtn, BTN_MUSIC);
mapper->setMapping(ui->videoBtn, BTN_VIDEO);
mapper->setMapping(ui->appBtn, BTN_APPS);
mapper->setMapping(ui->urlBtn, BTN_URL);
mapper->setMapping(ui->pkgBtn, BTN_PKG);
connect(ui->photoBtn, SIGNAL(clicked()), mapper, SLOT(map()));
connect(ui->musicBtn, SIGNAL(clicked()), mapper, SLOT(map()));
connect(ui->videoBtn, SIGNAL(clicked()), mapper, SLOT(map()));
connect(ui->appBtn, SIGNAL(clicked()), mapper, SLOT(map()));
connect(ui->urlBtn, SIGNAL(clicked()), mapper, SLOT(map()));
connect(ui->pkgBtn, SIGNAL(clicked()), mapper, SLOT(map()));
connect(mapper, SIGNAL(mapped(int)), this, SLOT(browseBtnPressed(int)));
connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(ui->protocolModeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(protocolModeChanged(int)));
}
void ConfigWidget::protocolModeChanged(int index)
{
switch(index)
{
case 0:
ui->protocolBox->setEnabled(false);
ui->protocolEdit->setEnabled(false);
break;
case 1:
ui->protocolBox->setEnabled(true);
ui->protocolEdit->setEnabled(false);
break;
case 2:
ui->protocolBox->setEnabled(false);
ui->protocolEdit->setEnabled(true);
break;
default:
ui->protocolBox->setEnabled(false);
ui->protocolEdit->setEnabled(false);
break;
}
}
void ConfigWidget::setDefaultData()
{
QString defaultdir;
QSettings settings;
defaultdir = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
ui->photoPath->setText(QDir::toNativeSeparators(settings.value("photoPath", defaultdir).toString()));
defaultdir = QStandardPaths::writableLocation(QStandardPaths::MusicLocation);
ui->musicPath->setText(QDir::toNativeSeparators(settings.value("musicPath", defaultdir).toString()));
defaultdir = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);
ui->videoPath->setText(QDir::toNativeSeparators(settings.value("videoPath", defaultdir).toString()));
defaultdir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
defaultdir.append(QDir::separator()).append("PS Vita");
ui->appPath->setText(QDir::toNativeSeparators(settings.value("appsPath", defaultdir).toString()));
defaultdir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
defaultdir.append(QDir::separator()).append("PSV Updates");
ui->urlPath->setText(QDir::toNativeSeparators(settings.value("urlPath", defaultdir).toString()));
defaultdir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
defaultdir.append(QDir::separator()).append("PSV Packages");
ui->pkgPath->setText(QDir::toNativeSeparators(settings.value("pkgPath", defaultdir).toString()));
ui->offlineCheck->setChecked(settings.value("offlineMode", true).toBool());
ui->metadataCheck->setChecked(settings.value("skipMetadata", false).toBool());
ui->usbCheck->setChecked(settings.value("disableUSB", false).toBool());
ui->wifiCheck->setChecked(settings.value("disableWireless", false).toBool());
ui->databaseSelect->setCurrentIndex(settings.value("useMemoryStorage", true).toBool() ? 0 : 1);
ui->photoSkipCheck->setChecked(settings.value("photoSkip", false).toBool());
ui->videoSkipCheck->setChecked(settings.value("videoSkip", false).toBool());
ui->musicSkipCheck->setChecked(settings.value("musicSkip", false).toBool());
QString protocol_mode = settings.value("protocolMode", "automatic").toString();
if(protocol_mode == "manual")
ui->protocolModeBox->setCurrentIndex(1);
else if(protocol_mode == "custom")
ui->protocolModeBox->setCurrentIndex(2);
else
ui->protocolModeBox->setCurrentIndex(0);
protocolModeChanged(ui->protocolModeBox->currentIndex());
ui->protocolBox->setCurrentIndex(settings.value("protocolIndex", 0).toInt());
bool ok;
int protocol_version = settings.value("protocolVersion", VITAMTP_PROTOCOL_MAX_VERSION).toInt(&ok);
if(ok && protocol_version > 0)
ui->protocolEdit->setText(QString::number(protocol_version));
else
ui->protocolEdit->setText(QString::number(VITAMTP_PROTOCOL_MAX_VERSION));
}
ConfigWidget::~ConfigWidget()
{
delete ui;
}
void ConfigWidget::browseBtnPressed(int btn)
{
QString msg;
QLineEdit *lineedit;
switch(btn) {
case BTN_PHOTO:
lineedit = ui->photoPath;
msg = tr("Select the folder to be used as a photo source");
break;
case BTN_MUSIC:
lineedit = ui->musicPath;
msg = tr("Select the folder to be used as a music source");
break;
case BTN_VIDEO:
lineedit = ui->videoPath;
msg = tr("Select the folder to be used as a video source");
break;
case BTN_APPS:
lineedit = ui->appPath;
msg = tr("Select the folder to be used to save PS Vita games and backups");
break;
case BTN_URL:
lineedit = ui->urlPath;
msg = tr("Select the folder to be used to fetch software updates");
break;
case BTN_PKG:
lineedit = ui->pkgPath;
msg = tr("Select the folder to be used to software packages");
break;
default:
return;
}
QString selected = QFileDialog::getExistingDirectory(this, msg, lineedit->text(), QFileDialog::ShowDirsOnly);
if(!selected.isEmpty()) {
lineedit->setText(QDir::toNativeSeparators((selected)));
}
}
void ConfigWidget::savePath(QSettings &settings, const QLineEdit *edit, const QString &key)
{
QString path = edit->text();
if(path.endsWith(QDir::separator())) {
path.chop(1);
}
settings.setValue(key, QDir::fromNativeSeparators(path));
QDir(QDir::root()).mkpath(path);
}
void ConfigWidget::accept()
{
QSettings settings;
savePath(settings, ui->photoPath, "photoPath");
savePath(settings, ui->musicPath, "musicPath");
savePath(settings, ui->videoPath, "videoPath");
savePath(settings, ui->appPath, "appsPath");
savePath(settings, ui->urlPath, "urlPath");
savePath(settings, ui->pkgPath, "pkgPath");
settings.setValue("offlineMode", ui->offlineCheck->isChecked());
settings.setValue("skipMetadata", ui->metadataCheck->isChecked());
settings.setValue("disableUSB", ui->usbCheck->isChecked());
settings.setValue("disableWireless", ui->wifiCheck->isChecked());
settings.setValue("useMemoryStorage", ui->databaseSelect->currentIndex() == 0);
settings.setValue("photoSkip", ui->photoSkipCheck->isChecked());
settings.setValue("videoSkip", ui->videoSkipCheck->isChecked());
settings.setValue("musicSkip", ui->musicSkipCheck->isChecked());
settings.setValue("protocolIndex", ui->protocolBox->currentIndex());
if(ui->protocolModeBox->currentIndex() == 0)
settings.setValue("protocolMode", "automatic");
else if(ui->protocolModeBox->currentIndex() == 1)
settings.setValue("protocolMode", "manual");
else if(ui->protocolModeBox->currentIndex() == 2)
settings.setValue("protocolMode", "custom");
bool ok;
int protocol = ui->protocolEdit->text().toInt(&ok);
if(ok && protocol > 0)
settings.setValue("protocolVersion", protocol);
else
settings.setValue("protocolVersion", VITAMTP_PROTOCOL_MAX_VERSION);
settings.sync();
done(Accepted);
}

55
gui/forms/configwidget.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONFIGWIDGET_H
#define CONFIGWIDGET_H
#include <QDialog>
#include <QLineEdit>
#include <QSettings>
#include <QSignalMapper>
namespace Ui {
class ConfigWidget;
}
class ConfigWidget : public QDialog
{
Q_OBJECT
public:
explicit ConfigWidget(QWidget *parent = 0);
~ConfigWidget();
private:
enum browse_buttons {BTN_PHOTO, BTN_MUSIC, BTN_VIDEO, BTN_APPS, BTN_URL, BTN_PKG};
void connectSignals();
void setDefaultData();
void savePath(QSettings &settings, const QLineEdit *edit, const QString &key);
Ui::ConfigWidget *ui;
private slots:
void protocolModeChanged(int index);
void browseBtnPressed(int from);
void accept();
};
#endif // CONFIGWIDGET_H

525
gui/forms/configwidget.ui Normal file
View File

@@ -0,0 +1,525 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigWidget</class>
<widget class="QWidget" name="ConfigWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>519</width>
<height>525</height>
</rect>
</property>
<property name="windowTitle">
<string>QCMA Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Folders</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Specify the folders that the PS Vita will access for each content type.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="whatsThis">
<string>This is the location your Screenshots and Pictures are Saved to/Imported from.</string>
</property>
<property name="text">
<string>Photo Folder</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLineEdit" name="photoPath">
<property name="toolTip">
<string>This is the location your Screenshots and Pictures are Saved to/Imported from.</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="photoBtn">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label_3">
<property name="whatsThis">
<string>This is the location your Videos are Saved to/Imported from.</string>
</property>
<property name="text">
<string>Video Folder</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLineEdit" name="videoPath">
<property name="toolTip">
<string>This is the location your Videos are Saved to/Imported from.</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="videoBtn">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="label_4">
<property name="whatsThis">
<string>This is the location your Music is Saved to/Imported from.</string>
</property>
<property name="text">
<string>Music Folder</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLineEdit" name="musicPath">
<property name="toolTip">
<string>This is the location your Music is Saved to/Imported from.</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="musicBtn">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QLabel" name="label_6">
<property name="whatsThis">
<string>This is the location your Games, Apps, Savegames, and System Backups are Saved to/Imported from.</string>
</property>
<property name="text">
<string>Applications / Backups</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="appPath">
<property name="toolTip">
<string>This is the location your Games, Apps, Savegames, and System Backups are Saved to/Imported from.</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="appBtn">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QLabel" name="label_5">
<property name="whatsThis">
<string>This is the location your Software Updates and Browser Data is Saved to/Imported from.</string>
</property>
<property name="text">
<string>Updates / Web content</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QLineEdit" name="urlPath">
<property name="toolTip">
<string>This is the location your PS Vita system will read all the content that it tries to download.</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="urlBtn">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QLabel" name="label_9">
<property name="text">
<string>Packages</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="pkgPath"/>
</item>
<item>
<widget class="QPushButton" name="pkgBtn">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Other</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="2" column="0">
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:14pt; font-weight:600;&quot;&gt;Advanced settings&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="offlineCheck">
<property name="text">
<string>Offline Mode</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="metadataCheck">
<property name="text">
<string>Skip metadata extraction</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="usbCheck">
<property name="text">
<string>Disable USB monitoring</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="wifiCheck">
<property name="text">
<string>Disable Wi-Fi monitoring</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_3">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Update database automatically when files on the PC are changed</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Database backend</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="databaseSelect">
<property name="enabled">
<bool>false</bool>
</property>
<item>
<property name="text">
<string>In Memory</string>
</property>
</item>
<item>
<property name="text">
<string>SQLite</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="photoSkipCheck">
<property name="text">
<string>Skip photo scanning</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="videoSkipCheck">
<property name="text">
<string>Skip video scanning</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="musicSkipCheck">
<property name="text">
<string>Skip music scanning</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4"/>
</item>
<item>
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>CMA protocol version</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="protocolBox">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<item>
<property name="text">
<string notr="true">FW 3.30 - 1900010</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">FW 3.10 - 1800010</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">FW 3.00 - 1700010</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">FW 2.60 - 1600010</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">FW 2.10 - 1500010</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">FW 2.00 - 1400010</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">FW 1.80 - 1300010</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">FW 1.60 - 1200010</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">FW 1.50 - 1100010</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">FW 1.00 - 1000010</string>
</property>
</item>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="protocolEdit">
<property name="inputMask">
<string notr="true">9999999</string>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>CMA protocol selection</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="protocolModeBox">
<item>
<property name="text">
<string>Latest</string>
</property>
</item>
<item>
<property name="text">
<string>Manual</string>
</property>
</item>
<item>
<property name="text">
<string>Custom</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>CMA custom version</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,50 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "confirmdialog.h"
#include "ui_confirmdialog.h"
const QString ConfirmDialog::messageTemplate = "<html><head/><body>"
"<p><span style=\"font-size:10pt;\">%1</span></p>"
"<p><span style=\"font-size:12pt; font-weight:600;\">%2</span></p>"
"</body></html>";
ConfirmDialog::ConfirmDialog(QWidget *obj_parent) :
QDialog(obj_parent),
ui(new Ui::ConfirmDialog)
{
ui->setupUi(this);
this->layout()->setSizeConstraint(QLayout::SetFixedSize);
}
void ConfirmDialog::setMessageText(const QString message, const QString game_title)
{
ui->confirmText->setText(messageTemplate.arg(message, game_title));
}
void ConfirmDialog::setMessagePixmap(const QPixmap &pixmap, int dialog_width)
{
ui->itemPicture->setPixmap(pixmap);
ui->itemPicture->setMinimumWidth(dialog_width);
}
ConfirmDialog::~ConfirmDialog()
{
delete ui;
}

46
gui/forms/confirmdialog.h Normal file
View File

@@ -0,0 +1,46 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONFIRMDIALOG_H
#define CONFIRMDIALOG_H
#include <QDialog>
namespace Ui {
class ConfirmDialog;
}
class ConfirmDialog : public QDialog
{
Q_OBJECT
public:
explicit ConfirmDialog(QWidget *parent = 0);
~ConfirmDialog();
void setMessageText(const QString message, const QString game_title);
void setMessagePixmap(const QPixmap &pixmap, int width);
static const QString messageTemplate;
private:
Ui::ConfirmDialog *ui;
};
#endif // CONFIRMDIALOG_H

120
gui/forms/confirmdialog.ui Normal file
View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfirmDialog</class>
<widget class="QDialog" name="ConfirmDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>519</width>
<height>106</height>
</rect>
</property>
<property name="windowTitle">
<string>Confirmation Message</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="itemPicture">
<property name="minimumSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>0</width>
<height>48</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="confirmText">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Are you sure to delete the backup of the following game?&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt; font-weight:600;&quot;&gt;Game Name&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
</string>
</property>
<property name="margin">
<number>0</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ConfirmDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfirmDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

71
gui/forms/pinform.cpp Normal file
View File

@@ -0,0 +1,71 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pinform.h"
#include "ui_pinform.h"
#include <QDebug>
#include <QDesktopWidget>
const QString PinForm::pinFormat =
"<html><head/><body>"
"<p><span style=\"font-size:24pt; font-weight:600;\">%1</span></p>"
"</body></html>";
PinForm::PinForm(QWidget *obj_parent) :
QWidget(obj_parent),
ui(new Ui::PinForm)
{
ui->setupUi(this);
move(QApplication::desktop()->screen()->rect().center() - rect().center());
setFixedSize(size());
setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(hide()));
}
void PinForm::setPin(QString name, int pin)
{
qDebug() << "Got pin from user " << name;
ui->deviceLabel->setText(tr("Device: %1 (PS Vita)").arg(name));
ui->pinLabel->setText(pinFormat.arg(QString::number(pin), 8, QChar('0')));
show();
}
void PinForm::startCountdown()
{
timer.setInterval(1000);
counter = 300;
connect(&timer, SIGNAL(timeout()), this, SLOT(decreaseTimer()));
timer.start();
}
void PinForm::decreaseTimer()
{
counter--;
if(counter == 0) {
timer.stop();
hide();
}
ui->timeLabel->setText(tr("Time remaining: %1 seconds").arg(counter));
}
PinForm::~PinForm()
{
delete ui;
}

55
gui/forms/pinform.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PINFORM_H
#define PINFORM_H
#include <QTimer>
#include <QWidget>
namespace Ui {
class PinForm;
}
class PinForm : public QWidget
{
Q_OBJECT
public:
explicit PinForm(QWidget *parent = 0);
~PinForm();
private:
Ui::PinForm *ui;
// pin timeout
int counter;
QTimer timer;
static const QString pinFormat;
public slots:
void startCountdown();
void setPin(QString name, int pin);
private slots:
void decreaseTimer();
};
#endif // PINFORM_H

115
gui/forms/pinform.ui Normal file
View File

@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PinForm</class>
<widget class="QWidget" name="PinForm">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>526</width>
<height>216</height>
</rect>
</property>
<property name="windowTitle">
<string>Device pairing</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>An unregistered PS Vita system is connecting with QCMA via Wi-Fi</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="deviceLabel">
<property name="text">
<string>Device: PS Vita</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Input the following number in the PS Vita system to register it with QCMA</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="pinLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:24pt; font-weight:600;&quot;&gt;12345678&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="timeLabel">
<property name="text">
<string>Time remaining: 300 seconds</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,77 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "progressform.h"
#include "ui_progressform.h"
#include <QDesktopWidget>
#include <QMessageBox>
ProgressForm::ProgressForm(QWidget *obj_parent) :
QWidget(obj_parent),
ui(new Ui::ProgressForm)
{
ui->setupUi(this);
move(QApplication::desktop()->screen()->rect().center() - rect().center());
setFixedSize(size());
setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(cancelConfirm()));
}
ProgressForm::~ProgressForm()
{
delete ui;
}
void ProgressForm::cancelConfirm()
{
QMessageBox box;
box.setText(tr("Database indexing in progress"));
box.setInformativeText(tr("Are you sure to cancel the database indexing?"));
box.setIcon(QMessageBox::Warning);
box.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
if(box.exec() == QMessageBox::Ok) {
emit canceled();
}
}
void ProgressForm::setFileName(QString file)
{
QString elided = ui->fileLabel->fontMetrics().elidedText(file, Qt::ElideMiddle, ui->fileLabel->width(), 0);
ui->fileLabel->setText(elided);
}
void ProgressForm::setDirectoryName(QString dir)
{
QString elided = ui->directoryLabel->fontMetrics().elidedText(dir, Qt::ElideMiddle, ui->directoryLabel->width(), 0);
ui->directoryLabel->setText(elided);
}
void ProgressForm::showDelayed(int msec)
{
timer.setSingleShot(true);
timer.setInterval(msec);
connect(&timer, SIGNAL(timeout()), this, SLOT(show()));
timer.start();
}
void ProgressForm::interruptShow()
{
timer.stop();
}

57
gui/forms/progressform.h Normal file
View File

@@ -0,0 +1,57 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PROGRESSFORM_H
#define PROGRESSFORM_H
#include <QTimer>
#include <QWidget>
namespace Ui {
class ProgressForm;
}
class ProgressForm : public QWidget
{
Q_OBJECT
public:
explicit ProgressForm(QWidget *parent = 0);
~ProgressForm();
void showDelayed(int msec = 1000);
void interruptShow();
private:
Ui::ProgressForm *ui;
QTimer timer;
signals:
void canceled();
private slots:
void cancelConfirm();
public slots:
void setDirectoryName(QString dir);
void setFileName(QString file);
};
#endif // PROGRESSFORM_H

80
gui/forms/progressform.ui Normal file
View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ProgressForm</class>
<widget class="QWidget" name="ProgressForm">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>602</width>
<height>138</height>
</rect>
</property>
<property name="windowTitle">
<string>Refreshing database...</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Reading directory:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="directoryLabel">
<property name="text">
<string>directory name</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Processing file:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fileLabel">
<property name="text">
<string>file name</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

83
gui/gui.pro Normal file
View File

@@ -0,0 +1,83 @@
include(../config.pri)
include(../common/defines.pri)
TARGET = qcma
TEMPLATE += app
QT += gui widgets network sql
LIBS += -L../common -lqcma_common
SOURCES += \
main.cpp \
mainwidget.cpp \
singleapplication.cpp \
clientmanager.cpp \
filterlineedit.cpp \
qtrayicon.cpp \
# forms
forms/backupitem.cpp \
forms/backupmanagerform.cpp \
forms/configwidget.cpp \
forms/confirmdialog.cpp \
forms/pinform.cpp \
forms/progressform.cpp
HEADERS += \
mainwidget.h \
singleapplication.h \
clientmanager.h \
filterlineedit.h \
qtrayicon.h \
trayindicator.h \
trayindicator_global.h \
# forms
forms/backupitem.h \
forms/backupmanagerform.h \
forms/configwidget.h \
forms/confirmdialog.h \
forms/pinform.h \
forms/progressform.h
FORMS += \
forms/configwidget.ui \
forms/backupmanagerform.ui \
forms/backupitem.ui \
forms/confirmdialog.ui \
forms/progressform.ui \
forms/pinform.ui
OTHER_FILES += \
resources/images/psv_icon.png \
resources/images/psv_icon_16.png \
resources/images/qcma.png \
resources/qcma.desktop \
qcma.rc
RESOURCES += gui.qrc
# find packages using pkg-config
PKGCONFIG = libvitamtp libavformat libavcodec libavutil libswscale
#Linux-only config
unix:!macx {
PKGCONFIG += libnotify
# config for desktop file and icon
desktop.path = $$DATADIR/applications/$${TARGET}
desktop.files += resources/$${TARGET}.desktop
icon64.path = $$DATADIR/icons/hicolor/64x64/apps
icon64.files += resources/images/$${TARGET}.png
man.files = qcma.1
man.path = $$MANDIR
target.path = $$BINDIR
INSTALLS += target desktop icon64 man
}
# Windows config
win32 {
# Windows icon
RC_FILE = qcma.rc
}

12
gui/gui.qrc Normal file
View File

@@ -0,0 +1,12 @@
<RCC>
<qresource prefix="/main">
<file>resources/images/tray/qcma_on.png</file>
<file>resources/images/tray/qcma_on_16.png</file>
<file>resources/images/qcma.png</file>
<file>resources/images/edit-clear-locationbar-rtl.png</file>
<file>resources/images/tray/qcma_off.png</file>
<file>resources/images/tray/qcma_off_16.png</file>
<file>resources/images/qcma_on.png</file>
<file>resources/images/qcma_off.png</file>
</qresource>
</RCC>

152
gui/main.cpp Normal file
View File

@@ -0,0 +1,152 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef Q_OS_WIN32
#include <signal.h>
#endif
#include <QDebug>
#include <QLibraryInfo>
#include <QLocale>
#include <QTextCodec>
#include <QTextStream>
#include <QThread>
#include <QTranslator>
#include <inttypes.h>
#include "singleapplication.h"
#include "mainwidget.h"
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
static void noMessageOutput(QtMsgType type, const QMessageLogContext &, const QString & str)
{
const char * msg = str.toStdString().c_str();
#else
static void noMessageOutput(QtMsgType type, const char *msg)
{
#endif
Q_UNUSED(type);
Q_UNUSED(msg);
}
#ifndef Q_OS_WIN32
static bool setup_handlers()
{
struct sigaction hup, term;
hup.sa_handler = ClientManager::hupSignalHandler;
sigemptyset(&hup.sa_mask);
hup.sa_flags = 0;
hup.sa_flags |= SA_RESTART;
if (sigaction(SIGHUP, &hup, NULL) != 0) {
qCritical("SIGHUP signal handle failed");
return false;
}
term.sa_handler = ClientManager::termSignalHandler;
sigemptyset(&term.sa_mask);
term.sa_flags |= SA_RESTART;
if (sigaction(SIGTERM, &term, NULL) != 0) {
qCritical("SIGTERM signal handle failed");
return false;
}
return true;
}
#endif
int main(int argc, char *argv[])
{
if(SingleApplication::sendMessage(QObject::tr("An instance of Qcma is already running"))) {
return 0;
}
SingleApplication app(argc, argv);
#ifndef Q_OS_WIN32
// FIXME: libmtp sends SIGPIPE if a socket write fails crashing the whole app
// the proper fix is to libmtp to handle the cancel properly or ignoring
// SIGPIPE on the socket
signal(SIGPIPE, SIG_IGN);
setup_handlers();
#endif
if(app.arguments().contains("--with-debug")) {
VitaMTP_Set_Logging(VitaMTP_DEBUG);
} else if(app.arguments().contains("--verbose")) {
VitaMTP_Set_Logging(VitaMTP_VERBOSE);
} else {
VitaMTP_Set_Logging(VitaMTP_NONE);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
qInstallMessageHandler(noMessageOutput);
#else
qInstallMsgHandler(noMessageOutput);
#endif
}
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
#endif
QTextStream(stdout) << "Starting Qcma " << QCMA_VER << endl;
QTranslator translator;
QString locale = QLocale().system().name();
qDebug() << "Current locale:" << locale;
if(app.arguments().contains("--set-locale")) {
int index = app.arguments().indexOf("--set-locale");
if(index + 1 < app.arguments().length()) {
qDebug("Enforcing locale: %s", app.arguments().at(index + 1).toUtf8().data());
locale = app.arguments().at(index + 1);
}
}
if(translator.load("qcma_" + locale, ":/resources/translations")) {
app.installTranslator(&translator);
} else {
qWarning() << "Cannot load translation for locale:" << locale;
}
QTranslator system_translator;
system_translator.load("qt_" + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
app.installTranslator(&system_translator);
qDebug("Starting main thread: 0x%016" PRIxPTR, (uintptr_t)QThread::currentThreadId());
// set the organization/application for QSettings to work properly
app.setOrganizationName("codestation");
app.setApplicationName("qcma");
//TODO: check if this is actually needed since we don't have a main window by default
QApplication::setQuitOnLastWindowClosed(false);
bool showSystray = !app.arguments().contains("--no-systray");
MainWidget widget;
widget.prepareApplication(showSystray);
// receive the message from another process
QObject::connect(&app, SIGNAL(messageAvailable(QString)), &widget, SLOT(receiveMessage(QString)));
return app.exec();
}

281
gui/mainwidget.cpp Normal file
View File

@@ -0,0 +1,281 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mainwidget.h"
#include "cmaclient.h"
#include "cmautils.h"
#include "qlistdb.h"
#include "sqlitedb.h"
#include "qtrayicon.h"
#include <QApplication>
#include <QDebug>
#include <QDir>
#include <QGridLayout>
#include <QLibrary>
#include <QMessageBox>
#include <QSettings>
#include <QTimer>
#include <QSettings>
#include <QSpacerItem>
const QStringList MainWidget::path_list = QStringList() << "photoPath" << "musicPath" << "videoPath" << "appsPath" << "urlPath";
bool sleptOnce = false;
MainWidget::MainWidget(QWidget *obj_parent) :
QWidget(obj_parent), db(NULL), configForm(NULL), managerForm(NULL), backupForm(NULL)
{
trayIcon = NULL;
}
void MainWidget::checkSettings()
{
QSettings settings;
// make sure that all the paths are set, else show the config dialog
foreach(const QString &path, path_list) {
if(!settings.contains(path)) {
first_run = true;
configForm->show();
return;
}
}
first_run = false;
managerForm->start();
}
void MainWidget::dialogResult(int result)
{
if(result == QDialog::Accepted) {
if(first_run) {
first_run = false;
managerForm->start();
}
} else if(first_run) {
qApp->quit();
}
}
void MainWidget::stopServer()
{
setTrayTooltip(tr("Shutting down..."));
if(CmaClient::isRunning()) {
receiveMessage(tr("Stopping QCMA (disconnect your PS Vita)"));
}
managerForm->stop();
}
void MainWidget::deviceDisconnect()
{
#ifndef Q_OS_WIN32
trayIcon->setIcon("qcma_off");
#else
trayIcon->setIcon("tray/qcma_off_16");
#endif
qDebug("Icon changed - disconnected");
setTrayTooltip(tr("Disconnected"));
receiveMessage(tr("The device has been disconnected"));
}
void MainWidget::deviceConnect(QString message)
{
#ifndef Q_OS_WIN32
trayIcon->setIcon("qcma_on");
#else
trayIcon->setIcon("tray/qcma_on_16");
#endif
qDebug("Icon changed - connected");
setTrayTooltip(message);
receiveMessage(message);
}
void MainWidget::prepareApplication(bool showSystray)
{
//TODO: delete database before exit
if(QSettings().value("useMemoryStorage", true).toBool()) {
db = new QListDB();
} else {
db = new SQLiteDB();
}
configForm = new ConfigWidget();
backupForm = new BackupManagerForm(db, this);
managerForm = new ClientManager(db, this);
connectSignals();
if(showSystray) {
createTrayIcon();
}
checkSettings();
}
void MainWidget::connectSignals()
{
connect(configForm, SIGNAL(finished(int)), this, SLOT(dialogResult(int)));
connect(managerForm, SIGNAL(stopped()), qApp, SLOT(quit()));
connect(managerForm, SIGNAL(deviceConnected(QString)), this, SIGNAL(deviceConnected(QString)));
connect(managerForm, SIGNAL(deviceDisconnected()), this, SIGNAL(deviceDisconnected()));
connect(managerForm, SIGNAL(messageSent(QString)), this, SIGNAL(messageReceived(QString)));
connect(managerForm, SIGNAL(updated(int)), this, SIGNAL(databaseUpdated(int)));
}
void MainWidget::setTrayTooltip(QString message)
{
if(trayIcon) {
trayIcon->setToolTip(message);
}
}
void MainWidget::openManager()
{
backupForm->loadBackupListing(-1);
backupForm->show();
}
void MainWidget::showAboutDialog()
{
QMessageBox about;
about.setText(QString("Qcma ") + QCMA_VER);
about.setWindowTitle(tr("About Qcma"));
#ifndef QCMA_BUILD_HASH
about.setInformativeText(tr("Copyright (C) 2015 Codestation") + "\n");
#else
about.setInformativeText(tr("Copyright (C) 2015 Codestation\n\nbuild hash: %1\nbuild branch: %2").arg(QCMA_BUILD_HASH).arg(QCMA_BUILD_BRANCH));
#endif
about.setStandardButtons(QMessageBox::Ok);
about.setIconPixmap(QPixmap(":/main/resources/images/qcma.png"));
about.setDefaultButton(QMessageBox::Ok);
// hack to expand the messagebox minimum size
QSpacerItem* horizontalSpacer = new QSpacerItem(300, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
QGridLayout* widget_layout = (QGridLayout*)about.layout();
widget_layout->addItem(horizontalSpacer, widget_layout->rowCount(), 0, 1, widget_layout->columnCount());
about.show();
about.exec();
}
void MainWidget::showAboutQt()
{
QMessageBox::aboutQt(this);
}
void MainWidget::openConfig()
{
configForm->open();
}
void MainWidget::refreshDatabase()
{
managerForm->refreshDatabase();
}
TrayIndicator *MainWidget::createTrayObject(QWidget *obj_parent)
{
TrayFunctionPointer create_tray = NULL;
#ifdef Q_OS_LINUX
QString desktop = getenv("XDG_CURRENT_DESKTOP");
qDebug() << "Current desktop: " << desktop;
#ifdef QT_DEBUG
QString library_path = QApplication::applicationDirPath();
#else
QString library_path = "/usr/lib/qcma";
#endif
if(desktop.toLower() == "kde")
{
#ifdef QT_DEBUG
library_path += "/../kdenotifier";
#endif
// KDENotifier
QLibrary library(library_path + "/libqcma_kdenotifier.so");
if(library.load())
create_tray = reinterpret_cast<TrayFunctionPointer>(library.resolve("createTrayIndicator"));
else
qWarning() << "Cannot load libqcma_kdenotifier plugin from" << library_path;
}
else
// try to use the appindicator if is available
// if(desktop.toLower() == "unity")
{
#ifdef QT_DEBUG
library_path += "/../appindicator";
#endif
// AppIndicator
QLibrary library(library_path + "/libqcma_appindicator.so");
if(library.load())
create_tray = reinterpret_cast<TrayFunctionPointer>(library.resolve("createTrayIndicator"));
else
qWarning() << "Cannot load libqcma_appindicator plugin from" << library_path;
}
#endif
// else QSystemTrayIcon
return (create_tray != NULL) ? create_tray(obj_parent) : createTrayIndicator(obj_parent);
}
void MainWidget::createTrayIcon()
{
trayIcon = createTrayObject(this);
trayIcon->init();
#ifndef Q_OS_WIN32
trayIcon->setIcon("qcma_off");
#else
trayIcon->setIcon("tray/qcma_off_16");
#endif
trayIcon->show();
connect(trayIcon, SIGNAL(openConfig()), this, SLOT(openConfig()));
connect(trayIcon, SIGNAL(openManager()), this, SLOT(openManager()));
connect(trayIcon, SIGNAL(refreshDatabase()), this, SLOT(refreshDatabase()));
connect(trayIcon, SIGNAL(showAboutDialog()), this, SLOT(showAboutDialog()));
connect(trayIcon, SIGNAL(showAboutQt()), this, SLOT(showAboutQt()));
connect(trayIcon, SIGNAL(stopServer()), this, SLOT(stopServer()));
connect(managerForm, SIGNAL(deviceConnected(QString)), this, SLOT(deviceConnect(QString)));
connect(managerForm, SIGNAL(deviceDisconnected()), this, SLOT(deviceDisconnect()));
connect(managerForm, SIGNAL(messageSent(QString)), this, SLOT(receiveMessage(QString)));
}
void MainWidget::receiveMessage(QString message)
{
// a timeout is added before the popups are displayed to prevent them from
// appearing in the wrong location
if(!sleptOnce) {
Sleeper::sleep(1);
sleptOnce = true;
}
if(trayIcon->isVisible())
trayIcon->showMessage(tr("Information"), message);
}
MainWidget::~MainWidget()
{
if(trayIcon) {
trayIcon->hide();
}
delete db;
}

90
gui/mainwidget.h Normal file
View File

@@ -0,0 +1,90 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include "cmaclient.h"
#include "clientmanager.h"
#include "forms/configwidget.h"
#include "forms/backupmanagerform.h"
#include "forms/progressform.h"
#include <QAction>
#include <QWidget>
#include <QSystemTrayIcon>
#include <vitamtp.h>
class TrayIndicator;
class MainWidget : public QWidget
{
Q_OBJECT
public:
explicit MainWidget(QWidget *parent = 0);
~MainWidget();
void prepareApplication(bool showSystray);
private:
void connectSignals();
void createTrayIcon();
void checkSettings();
TrayIndicator *createTrayObject(QWidget *parent);
bool first_run;
// database
Database *db;
// forms
ConfigWidget *configForm;
ClientManager *managerForm;
BackupManagerForm *backupForm;
TrayIndicator *trayIcon;
const static QStringList path_list;
signals:
void deviceConnected(QString);
void deviceDisconnected();
void databaseUpdated(int count);
void messageReceived(QString message);
public slots:
void openConfig();
void openManager();
void showAboutQt();
void showAboutDialog();
void refreshDatabase();
void stopServer();
private slots:
void deviceConnect(QString message);
void deviceDisconnect();
void dialogResult(int result);
void receiveMessage(QString message);
void setTrayTooltip(QString message);
};
#endif // MAINWIDGET_H

14
gui/qcma.1 Normal file
View File

@@ -0,0 +1,14 @@
.TH QCMA 1 "MARCH 2015"
.SH NAME
qcma \- Content manager for the PlayStation Vita
.SH SYNOPSIS
.B qcma
.SH DESCRIPTION
\fBqcma\fR is a cross-platform application to provide an Open Source implementation
of the original Content Manager Assistant that comes with the PS Vita.
.TP
Qcma will let you backup your games, songs, photos & videos via USB or wireless.
.SH "SEE ALSO"
.I /usr/share/doc/qcma/README.md

1
gui/qcma.rc Normal file
View File

@@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "resources/images/qcma.ico"

117
gui/qtrayicon.cpp Normal file
View File

@@ -0,0 +1,117 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2014 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qtrayicon.h"
#include <QIcon>
#include <QMenu>
#include <QSystemTrayIcon>
#ifdef Q_OS_LINUX
#undef signals
extern "C" {
#include <libnotify/notify.h>
}
#define signals public
#endif
QTrayIcon::QTrayIcon(QWidget *obj_parent)
: TrayIndicator(obj_parent)
{
#ifdef Q_OS_LINUX
notify_init("qcma");
#endif
}
QTrayIcon::~QTrayIcon()
{
#ifdef Q_OS_LINUX
notify_uninit();
#endif
}
void QTrayIcon::init()
{
options = new QAction(tr("Settings"), this);
reload = new QAction(tr("Refresh database"), this);
backup = new QAction(tr("Backup Manager"), this);
about = new QAction(tr("About QCMA"), this);
about_qt = new QAction(tr("About Qt"), this);
quit = new QAction(tr("Quit"), this);
connect(options, SIGNAL(triggered()), this, SIGNAL(openConfig()));
connect(backup, SIGNAL(triggered()), this, SIGNAL(openManager()));
connect(reload, SIGNAL(triggered()), this, SIGNAL(refreshDatabase()));
connect(about, SIGNAL(triggered()), this, SIGNAL(showAboutDialog()));
connect(about_qt, SIGNAL(triggered()), this, SIGNAL(showAboutQt()));
connect(quit, SIGNAL(triggered()), this, SIGNAL(stopServer()));
QMenu *tray_icon_menu = new QMenu(this);
tray_icon_menu->addAction(options);
tray_icon_menu->addAction(reload);
tray_icon_menu->addAction(backup);
tray_icon_menu->addSeparator();
tray_icon_menu->addAction(about);
tray_icon_menu->addAction(about_qt);
tray_icon_menu->addSeparator();
tray_icon_menu->addAction(quit);
m_tray_icon = new QSystemTrayIcon(this);
m_tray_icon->setContextMenu(tray_icon_menu);
}
void QTrayIcon::showMessage(const QString &title, const QString &message)
{
#ifdef Q_OS_LINUX
NotifyNotification *notif = notify_notification_new(qPrintable(title), qPrintable(message), "dialog-information");
notify_notification_show(notif, NULL);
g_object_unref(G_OBJECT(notif));
#else
m_tray_icon->showMessage(title, message);
#endif
}
bool QTrayIcon::isVisible()
{
return m_tray_icon->isVisible();
}
void QTrayIcon::setIcon(const QString &icon)
{
QIcon qicon(":/main/resources/images/" + icon + ".png");
m_tray_icon->setIcon(qicon);
}
void QTrayIcon::show()
{
m_tray_icon->show();
}
void QTrayIcon::hide()
{
m_tray_icon->hide();
}
// exported library function
TrayIndicator *createTrayIndicator(QWidget *parent)
{
return new QTrayIcon(parent);
}

53
gui/qtrayicon.h Normal file
View File

@@ -0,0 +1,53 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2014 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QTRAYICON_H
#define QTRAYICON_H
#include "trayindicator.h"
class QAction;
class QSystemTrayIcon;
class QTrayIcon : public TrayIndicator
{
Q_OBJECT
public:
explicit QTrayIcon(QWidget *parent = 0);
~QTrayIcon();
void init();
void setIcon(const QString &icon);
bool isVisible();
void show();
void hide();
void showMessage(const QString &title, const QString &message);
private:
//system tray
QAction *quit;
QAction *reload;
QAction *options;
QAction *backup;
QAction *about;
QAction *about_qt;
QSystemTrayIcon *m_tray_icon;
};
#endif // QTRAYICON_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

View File

@@ -0,0 +1,18 @@
[Desktop Entry]
Encoding=UTF-8
Exec=qcma
GenericName=Content Manager Assistant
GenericName[es]=Asistente del Gestor de Contenido
GenericName[fr]=Gestionnaire de contenu
Comment=Content Manager Assistant for the PS Vita
Comment[es]=Asistente del Gestor de Contenido para PS Vita
Comment[fr]=Gestionnaire de contenu pour la PS Vita
Icon=qcma.png
Name=QCMA
Path=
StartupNotify=false
Terminal=false
Type=Application
Version=1.0
X-KDE-SubstituteUID=false
Categories=Qt;Utility;

69
gui/singleapplication.cpp Normal file
View File

@@ -0,0 +1,69 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "singleapplication.h"
#include <QDebug>
const int SingleApplication::timeout = 500;
const QString SingleApplication::SHARED_KEY = "QCMA_KEY";
SingleApplication::SingleApplication(int &s_argc, char **s_argv) :
QApplication(s_argc, s_argv)
{
server = new QLocalServer(this);
connect(server, SIGNAL(newConnection()), this, SLOT(receiveMessage()));
QLocalServer::removeServer(SHARED_KEY);
server->listen(SHARED_KEY);
}
void SingleApplication::receiveMessage()
{
QLocalSocket *socket = server->nextPendingConnection();
if(!socket->waitForReadyRead(timeout)) {
qDebug() << socket->errorString();
return;
}
QByteArray byteArray = socket->readAll();
QString message = QString::fromUtf8(byteArray.constData());
emit messageAvailable(message);
socket->disconnectFromServer();
}
bool SingleApplication::sendMessage(const QString &message)
{
QLocalSocket socket;
socket.connectToServer(SHARED_KEY, QIODevice::WriteOnly);
if(!socket.waitForConnected(timeout)) {
return false;
}
socket.write(message.toUtf8());
if(!socket.waitForBytesWritten(timeout)) {
qDebug() << socket.errorString();
return false;
}
socket.disconnectFromServer();
return true;
}

50
gui/singleapplication.h Normal file
View File

@@ -0,0 +1,50 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2013 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SINGLEAPPLICATION_H
#define SINGLEAPPLICATION_H
#include <QApplication>
#include <QLocalSocket>
#include <QLocalServer>
#include <QSharedMemory>
class SingleApplication : public QApplication
{
Q_OBJECT
public:
explicit SingleApplication(int &argc, char **argv);
static bool sendMessage(const QString &message);
private:
QLocalServer *server;
static const int timeout;
static const QString SHARED_KEY;
signals:
void messageAvailable(QString message);
public slots:
void receiveMessage();
};
#endif // SINGLEAPPLICATION_H

62
gui/trayindicator.h Normal file
View File

@@ -0,0 +1,62 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2014 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRAYINDICATOR_H
#define TRAYINDICATOR_H
#include <QString>
#include <QWidget>
#include "trayindicator_global.h"
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
// in Qt4 signals are protected
#undef signals
#define signals public
#endif
class TrayIndicator : public QWidget
{
Q_OBJECT
public:
~TrayIndicator() {}
virtual void init() = 0;
virtual bool isVisible() = 0;
virtual void setIcon(const QString &icon) = 0;
virtual void showMessage(const QString &title, const QString &message) = 0;
virtual void show() = 0;
virtual void hide() = 0;
protected:
TrayIndicator(QWidget *obj_parent = 0) : QWidget(obj_parent) {}
signals:
void openConfig();
void openManager();
void refreshDatabase();
void showAboutDialog();
void showAboutQt();
void stopServer();
};
typedef TrayIndicator *(*TrayFunctionPointer)(QWidget *parent);
extern "C" TRAYINDICATORSHARED_EXPORT TrayIndicator *createTrayIndicator(QWidget *parent = 0);
#endif // TRAYINDICATOR_H

View File

@@ -0,0 +1,35 @@
/*
* QCMA: Cross-platform content manager assistant for the PS Vita
*
* Copyright (C) 2014 Codestation
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRAYINDICATOR_GLOBAL_H
#define TRAYINDICATOR_GLOBAL_H
#include <QtCore/qglobal.h>
#ifdef Q_OS_LINUX
#if defined(QCMA_TRAYINDICATOR_LIBRARY)
# define TRAYINDICATORSHARED_EXPORT Q_DECL_EXPORT
#else
# define TRAYINDICATORSHARED_EXPORT Q_DECL_IMPORT
#endif
#else
# define TRAYINDICATORSHARED_EXPORT
#endif
#endif // TRAYINDICATOR_GLOBAL_H

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