diff --git a/qcma.pro b/qcma.pro
index 42986a8..604c773 100644
--- a/qcma.pro
+++ b/qcma.pro
@@ -7,7 +7,8 @@
QT += core \
gui \
widgets \
- network
+ network \
+ sql
TARGET = qcma
@@ -17,10 +18,9 @@ TEMPLATE = app
SOURCES += src/main.cpp \
src/capability.cpp \
- src/database.cpp \
src/cmaobject.cpp \
src/cmarootobject.cpp \
- src/utils.cpp \
+ src/cmautils.cpp \
src/mainwidget.cpp \
src/singleapplication.cpp \
src/sforeader.cpp \
@@ -31,21 +31,23 @@ SOURCES += src/main.cpp \
src/clientmanager.cpp \
src/filterlineedit.cpp \
src/dds.cpp \
+ src/sqlitedb.cpp \
+ src/httpdownloader.cpp \
+ src/qlistdb.cpp \
+ src/database.cpp \
# forms
src/forms/backupitem.cpp \
src/forms/backupmanagerform.cpp \
src/forms/configwidget.cpp \
src/forms/confirmdialog.cpp \
src/forms/pinform.cpp \
- src/forms/progressform.cpp \
- src/httpdownloader.cpp
+ src/forms/progressform.cpp
HEADERS += \
src/capability.h \
- src/database.h \
src/cmaobject.h \
src/cmarootobject.h \
- src/utils.h \
+ src/cmautils.h \
src/mainwidget.h \
src/singleapplication.h \
src/sforeader.h \
@@ -56,14 +58,17 @@ HEADERS += \
src/clientmanager.h \
src/filterlineedit.h \
src/dds.h \
+ src/sqlitedb.h \
+ src/httpdownloader.h \
+ src/qlistdb.h \
+ src/database.h \
# forms
src/forms/backupitem.h \
src/forms/backupmanagerform.h \
src/forms/configwidget.h \
src/forms/confirmdialog.h \
src/forms/pinform.h \
- src/forms/progressform.h \
- src/httpdownloader.h
+ src/forms/progressform.h
FORMS += \
src/forms/configwidget.ui \
diff --git a/src/avdecoder.cpp b/src/avdecoder.cpp
index 2f70bff..c5b0142 100644
--- a/src/avdecoder.cpp
+++ b/src/avdecoder.cpp
@@ -17,9 +17,9 @@
* along with this program. If not, see .
*/
-#include "utils.h"
+#include "cmautils.h"
#include "avdecoder.h"
-#include "cmaobject.h"
+#include "database.h"
#include
#include
diff --git a/src/clientmanager.cpp b/src/clientmanager.cpp
index 3b2e6b5..7cce9d9 100644
--- a/src/clientmanager.cpp
+++ b/src/clientmanager.cpp
@@ -19,15 +19,15 @@
#include "clientmanager.h"
#include "cmaclient.h"
-#include "utils.h"
+#include "cmautils.h"
#include "forms/progressform.h"
#include
#include
-ClientManager::ClientManager(QObject *parent) :
- QObject(parent)
+ClientManager::ClientManager(Database *db, QObject *parent) :
+ QObject(parent), m_db(db)
{
}
@@ -63,11 +63,10 @@ void ClientManager::start()
// initializing database for the first use
refreshDatabase();
- CmaEvent::db = &db;
- connect(&db, SIGNAL(fileAdded(QString)), &progress, SLOT(setFileName(QString)));
- connect(&db, SIGNAL(directoryAdded(QString)), &progress, SLOT(setDirectoryName(QString)));
- connect(&db, SIGNAL(updated(int)), this, SLOT(databaseUpdated(int)));
- connect(&progress, SIGNAL(canceled()), &db, SLOT(cancelOperation()), Qt::DirectConnection);
+ 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");
@@ -76,7 +75,7 @@ void ClientManager::start()
if(!settings.value("disableUSB", false).toBool()) {
usb_thread = new QThread();
- client = new CmaClient();
+ 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)));
@@ -96,7 +95,7 @@ void ClientManager::start()
if(!settings.value("disableWireless", false).toBool()) {
wireless_thread = new QThread();
- client = new CmaClient();
+ client = new CmaClient(m_db);
wireless_thread->setObjectName("wireless_thread");
connect(wireless_thread, SIGNAL(started()), client, SLOT(connectWireless()));
connect(client, SIGNAL(messageSent(QString)), this, SIGNAL(messageSent(QString)));
@@ -123,13 +122,12 @@ void ClientManager::start()
void ClientManager::refreshDatabase()
{
- bool prepared;
- if(!db.reload(prepared)) {
- if(prepared) {
- emit messageSent(tr("Cannot refresh the database while is in use"));
- } else {
- emit messageSent(tr("No PS Vita system has been registered"));
- }
+ if(m_db->load()) {
+ return;
+ }
+
+ if(!m_db->rescan()) {
+ emit messageSent(tr("No PS Vita system has been registered"));
} else {
progress.showDelayed(1000);
}
diff --git a/src/clientmanager.h b/src/clientmanager.h
index 9ff8792..40fa562 100644
--- a/src/clientmanager.h
+++ b/src/clientmanager.h
@@ -20,7 +20,7 @@
#ifndef CLIENTMANAGER_H
#define CLIENTMANAGER_H
-#include "database.h"
+#include "qlistdb.h"
#include "forms/pinform.h"
#include "forms/progressform.h"
@@ -31,18 +31,18 @@ class ClientManager : public QObject
{
Q_OBJECT
public:
- explicit ClientManager(QObject *parent = 0);
+ explicit ClientManager(Database *db, QObject *parent = 0);
~ClientManager();
void start();
void stop();
- Database db;
-
private:
int thread_count;
QMutex mutex;
+ Database *m_db;
+
PinForm pinForm;
ProgressForm progress;
diff --git a/src/cmaclient.cpp b/src/cmaclient.cpp
index daa3efd..b843230 100644
--- a/src/cmaclient.cpp
+++ b/src/cmaclient.cpp
@@ -21,7 +21,7 @@
#include "capability.h"
#include "avdecoder.h"
#include "cmaevent.h"
-#include "utils.h"
+#include "cmautils.h"
#include
#include
@@ -45,8 +45,8 @@ bool CmaClient::in_progress = false;
CmaClient *CmaClient::this_object = NULL;
-CmaClient::CmaClient(QObject *parent) :
- QObject(parent)
+CmaClient::CmaClient(Database *db, QObject *parent) :
+ QObject(parent), m_db(db)
{
this_object = this;
}
@@ -188,7 +188,7 @@ void CmaClient::enterEventLoop(vita_device_t *device)
qDebug("Starting event loop");
- CmaEvent eventLoop (device);
+ CmaEvent eventLoop(m_db, device);
QThread thread;
thread.setObjectName("event_thread");
diff --git a/src/cmaclient.h b/src/cmaclient.h
index d2023f8..4fd0700 100644
--- a/src/cmaclient.h
+++ b/src/cmaclient.h
@@ -20,9 +20,8 @@
#ifndef CMACLIENT_H
#define CMACLIENT_H
-#include "database.h"
+#include "qlistdb.h"
#include "cmaevent.h"
-#include "cmaobject.h"
#include "cmabroadcast.h"
#include
@@ -36,7 +35,7 @@ class CmaClient : public QObject
{
Q_OBJECT
public:
- explicit CmaClient(QObject *parent = 0);
+ explicit CmaClient(Database *db, QObject *parent = 0);
static bool isRunning();
void launch();
@@ -56,6 +55,7 @@ private:
static void registrationComplete();
CmaBroadcast broadcast;
+ Database *m_db;
static QString tempOnlineId;
//TODO: move all the control variables to the client manager class
diff --git a/src/cmaevent.cpp b/src/cmaevent.cpp
index add1f2d..d504b67 100644
--- a/src/cmaevent.cpp
+++ b/src/cmaevent.cpp
@@ -18,24 +18,22 @@
*/
#include "cmaevent.h"
-#include "utils.h"
+#include "cmautils.h"
#include
#include
#include
#include
#include
+#include
#include
-#include
-
-Database *CmaEvent::db = NULL;
QFile *CmaEvent::m_file = NULL;
-metadata_t CmaEvent::g_thumbmeta = {0, 0, 0, NULL, NULL, 0, 0, 0, Thumbnail, {{17, 240, 136, 0, 1, 1.0f, 2}}, NULL};
+static metadata_t g_thumbmeta = {0, 0, 0, NULL, NULL, 0, 0, 0, Thumbnail, {{17, 240, 136, 0, 1, 1.0f, 2}}, NULL};
-CmaEvent::CmaEvent(vita_device_t *s_device) :
- device(s_device), is_active(true)
+CmaEvent::CmaEvent(Database *db, vita_device_t *s_device) :
+ device(s_device), m_db(db), is_active(true)
{
}
@@ -149,15 +147,13 @@ void CmaEvent::processEvent()
qDebug("Ended event, code: 0x%x, id: %d", t_event.Code, t_event.Param1);
}
-quint16 CmaEvent::processAllObjects(CMAObject *parent, quint32 handle)
+quint16 CmaEvent::processAllObjects(metadata_t &parent_metadata, quint32 handle)
{
- qDebug("Called %s, handle: %d, parent name: %s", Q_FUNC_INFO, handle, parent->metadata.name);
+ qDebug("Called %s, handle: %d, parent name: %s", Q_FUNC_INFO, handle, parent_metadata.name);
char *name;
- int fileType = -1;
int dataType;
-
- uint32_t *p_handles;
+ quint32 *p_handles;
unsigned int p_len;
if(VitaMTP_GetObject_Info(device, handle, &name, &dataType) != PTP_RC_OK) {
@@ -174,15 +170,17 @@ quint16 CmaEvent::processAllObjects(CMAObject *parent, quint32 handle)
}
- CMAObject *object = db->pathToObject(name, parent->metadata.ohfi);
+ int ohfi = m_db->getPathId(name, parent_metadata.ohfi);
- if(object) {
- qDebug("Deleting %s", object->path.toStdString().c_str());
- removeRecursively(object->path);
- db->remove(object);
+ if(ohfi > 0) {
+ const QString fullpath = m_db->getAbsolutePath(ohfi);
+ qDebug() << "Deleting" << fullpath;
+ removeRecursively(fullpath);
+ m_db->deleteEntry(ohfi);
}
- QDir dir(parent->path);
+ QString fullpath = m_db->getAbsolutePath(parent_metadata.ohfi);
+ QDir dir(fullpath);
if(dataType & Folder) {
if(!dir.mkpath(name)) {
@@ -205,33 +203,24 @@ quint16 CmaEvent::processAllObjects(CMAObject *parent, quint32 handle)
VitaMTP_GetObject_Callback(device, handle, &size, CmaEvent::writeCallback);
m_file->close();
delete m_file;
-
- // get the root ohfi type
- CMAObject *root_obj = parent;
- while(root_obj->parent) {
- root_obj = root_obj->parent;
- }
-
- fileType = Database::checkFileType(dir.absoluteFilePath(name), root_obj->metadata.ohfi);
}
}
- QFileInfo info(dir, name);
- object = new CMAObject(parent);
- object->initObject(info, fileType);
- object->metadata.handle = handle;
- db->append(parent->metadata.ohfi, object);
+ int new_ohfi = m_db->insertObjectEntry(fullpath, name, parent_metadata.ohfi);
+ qDebug("Added object %s with OHFI %i to database", name, new_ohfi);
free(name);
- qDebug("Added object %s with OHFI %i to database", object->metadata.path, object->metadata.ohfi);
-
if(dataType & Folder) {
+ metadata_t folder_metadata;
+ m_db->getObjectMetadata(new_ohfi, folder_metadata);
+ folder_metadata.handle = handle;
+
for(unsigned int i = 0; i < p_len; i++) {
- quint16 ret = processAllObjects(object, p_handles[i]);
+ quint16 ret = processAllObjects(folder_metadata, p_handles[i]);
if(ret != PTP_RC_OK) {
- qDebug("Deleteting object with OHFI %d", object->metadata.ohfi);
- db->remove(object);
+ qDebug("Deleteting object with OHFI %d", new_ohfi);
+ m_db->deleteEntry(new_ohfi);
return ret;
}
}
@@ -251,17 +240,17 @@ void CmaEvent::vitaEventGetTreatObject(vita_event_t *event, int eventId)
return;
}
- QMutexLocker locker(&db->mutex);
+ QMutexLocker locker(&m_db->mutex);
- CMAObject *parent = db->ohfiToObject(treatObject.ohfiParent);
+ metadata_t metadata;
- if(parent == NULL) {
+ if(!m_db->getObjectMetadata(treatObject.ohfiParent, metadata)) {
qWarning("Cannot find parent OHFI %d", treatObject.ohfiParent);
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI);
return;
}
- VitaMTP_ReportResult(device, eventId, processAllObjects(parent, treatObject.handle));
+ VitaMTP_ReportResult(device, eventId, processAllObjects(metadata, treatObject.handle));
}
void CmaEvent::vitaEventSendCopyConfirmationInfo(vita_event_t *event, int eventId)
@@ -274,23 +263,22 @@ void CmaEvent::vitaEventSendCopyConfirmationInfo(vita_event_t *event, int eventI
return;
}
- QMutexLocker locker(&db->mutex);
+ QMutexLocker locker(&m_db->mutex);
- quint64 size = 0;
+ qint64 size;
+ qint64 total_size = 0;
for(quint32 i = 0; i < info->count; i++) {
- CMAObject *object;
-
- if((object = db->ohfiToObject(info->ohfi[i])) == NULL) {
+ if((size = m_db->getObjectSize(info->ohfi[i])) < 0) {
qWarning("Cannot find OHFI %d", info->ohfi[i]);
free(info);
return;
}
- size += object->metadata.size;
+ total_size += size;
}
- if(VitaMTP_SendCopyConfirmationInfo(device, eventId, info, size) != PTP_RC_OK) {
+ if(VitaMTP_SendCopyConfirmationInfo(device, eventId, info, total_size) != PTP_RC_OK) {
qWarning("Error sending copy confirmation");
} else {
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
@@ -309,21 +297,20 @@ void CmaEvent::vitaEventSendObjectMetadataItems(vita_event_t *event, int eventId
return;
}
- QMutexLocker locker(&db->mutex);
+ QMutexLocker locker(&m_db->mutex);
- CMAObject *object = db->ohfiToObject(ohfi);
+ metadata_t metadata;
- if(object == NULL) {
+ if(!m_db->getObjectMetadata(ohfi, metadata)) {
qWarning("Cannot find OHFI %d in database", ohfi);
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI);
return;
}
- metadata_t *metadata = &object->metadata;
- metadata->next_metadata = NULL;
- qDebug("Sending metadata for OHFI %d (%s)", ohfi, metadata->path);
+ metadata.next_metadata = NULL;
+ qDebug("Sending metadata for OHFI %d (%s)", ohfi, metadata.path);
- quint16 ret = VitaMTP_SendObjectMetadata(device, eventId, metadata);
+ quint16 ret = VitaMTP_SendObjectMetadata(device, eventId, &metadata);
if(ret != PTP_RC_OK) {
qWarning("Error sending metadata. Code: %04X", ret);
} else {
@@ -366,10 +353,10 @@ void CmaEvent::vitaEventSendNumOfObject(vita_event_t *event, int eventId)
{
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
- QMutexLocker locker(&db->mutex);
+ QMutexLocker locker(&m_db->mutex);
- uint ohfi = event->Param2;
- int items = db->filterObjects(ohfi, NULL);
+ int ohfi = event->Param2;
+ int items = m_db->childObjectCount(ohfi);
if(VitaMTP_SendNumOfObject(device, eventId, items) != PTP_RC_OK) {
qWarning("Error occured receiving object count for OHFI parent %d", ohfi);
@@ -389,10 +376,11 @@ void CmaEvent::vitaEventSendObjectMetadata(vita_event_t *event, int eventId)
qWarning("GetBrowseInfo failed");
return;
}
- QMutexLocker locker(&db->mutex);
+ QMutexLocker locker(&m_db->mutex);
- metadata_t *meta;
- int count = db->filterObjects(browse.ohfiParent, &meta, browse.index, browse.numObjects); // if meta is null, will return empty XML
+ metadata_t *meta = NULL;
+
+ int count = m_db->getObjectMetadatas(browse.ohfiParent, &meta, browse.index, browse.numObjects); // if meta is null, will return empty XML
qDebug("Sending %i metadata filtered objects for OHFI %d", count, browse.ohfiParent);
if(VitaMTP_SendObjectMetadata(device, eventId, meta) != PTP_RC_OK) { // send all objects with OHFI parent
@@ -400,6 +388,7 @@ void CmaEvent::vitaEventSendObjectMetadata(vita_event_t *event, int eventId)
} else {
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
}
+ m_db->freeMetadata(meta);
}
void CmaEvent::vitaEventSendObject(vita_event_t *event, int eventId)
@@ -408,33 +397,31 @@ void CmaEvent::vitaEventSendObject(vita_event_t *event, int eventId)
int ohfi = event->Param2;
- QMutexLocker locker(&db->mutex);
+ QMutexLocker locker(&m_db->mutex);
qDebug("Searching object with OHFI %d", ohfi);
- Database::find_data iters;
- if(!db->find(ohfi, iters)) {
+ metadata_t *metadata = NULL;
+ if(!m_db->getObjectMetadatas(ohfi, &metadata)) {
qWarning("Failed to find OHFI %d", ohfi);
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI);
return;
}
- unsigned long len = 0;
- CMAObject *object = *iters.it;
- CMAObject *start = object;
- uint parentHandle = event->Param3;
- bool send_folder = object->metadata.dataType & Folder;
- uint handle;
+ metadata_t *start = metadata;
+ quint32 parentHandle = event->Param3;
+ bool send_folder = metadata->dataType & Folder;
+ quint32 handle;
do {
- len = object->metadata.size;
- m_file = new QFile(object->path);
+ unsigned long len = metadata->size;
+ m_file = new QFile(m_db->getAbsolutePath(metadata->ohfi));
// read the file to send if it's not a directory
// if it is a directory, data and len are not used by VitaMTP
- if(object->metadata.dataType & File) {
+ if(metadata->dataType & File) {
if(!m_file->open(QIODevice::ReadOnly)) {
- qWarning("Failed to read %s", object->path.toStdString().c_str());
+ qWarning() << "Failed to read" << m_file->fileName();
delete m_file;
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Not_Exist_Object);
return;
@@ -445,18 +432,20 @@ void CmaEvent::vitaEventSendObject(vita_event_t *event, int eventId)
// we know the parent has to be before the current node
// the first time this is called, parentHandle is left untouched
- if(start != object) {
- parentHandle = object->parent->metadata.handle;
+ if(start != metadata) {
+ metadata_t parent_metadata;
+ m_db->getObjectMetadata(metadata->ohfiParent, parent_metadata);
+ parentHandle = parent_metadata.handle;
}
// send the data over
- qDebug("Sending %s of %lu bytes to device", object->metadata.name, len);
+ qDebug("Sending %s of %lu bytes to device", metadata->name, len);
qDebug("OHFI %d with handle 0x%08X", ohfi, parentHandle);
VitaMTP_RegisterCancelEventId(eventId);
- quint16 ret = VitaMTP_SendObject_Callback(device, &parentHandle, &handle, &object->metadata, &CmaEvent::readCallback);
+ quint16 ret = VitaMTP_SendObject_Callback(device, &parentHandle, &handle, metadata, &CmaEvent::readCallback);
if(ret != PTP_RC_OK) {
- qWarning("Sending of %s failed. Code: %04X", object->metadata.name, ret);
+ qWarning("Sending of %s failed. Code: %04X", metadata->name, ret);
if(ret == PTP_ERROR_CANCEL) {
VitaMTP_ReportResult(device, eventId, PTP_RC_GeneralError);
}
@@ -465,9 +454,9 @@ void CmaEvent::vitaEventSendObject(vita_event_t *event, int eventId)
return;
}
- object->metadata.handle = handle;
+ metadata->handle = handle;
- if(object->metadata.dataType & File) {
+ if(metadata->dataType & File) {
m_file->close();
delete m_file;
}
@@ -477,9 +466,11 @@ void CmaEvent::vitaEventSendObject(vita_event_t *event, int eventId)
break;
}
- object = *++iters.it;
+ metadata = metadata->next_metadata;
- } while(iters.it != iters.end && object->metadata.ohfiParent >= OHFI_OFFSET); // get everything under this "folder"
+ } while(metadata && metadata->ohfiParent >= OHFI_BASE_VALUE); // get everything under this "folder"
+
+ m_db->freeMetadata(start);
VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, handle);
@@ -571,20 +562,22 @@ void CmaEvent::vitaEventSendObjectStatus(vita_event_t *event, int eventId)
return;
}
- QMutexLocker locker(&db->mutex);
+ QMutexLocker locker(&m_db->mutex);
- CMAObject *object = db->pathToObject(objectstatus.title, objectstatus.ohfiRoot);
+ qDebug("Checking for path %s under ohfi %i", objectstatus.title, objectstatus.ohfiRoot);
+ int ohfi = m_db->getPathId(objectstatus.title, objectstatus.ohfiRoot);
- if(object == NULL) { // not in database, don't return metadata
+ if(ohfi == 0) { // not in database, don't return metadata
qDebug("Object %s not in database (OHFI: %i). Sending OK response for non-existence", objectstatus.title, objectstatus.ohfiRoot);
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
} else {
- metadata_t *metadata = &object->metadata;
- metadata->next_metadata = NULL;
- qDebug("Sending metadata for OHFI %d", object->metadata.ohfi);
+ metadata_t metadata = metadata_t();
+ m_db->getObjectMetadata(ohfi, metadata);
+ metadata.next_metadata = NULL;
+ qDebug("Sending metadata for OHFI %d", ohfi);
- if(VitaMTP_SendObjectMetadata(device, eventId, metadata) != PTP_RC_OK) {
- qWarning("Error sending metadata for %d", object->metadata.ohfi);
+ if(VitaMTP_SendObjectMetadata(device, eventId, &metadata) != PTP_RC_OK) {
+ qWarning("Error sending metadata for %d", ohfi);
} else {
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
}
@@ -597,25 +590,28 @@ void CmaEvent::vitaEventSendObjectThumb(vita_event_t *event, int eventId)
{
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
- QMutexLocker locker(&db->mutex);
+ QMutexLocker locker(&m_db->mutex);
int ohfi = event->Param2;
- CMAObject *object = db->ohfiToObject(ohfi);
+ metadata_t metadata;
- if(object == NULL) {
+ if(!m_db->getObjectMetadata(ohfi, metadata)) {
qWarning("Cannot find OHFI %d in database.", ohfi);
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI);
return;
}
- QByteArray data = getThumbnail(object->path, object->metadata.dataType, &g_thumbmeta);
+ QString fullpath = m_db->getAbsolutePath(ohfi);
+ QByteArray data = getThumbnail(fullpath, metadata.dataType, &g_thumbmeta);
if(data.size() == 0) {
- qWarning("Cannot find/read thumbnail for %s", object->path.toStdString().c_str());
+ qWarning() << "Cannot find/read thumbnail for" << fullpath;
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Data);
return;
}
+ //FIXME: remove this after fixing vitamtp
+
// workaround for the vitamtp locale bug
char *locale = strdup(setlocale(LC_ALL, NULL));
setlocale(LC_ALL, "C");
@@ -635,20 +631,21 @@ void CmaEvent::vitaEventDeleteObject(vita_event_t *event, int eventId)
{
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
- QMutexLocker locker(&db->mutex);
+ QMutexLocker locker(&m_db->mutex);
int ohfi = event->Param2;
- CMAObject *object = db->ohfiToObject(ohfi);
+ metadata_t metadata;
- if(object == NULL) {
+ if(!m_db->getObjectMetadata(ohfi, metadata)) {
qWarning("OHFI %d not found", ohfi);
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI);
return;
}
- qDebug("Deleting %s, OHFI: %i", object->metadata.path, object->metadata.ohfi);
- removeRecursively(object->path);
- db->remove(object);
+ QString fullpath = m_db->getAbsolutePath(ohfi);
+ qDebug() << QString("Deleting %1, OHFI: %2").arg(fullpath, QString::number(ohfi));
+ removeRecursively(fullpath);
+ m_db->deleteEntry(ohfi);
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
}
@@ -668,7 +665,7 @@ void CmaEvent::vitaEventGetSettingInfo(vita_event_t *event, int eventId)
QSettings settings;
if(settings.value("lastAccountId").toString() != settingsinfo->current_account.accountId) {
- db->setUUID(settingsinfo->current_account.accountId);
+ m_db->setUUID(settingsinfo->current_account.accountId);
// set the database to be updated ASAP
emit refreshDatabase();
}
@@ -725,26 +722,28 @@ void CmaEvent::vitaEventSendPartOfObject(vita_event_t *event, int eventId)
return;
}
- QMutexLocker locker(&db->mutex);
+ QMutexLocker locker(&m_db->mutex);
- CMAObject *object = db->ohfiToObject(part_init.ohfi);
+ QString fullpath = m_db->getAbsolutePath(part_init.ohfi);
- if(object == NULL) {
+ if(fullpath.isNull()) {
qWarning("Cannot find object for OHFI %d", part_init.ohfi);
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Context);
return;
}
- QFile file(object->path);
+ QFile file(fullpath);
if(!file.open(QIODevice::ReadOnly)) {
- qWarning("Cannot read %s", object->path.toStdString().c_str());
+ qWarning() << "Cannot read" << fullpath;
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Not_Exist_Object);
return;
} else {
file.seek(part_init.offset);
QByteArray data = file.read(part_init.size);
- qDebug("Sending %s at file offset %" PRIu64" for %" PRIu64" bytes", object->metadata.path, part_init.offset, part_init.size);
+ qDebug() << QString("Sending %1 at file offset %2 for %3 bytes").arg(
+ fullpath, QString::number(part_init.offset), QString::number(part_init.size)
+ );
if(VitaMTP_SendPartOfObject(device, eventId, (unsigned char *)data.data(), data.size()) != PTP_RC_OK) {
qWarning("Failed to send part of object OHFI %d", part_init.ohfi);
@@ -765,12 +764,12 @@ void CmaEvent::vitaEventOperateObject(vita_event_t *event, int eventId)
return;
}
- QMutexLocker locker(&db->mutex);
+ QMutexLocker locker(&m_db->mutex);
- CMAObject *root = db->ohfiToObject(operateobject.ohfi);
+ QString fullpath = m_db->getAbsolutePath(operateobject.ohfi);
// end for renaming only
- if(root == NULL) {
+ if(fullpath.isNull()) {
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Not_Exist_Object);
return;
}
@@ -779,64 +778,46 @@ void CmaEvent::vitaEventOperateObject(vita_event_t *event, int eventId)
case VITA_OPERATE_CREATE_FOLDER: {
qDebug("Operate command %d: Create folder %s", operateobject.cmd, operateobject.title);
- QDir dir(root->path);
+ QDir dir(fullpath);
if(!dir.mkdir(operateobject.title)) {
qWarning("Unable to create temporary folder: %s", operateobject.title);
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Operate_Object);
} else {
- CMAObject *newobj = new CMAObject(root);
- newobj->initObject(QFileInfo(dir, operateobject.title));
- db->append(operateobject.ohfi, newobj);
- qDebug("Created folder %s with OHFI %d under parent %s", newobj->metadata.path, newobj->metadata.ohfi, root->metadata.path);
- VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, newobj->metadata.ohfi);
+ int new_ohfi = m_db->insertObjectEntry(fullpath, operateobject.title, operateobject.ohfi);
+ qDebug("Created folder %s with OHFI %d", operateobject.title, new_ohfi);
+ VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, new_ohfi);
}
break;
}
case VITA_OPERATE_CREATE_FILE: {
qDebug("Operate command %d: Create file %s", operateobject.cmd, operateobject.title);
- QFile file(root->path + QDir::separator() + operateobject.title);
+ QFile file(fullpath + QDir::separator() + operateobject.title);
if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qWarning("Unable to create temporary file: %s", operateobject.title);
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Operate_Object);
} else {
- CMAObject *newobj = new CMAObject(root);
- newobj->initObject(file);
- db->append(root->metadata.ohfi, newobj);
- qDebug("Created file %s with OHFI %d under parent %s", newobj->metadata.path, newobj->metadata.ohfi, root->metadata.path);
- VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, newobj->metadata.ohfi);
+ int new_ohfi = m_db->insertObjectEntry(fullpath, operateobject.title, operateobject.ohfi);
+ //qDebug("Created file %s with OHFI %d under parent %s", newobj->metadata.path, new_ohfi, root->metadata.path);
+ VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, new_ohfi);
}
break;
}
case VITA_OPERATE_RENAME: {
- qDebug("Operate command %d: Rename %s to %s", operateobject.cmd, root->metadata.name, operateobject.title);
+ //qDebug("Operate command %d: Rename %s to %s", operateobject.cmd, root->metadata.name, operateobject.title);
- QString oldpath = root->path;
- QString oldname = root->metadata.name;
-
- //rename the current object
- root->rename(operateobject.title);
- Database::find_data iters;
- db->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();
- }
- }
+ m_db->renameObject(operateobject.ohfi, operateobject.title);
+ QString newpath = m_db->getAbsolutePath(operateobject.ohfi);
// rename in filesystem
- if(!QFile(oldpath).rename(root->path)) {
- qWarning("Unable to rename %s to %s", oldname.toStdString().c_str(), operateobject.title);
+ if(!QFile(fullpath).rename(newpath)) {
+ qWarning("Unable to rename %s to %s", fullpath.toLocal8Bit().constData(), operateobject.title);
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Operate_Object);
break;
}
- qDebug("Renamed OHFI %d from %s to %s", root->metadata.ohfi, oldname.toStdString().c_str(), root->metadata.name);
- VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, root->metadata.ohfi);
+ qDebug("Renamed OHFI %d from %s to %s", operateobject.ohfi, fullpath.toLocal8Bit().constData(), newpath.toLocal8Bit().constData());
+ VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, operateobject.ohfi);
break;
}
@@ -861,27 +842,33 @@ void CmaEvent::vitaEventGetPartOfObject(vita_event_t *event, int eventId)
return;
}
- QMutexLocker locker(&db->mutex);
- CMAObject *object = db->ohfiToObject(part_init.ohfi);
+ QMutexLocker locker(&m_db->mutex);
+ QString fullpath = m_db->getAbsolutePath(part_init.ohfi);
- if(object == NULL) {
+ if(fullpath.isNull()) {
qWarning("Cannot find OHFI %d", part_init.ohfi);
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI);
free(data);
return;
}
- qDebug("Receiving %s at offset %" PRIu64" for %" PRIu64" bytes", object->metadata.path, part_init.offset, part_init.size);
+ qDebug() << QString("Receiving %1 at offset %2 for %3 bytes").arg(
+ fullpath, QString::number(part_init.offset), QString::number(part_init.size)
+ );
- QFile file(object->path);
+ QFile file(fullpath);
if(!file.open(QIODevice::ReadWrite)) {
- qWarning("Cannot write to file %s", object->path.toStdString().c_str());
+ qWarning() << "Cannot write to file" << fullpath;
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Permission);
} else {
file.seek(part_init.offset);
file.write((const char *)data, part_init.size);
- object->updateObjectSize(part_init.size);
- qDebug("Written %" PRIu64" bytes to %s at offset %" PRIu64, part_init.size, object->path.toStdString().c_str(), part_init.offset);
+ m_db->setObjectSize(part_init.ohfi, part_init.size);
+
+ qDebug() << QString("Written %1 bytes to %2 at offset %3").arg(
+ QString::number(part_init.size), fullpath, QString::number(part_init.offset)
+ );
+
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
}
@@ -892,23 +879,23 @@ void CmaEvent::vitaEventSendStorageSize(vita_event_t *event, int eventId)
{
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
- QMutexLocker locker(&db->mutex);
+ QMutexLocker locker(&m_db->mutex);
int ohfi = event->Param2;
- CMAObject *object = db->ohfiToObject(ohfi);
+ QString fullpath = m_db->getAbsolutePath(ohfi);
- if(object == NULL) {
+ if(fullpath.isNull()) {
qWarning("Error: Cannot find OHFI %d", ohfi);
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI);
return;
} else {
- QFile file(object->path);
+ QFile file(fullpath);
if(!file.exists()) {
// create the directory if doesn't exist so the query don't fail
- qDebug("Creating %s", object->path.toStdString().c_str());
+ qDebug() << "Creating" << fullpath;
- if(!QDir(QDir::root()).mkpath(object->path)) {
+ if(!QDir(QDir::root()).mkpath(QDir(fullpath).absolutePath())) {
qWarning("Create directory failed");
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Permission);
return;
@@ -919,7 +906,7 @@ void CmaEvent::vitaEventSendStorageSize(vita_event_t *event, int eventId)
quint64 total;
quint64 free;
- if(!getDiskSpace(object->path, &free, &total)) {
+ if(!getDiskSpace(fullpath, &free, &total)) {
qWarning("Cannot get disk space");
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Permission);
return;
@@ -946,14 +933,14 @@ void CmaEvent::vitaEventCheckExistance(vita_event_t *event, int eventId)
return;
}
- QMutexLocker locker(&db->mutex);
+ QMutexLocker locker(&m_db->mutex);
- CMAObject *object = db->pathToObject(existance.name, 0);
+ int ohfi = m_db->getPathId(existance.name, 0);
- if(object == NULL) {
+ if(ohfi == 0) {
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Different_Object);
} else {
- VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_VITA_Same_Object, object->metadata.ohfi);
+ VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_VITA_Same_Object, ohfi);
}
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
diff --git a/src/cmaevent.h b/src/cmaevent.h
index e2f1d98..1119d03 100644
--- a/src/cmaevent.h
+++ b/src/cmaevent.h
@@ -20,10 +20,10 @@
#ifndef CMAEVENT_H
#define CMAEVENT_H
-#include "cmaobject.h"
#include "database.h"
#include "httpdownloader.h"
+#include
#include
#include
#include
@@ -34,15 +34,11 @@ class CmaEvent : public QObject
{
Q_OBJECT
public:
- explicit CmaEvent(vita_device_t *s_device);
-
+ explicit CmaEvent(Database *db, vita_device_t *s_device);
void vitaEventCancelTask(vita_event_t *event, int eventId);
- // don't make the db reference static
- static Database *db;
-
private:
- uint16_t processAllObjects(CMAObject *parent, uint32_t handle);
+ 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);
@@ -73,13 +69,14 @@ private:
vita_device_t *device;
vita_event_t t_event;
+ Database *m_db;
+
// control variables
bool is_active;
QMutex mutex;
QMutex active;
QSemaphore sema;
- static metadata_t g_thumbmeta;
static QFile *m_file;
signals:
diff --git a/src/cmaobject.cpp b/src/cmaobject.cpp
index 9236c55..6e0a3fc 100644
--- a/src/cmaobject.cpp
+++ b/src/cmaobject.cpp
@@ -20,7 +20,8 @@
#include "cmaobject.h"
#include "sforeader.h"
#include "avdecoder.h"
-#include "utils.h"
+#include "database.h"
+#include "cmautils.h"
#include
#include
@@ -29,24 +30,6 @@
int CMAObject::ohfi_count = OHFI_OFFSET;
-const CMAObject::file_type CMAObject::audio_list[] = {
- {"mp3", FILE_FORMAT_MP3, CODEC_TYPE_MP3},
- {"mp4", FILE_FORMAT_MP4, CODEC_TYPE_AAC},
- {"wav", FILE_FORMAT_WAV, CODEC_TYPE_PCM}
-};
-
-const CMAObject::file_type CMAObject::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 char *CMAObject::video_list[] = {"mp4"};
-
CMAObject::CMAObject(CMAObject *obj_parent) :
parent(obj_parent), metadata()
{
@@ -126,48 +109,6 @@ void CMAObject::loadSfoMetadata(const QString &path)
}
}
-void CMAObject::loadMusicMetadata(const QString &path)
-{
- AVDecoder decoder;
- bool skipMetadata = QSettings().value("skipMetadata", false).toBool();
-
- if(!skipMetadata && decoder.open(path)) {
- decoder.getAudioMetadata(metadata);
- } else {
- metadata.data.music.album = strdup(parent->metadata.name ? parent->metadata.name : "");
- metadata.data.music.artist = strdup("");
- metadata.data.music.title = strdup(metadata.name);
- }
-}
-
-void CMAObject::loadVideoMetadata(const QString &path)
-{
- 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 CMAObject::loadPhotoMetadata(const QString &path)
-{
- 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);
-}
-
void CMAObject::initObject(const QFileInfo &file, int file_type)
{
metadata.name = strdup(file.fileName().toUtf8().data());
@@ -199,7 +140,7 @@ void CMAObject::initObject(const QFileInfo &file, int file_type)
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[file_type].file_codec;
- loadMusicMetadata(file.absoluteFilePath());
+ 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().toTime_t();
@@ -209,7 +150,7 @@ void CMAObject::initObject(const QFileInfo &file, int file_type)
metadata.data.video.numTracks = 1;
metadata.data.video.tracks = new media_track();
metadata.data.video.tracks->type = VITA_TRACK_TYPE_VIDEO;
- loadVideoMetadata(file.absoluteFilePath());
+ Database::loadVideoMetadata(file.absoluteFilePath(), metadata);
} else if(MASK_SET(metadata.dataType, Photo | File)) {
if(file_type < 0) {
@@ -225,7 +166,7 @@ void CMAObject::initObject(const QFileInfo &file, int file_type)
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[file_type].file_codec;
- loadPhotoMetadata(file.absoluteFilePath());
+ Database::loadPhotoMetadata(file.absoluteFilePath(), metadata);
}
path = file.absoluteFilePath();
@@ -240,15 +181,6 @@ void CMAObject::initObject(const QFileInfo &file, int file_type)
updateObjectSize(file.size());
}
-bool CMAObject::removeReferencedObject()
-{
- if(metadata.dataType & Folder) {
- return removeRecursively(path);
- } else {
- return QFile::remove(path);
- }
-}
-
void CMAObject::updateObjectSize(qint64 size)
{
if(parent) {
diff --git a/src/cmaobject.h b/src/cmaobject.h
index eb49b8c..4f36a07 100644
--- a/src/cmaobject.h
+++ b/src/cmaobject.h
@@ -27,26 +27,6 @@
#define OHFI_OFFSET 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
-
class CMAObject
{
public:
@@ -54,7 +34,6 @@ public:
~CMAObject();
void refreshPath();
- bool removeReferencedObject();
void rename(const QString &name);
void updateObjectSize(qint64 size);
bool hasParent(const CMAObject *obj);
@@ -72,16 +51,6 @@ public:
ohfi_count = OHFI_OFFSET;
}
- typedef struct {
- const char *file_ext;
- int file_format;
- int file_codec;
- } file_type;
-
- static const file_type audio_list[3];
- static const file_type photo_list[7];
- static const char *video_list[1];
-
QString path;
CMAObject *parent;
metadata_t metadata;
@@ -91,9 +60,6 @@ protected:
private:
void loadSfoMetadata(const QString &path);
- void loadMusicMetadata(const QString &path);
- void loadVideoMetadata(const QString &path);
- void loadPhotoMetadata(const QString &path);
};
#endif // CMAOBJECT_H
diff --git a/src/utils.cpp b/src/cmautils.cpp
similarity index 84%
rename from src/utils.cpp
rename to src/cmautils.cpp
index 22059f0..0275180 100644
--- a/src/utils.cpp
+++ b/src/cmautils.cpp
@@ -17,7 +17,7 @@
* along with this program. If not, see .
*/
-#include "utils.h"
+#include "cmautils.h"
#include "avdecoder.h"
#include
@@ -52,31 +52,37 @@ bool getDiskSpace(const QString &dir, quint64 *free, quint64 *total)
return false;
}
-bool removeRecursively(const QString &dirName)
+bool removeRecursively(const QString &path)
{
+ QFileInfo info(path);
+
+ if(info.isDir()) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
- return QDir(dirName).removeRecursively();
+ return QDir(path).removeRecursively();
#else
- bool result = false;
- QDir dir(dirName);
+ bool result = false;
+ QDir dir(path);
- if(dir.exists(dirName)) {
- Q_FOREACH(QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) {
- if(info.isDir()) {
- result = removeRecursively(info.absoluteFilePath());
- } else {
- result = QFile::remove(info.absoluteFilePath());
- }
+ if(dir.exists(path)) {
+ Q_FOREACH(QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) {
+ if(info.isDir()) {
+ result = removeRecursively(info.absoluteFilePath());
+ } else {
+ result = QFile::remove(info.absoluteFilePath());
+ }
- if(!result) {
- return result;
+ if(!result) {
+ return result;
+ }
}
+ result = dir.rmdir(path);
}
- result = dir.rmdir(dirName);
- }
- return result;
+ return result;
#endif
+ } else {
+ return QFile::remove(path);
+ }
}
QByteArray findFolderAlbumArt(const QString path, metadata_t *metadata)
@@ -156,7 +162,7 @@ QByteArray getThumbnail(const QString &path, DataType type, metadata_t *metadata
return data;
}
-QString readable_size(quint64 size, bool use_gib)
+QString readable_size(qint64 size, bool use_gib)
{
QStringList list;
list << "KiB" << "MiB";
diff --git a/src/utils.h b/src/cmautils.h
similarity index 93%
rename from src/utils.h
rename to src/cmautils.h
index 8d8deba..a69af6d 100644
--- a/src/utils.h
+++ b/src/cmautils.h
@@ -45,8 +45,8 @@ public:
};
#endif
-bool removeRecursively(const QString &dirName);
-QString readable_size(quint64 size, bool use_gib = false);
+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);
diff --git a/src/database.cpp b/src/database.cpp
index 448a3dd..ec6a33a 100644
--- a/src/database.cpp
+++ b/src/database.cpp
@@ -1,89 +1,45 @@
-/*
- * 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 .
- */
-
#include "database.h"
-#include "cmaobject.h"
+#include "avdecoder.h"
-#include
-#include
#include
-#include
#include
-#include
-Database::Database() :
+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 *parent) :
+ QObject(parent),
mutex(QMutex::Recursive)
{
- QString uuid = QSettings().value("lastAccountId", "ffffffffffffffff").toString();
- CMARootObject::uuid = uuid;
- thread = new QThread();
- timer = new QTimer();
- moveToThread(thread);
- thread->start();
-
- timer->setInterval(0);
- timer->setSingleShot(true);
- connect(timer, SIGNAL(timeout()), this, SLOT(process()));
}
-
-Database::~Database()
-{
- destroy();
- timer->stop();
- delete timer;
- thread->quit();
- thread->wait();
- delete thread;
-}
-
-void Database::setUUID(const QString uuid)
-{
- CMARootObject::uuid = uuid;
- QSettings().setValue("lastAccountId", uuid);
-}
-
-bool Database::reload(bool &prepared)
-{
- if(mutex.tryLock()) {
- if(CMARootObject::uuid != "ffffffffffffffff") {
- timer->start();
- prepared = true;
- } else {
- mutex.unlock();
- prepared = false;
- return false;
- }
- return true;
- } else {
- return false;
- }
-}
-
+#include
void Database::process()
{
- destroy();
+ qDebug("Starting database_thread: 0x%016" PRIxPTR, (uintptr_t)QThread::currentThreadId());
+ clear();
cancel_operation = false;
int count = create();
cancel_operation = false;
- qDebug("Added %i entries to the database", count);
+ qDebug("Total entries added to the database: %i", count);
if(count < 0) {
- destroy();
+ clear();
}
emit updated(count);
mutex.unlock();
@@ -101,403 +57,26 @@ bool Database::continueOperation()
return !cancel_operation;
}
-int Database::create()
-{
- int total_objects = 0;
- //QMutexLocker locker(&mutex);
- const int ohfi_array[] = { VITA_OHFI_MUSIC, VITA_OHFI_PHOTO, VITA_OHFI_VIDEO,
- 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]);
-
- switch(ohfi_array[i]) {
- case VITA_OHFI_MUSIC:
- obj->initObject(settings.value("musicPath").toString());
- break;
-
- case VITA_OHFI_PHOTO:
- obj->initObject(settings.value("photoPath").toString());
- break;
-
- case VITA_OHFI_VIDEO:
- obj->initObject(settings.value("videoPath").toString());
- 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());
- }
-
- root_list list;
- list << obj;
- emit directoryAdded(obj->path);
- int dir_count = recursiveScanRootDirectory(list, obj, ohfi_array[i]);
-
- if(dir_count < 0) {
- return -1;
- }
-
- total_objects += dir_count;
- object_list[ohfi_array[i]] = list;
- }
- return total_objects;
-}
-
-CMAObject *Database::getParent(CMAObject *last_dir, const QString ¤t_path)
-{
- while(last_dir && current_path != last_dir->path) {
- last_dir = last_dir->parent;
- }
-
- return last_dir;
-}
-
-int Database::scanRootDirectory(root_list &list, int ohfi_type)
-{
- int file_type = -1;
- int total_objects = 0;
- CMAObject *last_dir = list.first();
- QDir dir(last_dir->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((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, 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 Database::recursiveScanRootDirectory(root_list &list, CMAObject *parent, int ohfi_type)
-{
- int file_type = -1;
- int total_objects = 0;
-
- QDir dir(parent->path);
- dir.setSorting(QDir::Name | QDir::DirsFirst);
- QFileInfoList qsl = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot);
-
- foreach(const QFileInfo &info, qsl) {
-
- if(!continueOperation()) {
- return -1;
- }
-
- if(info.isFile() && (file_type = checkFileType(info.absoluteFilePath(), ohfi_type)) < 0) {
- //qDebug("Excluding %s from database", info.absoluteFilePath().toStdString().c_str());>
- } else {
- CMAObject *obj = new CMAObject(parent);
- obj->initObject(info, 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->path);
- total_objects += recursiveScanRootDirectory(list, obj, ohfi_type);
- } else {
- total_objects++;
- }
- }
- }
-
- return total_objects;
-}
-
-void Database::destroy()
-{
- //QMutexLocker locker(&mutex);
-
- for(map_list::iterator root = object_list.begin(); root != object_list.end(); ++root) {
- CMARootObject *first = static_cast((*root).takeFirst());
- delete first;
- qDeleteAll(*root);
- }
-
- object_list.clear();
-}
-
-bool Database::removeInternal(root_list &list, const CMAObject *obj)
-{
- bool found = false;
- QList::iterator it = list.begin();
-
- while(it != list.end()) {
- if(!found && (*it) == obj) {
- // update the size of the parent objects
- (*it)->updateObjectSize(-(*it)->metadata.size);
- it = list.erase(it);
- found = true;
- } else if(found && (*it)->metadata.ohfiParent == obj->metadata.ohfi) {
- it = list.erase(it);
- } else {
- ++it;
- }
- }
-
- return found;
-}
-
-bool Database::remove(const CMAObject *obj, int ohfi_root)
-{
- QMutexLocker locker(&mutex);
-
- if(ohfi_root) {
- return removeInternal(object_list[ohfi_root], obj);
- } else {
- for(map_list::iterator root = object_list.begin(); root != object_list.end(); ++root) {
- if(removeInternal(*root, obj)) {
- return true;
- }
- }
- }
- return false;
-}
-
-bool Database::lessThanComparator(const CMAObject *a, const CMAObject *b)
-{
- return a->metadata.ohfi < b->metadata.ohfi;
-}
-
-bool Database::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 Database::findInternal(const root_list &list, int ohfi, find_data &data)
-{
- if(hasFilter(static_cast(list.first()), ohfi)) {
- data.it = list.begin();
- } else {
- CMAObject obj;
- obj.setOhfi(ohfi);
- data.it = qBinaryFind(list.begin(), list.end(), &obj, Database::lessThanComparator);
- }
- data.end = list.end();
- return data.it != data.end;
-}
-
-bool Database::find(int ohfi, Database::find_data &data)
-{
- QMutexLocker locker(&mutex);
-
- for(map_list::iterator root = object_list.begin(); root != object_list.end(); ++root) {
- if(findInternal(*root, ohfi, data)) {
- return true;
- }
- }
- return false;
-}
-
-void Database::append(int parent_ohfi, CMAObject *object)
-{
- QMutexLocker locker(&mutex);
- CMAObject parent;
- parent.setOhfi(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, Database::lessThanComparator);
-
- if(it != cat_list->end()) {
- cat_list->append(object);
- break;
- }
- }
-}
-
-CMAObject *Database::ohfiToObject(int ohfi)
-{
- QMutexLocker locker(&mutex);
- find_data data;
- return find(ohfi, data) ? *data.it : NULL;
-}
-
-CMAObject *Database::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;
-}
-
-CMAObject *Database::pathToObject(const char *path, int ohfiRoot)
-{
- QMutexLocker locker(&mutex);
-
- for(map_list::iterator root = object_list.begin(); root != object_list.end(); ++root) {
-
- if(ohfiRoot && (*root).first()->metadata.ohfi != ohfiRoot) {
- continue;
- }
- CMAObject *obj = pathToObjectInternal(*root, path);
-
- if(obj) {
- return obj;
- }
- }
- return NULL;
-}
-
-int Database::acceptFilteredObject(const CMAObject *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(type == (VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ARTISTS)) {
- // unimplemented
- return 0;
- } else if(type == (VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_GENRES)) {
- // unimplemented
- return 0;
- } else if(type == (VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_PLAYLISTS)) {
- // 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 = (parent->metadata.ohfi == current->metadata.ohfiParent);
- }
-
- // TODO: Support other filter types
- return result;
-}
-
-void Database::dumpMetadataList(const metadata_t *p_head)
-{
- while(p_head) {
- qDebug("Metadata: %s with OHFI %d", p_head->name, p_head->ohfi);
- p_head = p_head->next_metadata;
- }
-}
-
-int Database::filterObjects(int ohfiParent, metadata_t **p_head, int index, int max_number)
-{
- QMutexLocker locker(&mutex);
- CMARootObject *parent = static_cast(ohfiToObject(ohfiParent));
-
- if(parent == NULL) {
- return 0;
- }
-
- int type = parent->metadata.type;
-
- if(parent->metadata.ohfi < OHFI_OFFSET && parent->filters) { // if we have filters
- if(ohfiParent == parent->metadata.ohfi) { // if we are looking at root
- return parent->getFilters(p_head);
- } else { // we are looking at a filter
- for(int j = 0; j < parent->num_filters; j++) {
- if(parent->filters[j].ohfi == ohfiParent) {
- type = 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(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(p_head != NULL) {
- *p_head = temp.next_metadata;
- }
-
- return numObjects;
-}
-
int Database::checkFileType(const QString path, int ohfi_root)
{
switch(ohfi_root) {
case VITA_OHFI_MUSIC:
- for(int i = 0, max = sizeof(CMAObject::audio_list) / sizeof(CMAObject::file_type); i < max; i++) {
- if(path.endsWith(CMAObject::audio_list[i].file_ext, Qt::CaseInsensitive)) {
+ 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(CMAObject::photo_list) / sizeof(CMAObject::file_type); i < max; i++) {
- if(path.endsWith(CMAObject::photo_list[i].file_ext, Qt::CaseInsensitive)) {
+ 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(CMAObject::video_list) / sizeof(const char *); i < max; i++) {
- if(path.endsWith(CMAObject::video_list[i], Qt::CaseInsensitive)) {
+ 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;
}
}
@@ -507,3 +86,45 @@ int Database::checkFileType(const QString path, int ohfi_root)
}
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);
+}
diff --git a/src/database.h b/src/database.h
index 0f5a184..95b98f9 100644
--- a/src/database.h
+++ b/src/database.h
@@ -1,92 +1,87 @@
-/*
- * 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 .
- */
-
#ifndef DATABASE_H
#define DATABASE_H
-#include "cmarootobject.h"
-
-#include
-#include
#include
#include
-#include
#include
+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:
- typedef struct {
- QList::const_iterator it;
- QList::const_iterator end;
- } find_data;
+ explicit Database(QObject *parent = 0);
- explicit Database();
- ~Database();
+ virtual bool load() = 0;
+ virtual bool rescan() = 0;
+ virtual void setUUID(const QString &uuid) = 0;
- bool reload(bool &prepared);
- void setUUID(const QString uuid);
- void addEntries(CMAObject *root);
- CMAObject *ohfiToObject(int ohfi);
- bool find(int ohfi, find_data &data);
- void append(int parent_ohfi, CMAObject *object);
- bool remove(const CMAObject *obj, int ohfi_root = 0);
- int filterObjects(int ohfiParent, metadata_t **p_head, int index = 0, int max_number = 0);
- CMAObject *pathToObject(const char *path, int ohfiRoot);
- int acceptFilteredObject(const CMAObject *parent, const CMAObject *current, int type);
+ 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 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;
-private:
- typedef QList root_list;
- typedef QMap map_list;
-
- static const QStringList audio_types;
- static const QStringList image_types;
- static const QStringList video_types;
-
-
- int create();
- void destroy();
- 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, const CMAObject *obj);
- bool findInternal(const root_list &list, int ohfi, find_data &data);
- CMAObject *getParent(CMAObject *last_dir, const QString ¤t_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);
+protected:
bool continueOperation();
+private:
+
+ virtual void clear() = 0;
+ virtual int create() = 0;
+
// control variables
QMutex cancel;
bool cancel_operation;
- QTimer *timer;
- QThread *thread;
- map_list object_list;
-
signals:
void fileAdded(QString);
void directoryAdded(QString);
diff --git a/src/forms/backupitem.cpp b/src/forms/backupitem.cpp
index 3cb6198..8257ffb 100644
--- a/src/forms/backupitem.cpp
+++ b/src/forms/backupitem.cpp
@@ -19,7 +19,7 @@
#include "backupitem.h"
#include "ui_backupitem.h"
-#include "utils.h"
+#include "cmautils.h"
#include "dds.h"
#include
diff --git a/src/forms/backupmanagerform.cpp b/src/forms/backupmanagerform.cpp
index c5fb9ab..3f83244 100644
--- a/src/forms/backupmanagerform.cpp
+++ b/src/forms/backupmanagerform.cpp
@@ -22,7 +22,7 @@
#include "cmaobject.h"
#include "sforeader.h"
#include "confirmdialog.h"
-#include "utils.h"
+#include "cmautils.h"
#include "filterlineedit.h"
#include
@@ -32,8 +32,8 @@
#include
-BackupManagerForm::BackupManagerForm(QWidget *parent) :
- QWidget(parent),
+BackupManagerForm::BackupManagerForm(Database *db, QWidget *parent) :
+ QDialog(parent), m_db(db),
ui(new Ui::BackupManagerForm)
{
ui->setupUi(this);
@@ -64,13 +64,11 @@ void BackupManagerForm::removeEntry(BackupItem *item)
return;
}
- QMutexLocker locker(&db->mutex);
+ QMutexLocker locker(&m_db->mutex);
- CMAObject *obj = db->ohfiToObject(item->ohfi);
- if(obj) {
- obj->removeReferencedObject();
- db->remove(obj);
- }
+ 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(ui->tableWidget->cellWidget(i, 0));
@@ -80,13 +78,12 @@ void BackupManagerForm::removeEntry(BackupItem *item)
}
}
- obj = db->ohfiToObject(obj->metadata.ohfiParent);
- if(obj) {
- setBackupUsage(obj->metadata.size);
+ if(parent_ohfi > 0) {
+ setBackupUsage(m_db->getObjectSize(parent_ohfi));
}
}
-void BackupManagerForm::setBackupUsage(quint64 size)
+void BackupManagerForm::setBackupUsage(qint64 size)
{
ui->usageLabel->setText(tr("Backup disk usage: %1").arg(readable_size(size, true)));
}
@@ -144,18 +141,17 @@ void BackupManagerForm::loadBackupListing(int index)
sys_dir = true;
}
- db->mutex.lock();
-
- // get the item list
- metadata_t *meta;
- int row_count = db->filterObjects(ohfi, &meta);
+ 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);
- db->mutex.unlock();
+ m_db->mutex.unlock();
return;
}
@@ -167,13 +163,14 @@ void BackupManagerForm::loadBackupListing(int index)
#else
horiz_header->setResizeMode(QHeaderView::Stretch);
#endif
- CMAObject *obj = db->ohfiToObject(ohfi);
- setBackupUsage(obj->metadata.size);
-
+ qint64 backup_size = m_db->getObjectSize(ohfi);
+ setBackupUsage(backup_size);
+ QString path = m_db->getAbsolutePath(ohfi);
QList item_list;
+ metadata_t *first = meta;
while(meta) {
- QString base_path = obj->path + QDir::separator() + meta->name;
+ 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;
@@ -213,13 +210,15 @@ void BackupManagerForm::loadBackupListing(int index)
item->setItemInfo(game_name, size, info);
item->setItemIcon(QDir(parent_path).absoluteFilePath(sys_dir ? "icon0.png" : "ICON0.PNG"), img_width, ohfi == VITA_OHFI_PSMAPP);
- item->setDirectory(obj->path + QDir::separator() + meta->name);
+ 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;
@@ -233,7 +232,7 @@ void BackupManagerForm::loadBackupListing(int index)
}
vert_header->setUpdatesEnabled(true);
- db->mutex.unlock();
+ m_db->mutex.unlock();
// apply filter
this->on_filterLineEdit_textChanged(ui->filterLineEdit->text());
diff --git a/src/forms/backupmanagerform.h b/src/forms/backupmanagerform.h
index e09fef7..2613df3 100644
--- a/src/forms/backupmanagerform.h
+++ b/src/forms/backupmanagerform.h
@@ -23,25 +23,25 @@
#include "database.h"
#include "backupitem.h"
-#include
+#include
namespace Ui {
class BackupManagerForm;
}
-class BackupManagerForm : public QWidget
+class BackupManagerForm : public QDialog
{
Q_OBJECT
public:
- explicit BackupManagerForm(QWidget *parent = 0);
+ explicit BackupManagerForm(Database *db, QWidget *parent = 0);
~BackupManagerForm();
- Database *db;
+ Database *m_db;
private:
void setupForm();
- void setBackupUsage(quint64 size);
+ void setBackupUsage(qint64 size);
Ui::BackupManagerForm *ui;
diff --git a/src/forms/configwidget.cpp b/src/forms/configwidget.cpp
index f7ebf24..6f5b14a 100644
--- a/src/forms/configwidget.cpp
+++ b/src/forms/configwidget.cpp
@@ -83,6 +83,7 @@ void ConfigWidget::setDefaultData()
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);
}
ConfigWidget::~ConfigWidget()
@@ -154,6 +155,7 @@ void ConfigWidget::accept()
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.sync();
done(Accepted);
diff --git a/src/forms/configwidget.ui b/src/forms/configwidget.ui
index 9fe7cc7..3e3d5fd 100644
--- a/src/forms/configwidget.ui
+++ b/src/forms/configwidget.ui
@@ -282,7 +282,7 @@
-
-
+
false
@@ -291,6 +291,11 @@
In Memory
+ -
+
+ SQLite
+
+
diff --git a/src/mainwidget.cpp b/src/mainwidget.cpp
index 35d8088..e6c178e 100644
--- a/src/mainwidget.cpp
+++ b/src/mainwidget.cpp
@@ -19,8 +19,10 @@
#include "mainwidget.h"
#include "cmaclient.h"
-#include "cmaevent.h"
-#include "utils.h"
+#include "cmautils.h"
+
+#include "qlistdb.h"
+#include "sqlitedb.h"
#include
#include
@@ -43,7 +45,7 @@ const QStringList MainWidget::path_list = QStringList() << "photoPath" << "music
bool sleptOnce = false;
MainWidget::MainWidget(QWidget *parent) :
- QWidget(parent)
+ QWidget(parent), db(NULL), configForm(NULL), managerForm(NULL), backupForm(NULL)
{
}
@@ -54,12 +56,12 @@ void MainWidget::checkSettings()
foreach(const QString &path, path_list) {
if(!settings.contains(path)) {
first_run = true;
- dialog.show();
+ configForm->show();
return;
}
}
first_run = false;
- manager.start();
+ managerForm->start();
}
void MainWidget::dialogResult(int result)
@@ -67,7 +69,7 @@ void MainWidget::dialogResult(int result)
if(result == QDialog::Accepted) {
if(first_run) {
first_run = false;
- manager.start();
+ managerForm->start();
}
} else if(first_run) {
qApp->quit();
@@ -80,7 +82,7 @@ void MainWidget::stopServer()
if(CmaClient::isRunning()) {
receiveMessage(tr("Stopping QCMA (disconnect your PS Vita)"));
}
- manager.stop();
+ managerForm->stop();
}
void MainWidget::deviceDisconnect()
@@ -117,6 +119,16 @@ void MainWidget::deviceConnected(QString message)
void MainWidget::prepareApplication()
{
+ //TODO: delete database before exit
+ if(QSettings().value("useMemoryStorage", true).toBool()) {
+ db = new QListDB();
+ } else {
+ db = new SQLiteDB();
+ }
+
+ configForm = new ConfigWidget(this);
+ backupForm = new BackupManagerForm(db, this);
+ managerForm = new ClientManager(db, this);
connectSignals();
createTrayIcon();
checkSettings();
@@ -124,13 +136,13 @@ void MainWidget::prepareApplication()
void MainWidget::connectSignals()
{
- connect(&dialog, SIGNAL(finished(int)), this, SLOT(dialogResult(int)));
- connect(&manager, SIGNAL(stopped()), qApp, SLOT(quit()));
- connect(&manager, SIGNAL(deviceConnected(QString)), this, SLOT(deviceConnected(QString)));
- connect(&manager, SIGNAL(deviceDisconnected()), this, SLOT(deviceDisconnect()));
- connect(&manager, SIGNAL(messageSent(QString)), this, SLOT(receiveMessage(QString)));
+ connect(configForm, SIGNAL(finished(int)), this, SLOT(dialogResult(int)));
+ connect(managerForm, SIGNAL(stopped()), qApp, SLOT(quit()));
+ connect(managerForm, SIGNAL(deviceConnected(QString)), this, SLOT(deviceConnected(QString)));
+ connect(managerForm, SIGNAL(deviceDisconnected()), this, SLOT(deviceDisconnect()));
+ connect(managerForm, SIGNAL(messageSent(QString)), this, SLOT(receiveMessage(QString)));
- form.db = &manager.db;
+ //backupForm.db = managerForm.db;
}
void MainWidget::setTrayTooltip(QString message)
@@ -144,8 +156,8 @@ void MainWidget::setTrayTooltip(QString message)
void MainWidget::openManager()
{
- form.loadBackupListing(-1);
- form.show();
+ backupForm->loadBackupListing(-1);
+ backupForm->show();
}
void MainWidget::showAboutDialog()
@@ -186,9 +198,9 @@ void MainWidget::createTrayIcon()
about_qt = new QAction(tr("Abou&t Qt"), this);
quit = new QAction(tr("&Quit"), this);
- connect(options, SIGNAL(triggered()), &dialog, SLOT(open()));
+ connect(options, SIGNAL(triggered()), configForm, SLOT(open()));
connect(backup, SIGNAL(triggered()), this, SLOT(openManager()));
- connect(reload, SIGNAL(triggered()), &manager, SLOT(refreshDatabase()));
+ connect(reload, SIGNAL(triggered()), managerForm, SLOT(refreshDatabase()));
connect(about, SIGNAL(triggered()), this, SLOT(showAboutDialog()));
connect(about_qt, SIGNAL(triggered()), this, SLOT(showAboutQt()));
connect(quit, SIGNAL(triggered()), this, SLOT(stopServer()));
@@ -255,4 +267,5 @@ MainWidget::~MainWidget()
#ifndef ENABLE_KDE_NOTIFIER
trayIcon->hide();
#endif
+ delete db;
}
diff --git a/src/mainwidget.h b/src/mainwidget.h
index 2d0080f..ac80db3 100644
--- a/src/mainwidget.h
+++ b/src/mainwidget.h
@@ -52,10 +52,13 @@ private:
bool first_run;
+ // database
+ Database *db;
+
// forms
- ConfigWidget dialog;
- ClientManager manager;
- BackupManagerForm form;
+ ConfigWidget *configForm;
+ ClientManager *managerForm;
+ BackupManagerForm *backupForm;
//system tray
QAction *quit;
diff --git a/src/qlistdb.cpp b/src/qlistdb.cpp
new file mode 100644
index 0000000..ed5f13a
--- /dev/null
+++ b/src/qlistdb.cpp
@@ -0,0 +1,568 @@
+/*
+ * 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 .
+ */
+
+#include "cmautils.h"
+#include "qlistdb.h"
+#include "cmaobject.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+QListDB::QListDB(QObject *parent) :
+ Database(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((*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_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]);
+
+ switch(ohfi_array[i]) {
+ case VITA_OHFI_MUSIC:
+ obj->initObject(settings.value("musicPath").toString());
+ break;
+
+ case VITA_OHFI_PHOTO:
+ obj->initObject(settings.value("photoPath").toString());
+ break;
+
+ case VITA_OHFI_VIDEO:
+ obj->initObject(settings.value("videoPath").toString());
+ 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());
+ }
+
+ root_list list;
+ list << obj;
+ emit directoryAdded(obj->path);
+ int dir_count = recursiveScanRootDirectory(list, obj, ohfi_array[i]);
+
+ 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 ¤t_path)
+{
+ while(last_dir && current_path != last_dir->path) {
+ last_dir = last_dir->parent;
+ }
+
+ return last_dir;
+}
+
+int QListDB::scanRootDirectory(root_list &list, int ohfi_type)
+{
+ int file_type = -1;
+ int total_objects = 0;
+ CMAObject *last_dir = list.first();
+ QDir dir(last_dir->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((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, 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 *parent, int ohfi_type)
+{
+ int file_type = -1;
+ int total_objects = 0;
+
+ QDir dir(parent->path);
+ dir.setSorting(QDir::Name | QDir::DirsFirst);
+ QFileInfoList qsl = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot);
+
+ foreach(const QFileInfo &info, qsl) {
+
+ if(!continueOperation()) {
+ return -1;
+ }
+
+ if(info.isFile() && (file_type = checkFileType(info.absoluteFilePath(), ohfi_type)) < 0) {
+ //qDebug("Excluding %s from database", info.absoluteFilePath().toStdString().c_str());>
+ } else {
+ CMAObject *obj = new CMAObject(parent);
+ obj->initObject(info, 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->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::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(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 *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(type == (VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ARTISTS)) {
+ // unimplemented
+ return 0;
+ } else if(type == (VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_GENRES)) {
+ // unimplemented
+ return 0;
+ } else if(type == (VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_PLAYLISTS)) {
+ // 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 = (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;
+}
+
+int QListDB::getObjectMetadatas(int parent_ohfi, metadata_t **metadata, int index, int max_number)
+{
+ QMutexLocker locker(&mutex);
+ CMARootObject *parent = static_cast(ohfiToObject(parent_ohfi));
+
+ if(parent == NULL) {
+ return 0;
+ }
+
+ int type = parent->metadata.type;
+
+ if(parent->metadata.ohfi < OHFI_OFFSET && parent->filters) { // if we have filters
+ if(parent_ohfi == parent->metadata.ohfi) { // if we are looking at root
+ return parent->getFilters(metadata);
+ } else { // we are looking at a filter
+ for(int j = 0; j < parent->num_filters; j++) {
+ if(parent->filters[j].ohfi == parent_ohfi) {
+ type = 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(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->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;
+}
diff --git a/src/qlistdb.h b/src/qlistdb.h
new file mode 100644
index 0000000..da45c86
--- /dev/null
+++ b/src/qlistdb.h
@@ -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 .
+ */
+
+#ifndef QLISTDB_H
+#define QLISTDB_H
+
+#include "database.h"
+#include "cmarootobject.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+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 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::const_iterator it;
+ QList::const_iterator end;
+ } find_data;
+
+ typedef QList root_list;
+ typedef QMap 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 ¤t_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
diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp
new file mode 100644
index 0000000..039c248
--- /dev/null
+++ b/src/sqlitedb.cpp
@@ -0,0 +1,1300 @@
+/*
+ * 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 .
+ */
+
+#include "cmautils.h"
+#include "sqlitedb.h"
+#include "sforeader.h"
+#include "avdecoder.h"
+
+#include
+#include
+#include
+#include
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+#include
+#else
+#include
+#define QStandardPaths QDesktopServices
+#define writableLocation storageLocation
+#endif
+
+#include
+#include
+
+static const char create_adjacent[] = "CREATE TABLE IF NOT EXISTS adjacent_objects ("
+ "parent_id INTEGER NOT NULL REFERENCES object_node(object_id) ON DELETE CASCADE,"
+ "child_id INTEGER NOT NULL REFERENCES object_node(object_id) ON DELETE CASCADE)";
+
+static const char create_obj_node[] = "CREATE TABLE IF NOT EXISTS object_node ("
+ "object_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "type INTEGER NOT NULL,"
+ "data_type INTEGER NOT NULL,"
+ "title TEXT,"
+ "child_count INTEGER NOT NULL DEFAULT 0,"
+ "reference_count INTEGER NOT NULL DEFAULT 0);";
+
+static const char create_sources[] = "CREATE TABLE IF NOT EXISTS sources ("
+ "object_id INTEGER PRIMARY KEY REFERENCES object_node(object_id) ON DELETE CASCADE,"
+ "path TEXT NOT NULL CHECK (LENGTH(path) > 0),"
+ "size INTEGER,"
+ "date_created TIMESTAMP,"
+ "date_modified TIMESTAMP)";
+
+static const char create_music[] = "CREATE TABLE IF NOT EXISTS music ("
+ "object_id INTEGER PRIMARY KEY REFERENCES object_node(object_id) ON DELETE CASCADE,"
+ "file_format INTEGER,"
+ "audio_bitrate INTEGER,"
+ "audio_codec INTEGER,"
+ "duration INTEGER,"
+ "genre_id INTEGER REFERENCES object_node(object_id) ON DELETE SET NULL,"
+ "track_id INTEGER REFERENCES object_node(object_id) ON DELETE SET NULL,"
+ "artist_id INTEGER REFERENCES object_node(object_id) ON DELETE SET NULL,"
+ "album_id INTEGER REFERENCES object_node(object_id) ON DELETE SET NULL,"
+ "artist TEXT,"
+ "album TEXT,"
+ "track_number INTEGER)";
+
+static const char create_apps[] = "CREATE TABLE IF NOT EXISTS application ("
+ "object_id INTEGER PRIMARY KEY REFERENCES object_node(object_id) ON DELETE CASCADE,"
+ "title TEXT NOT NULL CHECK (LENGTH(title) > 0),"
+ "app_type INTEGER)";
+
+static const char create_virtual[] = "CREATE TABLE IF NOT EXISTS virtual_nodes ("
+ "object_id INTEGER PRIMARY KEY REFERENCES object_node(object_id) ON DELETE CASCADE,"
+ "app_type INTEGER)";
+
+static const char create_photos[] = "CREATE TABLE IF NOT EXISTS photos ("
+ "object_id INTEGER PRIMARY KEY REFERENCES object_node(object_id) ON DELETE CASCADE,"
+ "date_created TIMESTAMP,"
+ "month_created TEXT,"
+ "file_format INTEGER,"
+ "photo_codec INTEGER,"
+ "width INTEGER,"
+ "height INTEGER)";
+
+static const char create_videos[] = "CREATE TABLE IF NOT EXISTS videos ("
+ "object_id INTEGER PRIMARY KEY REFERENCES object_node(object_id) ON DELETE CASCADE,"
+ "file_format INTEGER,"
+ "parental_level INTEGER,"
+ "explanation TEXT,"
+ "copyright TEXT,"
+ "width INTEGER,"
+ "height INTEGER,"
+ "video_codec INTEGER,"
+ "video_bitrate INTEGER,"
+ "audio_codec INTEGER,"
+ "audio_bitrate INTEGER,"
+ "duration INTEGER)";
+
+static const char create_savedata[] = "CREATE TABLE IF NOT EXISTS savedata ("
+ "object_id INTEGER PRIMARY KEY REFERENCES object_node(object_id) ON DELETE CASCADE,"
+ "detail TEXT,"
+ "dir_name TEXT,"
+ "title TEXT,"
+ "date_updated TIMESTAMP)";
+
+static const char create_trigger_node[] = "CREATE TRIGGER IF NOT EXISTS trg_objnode_deletechilds BEFORE DELETE ON object_node "
+ "FOR EACH ROW BEGIN "
+ "DELETE FROM object_node WHERE object_id IN "
+ "(SELECT child_id FROM adjacent_objects WHERE parent_id == OLD.object_id);"
+ "END";
+
+static const char create_trigger_adjins[] = "CREATE TRIGGER IF NOT EXISTS trg_adjacentobjects_ins AFTER INSERT ON adjacent_objects "
+ "FOR EACH ROW BEGIN "
+ "UPDATE object_node SET child_count = child_count + 1 WHERE object_id = NEW.parent_id;"
+ "UPDATE object_node SET reference_count = reference_count + 1 WHERE object_id = NEW.child_id;"
+ "END";
+
+static const char create_trigger_adjdel[] = "CREATE TRIGGER IF NOT EXISTS trg_adjacentobjects_del AFTER DELETE ON adjacent_objects "
+ "FOR EACH ROW BEGIN "
+ "UPDATE object_node SET child_count = child_count - 1 WHERE object_id = OLD.parent_id;"
+ "UPDATE object_node SET reference_count = reference_count - 1 WHERE object_id = OLD.child_id;"
+ "DELETE FROM object_node WHERE object_id = OLD.parent_id AND child_count <= 0;"
+ "DELETE FROM object_node WHERE object_id = OLD.child_id AND reference_count <= 0;"
+ "END";
+
+static const char *table_list[] = {
+ create_adjacent, create_obj_node, create_sources,
+ create_music, create_photos, create_videos, create_savedata, create_apps, create_virtual
+};
+
+static const char *trigger_list[] = {
+ create_trigger_node, create_trigger_adjins, create_trigger_adjdel
+};
+
+static const int ohfi_array[] = { VITA_OHFI_MUSIC, VITA_OHFI_PHOTO, VITA_OHFI_VIDEO,
+ VITA_OHFI_BACKUP, VITA_OHFI_VITAAPP, VITA_OHFI_PSPAPP,
+ VITA_OHFI_PSPSAVE, VITA_OHFI_PSXAPP, VITA_OHFI_PSMAPP
+ };
+
+SQLiteDB::SQLiteDB(QObject *parent) :
+ Database(parent)
+{
+ uuid = QSettings().value("lastAccountId", "ffffffffffffffff").toString();
+ thread = new QThread();
+ moveToThread(thread);
+ timer = new QTimer();
+ thread->start();
+ timer->setInterval(0);
+ timer->setSingleShot(true);
+ connect(timer, SIGNAL(timeout()), this, SLOT(process()));
+
+ // fetch a configured database path if it exists
+ QString db_path = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
+ db_path = QSettings().value("databasePath", db_path).toString();
+ QDir(QDir::root()).mkpath(db_path);
+
+ db = QSqlDatabase::addDatabase("QSQLITE");
+ db.setDatabaseName(db_path + QDir::separator() + "qcma.sqlite");
+}
+
+SQLiteDB::~SQLiteDB()
+{
+ db.close();
+ timer->stop();
+ delete timer;
+ thread->quit();
+ thread->wait();
+ delete thread;
+}
+
+
+void SQLiteDB::setUUID(const QString &uuid)
+{
+ this->uuid = uuid;
+ QSettings().setValue("lastAccountId", uuid);
+}
+
+bool SQLiteDB::load()
+{
+ bool success = false;
+
+ if(db.open()) {
+ QSqlQuery query;
+ query.exec("PRAGMA foreign_keys = ON");
+ query.exec("PRAGMA synchronous = OFF");
+ query.exec("PRAGMA user_version = 1");
+ query.exec("PRAGMA journal_mode = MEMORY");
+ query.exec("PRAGMA recursive_triggers = true");
+ query.exec("PRAGMA vacuum_db.synchronous = OFF");
+
+ qDebug() << "Database created/registered";
+ success = !initialize();
+ } else {
+ const QSqlError error = db.lastError();
+ qWarning() << "Error opening connection to the database:" << error.text();
+ }
+
+ return success;
+}
+
+bool SQLiteDB::rescan()
+{
+ if(mutex.tryLock(1000)) {
+ if(uuid != "ffffffffffffffff") {
+ timer->start();
+ return true;
+ } else {
+ mutex.unlock();
+ return false;
+ }
+ }
+ return false;
+}
+
+void SQLiteDB::clear()
+{
+ db.close();
+ //QSqlDatabase::removeDatabase("QSQLITE");
+ QString db_path = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
+ db_path = QSettings().value("databasePath", db_path).toString();
+ QFile(db_path + QDir::separator() + "qcma.sqlite").remove();
+ load();
+}
+
+bool SQLiteDB::initialize()
+{
+ QSqlQuery query;
+
+ for(unsigned int i = 0; i < sizeof(table_list) / sizeof(const char *); i++) {
+ if(!query.exec(table_list[i])) {
+ qDebug() << query.lastError();
+ return false;
+ }
+ }
+
+ for(unsigned int i = 0; i < sizeof(trigger_list) / sizeof(const char *); i++) {
+ if(!query.exec(trigger_list[i])) {
+ qDebug() << query.lastError();
+ return false;
+ }
+ }
+
+ // force object_id to start at 256
+ if(query.exec("INSERT INTO object_node (object_id, data_type, type) VALUES (255, 0, 0)")) {
+ query.exec("DELETE FROM object_node WHERE object_id = 255");
+ }
+ return true;
+}
+
+QSqlError SQLiteDB::getLastError()
+{
+ return db.lastError();
+}
+
+int SQLiteDB::create()
+{
+ int total_objects = 0;
+
+ db.transaction();
+
+ if(!insertVirtualEntries()) {
+ db.rollback();
+ return -1;
+ }
+
+ for(int i = 0, max = sizeof(ohfi_array) / sizeof(int); i < max; i++) {
+ QString base_path = getBasePath(ohfi_array[i]);
+ int dir_count = recursiveScanRootDirectory(base_path, NULL, ohfi_array[i], ohfi_array[i]);
+
+ if(dir_count < 0) {
+ db.rollback();
+ return -1;
+ }
+
+ //qDebug("Added %i objects for OHFI %#02X", dir_count, ohfi_array[i]);
+
+ total_objects += dir_count;
+ }
+ db.commit();
+ return total_objects;
+}
+
+QString SQLiteDB::getBasePath(int root_ohfi)
+{
+ QString base_path;
+ QSettings settings;
+
+ switch(root_ohfi) {
+ case VITA_OHFI_MUSIC:
+ base_path = settings.value("musicPath").toString();
+ break;
+ case VITA_OHFI_VIDEO:
+ base_path = settings.value("videoPath").toString();
+ break;
+ case VITA_OHFI_PHOTO:
+ base_path = settings.value("photoPath").toString();
+ break;
+ case VITA_OHFI_BACKUP:
+ base_path = settings.value("appsPath").toString() + "/SYSTEM/" + uuid;
+ break;
+ case VITA_OHFI_VITAAPP:
+ base_path = settings.value("appsPath").toString() + "/APP/" + uuid;
+ break;
+ case VITA_OHFI_PSPAPP:
+ base_path = settings.value("appsPath").toString() + "/PGAME/" + uuid;
+ break;
+ case VITA_OHFI_PSPSAVE:
+ base_path = settings.value("appsPath").toString() + "/PSAVEDATA/" + uuid;
+ break;
+ case VITA_OHFI_PSXAPP:
+ base_path = settings.value("appsPath").toString() + "/PSGAME/" + uuid;
+ break;
+ case VITA_OHFI_PSMAPP:
+ base_path = settings.value("appsPath").toString() + "/PSM/" + uuid;
+ break;
+ }
+ return base_path;
+}
+
+int SQLiteDB::recursiveScanRootDirectory(const QString &base_path, const QString &rel_path, int parent_ohfi, int root_ohfi)
+{
+ int total_objects = 0;
+
+ QDir dir(base_path + "/" + rel_path);
+ dir.setSorting(QDir::Name);
+ QFileInfoList qsl = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot);
+
+ foreach(const QFileInfo &info, qsl) {
+
+ if(!continueOperation()) {
+ return -1;
+ }
+
+ //qDebug() << "Processing " << info.fileName();
+
+ QString rel_name = rel_path.isNull() ? info.fileName() : rel_path + "/" + info.fileName();
+
+ int ohfi = insertObjectEntryInternal(base_path, rel_name, parent_ohfi, root_ohfi);
+
+ if(ohfi > 0) {
+ // update progress dialog
+ if(info.isDir()) {
+ emit directoryAdded(base_path + "/" + rel_name);
+ int inserted = recursiveScanRootDirectory(base_path, rel_name, ohfi, root_ohfi);
+ if(inserted < 0) {
+ return -1;
+ }
+
+ total_objects += inserted;
+ qint64 dirsize = getChildenTotalSize(ohfi);
+ setObjectSize(ohfi, dirsize);
+ } else if(info.isFile()) {
+ emit fileAdded(info.fileName());
+ total_objects++;
+ }
+ }
+ }
+
+ return total_objects;
+}
+
+int SQLiteDB::insertObjectEntry(const QString &path, const QString &name, int parent_ohfi)
+{
+ int ohfi;
+ int type;
+
+ if((type = getObjectType(parent_ohfi)) == 0) {
+ return 0;
+ }
+ db.transaction();
+ if((ohfi = insertObjectEntryInternal(path, name, parent_ohfi, type)) == 0) {
+ db.rollback();
+ }
+ db.commit();
+
+ return ohfi;
+}
+
+int SQLiteDB::insertObjectEntryInternal(const QString &path, const QString &name, int parent_ohfi, int root_ohfi)
+{
+ int ohfi;
+ QFileInfo info(path, name);
+
+ if(info.isDir()) {
+ ohfi = insertDefaultEntry(path, name, info.fileName(), parent_ohfi, Folder);
+ switch(parent_ohfi) {
+ case VITA_OHFI_VITAAPP:
+ case VITA_OHFI_PSPAPP:
+ case VITA_OHFI_PSXAPP:
+ case VITA_OHFI_PSMAPP:
+ case VITA_OHFI_BACKUP:
+ insertApplicationEntry(name, ohfi, parent_ohfi);
+ }
+ } else {
+ switch(root_ohfi) {
+ case VITA_OHFI_MUSIC:
+ ohfi = insertMusicEntry(path, name, parent_ohfi, File | Music);
+ break;
+ case VITA_OHFI_PHOTO:
+ ohfi = insertPhotoEntry(path, name, parent_ohfi, File | Photo);
+ break;
+ case VITA_OHFI_VIDEO:
+ ohfi = insertVideoEntry(path, name, parent_ohfi, File | Video);
+ break;
+ case VITA_OHFI_PSPSAVE:
+ ohfi = insertSavedataEntry(path, name, parent_ohfi, File | SaveData);
+ break;
+ case VITA_OHFI_VITAAPP:
+ case VITA_OHFI_PSPAPP:
+ case VITA_OHFI_PSXAPP:
+ case VITA_OHFI_PSMAPP:
+ case VITA_OHFI_BACKUP:
+ ohfi = insertDefaultEntry(path, name, info.fileName(), parent_ohfi, File | App);
+ }
+ }
+ return ohfi;
+}
+
+
+int SQLiteDB::getPathId(const QString &path)
+{
+ QSqlQuery query(QString("SELECT object_id from sources WHERE path = %1").arg(path));
+ if(query.next()) {
+ return query.value(0).toInt();
+ } else {
+ qDebug() << query.lastError();
+ return -1;
+ }
+}
+
+QString SQLiteDB::getPathFromId(int ohfi)
+{
+ QSqlQuery query(QString("SELECT path FROM sources WHERE object_id = %1").arg(ohfi));
+ if(query.next()) {
+ return query.value(0).toString();
+ } else {
+ qDebug() << query.lastError();
+ return QString();
+ }
+}
+
+bool SQLiteDB::updateSize(int ohfi, quint64 size)
+{
+ QSqlQuery query(QString("UPDATE sources SET size = %1 WHERE object_id == %2").arg(size).arg(ohfi));
+ return query.exec();
+}
+
+bool SQLiteDB::deleteEntry(int ohfi)
+{
+ QSqlQuery query(QString("DELETE FROM object_node WHERE object_id == %1").arg(ohfi));
+ bool ret = query.exec();
+ if(!ret) {
+ qDebug() << query.lastError();
+ }
+ return ret;
+}
+
+bool SQLiteDB::updateAdjacencyList(int ohfi, int parent)
+{
+ QSqlQuery query;
+ query.prepare("SELECT * FROM adjacent_objects WHERE parent_id == :parent_id AND child_id == :child_id");
+ query.bindValue(0, parent);
+ query.bindValue(1, ohfi);
+
+ if(query.exec() && query.next()) {
+ return true;
+ }
+
+ query.prepare("INSERT INTO adjacent_objects (parent_id, child_id)"
+ "VALUES (:parentid, :child_id)");
+ query.bindValue(0, parent);
+ query.bindValue(1, ohfi);
+ bool ret = query.exec();
+ if(!ret) {
+ qDebug() << query.lastError();
+ }
+ return ret;
+}
+
+int SQLiteDB::insertDefaultEntry(const QString &path, const QString &name, const QString &title, int parent, int type)
+{
+ int ohfi = 0;
+
+ if((ohfi = insertNodeEntry(title, VITA_DIR_TYPE_MASK_REGULAR, type)) == 0) {
+ return 0;
+ }
+
+ if(parent >= OHFI_BASE_VALUE && !updateAdjacencyList(ohfi, parent)) {
+ return 0;
+ }
+
+ if(!name.isNull() && !insertSourceEntry(ohfi, path, name)) {
+ return 0;
+ }
+ return ohfi;
+}
+
+int SQLiteDB::insertNodeEntry(const QString &title, int type, int data_type)
+{
+ QSqlQuery query;
+
+ query.prepare("INSERT INTO object_node (type, data_type, title) VALUES (:type, :data_type, :title)");
+ query.bindValue(0, type);
+ query.bindValue(1, data_type);
+ query.bindValue(2, title);
+
+ if(!query.exec() || !query.exec("SELECT last_insert_rowid()") || !query.next()) {
+ qDebug() << query.lastError();
+ return 0;
+ }
+
+ return query.value(0).toInt();
+}
+
+bool SQLiteDB::insertSourceEntry(uint object_id, const QString &path, const QString &name)
+{
+ QVariant size, date_created, date_modified;
+
+ QFileInfo info(path, name);
+ if(info.isFile()) {
+ size = QVariant(info.size());
+ date_created = QVariant(info.created().toTime_t());
+ } else {
+ size = QVariant(QVariant::LongLong);
+ date_created = QVariant(QVariant::UInt);
+ }
+
+ date_modified = QVariant(info.lastModified().toTime_t());
+
+ QSqlQuery query;
+ query.prepare("REPLACE INTO sources (object_id, path, size, date_created, date_modified)"
+ "VALUES (:object_id, :path, :size, :date_created, :date_modified)");
+ query.bindValue(0, object_id);
+ query.bindValue(1, name);
+ query.bindValue(2, size);
+ query.bindValue(3, date_created);
+ query.bindValue(4, date_modified);
+ bool ret = query.exec();
+ if(!ret) {
+ qDebug() << query.lastError();
+ }
+ return ret;
+}
+
+uint SQLiteDB::insertMusicEntry(const QString &path, const QString &name, int parent, int type)
+{
+ bool ok;
+ int ohfi;
+ AVDecoder decoder;
+ quint64 duration;
+ const char *artist, *album, *albumartist, *genre, *track, *title;
+ int file_format, audio_codec, audio_bitrate, genre_id, artist_id, track_id, album_id, track_number;
+
+ int file_type = checkFileType(name, VITA_OHFI_MUSIC);
+ if(file_type < 0) {
+ //qDebug() << "Excluding from database:" << path;
+ return 0;
+ }
+
+ if(!decoder.open(path + "/" + name)) {
+ return 0;
+ }
+
+ album = decoder.getMetadataEntry("album");
+ genre = decoder.getMetadataEntry("genre");
+ artist = decoder.getMetadataEntry("artist");
+ albumartist = decoder.getMetadataEntry("album_artist");
+ track = decoder.getMetadataEntry("track");
+
+ track_number = QString(track).split("/")[0].toInt(&ok);
+ if(!ok) {
+ // set track number to 1 by default
+ track_number = 1;
+ }
+
+ if(decoder.loadCodec(AVDecoder::CODEC_AUDIO)) {
+ audio_bitrate = decoder.getBitrate();
+ } else {
+ audio_bitrate = 0;
+ }
+
+ duration = decoder.getDuration();
+
+ file_format = audio_list[file_type].file_format;
+ audio_codec = audio_list[file_type].file_codec;
+
+ QByteArray basename = QFileInfo(name).baseName().toUtf8();
+ title = decoder.getMetadataEntry("title", basename.constData());
+
+ if((ohfi = insertDefaultEntry(path, name, title, parent, type)) == 0) {
+ return 0;
+ }
+
+ if(genre) {
+ genre_id = insertNodeEntry(genre, VITA_DIR_TYPE_MASK_GENRES, type);
+ if(!updateAdjacencyList(ohfi, genre_id)) {
+ return 0;
+ }
+ } else {
+ genre_id = 0;
+ }
+
+ if(artist) {
+ track_id = insertNodeEntry(artist, VITA_DIR_TYPE_MASK_ARTISTS, type);
+ if(!updateAdjacencyList(ohfi, track_id)) {
+ return 0;
+ }
+ } else {
+ track_id = 0;
+ }
+
+ if(albumartist) {
+ artist_id = insertNodeEntry(albumartist, VITA_DIR_TYPE_MASK_REGULAR, type);
+ if(!updateAdjacencyList(ohfi, artist_id)) {
+ return 0;
+ }
+ } else {
+ artist_id = 0;
+ }
+
+ if(album) {
+ album_id = insertNodeEntry(album, VITA_DIR_TYPE_MASK_ALBUMS, type);
+
+ if(track_id && !updateAdjacencyList(ohfi, album_id)) {
+ return 0;
+ }
+ if(track_id && !updateAdjacencyList(album_id, track_id)) {
+ return 0;
+ }
+ if(artist_id && !updateAdjacencyList(album_id, artist_id)) {
+ return 0;
+ }
+ } else {
+ album_id = 0;
+ }
+
+ QSqlQuery query;
+ query.prepare("REPLACE INTO music"
+ "(object_id, file_format, audio_codec, audio_bitrate, duration, genre_id, artist_id, album_id, track_id, artist, album, track_number)"
+ "VALUES (:object_id, :file_format, :audio_codec, :audio_bitrate, :duration, :genre_id, :artist_id, :album_id, :track_id, :artist, :album, :track_number)");
+
+ query.bindValue(0, ohfi);
+ query.bindValue(1, file_format);
+ query.bindValue(2, audio_codec);
+ query.bindValue(3, audio_bitrate);
+ query.bindValue(4, duration);
+ query.bindValue(5, genre_id ? genre_id : QVariant(QVariant::Int));
+ query.bindValue(6, artist_id ? artist_id : QVariant(QVariant::Int));
+ query.bindValue(7, album_id ? album_id : QVariant(QVariant::Int));
+ query.bindValue(8, track_id ? track_id : QVariant(QVariant::Int));
+ query.bindValue(9, artist);
+ query.bindValue(10, album);
+ query.bindValue(11, track_number);
+
+ if(!query.exec()) {
+ return 0;
+ }
+
+ return ohfi;
+}
+
+uint SQLiteDB::insertVideoEntry(const QString &path, const QString &name, int parent, int type)
+{
+ int ohfi;
+ AVDecoder decoder;
+ quint64 duration;
+ int file_format, parental_level, width, height, video_codec, video_bitrate, audio_codec, audio_bitrate;
+ const char *explanation, *copyright, *title;
+
+ if(!decoder.open(path + "/" + name) || !decoder.loadCodec(AVDecoder::CODEC_VIDEO)) {
+ return 0;
+ }
+
+ int file_type = checkFileType(name, VITA_OHFI_VIDEO);
+ if(file_type < 0) {
+ //qDebug() << "Excluding from database:" << path;
+ return 0;
+ }
+
+ parental_level = 0;
+ explanation = decoder.getMetadataEntry("comments", "");
+ copyright = decoder.getMetadataEntry("copyright", "");
+ width = decoder.getWidth();
+ height = decoder.getHeight();
+ duration = decoder.getDuration();
+ video_codec = CODEC_TYPE_AVC;
+ video_bitrate = decoder.getBitrate();
+ file_format = video_list[file_type].file_format;
+
+ if(decoder.loadCodec(AVDecoder::CODEC_AUDIO)) {
+ audio_codec = CODEC_TYPE_AAC;
+ audio_bitrate = decoder.getBitrate();
+ } else {
+ audio_codec = 0;
+ audio_bitrate = 0;
+ }
+
+ QByteArray basename = QFileInfo(name).baseName().toUtf8();
+ //title = decoder.getMetadataEntry("title", basename.constData());
+ title = basename.constData();
+
+
+ if((ohfi = insertDefaultEntry(path, name, title, parent, type)) == 0) {
+ return 0;
+ }
+
+ QSqlQuery query;
+ query.prepare("REPLACE INTO videos"
+ "(object_id, file_format, parental_level, explanation, copyright, width, height, video_codec, video_bitrate, audio_codec, audio_bitrate, duration)"
+ "VALUES (:object_id, :file_format, :parental_level, :explanation, :copyright, :width, :height, :video_codec, :video_bitrate, :audio_codec, :audio_bitrate, :duration)");
+
+ query.bindValue(0, ohfi);
+ query.bindValue(1, file_format);
+ query.bindValue(2, parental_level);
+ query.bindValue(3, explanation);
+ query.bindValue(4, copyright);
+ query.bindValue(5, width);
+ query.bindValue(6, height);
+ query.bindValue(7, video_codec);
+ query.bindValue(8, video_bitrate);
+ query.bindValue(9, audio_codec);
+ query.bindValue(10, audio_bitrate);
+ query.bindValue(11, duration);
+
+ if(!query.exec()) {
+ qDebug() << query.lastError().text();
+ return 0;
+ }
+
+ return ohfi;
+}
+
+uint SQLiteDB::insertPhotoEntry(const QString &path, const QString &name, int parent, int type)
+{
+ int ohfi;
+ QImage img;
+ uint date_created;
+ int width, height, file_format, photo_codec;
+
+ int file_type = checkFileType(name, VITA_OHFI_PHOTO);
+ if(file_type < 0) {
+ //qDebug() << "Excluding from database:" << path;
+ return 0;
+ }
+
+ if(!img.load(path + "/" + name)) {
+ return 0;
+ }
+
+ QDateTime date = QFileInfo(path + "/" + name).created();
+ date_created = date.toTime_t();
+ QString month_created = date.toString("yyyy/MM");
+
+ width = img.width();
+ height = img.height();
+ file_format = photo_list[file_type].file_format;
+ photo_codec = photo_list[file_type].file_codec;
+
+ QByteArray basename = QFileInfo(name).baseName().toUtf8();
+
+ if((ohfi = insertDefaultEntry(path, name, basename, parent, type)) == 0) {
+ return 0;
+ }
+
+ QSqlQuery query;
+ query.prepare("REPLACE INTO photos"
+ "(object_id, date_created, file_format, photo_codec, width, height, month_created)"
+ "VALUES (:object_id, :date_created, :file_format, :photo_codec, :width, :height, :month_created)");
+
+ query.bindValue(0, ohfi);
+ query.bindValue(1, date_created);
+ query.bindValue(2, file_format);
+ query.bindValue(3, photo_codec);
+ query.bindValue(4, width);
+ query.bindValue(5, height);
+ query.bindValue(6, month_created);
+
+ if(!query.exec()) {
+ return 0;
+ }
+
+ return ohfi;
+}
+
+uint SQLiteDB::insertSavedataEntry(const QString &path, const QString &name, int parent, int type)
+{
+ int ohfi;
+ SfoReader reader;
+ uint date_updated = 0;
+ const char *title = NULL;
+ const char *savedata_detail = NULL;
+ const char *savedata_directory = NULL;
+
+ QString file_name = QFileInfo(name).fileName();
+ QByteArray utf8name = file_name.toUtf8();
+
+ if(file_name.endsWith(".sfo", Qt::CaseInsensitive) && reader.load(path + "/" + name)) {
+ title = reader.value("TITLE", utf8name.constData());
+ savedata_detail = reader.value("SAVEDATA_DETAIL", "");
+ savedata_directory = reader.value("SAVEDATA_DIRECTORY", utf8name.constData());
+ date_updated = QFileInfo(path + "/" + name).lastModified().toTime_t();
+ }
+
+ if((ohfi = insertDefaultEntry(path, name, title, parent, type)) == 0) {
+ return 0;
+ }
+
+ if(!title) {
+ return ohfi;
+ }
+
+ QSqlQuery query;
+ query.prepare("REPLACE INTO savedata"
+ "(object_id, detail, dir_name, title, date_updated)"
+ "VALUES (:object_id, :detail, :dir_name, :title, :updated)");
+
+ query.bindValue(0, parent);
+ query.bindValue(1, savedata_detail);
+ query.bindValue(2, savedata_directory);
+ query.bindValue(3, title);
+ query.bindValue(4, date_updated);
+
+ if(!query.exec()) {
+ return 0;
+ }
+
+ return ohfi;
+}
+
+
+bool SQLiteDB::insertApplicationEntry(const QString &name, int ohfi, int app_type)
+{
+ QString title_id = QFileInfo(name).fileName();
+
+ QSqlQuery query;
+ query.prepare("REPLACE INTO application"
+ "(object_id, title, app_type)"
+ "VALUES (:object_id, :title, :app_type)");
+
+ query.bindValue(0, ohfi);
+ query.bindValue(1, title_id);
+ query.bindValue(2, app_type);
+
+ if(!query.exec()) {
+ return false;
+ }
+
+ return true;
+}
+
+int SQLiteDB::childObjectCount(int parent_ohfi)
+{
+ QSqlQuery query;
+ query.prepare("SELECT count(child_id) FROM adjacent_objects WHERE parent_id = :object_id");
+ query.bindValue(0, parent_ohfi);
+ if(!query.exec() || !query.next()) {
+ qDebug() << query.lastError();
+ return -1;
+ } else {
+ return query.value(0).toInt();
+ }
+}
+
+bool SQLiteDB::deleteEntry(int ohfi, int root_ohfi)
+{
+ Q_UNUSED(root_ohfi);
+ qDebug("Deleting node: %i", ohfi);
+
+ QSqlQuery query("DELETE FROM object_node WHERE object_id = :object_id");
+ query.bindValue(0, ohfi);
+ return query.exec();
+}
+
+void SQLiteDB::fillMetadata(const QSqlQuery &query, metadata_t &metadata)
+{
+ metadata.ohfi = query.value("ohfi").toInt();
+ metadata.ohfiParent = query.value("parent").toInt();
+ metadata.name = strdup(query.value("name").toByteArray().constData());
+ metadata.path = strdup(query.value("path").toByteArray().constData());
+ metadata.type = VITA_DIR_TYPE_MASK_REGULAR;
+ metadata.dataType = (DataType)query.value("data_type").toInt();
+ metadata.size = query.value("size").toULongLong();
+ metadata.dateTimeCreated = query.value("date_created").toInt();
+ metadata.next_metadata = NULL;
+ //TODO: fill the rest of the metadata
+}
+
+bool SQLiteDB::getObjectMetadata(int ohfi, metadata_t &metadata)
+{
+ QSqlQuery query(
+ "SELECT "
+ "t0.object_id as ohfi,"
+ "t1.parent_id as parent,"
+ "t2.path as path,"
+ "t0.title as name,"
+ "t0.type as type,"
+ "t0.data_type as data_type,"
+ "t2.size as size,"
+ "t2.date_created as date_created "
+ "FROM object_node t0 "
+ "JOIN adjacent_objects t1 ON t1.child_id = t0.object_id "
+ "JOIN sources t2 ON t2.object_id = t0.object_id "
+ "WHERE t0.object_id = :object_id");
+ query.bindValue(0, ohfi);
+
+ if(query.exec() && query.next()) {
+ fillMetadata(query, metadata);
+ return true;
+ } else {
+ qDebug() << query.lastError();
+ return false;
+ }
+}
+
+int SQLiteDB::getObjectMetadatas(int parent_ohfi, metadata_t **metadata, int index, int max_number)
+{
+ if(metadata == NULL) {
+ return childObjectCount(parent_ohfi);
+ }
+
+ if(parent_ohfi < OHFI_BASE_VALUE) {
+ return getRootItems(parent_ohfi, metadata);
+ }
+
+ int count = 0;
+ QString query_str(
+ "SELECT "
+ "t0.child_id as ohfi,"
+ "t0.parent_id as parent,"
+ "t2.path as path,"
+ "t1.title as name,"
+ "t1.type as type,"
+ "t1.data_type as data_type,"
+ "t2.size as size,"
+ "t2.date_created as date_created "
+ "FROM adjacent_objects t0 "
+ "JOIN object_node t1 ON t0.child_id = t1.object_id "
+ "JOIN sources t2 ON t0.child_id = t2.object_id "
+ "WHERE t0.parent_id = :parent_id ");
+
+ if(max_number > 0) {
+ query_str += "LIMIT :limit OFFSET :offset";
+ }
+
+ QSqlQuery query(query_str);
+ query.bindValue(0, parent_ohfi);
+
+ if(max_number > 0) {
+ query.bindValue(1, max_number);
+ query.bindValue(2, index);
+ }
+
+ if(query.exec()) {
+ metadata_t **last = &*metadata;
+ while(query.next()) {
+ metadata_t *meta = new metadata_t();
+ fillMetadata(query, *meta);
+ *last = meta;
+ last = &meta->next_metadata;
+ count++;
+ }
+ } else {
+ qDebug() << query.lastError();
+ }
+ return count;
+}
+
+qint64 SQLiteDB::getObjectSize(int ohfi)
+{
+ if(ohfi < OHFI_BASE_VALUE) {
+ return getChildenTotalSize(ohfi);
+ }
+
+ QSqlQuery query;
+ query.prepare("SELECT size FROM sources WHERE object_id = :object_id");
+ query.bindValue(0, ohfi);
+ if(!query.exec() || !query.next()) {
+ qDebug() << query.lastError();
+ return -1;
+ } else {
+ return query.value(0).toInt();
+ }
+}
+
+int SQLiteDB::getPathId(const char *name, int ohfi)
+{
+ //FIXME: use ohfi to filter by category
+ Q_UNUSED(ohfi);
+ QSqlQuery query;
+
+ query.prepare("SELECT object_id FROM sources WHERE path = :path");
+ query.bindValue(0, name);
+ if(!query.exec() || !query.next()) {
+ qDebug() << query.lastError();
+ return 0;
+ } else {
+ return query.value(0).toInt();
+ }
+}
+
+QString SQLiteDB::getAbsolutePath(int ohfi)
+{
+ int root_ohfi = ohfi < OHFI_BASE_VALUE ? ohfi : getRootId(ohfi);
+ QString base_path = getBasePath(root_ohfi);
+ QString rel_path = getRelativePath(ohfi);
+ return rel_path.isNull() ? base_path : base_path + "/" + rel_path;
+}
+
+QString SQLiteDB::getRelativePath(int ohfi)
+{
+ QSqlQuery query;
+ query.prepare("SELECT path FROM sources WHERE object_id = :object_id");
+ query.bindValue(0, ohfi);
+ if(!query.exec() || !query.next()) {
+ qDebug() << query.lastError();
+ return NULL;
+ } else {
+ return query.value(0).toString();
+ }
+}
+
+bool SQLiteDB::updateObjectPath(int ohfi, const QString &name)
+{
+ int parent_ohfi = getParentId(ohfi);
+ QString parent_path, file;
+
+ if(name.isNull()) {
+ QSqlQuery name_query("SELECT title FROM object_node WHERE object_id = :object_id");
+ name_query.bindValue(0, ohfi);
+ if(!name_query.exec() || !name_query.next()) {
+ return false;
+ }
+ file = name_query.value(0).toString();
+ } else {
+ file = name;
+ }
+
+ if(parent_ohfi >= OHFI_BASE_VALUE) {
+ parent_path = getRelativePath(parent_ohfi);
+ if(parent_path.isNull()) {
+ parent_path = file;
+ } else {
+ parent_path += "/" + file;
+ }
+ } else {
+ parent_path = file;
+ }
+
+ QSqlQuery query("UPDATE sources SET path = :path WHERE object_id = :object_id");
+ query.bindValue(0, parent_path);
+ query.bindValue(1, ohfi);
+
+ if(!query.exec()) {
+ return false;
+ }
+
+ DataType type = (DataType)getObjectType(ohfi);
+
+ if(type & Folder) {
+ QSqlQuery child_query("SELECT child_id, data_type FROM adjacent_objects "
+ "JOIN object_node ON object_id = child_id"
+ "WHERE parent_id = :parent_id");
+ child_query.bindValue(0, ohfi);
+
+ if(query.exec()) {
+ while(query.next()) {
+ int child_ohfi = query.value(0).toInt();
+ if(!updateObjectPath(child_ohfi, NULL)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool SQLiteDB::renameObject(int ohfi, const QString &name)
+{
+ QSqlQuery query("UPDATE object_node SET title = :title WHERE object_id = :object_id");
+ query.bindValue(0, name);
+ query.bindValue(1, ohfi);
+
+ if(!query.exec()) {
+ return false;
+ }
+
+ return updateObjectPath(ohfi, name);
+}
+
+void SQLiteDB::setObjectSize(int ohfi, qint64 size)
+{
+ QSqlQuery query;
+ query.prepare("UPDATE sources SET size = :size WHERE object_id = :object_id");
+ query.bindValue(0, size);
+ query.bindValue(1, ohfi);
+ if(!query.exec()) {
+ qDebug() << query.lastError();
+ }
+}
+
+qint64 SQLiteDB::getChildenTotalSize(int ohfi)
+{
+ QSqlQuery query;
+ query.prepare("SELECT SUM(t0.size) FROM sources t0 "
+ "JOIN adjacent_objects t1 ON t0.object_id = t1.child_id "
+ "where t1.parent_id = :parent_id");
+ query.bindValue(0, ohfi);
+ if(!query.exec() || !query.next()) {
+ qDebug() << query.lastError();
+ return -1;
+ } else {
+ return query.value(0).toLongLong();
+ }
+}
+
+int SQLiteDB::getRootId(int ohfi)
+{
+ QSqlQuery query;
+ int root_ohfi = ohfi;
+
+ query.prepare("SELECT parent_id FROM adjacent_objects WHERE child_id = :child_id");
+ while(root_ohfi >= OHFI_BASE_VALUE) {
+ query.bindValue(0, root_ohfi);
+ if(!query.exec() || !query.next()) {
+ qDebug() << query.lastError();
+ root_ohfi = 0;
+ } else {
+ root_ohfi = query.value(0).toInt();
+ }
+ }
+ return root_ohfi;
+}
+
+int SQLiteDB::getObjectType(int ohfi)
+{
+ QSqlQuery query;
+ query.prepare("SELECT type FROM object_node WHERE object_id = :object_id");
+ query.bindValue(0, ohfi);
+ if(!query.exec() || !query.next()) {
+ qDebug() << query.lastError();
+ return 0;
+ } else {
+ return query.value(0).toInt();
+ }
+}
+
+int SQLiteDB::getParentId(int ohfi)
+{
+ QSqlQuery query;
+ query.prepare("SELECT parent_id FROM adjacent_objects WHERE child_id = :child_id");
+ query.bindValue(0, ohfi);
+ if(!query.exec() || !query.next()) {
+ qDebug() << query.lastError();
+ return 0;
+ } else {
+ return query.value(0).toInt();
+ }
+}
+
+void SQLiteDB::freeMetadata(metadata_t *metadata)
+{
+ while(metadata) {
+ metadata_t *current = metadata;
+ metadata = metadata->next_metadata;
+ delete current;
+ }
+}
+
+int SQLiteDB::getRootItems(int root_ohfi, metadata_t **metadata)
+{
+ QSqlQuery query;
+ int count = 0;
+
+ switch(root_ohfi) {
+ case VITA_OHFI_MUSIC:
+ //query = QSqlQuery("SELECT * FROM music");
+ break;
+ case VITA_OHFI_PHOTO:
+ //query = QSqlQuery("SELECT * FROM photos");
+ break;
+ case VITA_OHFI_VIDEO:
+ //query = QSqlQuery("SELECT * FROM videos");
+ break;
+ case VITA_OHFI_PSPSAVE:
+ //query = QSqlQuery("SELECT * FROM savedata");
+ break;
+ case VITA_OHFI_VITAAPP:
+ case VITA_OHFI_PSPAPP:
+ case VITA_OHFI_PSXAPP:
+ case VITA_OHFI_PSMAPP:
+ case VITA_OHFI_BACKUP:
+ query = QSqlQuery("SELECT "
+ "t0.object_id as ohfi,"
+ ":app_type as parent,"
+ "t3.path as path,"
+ "t0.title as name,"
+ "t1.type as type,"
+ "t1.data_type as data_type,"
+ "t3.size as size,"
+ "t3.date_created as date_created "
+ "FROM application t0 "
+ "JOIN object_node t1 on t0.object_id = t1.object_id "
+ "JOIN sources t3 ON t3.object_id = t0.object_id "
+ "WHERE app_type = :app_type");
+ query.bindValue(0, root_ohfi);
+ if(query.exec()) {
+ metadata_t **last = &*metadata;
+ while(query.next()) {
+ metadata_t *meta = new metadata_t();
+ fillMetadata(query, *meta);
+ *last = meta;
+ last = &meta->next_metadata;
+ count++;
+ }
+ }
+ break;
+ default:
+ qFatal("Invalid root ohfi type");
+ }
+
+ return count;
+}
+
+bool SQLiteDB::insertVirtualEntry(int ohfi)
+{
+ QSqlQuery query;
+ query.prepare("REPLACE INTO virtual_nodes (object_id)"
+ "VALUES (:object_id)");
+ query.bindValue(0, ohfi);
+ bool ret = query.exec();
+ if(!ret) {
+ qDebug() << query.lastError();
+ }
+ return ret;
+}
+
+bool SQLiteDB::insertVirtualEntries()
+{
+ int ohfi;
+
+ if((ohfi = insertNodeEntry("Folders", VITA_DIR_TYPE_MASK_REGULAR, Video)) > 0)
+ insertVirtualEntry(ohfi);
+ else
+ return false;
+
+ if((ohfi = insertNodeEntry("All", VITA_DIR_TYPE_MASK_ALL, Video)) > 0)
+ insertVirtualEntry(ohfi);
+ else
+ return false;
+
+ if((ohfi = insertNodeEntry("Folders", VITA_DIR_TYPE_MASK_REGULAR, Photo)) > 0)
+ insertVirtualEntry(ohfi);
+ else
+ return false;
+
+ if((ohfi = insertNodeEntry("Month", VITA_DIR_TYPE_MASK_MONTH, Photo)) > 0)
+ insertVirtualEntry(ohfi);
+ else
+ return false;
+
+ if((ohfi = insertNodeEntry("All", VITA_DIR_TYPE_MASK_ALL, Photo)) > 0)
+ insertVirtualEntry(ohfi);
+ else
+ return false;
+
+ if((ohfi = insertNodeEntry("Artists", VITA_DIR_TYPE_MASK_ARTISTS, Music)) > 0)
+ insertVirtualEntry(ohfi);
+ else
+ return false;
+
+ if((ohfi = insertNodeEntry("Albums", VITA_DIR_TYPE_MASK_ALBUMS, Music)) > 0)
+ insertVirtualEntry(ohfi);
+ else
+ return false;
+
+ if((ohfi = insertNodeEntry("Songs", VITA_DIR_TYPE_MASK_SONGS, Music)) > 0)
+ insertVirtualEntry(ohfi);
+ else
+ return false;
+
+ if((ohfi = insertNodeEntry("Genres", VITA_DIR_TYPE_MASK_GENRES, Music)) > 0)
+ insertVirtualEntry(ohfi);
+ else
+ return false;
+
+ if((ohfi = insertNodeEntry("Playlists", VITA_DIR_TYPE_MASK_PLAYLISTS, Music)) > 0)
+ insertVirtualEntry(ohfi);
+ else
+ return false;
+
+ return true;
+}
diff --git a/src/sqlitedb.h b/src/sqlitedb.h
new file mode 100644
index 0000000..669d820
--- /dev/null
+++ b/src/sqlitedb.h
@@ -0,0 +1,101 @@
+/*
+ * 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 .
+ */
+
+#ifndef SQLITEDB_H
+#define SQLITEDB_H
+
+#include "database.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+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 &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 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 uuid;
+ QSqlDatabase db;
+};
+
+#endif // SQLITEDB_H