Changed folder structure
This commit is contained in:
278
src/avdecoder.cpp
Normal file
278
src/avdecoder.cpp
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "avdecoder.h"
|
||||
#include "cmaobject.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QBuffer>
|
||||
#include <QFile>
|
||||
#include <QSettings>
|
||||
|
||||
AVDecoder::AvInit init;
|
||||
|
||||
AVDecoder::AVDecoder() :
|
||||
pFormatCtx(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
AVDecoder::~AVDecoder()
|
||||
{
|
||||
if(pFormatCtx) {
|
||||
avformat_close_input(&pFormatCtx);
|
||||
}
|
||||
}
|
||||
|
||||
bool AVDecoder::open(const QString filename)
|
||||
{
|
||||
if(avformat_open_input(&pFormatCtx, QFile::encodeName(filename).constData(), NULL, NULL) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(avformat_find_stream_info(pFormatCtx, NULL) < 0) {
|
||||
avformat_close_input(&pFormatCtx);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AVDecoder::getAudioMetadata(metadata_t &metadata)
|
||||
{
|
||||
AVDictionaryEntry *entry;
|
||||
AVDictionary* file_metadata = pFormatCtx->metadata;
|
||||
|
||||
if((entry = av_dict_get(file_metadata, "artist", NULL, 0)) != NULL) {
|
||||
metadata.data.music.artist = strdup(entry->value);
|
||||
} else {
|
||||
metadata.data.music.artist = strdup("");
|
||||
}
|
||||
|
||||
if((entry = av_dict_get(file_metadata, "album", NULL, 0)) != NULL) {
|
||||
metadata.data.music.album = strdup(entry->value);
|
||||
} else {
|
||||
metadata.data.music.album = strdup("");
|
||||
}
|
||||
|
||||
if((entry = av_dict_get(file_metadata, "title", NULL, 0)) != NULL) {
|
||||
metadata.data.music.title = strdup(entry->value);
|
||||
} else {
|
||||
metadata.data.music.title = strdup("");
|
||||
}
|
||||
|
||||
metadata.data.music.tracks->data.track_audio.bitrate = pFormatCtx->bit_rate;
|
||||
}
|
||||
|
||||
void AVDecoder::getVideoMetadata(metadata_t &metadata)
|
||||
{
|
||||
AVDictionaryEntry *entry;
|
||||
AVDictionary* file_metadata = pFormatCtx->metadata;
|
||||
|
||||
if((entry = av_dict_get(file_metadata, "copyright", NULL, 0)) != NULL) {
|
||||
metadata.data.video.copyright = strdup(entry->value);
|
||||
} else {
|
||||
metadata.data.video.copyright = strdup("");
|
||||
}
|
||||
|
||||
if((entry = av_dict_get(file_metadata, "comments", NULL, 0)) != NULL) {
|
||||
metadata.data.video.explanation = strdup(entry->value);
|
||||
} else {
|
||||
metadata.data.video.explanation = strdup("");
|
||||
}
|
||||
|
||||
if((entry = av_dict_get(file_metadata, "title", NULL, 0)) != NULL) {
|
||||
metadata.data.video.title = strdup(entry->value);
|
||||
} else {
|
||||
metadata.data.video.title = strdup(metadata.name);
|
||||
}
|
||||
|
||||
metadata.data.video.tracks->data.track_video.duration = pFormatCtx->duration / 1000;
|
||||
metadata.data.video.tracks->data.track_video.bitrate = pFormatCtx->bit_rate;
|
||||
|
||||
int stream_index;
|
||||
AVCodec *codec = NULL;
|
||||
|
||||
if((stream_index = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0)) >= 0) {
|
||||
AVCodecContext *pCodecCtx = pFormatCtx->streams[stream_index]->codec;
|
||||
metadata.data.video.tracks->data.track_video.width = pCodecCtx->width;
|
||||
metadata.data.video.tracks->data.track_video.height = pCodecCtx->height;
|
||||
if(strcmp(codec->name, "h264") == 0) {
|
||||
metadata.data.video.tracks->data.track_video.codecType = CODEC_TYPE_AVC;
|
||||
} else if(strcmp(codec->name, "mpeg4") == 0) {
|
||||
metadata.data.video.tracks->data.track_video.codecType = CODEC_TYPE_MPEG4;
|
||||
} else {
|
||||
metadata.data.video.tracks->data.track_video.codecType = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AVDecoder::getPictureMetadata(metadata_t &metadata)
|
||||
{
|
||||
int stream_index;
|
||||
AVCodec *codec = NULL;
|
||||
|
||||
if((stream_index = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0)) >= 0) {
|
||||
AVCodecContext *pCodecCtx = pFormatCtx->streams[stream_index]->codec;
|
||||
metadata.data.photo.tracks->data.track_photo.width = pCodecCtx->width;
|
||||
metadata.data.photo.tracks->data.track_photo.height = pCodecCtx->height;
|
||||
}
|
||||
metadata.data.photo.title = strdup(metadata.name);
|
||||
}
|
||||
|
||||
QByteArray AVDecoder::getAudioThumbnail(int width, int height)
|
||||
{
|
||||
QByteArray data;
|
||||
|
||||
int stream_index;
|
||||
AVCodec *codec = NULL;
|
||||
if((stream_index = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0)) < 0) {
|
||||
// no thumbnail
|
||||
return data;
|
||||
}
|
||||
|
||||
AVPacket pkt;
|
||||
if(av_read_frame(pFormatCtx, &pkt) >= 0) {
|
||||
// first frame == first thumbnail (hopefully)
|
||||
QBuffer imgbuffer(&data);
|
||||
imgbuffer.open(QIODevice::WriteOnly);
|
||||
QImage img = QImage::fromData(QByteArray((const char *)pkt.data, pkt.size));
|
||||
QImage result = img.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
result.save(&imgbuffer, "JPEG");
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
AVFrame *AVDecoder::getDecodedFrame(AVCodecContext *pCodecCtx, int stream_index)
|
||||
{
|
||||
AVFrame *pFrame = avcodec_alloc_frame();
|
||||
|
||||
AVPacket packet;
|
||||
int frame_finished = 0;
|
||||
|
||||
while(!frame_finished && av_read_frame(pFormatCtx, &packet)>=0) {
|
||||
if(packet.stream_index == stream_index) {
|
||||
avcodec_decode_video2(pCodecCtx, pFrame, &frame_finished, &packet);
|
||||
}
|
||||
av_free_packet(&packet);
|
||||
}
|
||||
|
||||
if(frame_finished) {
|
||||
return pFrame;
|
||||
} else {
|
||||
av_free(pFrame);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray AVDecoder::getVideoThumbnail(int width, int height)
|
||||
{
|
||||
QByteArray data;
|
||||
int stream_index;
|
||||
AVFrame *pFrame;
|
||||
AVDictionary *opts = NULL;
|
||||
AVCodec *codec = NULL;
|
||||
AVCodecContext *pCodecCtx = NULL;
|
||||
|
||||
int percentage = QSettings().value("videoThumbnailSeekPercentage", 30).toInt();
|
||||
|
||||
if((stream_index = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0)) < 0) {
|
||||
return data;
|
||||
}
|
||||
|
||||
pCodecCtx = pFormatCtx->streams[stream_index]->codec;
|
||||
|
||||
if(avcodec_open2(pCodecCtx, codec, &opts) < 0) {
|
||||
avcodec_close(pCodecCtx);
|
||||
return data;
|
||||
}
|
||||
|
||||
qint64 seek_pos = pFormatCtx->duration * percentage / (AV_TIME_BASE * 100);
|
||||
qint64 frame = av_rescale(seek_pos,pFormatCtx->streams[stream_index]->time_base.den, pFormatCtx->streams[stream_index]->time_base.num);
|
||||
|
||||
if(avformat_seek_file(pFormatCtx, stream_index, 0, frame, frame, AVSEEK_FLAG_FRAME) < 0) {
|
||||
avcodec_close(pCodecCtx);
|
||||
return data;
|
||||
}
|
||||
|
||||
if((pFrame = getDecodedFrame(pCodecCtx, stream_index)) == NULL) {
|
||||
avcodec_close(pCodecCtx);
|
||||
return data;
|
||||
}
|
||||
|
||||
AVFrame *pFrameRGB = avcodec_alloc_frame();
|
||||
|
||||
int numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
|
||||
uint8_t *buffer = (uint8_t *)av_malloc(numBytes);
|
||||
|
||||
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
|
||||
|
||||
SwsContext *sws_ctx = sws_getContext(
|
||||
pCodecCtx->width,
|
||||
pCodecCtx->height,
|
||||
pCodecCtx->pix_fmt,
|
||||
pCodecCtx->width,
|
||||
pCodecCtx->height,
|
||||
PIX_FMT_RGB24,
|
||||
SWS_BICUBIC,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
if(!sws_ctx) {
|
||||
avcodec_close(pCodecCtx);
|
||||
return data;
|
||||
}
|
||||
|
||||
sws_scale(
|
||||
sws_ctx,
|
||||
pFrame->data,
|
||||
pFrame->linesize,
|
||||
0,
|
||||
pCodecCtx->height,
|
||||
pFrameRGB->data,
|
||||
pFrameRGB->linesize
|
||||
);
|
||||
|
||||
QImage image(pCodecCtx->width, pCodecCtx->height, QImage::Format_RGB888);
|
||||
|
||||
for(int y = 0, h = pCodecCtx->height, w = pCodecCtx->width; y < h; y++) {
|
||||
memcpy(image.scanLine(y), pFrameRGB->data[0] + y * pFrameRGB->linesize[0], w * 3);
|
||||
}
|
||||
|
||||
QBuffer imgbuffer(&data);
|
||||
imgbuffer.open(QIODevice::WriteOnly);
|
||||
QImage result = image.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
result.save(&imgbuffer, "JPEG");
|
||||
|
||||
av_free(buffer);
|
||||
av_free(pFrameRGB);
|
||||
av_free(pFrame);
|
||||
|
||||
avcodec_close(pCodecCtx);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void AVDecoder::close()
|
||||
{
|
||||
avformat_close_input(&pFormatCtx);
|
||||
pFormatCtx = NULL;
|
||||
}
|
68
src/avdecoder.h
Normal file
68
src/avdecoder.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AVDECODER_H
|
||||
#define AVDECODER_H
|
||||
|
||||
#include <QImage>
|
||||
#include <QString>
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libavutil/mathematics.h>
|
||||
}
|
||||
|
||||
#include <vitamtp.h>
|
||||
|
||||
class AVDecoder
|
||||
{
|
||||
public:
|
||||
AVDecoder();
|
||||
~AVDecoder();
|
||||
|
||||
bool open(const QString filename);
|
||||
void close();
|
||||
|
||||
QByteArray getAudioThumbnail(int width, int height);
|
||||
QByteArray getVideoThumbnail(int width, int height);
|
||||
void getPictureMetadata(metadata_t &metadata);
|
||||
void getAudioMetadata(metadata_t &metadata);
|
||||
void getVideoMetadata(metadata_t &metadata);
|
||||
|
||||
// simulate a static constructor to initialize libav only once
|
||||
class AvInit
|
||||
{
|
||||
public:
|
||||
AvInit() {
|
||||
av_register_all();
|
||||
}
|
||||
};
|
||||
|
||||
static AvInit init;
|
||||
|
||||
private:
|
||||
void AVFrameToQImage(AVFrame &frame, QImage &image, int width, int height);
|
||||
AVFrame *getDecodedFrame(AVCodecContext *pCodecCtx, int stream_index);
|
||||
|
||||
AVFormatContext *pFormatCtx;
|
||||
};
|
||||
|
||||
#endif // AVDECODER_H
|
115
src/capability.cpp
Normal file
115
src/capability.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "capability.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QHostInfo>
|
||||
|
||||
bool DeviceCapability::exchangeInfo(vita_device_t *device)
|
||||
{
|
||||
if(VitaMTP_GetVitaInfo(device, &vita_info) != PTP_RC_OK) {
|
||||
qWarning("Cannot retreve device information.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(vita_info.protocolVersion > VITAMTP_PROTOCOL_MAX_VERSION) {
|
||||
qWarning("Vita wants protocol version %08d while we only support %08d. Attempting to continue.",
|
||||
vita_info.protocolVersion, VITAMTP_PROTOCOL_MAX_VERSION);
|
||||
}
|
||||
|
||||
QString hostname = QHostInfo::localHostName();
|
||||
const initiator_info_t *pc_info = VitaMTP_Data_Initiator_New(hostname.toUtf8().data(), vita_info.protocolVersion);
|
||||
|
||||
// Next, we send the client's (this program) info (discard the const here)
|
||||
if(VitaMTP_SendInitiatorInfo(device, (initiator_info_t *)pc_info) != PTP_RC_OK) {
|
||||
qWarning("Cannot send host information.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(vita_info.protocolVersion >= VITAMTP_PROTOCOL_FW_2_10) {
|
||||
// Get the device's capabilities
|
||||
capability_info_t *vita_capabilities;
|
||||
|
||||
if(VitaMTP_GetVitaCapabilityInfo(device, &vita_capabilities) != PTP_RC_OK) {
|
||||
qWarning("Failed to get capability information from Vita.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: vitamtp needs to send the full metadata info to know the expected format
|
||||
// of thumbnails, for example. Until then lets discard the received info.
|
||||
|
||||
VitaMTP_Data_Free_Capability(vita_capabilities);
|
||||
// Send the host's capabilities
|
||||
capability_info_t *pc_capabilities = generate_pc_capability_info();
|
||||
|
||||
if(VitaMTP_SendPCCapabilityInfo(device, pc_capabilities) != PTP_RC_OK) {
|
||||
qWarning("Failed to send capability information to Vita.");
|
||||
free_pc_capability_info(pc_capabilities);
|
||||
return false;
|
||||
}
|
||||
|
||||
free_pc_capability_info(pc_capabilities);
|
||||
}
|
||||
|
||||
// Finally, we tell the Vita we are connected
|
||||
if(VitaMTP_SendHostStatus(device, VITA_HOST_STATUS_Connected) != PTP_RC_OK) {
|
||||
qWarning("Cannot send host status.");
|
||||
return false;
|
||||
}
|
||||
|
||||
VitaMTP_Data_Free_Initiator(pc_info);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeviceCapability::free_pc_capability_info(capability_info_t *info)
|
||||
{
|
||||
delete[] &info->functions.formats.next_item[-1];
|
||||
delete[] &info->functions.next_item[-1];
|
||||
delete info;
|
||||
}
|
||||
|
||||
capability_info_t *DeviceCapability::generate_pc_capability_info()
|
||||
{
|
||||
typedef capability_info::capability_info_function tfunction;
|
||||
typedef tfunction::capability_info_format tformat;
|
||||
|
||||
// TODO: Actually do this based on QCMA capabilities
|
||||
capability_info_t *pc_capabilities = new capability_info_t;
|
||||
pc_capabilities->version = "1.0";
|
||||
tfunction *functions = new tfunction[3]();
|
||||
tformat *game_formats = new tformat[5]();
|
||||
game_formats[0].contentType = "vitaApp";
|
||||
game_formats[0].next_item = &game_formats[1];
|
||||
game_formats[1].contentType = "PSPGame";
|
||||
game_formats[1].next_item = &game_formats[2];
|
||||
game_formats[2].contentType = "PSPSaveData";
|
||||
game_formats[2].next_item = &game_formats[3];
|
||||
game_formats[3].contentType = "PSGame";
|
||||
game_formats[3].next_item = &game_formats[4];
|
||||
game_formats[4].contentType = "PSMApp";
|
||||
functions[0].type = "game";
|
||||
functions[0].formats = game_formats[0];
|
||||
functions[0].next_item = &functions[1];
|
||||
functions[1].type = "backup";
|
||||
functions[1].next_item = &functions[2];
|
||||
functions[2].type = "systemUpdate";
|
||||
pc_capabilities->functions = functions[0];
|
||||
return pc_capabilities;
|
||||
}
|
52
src/capability.h
Normal file
52
src/capability.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CAPABILITY_H
|
||||
#define CAPABILITY_H
|
||||
|
||||
#include <vitamtp.h>
|
||||
|
||||
class DeviceCapability
|
||||
{
|
||||
public:
|
||||
explicit DeviceCapability() {}
|
||||
bool exchangeInfo(vita_device_t *device);
|
||||
|
||||
//TODO: vita_info_t doesn't retrieve this info, update vitamtp to get it
|
||||
const char *getVersion() {
|
||||
return "";
|
||||
}
|
||||
const char *getProtocol() {
|
||||
return "";
|
||||
}
|
||||
const char *getOnlineId() {
|
||||
return "PS Vita";
|
||||
}
|
||||
const char *getModelInfo() {
|
||||
return "";
|
||||
}
|
||||
|
||||
private:
|
||||
capability_info_t *generate_pc_capability_info();
|
||||
void free_pc_capability_info(capability_info_t *info);
|
||||
|
||||
vita_info_t vita_info;
|
||||
};
|
||||
|
||||
#endif // CAPABILITY_H
|
130
src/clientmanager.cpp
Normal file
130
src/clientmanager.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "clientmanager.h"
|
||||
#include "cmaclient.h"
|
||||
#include "utils.h"
|
||||
#include "forms/progressform.h"
|
||||
|
||||
#include <vitamtp.h>
|
||||
|
||||
ClientManager::ClientManager(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
ClientManager::~ClientManager()
|
||||
{
|
||||
VitaMTP_Cleanup();
|
||||
}
|
||||
|
||||
void ClientManager::databaseUpdated(int count)
|
||||
{
|
||||
progress.hide();
|
||||
if(count >= 0) {
|
||||
emit messageSent(tr("Added %1 items to the database").arg(count));
|
||||
} else {
|
||||
emit messageSent(tr("Database indexing aborted by user"));
|
||||
}
|
||||
}
|
||||
|
||||
void ClientManager::showPinDialog(QString name, int pin)
|
||||
{
|
||||
pinForm.setPin(name, pin);
|
||||
pinForm.startCountdown();
|
||||
}
|
||||
|
||||
void ClientManager::start()
|
||||
{
|
||||
if(VitaMTP_Init() < 0) {
|
||||
emit messageSent(tr("Cannot initialize VitaMTP library"));
|
||||
return;
|
||||
}
|
||||
|
||||
// initializing database for the first use
|
||||
refreshDatabase();
|
||||
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);
|
||||
|
||||
thread_count = 2;
|
||||
qDebug("Starting cma threads");
|
||||
CmaClient *client;
|
||||
|
||||
usb_thread = new QThread();
|
||||
client = new CmaClient();
|
||||
usb_thread->setObjectName("usb_thread");
|
||||
connect(usb_thread, SIGNAL(started()), client, SLOT(connectUsb()));
|
||||
connect(client, SIGNAL(finished()), usb_thread, SLOT(quit()), Qt::DirectConnection);
|
||||
connect(usb_thread, SIGNAL(finished()), usb_thread, SLOT(deleteLater()));
|
||||
connect(usb_thread, SIGNAL(finished()), this, SLOT(threadStopped()));
|
||||
connect(usb_thread, SIGNAL(finished()), client, SLOT(deleteLater()));
|
||||
|
||||
connect(client, SIGNAL(deviceConnected(QString)), this, SIGNAL(deviceConnected(QString)));
|
||||
connect(client, SIGNAL(deviceDisconnected()), this, SIGNAL(deviceDisconnected()));
|
||||
connect(client, SIGNAL(refreshDatabase()), this, SLOT(refreshDatabase()));
|
||||
|
||||
client->moveToThread(usb_thread);
|
||||
usb_thread->start();
|
||||
|
||||
wireless_thread = new QThread();
|
||||
client = new CmaClient();
|
||||
wireless_thread->setObjectName("wireless_thread");
|
||||
connect(wireless_thread, SIGNAL(started()), client, SLOT(connectWireless()));
|
||||
connect(client, SIGNAL(receivedPin(QString,int)), this, SLOT(showPinDialog(QString,int)));
|
||||
connect(client, SIGNAL(finished()), wireless_thread, SLOT(quit()), Qt::DirectConnection);
|
||||
connect(wireless_thread, SIGNAL(finished()), wireless_thread, SLOT(deleteLater()));
|
||||
connect(wireless_thread, SIGNAL(finished()), this, SLOT(threadStopped()));
|
||||
connect(wireless_thread, SIGNAL(finished()), client, SLOT(deleteLater()));
|
||||
|
||||
connect(client, SIGNAL(pinComplete()), &pinForm, SLOT(hide()));
|
||||
connect(client, SIGNAL(deviceConnected(QString)), this, SIGNAL(deviceConnected(QString)));
|
||||
connect(client, SIGNAL(deviceDisconnected()), this, SIGNAL(deviceDisconnected()));
|
||||
connect(client, SIGNAL(refreshDatabase()), this, SLOT(refreshDatabase()));
|
||||
|
||||
client->moveToThread(wireless_thread);
|
||||
wireless_thread->start();
|
||||
}
|
||||
|
||||
void ClientManager::refreshDatabase()
|
||||
{
|
||||
if(!db.reload()) {
|
||||
emit messageSent(tr("Cannot refresh the database while is in use"));
|
||||
} else {
|
||||
progress.show();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientManager::stop()
|
||||
{
|
||||
if(CmaClient::stop() < 0) {
|
||||
emit stopped();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientManager::threadStopped()
|
||||
{
|
||||
mutex.lock();
|
||||
if(--thread_count == 0) {
|
||||
emit stopped();
|
||||
}
|
||||
mutex.unlock();
|
||||
}
|
66
src/clientmanager.h
Normal file
66
src/clientmanager.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CLIENTMANAGER_H
|
||||
#define CLIENTMANAGER_H
|
||||
|
||||
#include "database.h"
|
||||
#include "forms/pinform.h"
|
||||
#include "forms/progressform.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
|
||||
class ClientManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ClientManager(QObject *parent = 0);
|
||||
~ClientManager();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
Database db;
|
||||
|
||||
private:
|
||||
int thread_count;
|
||||
QMutex mutex;
|
||||
|
||||
PinForm pinForm;
|
||||
ProgressForm progress;
|
||||
|
||||
QThread *usb_thread;
|
||||
QThread *wireless_thread;
|
||||
|
||||
signals:
|
||||
void stopped();
|
||||
void receivedPin(int);
|
||||
void deviceDisconnected();
|
||||
void messageSent(QString);
|
||||
void deviceConnected(QString);
|
||||
|
||||
private slots:
|
||||
void threadStopped();
|
||||
void refreshDatabase();
|
||||
void databaseUpdated(int count);
|
||||
void showPinDialog(QString name, int pin);
|
||||
};
|
||||
|
||||
#endif // CLIENTMANAGER_H
|
107
src/cmabroadcast.cpp
Normal file
107
src/cmabroadcast.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cmabroadcast.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QHostInfo>
|
||||
#include <QMutexLocker>
|
||||
#include <QSettings>
|
||||
#include <QUuid>
|
||||
|
||||
#include <vitamtp.h>
|
||||
|
||||
const QString CmaBroadcast::broadcast_reply =
|
||||
"%1\r\n"
|
||||
"host-id:%2\r\n"
|
||||
"host-type:%3\r\n"
|
||||
"host-name:%4\r\n"
|
||||
"host-mtp-protocol-version:%5\r\n"
|
||||
"host-request-port:%6\r\n"
|
||||
"host-wireless-protocol-version:%7\r\n"
|
||||
"host-supported-device:PS Vita, PS Vita TV\r\n";
|
||||
|
||||
const char *CmaBroadcast::broadcast_query_start = "SRCH";
|
||||
const char *CmaBroadcast::broadcast_query_end = " * HTTP/1.1\r\n";
|
||||
|
||||
const char *CmaBroadcast::broadcast_ok = "HTTP/1.1 200 OK";
|
||||
const char *CmaBroadcast::broadcast_unavailable = "HTTP/1.1 503 NG";
|
||||
|
||||
CmaBroadcast::CmaBroadcast(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
QSettings settings;
|
||||
// generate a GUID if doesn't exist yet in settings
|
||||
uuid = settings.value("guid").toString();
|
||||
if(uuid.isEmpty()) {
|
||||
uuid = QUuid::createUuid().toString().mid(1,36);
|
||||
settings.setValue("guid", uuid);
|
||||
}
|
||||
|
||||
hostname = QHostInfo::localHostName();
|
||||
setAvailable();
|
||||
|
||||
socket = new QUdpSocket(this);
|
||||
socket->bind(QHostAddress::Any, QCMA_REQUEST_PORT);
|
||||
connect(socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
|
||||
}
|
||||
|
||||
void CmaBroadcast::readPendingDatagrams()
|
||||
{
|
||||
if(socket->hasPendingDatagrams()) {
|
||||
QByteArray datagram;
|
||||
datagram.resize(socket->pendingDatagramSize());
|
||||
|
||||
QHostAddress sender;
|
||||
quint16 senderPort;
|
||||
|
||||
socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
|
||||
|
||||
if(datagram.startsWith(broadcast_query_start) && datagram.contains(broadcast_query_end)) {
|
||||
QMutexLocker locker(&mutex);
|
||||
socket->writeDatagram(reply, sender, senderPort);
|
||||
} else {
|
||||
qWarning("Unknown request: %.*s\n", datagram.length(), datagram.constData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CmaBroadcast::setAvailable()
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
reply.clear();
|
||||
reply.insert(0, broadcast_reply
|
||||
.arg(broadcast_ok, uuid, "win", hostname)
|
||||
.arg(VITAMTP_PROTOCOL_MAX_VERSION, 8, 10, QChar('0'))
|
||||
.arg(QCMA_REQUEST_PORT)
|
||||
.arg(VITAMTP_WIRELESS_MAX_VERSION, 8, 10, QChar('0')));
|
||||
reply.append('\0');
|
||||
}
|
||||
|
||||
void CmaBroadcast::setUnavailable()
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
reply.clear();
|
||||
reply.insert(0, broadcast_reply
|
||||
.arg(broadcast_unavailable, uuid, "win", hostname)
|
||||
.arg(VITAMTP_PROTOCOL_MAX_VERSION, 8, 10, QChar('0'))
|
||||
.arg(QCMA_REQUEST_PORT)
|
||||
.arg(VITAMTP_WIRELESS_MAX_VERSION, 8, 10, QChar('0')));
|
||||
reply.append('\0');
|
||||
}
|
58
src/cmabroadcast.h
Normal file
58
src/cmabroadcast.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CMABROADCAST_H
|
||||
#define CMABROADCAST_H
|
||||
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QUdpSocket>
|
||||
|
||||
#define QCMA_REQUEST_PORT 9309
|
||||
|
||||
class CmaBroadcast : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CmaBroadcast(QObject *parent = 0);
|
||||
|
||||
private:
|
||||
void replyBroadcast(const QByteArray &datagram);
|
||||
|
||||
QMutex mutex;
|
||||
QString uuid;
|
||||
QByteArray reply;
|
||||
QString hostname;
|
||||
QUdpSocket *socket;
|
||||
|
||||
static const QString broadcast_reply;
|
||||
static const char *broadcast_query_start;
|
||||
static const char *broadcast_query_end;
|
||||
static const char *broadcast_ok;
|
||||
static const char *broadcast_unavailable;
|
||||
|
||||
public slots:
|
||||
void setAvailable();
|
||||
void setUnavailable();
|
||||
|
||||
private slots:
|
||||
void readPendingDatagrams();
|
||||
};
|
||||
|
||||
#endif // CMABROADCAST_H
|
231
src/cmaclient.cpp
Normal file
231
src/cmaclient.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cmaclient.h"
|
||||
#include "capability.h"
|
||||
#include "avdecoder.h"
|
||||
#include "cmaevent.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QImage>
|
||||
#include <QTime>
|
||||
#include <QSettings>
|
||||
#include <QUrl>
|
||||
|
||||
QMutex CmaClient::mutex;
|
||||
QMutex CmaClient::runner;
|
||||
QMutex CmaClient::cancel;
|
||||
QSemaphore CmaClient::sema;
|
||||
|
||||
bool CmaClient::is_active = false;
|
||||
bool CmaClient::in_progress = false;
|
||||
int CmaClient::is_cancelled = false;
|
||||
|
||||
CmaClient *CmaClient::this_object = NULL;
|
||||
|
||||
CmaClient::CmaClient(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
this_object = this;
|
||||
}
|
||||
|
||||
void CmaClient::connectUsb()
|
||||
{
|
||||
vita_device_t *vita;
|
||||
|
||||
qDebug("Starting usb_thread: %lu", (unsigned long)QThread::currentThreadId());
|
||||
|
||||
setActive(true);
|
||||
|
||||
do {
|
||||
if((vita = VitaMTP_Get_First_USB_Vita()) !=NULL) {
|
||||
processNewConnection(vita);
|
||||
} else {
|
||||
//TODO: replace this with an event-driven setup
|
||||
Sleeper::msleep(2000);
|
||||
mutex.lock();
|
||||
if(in_progress) {
|
||||
sema.acquire();
|
||||
}
|
||||
mutex.unlock();
|
||||
}
|
||||
} while(isActive());
|
||||
|
||||
qDebug("Finishing usb_thread");
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void CmaClient::connectWireless()
|
||||
{
|
||||
vita_device_t *vita;
|
||||
wireless_host_info_t host = {NULL, NULL, NULL, QCMA_REQUEST_PORT};
|
||||
typedef CmaClient CC;
|
||||
|
||||
QTime now = QTime::currentTime();
|
||||
qsrand(now.msec());
|
||||
|
||||
qDebug("Starting wireless_thread: %lu", (unsigned long)QThread::currentThreadId());
|
||||
|
||||
setActive(true);
|
||||
|
||||
do {
|
||||
if((vita = VitaMTP_Get_First_Wireless_Vita(&host, 0, CC::cancelCallback, CC::deviceRegistered, CC::generatePin, CC::registrationComplete)) != NULL) {
|
||||
processNewConnection(vita);
|
||||
} else {
|
||||
Sleeper::msleep(2000);
|
||||
mutex.lock();
|
||||
if(in_progress) {
|
||||
sema.acquire();
|
||||
}
|
||||
mutex.unlock();;
|
||||
}
|
||||
} while(isActive());
|
||||
|
||||
qDebug("Finishing wireless_thread");
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void CmaClient::processNewConnection(vita_device_t *device)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
in_progress = true;
|
||||
broadcast.setUnavailable();
|
||||
|
||||
qDebug("Vita connected: id %s", VitaMTP_Get_Identification(device));
|
||||
DeviceCapability vita_info;
|
||||
|
||||
if(!vita_info.exchangeInfo(device)) {
|
||||
qCritical("Error while exchanging info with the vita");
|
||||
} else {
|
||||
// Conection successful, inform the user
|
||||
emit deviceConnected(QString(tr("Connected to ")) + vita_info.getOnlineId());
|
||||
enterEventLoop(device);
|
||||
}
|
||||
|
||||
VitaMTP_SendHostStatus(device, VITA_HOST_STATUS_EndConnection);
|
||||
qDebug("Releasing device...");
|
||||
VitaMTP_Release_Device(device);
|
||||
|
||||
emit deviceDisconnected();
|
||||
|
||||
broadcast.setAvailable();
|
||||
in_progress = false;
|
||||
sema.release();
|
||||
}
|
||||
|
||||
void CmaClient::registrationComplete()
|
||||
{
|
||||
qDebug("Registration completed");
|
||||
emit this_object->pinComplete();
|
||||
}
|
||||
|
||||
int CmaClient::deviceRegistered(const char *deviceid)
|
||||
{
|
||||
qDebug("Got connection request from %s", deviceid);
|
||||
// TODO: check the device to see if is already registered
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CmaClient::generatePin(wireless_vita_info_t *info, int *p_err)
|
||||
{
|
||||
qDebug("Registration request from %s (MAC: %s)", info->name, info->mac_addr);
|
||||
int pin = rand() % 10000 * 10000 | rand() % 10000;
|
||||
qDebug("Your registration PIN for %s is: %08d", info->name, pin);
|
||||
*p_err = 0;
|
||||
emit this_object->receivedPin(info->name, pin);
|
||||
return pin;
|
||||
}
|
||||
|
||||
int CmaClient::cancelCallback()
|
||||
{
|
||||
QMutexLocker locker(&cancel);
|
||||
return is_cancelled;
|
||||
}
|
||||
|
||||
void CmaClient::enterEventLoop(vita_device_t *device)
|
||||
{
|
||||
vita_event_t event;
|
||||
|
||||
qDebug("Starting event loop");
|
||||
|
||||
CmaEvent eventLoop (device);
|
||||
QThread thread;
|
||||
thread.setObjectName("event_thread");
|
||||
|
||||
eventLoop.moveToThread(&thread);
|
||||
connect(&thread, SIGNAL(started()), &eventLoop, SLOT(process()));
|
||||
connect(&eventLoop, SIGNAL(finishedEventLoop()), &thread, SLOT(quit()), Qt::DirectConnection);
|
||||
thread.start();
|
||||
|
||||
while(isActive()) {
|
||||
if(VitaMTP_Read_Event(device, &event) < 0) {
|
||||
qWarning("Error reading event from Vita.");
|
||||
break;
|
||||
}
|
||||
|
||||
// do not create a event for this since there aren't more events to read
|
||||
if(event.Code == PTP_EC_VITA_RequestTerminate) {
|
||||
qDebug("Terminating event thread");
|
||||
break;
|
||||
|
||||
// this one shuold be processed inmediately
|
||||
} else if(event.Code == PTP_EC_VITA_RequestCancelTask) {
|
||||
eventLoop.vitaEventCancelTask(&event, event.Param1);
|
||||
qDebug("Ended event, code: 0x%x, id: %d", event.Code, event.Param1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// the events are processed synchronously except for cancel/terminate
|
||||
qDebug("Sending new event");
|
||||
eventLoop.setEvent(event);
|
||||
}
|
||||
|
||||
eventLoop.stop();
|
||||
thread.wait();
|
||||
qDebug("Finishing event loop");
|
||||
}
|
||||
|
||||
int CmaClient::stop()
|
||||
{
|
||||
if(!isActive()) {
|
||||
return -1;
|
||||
}
|
||||
CmaClient::setActive(false);
|
||||
cancel.lock();
|
||||
is_cancelled = true;
|
||||
cancel.unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CmaClient::isActive()
|
||||
{
|
||||
QMutexLocker locker(&runner);
|
||||
return is_active;
|
||||
}
|
||||
|
||||
void CmaClient::setActive(bool state)
|
||||
{
|
||||
QMutexLocker locker(&runner);
|
||||
is_active = state;
|
||||
}
|
||||
|
86
src/cmaclient.h
Normal file
86
src/cmaclient.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CMACLIENT_H
|
||||
#define CMACLIENT_H
|
||||
|
||||
#include "database.h"
|
||||
#include "cmaevent.h"
|
||||
#include "cmaobject.h"
|
||||
#include "cmabroadcast.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QSemaphore>
|
||||
#include <QString>
|
||||
|
||||
#include <vitamtp.h>
|
||||
|
||||
class CmaClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CmaClient(QObject *parent = 0);
|
||||
|
||||
void launch();
|
||||
|
||||
private:
|
||||
static bool isActive();
|
||||
static void setActive(bool state);
|
||||
static bool isEventLoopEnabled();
|
||||
static void setEventLoop(bool state);
|
||||
void enterEventLoop(vita_device_t *device);
|
||||
|
||||
void processNewConnection(vita_device_t *device);
|
||||
|
||||
static int deviceRegistered(const char *deviceid);
|
||||
static int generatePin(wireless_vita_info_t *info, int *p_err);
|
||||
static int cancelCallback();
|
||||
static void registrationComplete();
|
||||
|
||||
CmaBroadcast broadcast;
|
||||
|
||||
//TODO: move all the control variables to the client manager class
|
||||
static bool is_active;
|
||||
static bool in_progress;
|
||||
static int is_cancelled;
|
||||
static CmaClient *this_object;
|
||||
static QMutex mutex;
|
||||
static QMutex runner;
|
||||
static QMutex cancel;
|
||||
static QSemaphore sema;
|
||||
|
||||
signals:
|
||||
void newEvent(vita_event_t event);
|
||||
void receivedPin(QString, int);
|
||||
void pinComplete();
|
||||
void deviceDetected();
|
||||
void deviceConnected(QString);
|
||||
void deviceDisconnected();
|
||||
void refreshDatabase();
|
||||
void finished();
|
||||
|
||||
public slots:
|
||||
static int stop();
|
||||
|
||||
private slots:
|
||||
void connectUsb();
|
||||
void connectWireless();
|
||||
};
|
||||
|
||||
#endif // CMACLIENT_H
|
905
src/cmaevent.cpp
Normal file
905
src/cmaevent.cpp
Normal file
@@ -0,0 +1,905 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cmaevent.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QSettings>
|
||||
#include <QUrl>
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
|
||||
Database *CmaEvent::db = NULL;
|
||||
|
||||
metadata_t CmaEvent::g_thumbmeta = {0, 0, 0, NULL, NULL, 0, 0, 0, Thumbnail, {{17, 144, 80, 0, 1, 1.0f, 2}}, NULL};
|
||||
|
||||
CmaEvent::CmaEvent(vita_device_t *s_device) :
|
||||
device(s_device), is_active(true)
|
||||
{
|
||||
}
|
||||
|
||||
void CmaEvent::process()
|
||||
{
|
||||
qDebug("Starting event_thread: %lu", (unsigned long)QThread::currentThreadId());
|
||||
while(true) {
|
||||
sema.acquire();
|
||||
if(!isActive()) {
|
||||
break;
|
||||
}
|
||||
mutex.lock();
|
||||
processEvent();
|
||||
mutex.unlock();
|
||||
}
|
||||
qDebug("Finishing event_thread");
|
||||
emit finishedEventLoop();
|
||||
}
|
||||
|
||||
bool CmaEvent::isActive()
|
||||
{
|
||||
QMutexLocker locker(&active);
|
||||
return is_active;
|
||||
}
|
||||
|
||||
void CmaEvent::stop()
|
||||
{
|
||||
QMutexLocker locker(&active);
|
||||
is_active = false;
|
||||
sema.release();
|
||||
}
|
||||
|
||||
void CmaEvent::setDevice(vita_device_t *device)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
this->device = device;
|
||||
}
|
||||
|
||||
void CmaEvent::setEvent(vita_event_t event)
|
||||
{
|
||||
mutex.lock();
|
||||
this->t_event = event;
|
||||
mutex.unlock();
|
||||
sema.release();
|
||||
}
|
||||
|
||||
void CmaEvent::processEvent()
|
||||
{
|
||||
switch(t_event.Code) {
|
||||
case PTP_EC_VITA_RequestSendNumOfObject:
|
||||
vitaEventSendNumOfObject(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestSendObjectMetadata:
|
||||
vitaEventSendObjectMetadata(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestSendObject:
|
||||
vitaEventSendObject(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestSendHttpObjectFromURL:
|
||||
vitaEventSendHttpObjectFromURL(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_Unknown1: // unimplemented
|
||||
vitaEventUnimplementated(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestSendObjectStatus:
|
||||
vitaEventSendObjectStatus(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestSendObjectThumb:
|
||||
vitaEventSendObjectThumb(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestDeleteObject:
|
||||
vitaEventDeleteObject(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestGetSettingInfo:
|
||||
vitaEventGetSettingInfo(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestSendHttpObjectPropFromURL:
|
||||
vitaEventSendHttpObjectPropFromURL(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestSendPartOfObject:
|
||||
vitaEventSendPartOfObject(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestOperateObject:
|
||||
vitaEventOperateObject(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestGetPartOfObject:
|
||||
vitaEventGetPartOfObject(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestSendStorageSize:
|
||||
vitaEventSendStorageSize(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestCheckExistance:
|
||||
vitaEventCheckExistance(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestGetTreatObject:
|
||||
vitaEventGetTreatObject(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestSendCopyConfirmationInfo:
|
||||
vitaEventSendCopyConfirmationInfo(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestSendObjectMetadataItems:
|
||||
vitaEventSendObjectMetadataItems(&t_event, t_event.Param1);
|
||||
break;
|
||||
case PTP_EC_VITA_RequestSendNPAccountInfo:
|
||||
vitaEventSendNPAccountInfo(&t_event, t_event.Param1);
|
||||
break;
|
||||
default:
|
||||
vitaEventUnimplementated(&t_event, t_event.Param1);
|
||||
}
|
||||
qDebug("Ended event, code: 0x%x, id: %d", t_event.Code, t_event.Param1);
|
||||
}
|
||||
|
||||
quint16 CmaEvent::processAllObjects(CMAObject *parent, quint32 handle)
|
||||
{
|
||||
union {
|
||||
unsigned char *fileData;
|
||||
uint32_t *handles;
|
||||
} data;
|
||||
|
||||
metadata_t remote_meta;
|
||||
unsigned int length;
|
||||
|
||||
if(VitaMTP_GetObject(device, handle, &remote_meta, (void **)&data, &length) != PTP_RC_OK) {
|
||||
qWarning("Cannot get object for handle %d", handle);
|
||||
return PTP_RC_VITA_Invalid_Data;
|
||||
}
|
||||
|
||||
CMAObject *object = db->pathToObject(remote_meta.name, parent->metadata.ohfi);
|
||||
|
||||
if(object) {
|
||||
qDebug("Deleting %s", object->path.toStdString().c_str());
|
||||
removeRecursively(object->path);
|
||||
db->remove(object);
|
||||
}
|
||||
|
||||
QDir dir(parent->path);
|
||||
|
||||
if(remote_meta.dataType & Folder) {
|
||||
if(!dir.mkpath(remote_meta.name)) {
|
||||
qWarning("Cannot create directory: %s", remote_meta.name);
|
||||
free(data.fileData);
|
||||
free(remote_meta.name);
|
||||
return PTP_RC_VITA_Failed_Operate_Object;
|
||||
}
|
||||
} else {
|
||||
QFile file(dir.absoluteFilePath(remote_meta.name));
|
||||
|
||||
if(!file.open(QIODevice::WriteOnly)) {
|
||||
qWarning("Cannot write to %s", remote_meta.name);
|
||||
free(data.fileData);
|
||||
free(remote_meta.name);
|
||||
return PTP_RC_VITA_Invalid_Permission;
|
||||
} else {
|
||||
file.write((const char *)data.fileData, remote_meta.size);
|
||||
}
|
||||
}
|
||||
|
||||
QFileInfo info(dir, remote_meta.name);
|
||||
object = new CMAObject(parent);
|
||||
object->initObject(info);
|
||||
object->metadata.handle = remote_meta.handle;
|
||||
db->append(parent->metadata.ohfi, object);
|
||||
free(remote_meta.name);
|
||||
|
||||
qDebug("Added object %s with OHFI %i to database", object->metadata.path, object->metadata.ohfi);
|
||||
|
||||
if(remote_meta.dataType & Folder) {
|
||||
for(unsigned int i = 0; i < length; i++) {
|
||||
quint16 ret = processAllObjects(object, data.handles[i]);
|
||||
|
||||
if(ret != PTP_RC_OK) {
|
||||
qDebug("Deleteting object with OHFI %d", object->metadata.ohfi);
|
||||
db->remove(object);
|
||||
free(data.fileData);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(data.fileData);
|
||||
return PTP_RC_OK;
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventGetTreatObject(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
|
||||
treat_object_t treatObject;
|
||||
|
||||
if(VitaMTP_GetTreatObject(device, eventId, &treatObject) != PTP_RC_OK) {
|
||||
qWarning("Cannot get information on object to get");
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&db->mutex);
|
||||
|
||||
CMAObject *parent = db->ohfiToObject(treatObject.ohfiParent);
|
||||
|
||||
if(parent == NULL) {
|
||||
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));
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventSendCopyConfirmationInfo(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
|
||||
copy_confirmation_info_t *info;
|
||||
if(VitaMTP_SendCopyConfirmationInfoInit(device, eventId, &info) != PTP_RC_OK) {
|
||||
qWarning("Error recieving initial information.");
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&db->mutex);
|
||||
|
||||
quint64 size = 0;
|
||||
|
||||
for(quint32 i = 0; i < info->count; i++) {
|
||||
CMAObject *object;
|
||||
|
||||
if((object = db->ohfiToObject(info->ohfi[i])) == NULL) {
|
||||
qWarning("Cannot find OHFI %d", info->ohfi[i]);
|
||||
free(info);
|
||||
return;
|
||||
}
|
||||
|
||||
size += object->metadata.size;
|
||||
}
|
||||
|
||||
if(VitaMTP_SendCopyConfirmationInfo(device, eventId, info, size) != PTP_RC_OK) {
|
||||
qWarning("Error sending copy confirmation");
|
||||
} else {
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
||||
|
||||
free(info);
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventSendObjectMetadataItems(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
|
||||
quint32 ohfi;
|
||||
if(VitaMTP_SendObjectMetadataItems(device, eventId, &ohfi) != PTP_RC_OK) {
|
||||
qWarning("Cannot get OHFI for retreving metadata");
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&db->mutex);
|
||||
|
||||
CMAObject *object = db->ohfiToObject(ohfi);
|
||||
|
||||
if(object == NULL) {
|
||||
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);
|
||||
|
||||
quint16 ret = VitaMTP_SendObjectMetadata(device, eventId, metadata);
|
||||
if(ret != PTP_RC_OK) {
|
||||
qWarning("Error sending metadata. Code: %04X", ret);
|
||||
} else {
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventSendNPAccountInfo(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
// AFAIK, Sony hasn't even implemented this in their CMA
|
||||
qWarning("Event 0x%x unimplemented!", event->Code);
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventUnimplementated(vita_event_t *event, int eventId)
|
||||
{
|
||||
qWarning("Unknown event not handled, code: 0x%x, id: %d", event->Code, eventId);
|
||||
qWarning("Param1: 0x%08X, Param2: 0x%08X, Param3: 0x%08X", event->Param1, event->Param2, event->Param3);
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventCancelTask(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
|
||||
quint32 eventIdToCancel = event->Param2;
|
||||
qDebug("Cancelling event %d", eventIdToCancel);
|
||||
quint16 ret = VitaMTP_CancelTask(device, eventIdToCancel);
|
||||
|
||||
// wait until the current event finishes so we can report the result to the device
|
||||
|
||||
qDebug("Waiting for send event to finish");
|
||||
mutex.lock();
|
||||
if(ret == PTP_RC_OK) {
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
uint ohfi = event->Param2;
|
||||
int items = db->filterObjects(ohfi, NULL);
|
||||
|
||||
if(VitaMTP_SendNumOfObject(device, eventId, items) != PTP_RC_OK) {
|
||||
qWarning("Error occured receiving object count for OHFI parent %d", ohfi);
|
||||
} else {
|
||||
qDebug("Returned count of %d objects for OHFI parent %d", items, ohfi);
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventSendObjectMetadata(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
|
||||
browse_info_t browse;
|
||||
|
||||
if(VitaMTP_GetBrowseInfo(device, eventId, &browse) != PTP_RC_OK) {
|
||||
qWarning("GetBrowseInfo failed");
|
||||
return;
|
||||
}
|
||||
QMutexLocker locker(&db->mutex);
|
||||
|
||||
metadata_t *meta;
|
||||
int count = db->filterObjects(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
|
||||
qWarning("Sending metadata for OHFI parent %d failed", browse.ohfiParent);
|
||||
} else {
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventSendObject(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
|
||||
int ohfi = event->Param2;
|
||||
|
||||
QMutexLocker locker(&db->mutex);
|
||||
|
||||
qDebug("Searching object with OHFI %d", ohfi);
|
||||
|
||||
Database::find_data iters;
|
||||
if(!db->find(ohfi, iters)) {
|
||||
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;
|
||||
|
||||
do {
|
||||
uchar *data = NULL;
|
||||
len = object->metadata.size;
|
||||
QFile file(object->path);
|
||||
|
||||
// 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(!file.open(QIODevice::ReadOnly) || (data = file.map(0, file.size())) == NULL) {
|
||||
qWarning("Failed to read %s", object->path.toStdString().c_str());
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Not_Exist_Object);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// get the PTP object ID for the parent to put the object
|
||||
// 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;
|
||||
}
|
||||
|
||||
// send the data over
|
||||
qDebug("Sending %s of %lu bytes to device", object->metadata.name, len);
|
||||
qDebug("OHFI %d with handle 0x%08X", ohfi, parentHandle);
|
||||
|
||||
VitaMTP_RegisterCancelEventId(eventId);
|
||||
quint16 ret = VitaMTP_SendObject(device, &parentHandle, &handle, &object->metadata, data);
|
||||
if(ret != PTP_RC_OK) {
|
||||
qWarning("Sending of %s failed. Code: %04X", object->metadata.name, ret);
|
||||
file.unmap(data);
|
||||
return;
|
||||
}
|
||||
|
||||
object->metadata.handle = handle;
|
||||
|
||||
if(object->metadata.dataType & File) {
|
||||
file.unmap(data);
|
||||
}
|
||||
|
||||
// break early if only a file needs to be sent
|
||||
if(!send_folder) {
|
||||
break;
|
||||
}
|
||||
|
||||
object = *++iters.it;
|
||||
|
||||
} while(iters.it != iters.end && object->metadata.ohfiParent >= OHFI_OFFSET); // get everything under this "folder"
|
||||
|
||||
VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, handle);
|
||||
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Data); // TODO: Send thumbnail
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventSendHttpObjectFromURL(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
|
||||
char *url;
|
||||
if(VitaMTP_GetUrl(device, eventId, &url) != PTP_RC_OK) {
|
||||
qWarning("Failed to recieve URL");
|
||||
return;
|
||||
}
|
||||
|
||||
QString urlpath = QSettings().value("urlPath").toString();
|
||||
QString basename = QFileInfo(QUrl(url).path()).fileName();
|
||||
QFile file(QDir(urlpath).absoluteFilePath(basename));
|
||||
|
||||
QByteArray data;
|
||||
|
||||
if(!file.open(QIODevice::ReadOnly)) {
|
||||
if(basename == "psp2-updatelist.xml") {
|
||||
qDebug("Found request for update list. Sending cached data");
|
||||
QFile res(":/main/resources/psp2-updatelist.xml");
|
||||
res.open(QIODevice::ReadOnly);
|
||||
data = res.readAll();
|
||||
} else {
|
||||
qWarning("Failed to download %s", url);
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Download);
|
||||
free(url);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
data = file.readAll();
|
||||
}
|
||||
|
||||
qDebug("Sending %i bytes of data for HTTP request %s", data.size(), url);
|
||||
|
||||
if(VitaMTP_SendHttpObjectFromURL(device, eventId, data.data(), data.size()) != PTP_RC_OK) {
|
||||
qWarning("Failed to send HTTP object");
|
||||
} else {
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
||||
|
||||
free(url);
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventSendObjectStatus(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
|
||||
object_status_t objectstatus;
|
||||
|
||||
if(VitaMTP_SendObjectStatus(device, eventId, &objectstatus) != PTP_RC_OK) {
|
||||
qWarning("Failed to get information for object status.");
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&db->mutex);
|
||||
|
||||
CMAObject *object = db->pathToObject(objectstatus.title, objectstatus.ohfiRoot);
|
||||
|
||||
if(object == NULL) { // 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);
|
||||
|
||||
if(VitaMTP_SendObjectMetadata(device, eventId, metadata) != PTP_RC_OK) {
|
||||
qWarning("Error sending metadata for %d", object->metadata.ohfi);
|
||||
} else {
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
||||
}
|
||||
|
||||
free(objectstatus.title);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
int ohfi = event->Param2;
|
||||
CMAObject *object = db->ohfiToObject(ohfi);
|
||||
|
||||
if(object == NULL) {
|
||||
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);
|
||||
|
||||
if(data.size() == 0) {
|
||||
qWarning("Cannot find/read thumbnail for %s", object->path.toStdString().c_str());
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Data);
|
||||
return;
|
||||
}
|
||||
|
||||
// workaround for the vitamtp locale bug
|
||||
char *locale = strdup(setlocale(LC_ALL, NULL));
|
||||
setlocale(LC_ALL, "C");
|
||||
|
||||
if(VitaMTP_SendObjectThumb(device, eventId, (metadata_t *)&g_thumbmeta, (uchar *)data.data(), data.size()) != PTP_RC_OK) {
|
||||
qWarning("Error sending thumbnail");
|
||||
} else {
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
||||
|
||||
// restore locale
|
||||
setlocale(LC_ALL, locale);
|
||||
free(locale);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
int ohfi = event->Param2;
|
||||
CMAObject *object = db->ohfiToObject(ohfi);
|
||||
|
||||
if(object == NULL) {
|
||||
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);
|
||||
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventGetSettingInfo(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
|
||||
settings_info_t *settingsinfo;
|
||||
if(VitaMTP_GetSettingInfo(device, eventId, &settingsinfo) != PTP_RC_OK) {
|
||||
qWarning("Failed to get setting info from Vita.");
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug("Current account id: %s", settingsinfo->current_account.accountId);
|
||||
|
||||
QSettings settings;
|
||||
|
||||
// Always refresh the account name
|
||||
settings.setValue("lastOnlineId", settingsinfo->current_account.userName);
|
||||
|
||||
if(settings.value("lastAccountId").toString() != settingsinfo->current_account.accountId) {
|
||||
db->setUUID(settingsinfo->current_account.accountId);
|
||||
// set the database to be updated ASAP
|
||||
emit refreshDatabase();
|
||||
}
|
||||
|
||||
// free all the information
|
||||
VitaMTP_Data_Free_Settings(settingsinfo);
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventSendHttpObjectPropFromURL(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
|
||||
char *url;
|
||||
if(VitaMTP_GetUrl(device, eventId, &url) != PTP_RC_OK) {
|
||||
qWarning("Failed to get URL");
|
||||
return;
|
||||
}
|
||||
|
||||
QString urlpath = QSettings().value("urlPath").toString();
|
||||
QString basename = QFileInfo(url).fileName();
|
||||
QFileInfo file(QDir(urlpath).absoluteFilePath(basename));
|
||||
|
||||
if(!file.exists()) {
|
||||
qWarning("The file %s is not accesible", url);
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Download);
|
||||
free(url);
|
||||
return;
|
||||
}
|
||||
|
||||
QString timestamp = file.lastModified().toString();
|
||||
|
||||
http_object_prop_t httpobjectprop;
|
||||
httpobjectprop.timestamp = timestamp.toUtf8().data();
|
||||
httpobjectprop.timestamp_len = timestamp.toUtf8().size();
|
||||
|
||||
if(VitaMTP_SendHttpObjectPropFromURL(device, eventId, &httpobjectprop) != PTP_RC_OK) {
|
||||
qWarning("Failed to send object properties");
|
||||
} else {
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
||||
|
||||
free(url);
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventSendPartOfObject(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
|
||||
send_part_init_t part_init;
|
||||
|
||||
if(VitaMTP_SendPartOfObjectInit(device, eventId, &part_init) != PTP_RC_OK) {
|
||||
qWarning("Cannot get information on object to send");
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&db->mutex);
|
||||
|
||||
CMAObject *object = db->ohfiToObject(part_init.ohfi);
|
||||
|
||||
if(object == NULL) {
|
||||
qWarning("Cannot find object for OHFI %d", part_init.ohfi);
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Context);
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(object->path);
|
||||
|
||||
if(!file.open(QIODevice::ReadOnly)) {
|
||||
qWarning("Cannot read %s", object->path.toStdString().c_str());
|
||||
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);
|
||||
|
||||
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);
|
||||
} else {
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventOperateObject(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
|
||||
operate_object_t operateobject;
|
||||
|
||||
if(VitaMTP_OperateObject(device, eventId, &operateobject) != PTP_RC_OK) {
|
||||
qWarning("Cannot get information on object to operate");
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&db->mutex);
|
||||
|
||||
CMAObject *root = db->ohfiToObject(operateobject.ohfi);
|
||||
|
||||
// end for renaming only
|
||||
if(root == NULL) {
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Not_Exist_Object);
|
||||
return;
|
||||
}
|
||||
|
||||
switch(operateobject.cmd) {
|
||||
case VITA_OPERATE_CREATE_FOLDER: {
|
||||
qDebug("Operate command %d: Create folder %s", operateobject.cmd, operateobject.title);
|
||||
|
||||
QDir dir(root->path);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VITA_OPERATE_RENAME: {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// rename in filesystem
|
||||
if(!QFile(oldpath).rename(root->path)) {
|
||||
qWarning("Unable to rename %s to %s", oldname.toStdString().c_str(), 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);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
qWarning("Operate command %d: Not implemented", operateobject.cmd);
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Operate_Object);
|
||||
break;
|
||||
}
|
||||
|
||||
free(operateobject.title);
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventGetPartOfObject(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
|
||||
unsigned char *data;
|
||||
send_part_init_t part_init;
|
||||
|
||||
if(VitaMTP_GetPartOfObject(device, eventId, &part_init, &data) != PTP_RC_OK) {
|
||||
qWarning("Cannot get object from device");
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&db->mutex);
|
||||
CMAObject *object = db->ohfiToObject(part_init.ohfi);
|
||||
|
||||
if(object == NULL) {
|
||||
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);
|
||||
|
||||
QFile file(object->path);
|
||||
if(!file.open(QIODevice::ReadWrite)) {
|
||||
qWarning("Cannot write to file %s", object->path.toStdString().c_str());
|
||||
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);
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
int ohfi = event->Param2;
|
||||
CMAObject *object = db->ohfiToObject(ohfi);
|
||||
|
||||
if(object == NULL) {
|
||||
qWarning("Error: Cannot find OHFI %d", ohfi);
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI);
|
||||
return;
|
||||
} else {
|
||||
QFile file(object->path);
|
||||
|
||||
if(!file.exists()) {
|
||||
// create the directory if doesn't exist so the query don't fail
|
||||
qDebug("Creating %s", object->path.toStdString().c_str());
|
||||
|
||||
if(!QDir(QDir::root()).mkpath(object->path)) {
|
||||
qWarning("Create directory failed");
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Permission);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quint64 total;
|
||||
quint64 free;
|
||||
|
||||
if(!getDiskSpace(object->path, &free, &total)) {
|
||||
qWarning("Cannot get disk space");
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Permission);
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug("Storage stats for drive containing OHFI %d, free: %llu, total: %llu", ohfi, free, total);
|
||||
|
||||
if(VitaMTP_SendStorageSize(device, eventId, total, free) != PTP_RC_OK) {
|
||||
qWarning("Send storage size failed");
|
||||
} else {
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void CmaEvent::vitaEventCheckExistance(vita_event_t *event, int eventId)
|
||||
{
|
||||
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
|
||||
|
||||
int handle = event->Param2;
|
||||
existance_object_t existance;
|
||||
|
||||
if(VitaMTP_CheckExistance(device, handle, &existance) != PTP_RC_OK) {
|
||||
qWarning("Cannot read information on object to be sent");
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&db->mutex);
|
||||
|
||||
CMAObject *object = db->pathToObject(existance.name, 0);
|
||||
|
||||
if(object == NULL) {
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Different_Object);
|
||||
} else {
|
||||
VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_VITA_Same_Object, object->metadata.ohfi);
|
||||
}
|
||||
|
||||
VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
|
||||
}
|
89
src/cmaevent.h
Normal file
89
src/cmaevent.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CMAEVENT_H
|
||||
#define CMAEVENT_H
|
||||
|
||||
#include "cmaobject.h"
|
||||
#include "database.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QSemaphore>
|
||||
|
||||
#include <vitamtp.h>
|
||||
|
||||
class CmaEvent : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CmaEvent(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);
|
||||
void vitaEventSendObject(vita_event_t *event, int eventId);
|
||||
void vitaEventSendObjectMetadata(vita_event_t *event, int eventId);
|
||||
void vitaEventSendNumOfObject(vita_event_t *event, int eventId);
|
||||
void vitaEventSendHttpObjectFromURL(vita_event_t *event, int eventId);
|
||||
void vitaEventUnimplementated(vita_event_t *event, int eventId);
|
||||
void vitaEventSendObjectStatus(vita_event_t *event, int eventId);
|
||||
void vitaEventSendObjectThumb(vita_event_t *event, int eventId);
|
||||
void vitaEventDeleteObject(vita_event_t *event, int eventId);
|
||||
void vitaEventGetSettingInfo(vita_event_t *event, int eventId);
|
||||
void vitaEventSendHttpObjectPropFromURL(vita_event_t *event, int eventId);
|
||||
void vitaEventSendPartOfObject(vita_event_t *event, int eventId);
|
||||
void vitaEventOperateObject(vita_event_t *event, int eventId);
|
||||
void vitaEventGetPartOfObject(vita_event_t *event, int eventId);
|
||||
void vitaEventSendStorageSize(vita_event_t *event, int eventId);
|
||||
void vitaEventCheckExistance(vita_event_t *event, int eventId);
|
||||
void vitaEventGetTreatObject(vita_event_t *event, int eventId);
|
||||
void vitaEventSendCopyConfirmationInfo(vita_event_t *event, int eventId);
|
||||
void vitaEventSendObjectMetadataItems(vita_event_t *event, int eventId);
|
||||
void vitaEventSendNPAccountInfo(vita_event_t *event, int eventId);
|
||||
|
||||
void processEvent();
|
||||
bool isActive();
|
||||
void setDevice(vita_device_t *device);
|
||||
|
||||
vita_device_t *device;
|
||||
vita_event_t t_event;
|
||||
|
||||
// control variables
|
||||
bool is_active;
|
||||
QMutex mutex;
|
||||
QMutex active;
|
||||
QSemaphore sema;
|
||||
|
||||
static metadata_t g_thumbmeta;
|
||||
|
||||
signals:
|
||||
void finishedEventLoop();
|
||||
void refreshDatabase();
|
||||
|
||||
public slots:
|
||||
void process();
|
||||
void setEvent(vita_event_t event);
|
||||
void stop();
|
||||
};
|
||||
|
||||
#endif // CMAEVENT_H
|
263
src/cmaobject.cpp
Normal file
263
src/cmaobject.cpp
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cmaobject.h"
|
||||
#include "sforeader.h"
|
||||
#include "avdecoder.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
CMAObject::~CMAObject()
|
||||
{
|
||||
free(metadata.name);
|
||||
free(metadata.path);
|
||||
|
||||
if(MASK_SET(metadata.dataType, SaveData | Folder)) {
|
||||
free(metadata.data.saveData.title);
|
||||
free(metadata.data.saveData.detail);
|
||||
free(metadata.data.saveData.dirName);
|
||||
free(metadata.data.saveData.savedataTitle);
|
||||
} else if(MASK_SET(metadata.dataType, Photo | File)) {
|
||||
free(metadata.data.photo.title);
|
||||
free(metadata.data.photo.fileName);
|
||||
delete metadata.data.photo.tracks;
|
||||
} else if(MASK_SET(metadata.dataType, Music | File)) {
|
||||
free(metadata.data.music.title);
|
||||
free(metadata.data.music.fileName);
|
||||
free(metadata.data.music.album);
|
||||
free(metadata.data.music.artist);
|
||||
delete metadata.data.music.tracks;
|
||||
} else if(MASK_SET(metadata.dataType, Video | File)) {
|
||||
free(metadata.data.video.title);
|
||||
free(metadata.data.video.explanation);
|
||||
free(metadata.data.video.fileName);
|
||||
free(metadata.data.video.copyright);
|
||||
delete metadata.data.video.tracks;
|
||||
}
|
||||
}
|
||||
|
||||
void CMAObject::loadSfoMetadata(const QString &path)
|
||||
{
|
||||
QString sfo = QDir(path).absoluteFilePath("PARAM.SFO");
|
||||
SfoReader reader;
|
||||
|
||||
if(reader.load(sfo)) {
|
||||
metadata.data.saveData.title = strdup(reader.value("TITLE", ""));
|
||||
metadata.data.saveData.detail = strdup(reader.value("SAVEDATA_DETAIL", ""));
|
||||
metadata.data.saveData.savedataTitle = strdup(reader.value("SAVEDATA_TITLE", ""));
|
||||
metadata.data.saveData.dateTimeUpdated = QFileInfo(sfo).created().toTime_t();
|
||||
} else {
|
||||
metadata.data.saveData.title = strdup(metadata.name);
|
||||
metadata.data.saveData.detail = strdup("");
|
||||
metadata.data.saveData.savedataTitle = strdup("");
|
||||
metadata.data.saveData.dateTimeUpdated = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CMAObject::loadMusicMetadata(const QString &path)
|
||||
{
|
||||
AVDecoder decoder;
|
||||
if(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;
|
||||
if(decoder.open(path)) {
|
||||
decoder.getVideoMetadata(metadata);
|
||||
} else {
|
||||
metadata.data.video.title = strdup(metadata.name);
|
||||
metadata.data.video.explanation = strdup("");
|
||||
metadata.data.video.copyright = strdup("");
|
||||
}
|
||||
}
|
||||
|
||||
void CMAObject::loadPhotoMetadata(const QString &path)
|
||||
{
|
||||
AVDecoder decoder;
|
||||
if(decoder.open(path)) {
|
||||
decoder.getPictureMetadata(metadata);
|
||||
} else {
|
||||
metadata.data.photo.title = strdup(metadata.name);
|
||||
}
|
||||
}
|
||||
|
||||
void CMAObject::initObject(const QFileInfo &file, int file_type)
|
||||
{
|
||||
metadata.name = strdup(file.fileName().toUtf8().data());
|
||||
metadata.ohfiParent = parent->metadata.ohfi;
|
||||
metadata.ohfi = ohfi_count++;
|
||||
|
||||
metadata.type = VITA_DIR_TYPE_MASK_REGULAR; // ignored for files
|
||||
metadata.dateTimeCreated = file.created().toTime_t();
|
||||
metadata.size = 0;
|
||||
DataType type = file.isFile() ? File : Folder;
|
||||
metadata.dataType = (DataType)(type | (parent->metadata.dataType & ~Folder));
|
||||
|
||||
// create additional metadata
|
||||
if(MASK_SET(metadata.dataType, SaveData | Folder)) {
|
||||
metadata.data.saveData.dirName = strdup(metadata.name);
|
||||
metadata.data.saveData.statusType = 1;
|
||||
loadSfoMetadata(file.absoluteFilePath());
|
||||
} else if(MASK_SET(metadata.dataType, Music | File)) {
|
||||
metadata.data.music.fileName = strdup(metadata.name);
|
||||
metadata.data.music.fileFormatType = audio_list[file_type].file_format;
|
||||
metadata.data.music.statusType = 1;
|
||||
metadata.data.music.numTracks = 1;
|
||||
metadata.data.music.tracks = new media_track();
|
||||
metadata.data.music.tracks->type = VITA_TRACK_TYPE_AUDIO;
|
||||
metadata.data.music.tracks->data.track_photo.codecType = audio_list[file_type].file_codec;
|
||||
loadMusicMetadata(file.absoluteFilePath());
|
||||
} else if(MASK_SET(metadata.dataType, Video | File)) {
|
||||
metadata.data.video.fileName = strdup(metadata.name);
|
||||
metadata.data.video.dateTimeUpdated = file.created().toTime_t();
|
||||
metadata.data.video.statusType = 1;
|
||||
metadata.data.video.fileFormatType = FILE_FORMAT_MP4;
|
||||
metadata.data.video.parentalLevel = 0;
|
||||
metadata.data.video.numTracks = 1;
|
||||
metadata.data.video.tracks = new media_track();
|
||||
metadata.data.video.tracks->type = VITA_TRACK_TYPE_VIDEO;
|
||||
loadVideoMetadata(file.absoluteFilePath());
|
||||
} else if(MASK_SET(metadata.dataType, Photo | File)) {
|
||||
metadata.data.photo.fileName = strdup(metadata.name);
|
||||
metadata.data.photo.fileFormatType = photo_list[file_type].file_format;
|
||||
metadata.data.photo.statusType = 1;
|
||||
metadata.data.photo.dateTimeOriginal = file.created().toTime_t();
|
||||
metadata.data.photo.numTracks = 1;
|
||||
metadata.data.photo.tracks = new media_track();
|
||||
metadata.data.photo.tracks->type = VITA_TRACK_TYPE_PHOTO;
|
||||
metadata.data.photo.tracks->data.track_photo.codecType = photo_list[file_type].file_codec;
|
||||
loadPhotoMetadata(file.absoluteFilePath());
|
||||
}
|
||||
|
||||
path = file.absoluteFilePath();
|
||||
|
||||
if(parent->metadata.path == NULL) {
|
||||
metadata.path = strdup(metadata.name);
|
||||
} else {
|
||||
QString newpath = QString(parent->metadata.path) + "/" + metadata.name;
|
||||
metadata.path = strdup(newpath.toUtf8().data());
|
||||
}
|
||||
|
||||
updateObjectSize(file.size());
|
||||
}
|
||||
|
||||
bool CMAObject::removeReferencedObject()
|
||||
{
|
||||
if(metadata.dataType & Folder) {
|
||||
return removeRecursively(path);
|
||||
} else {
|
||||
return QFile::remove(path);
|
||||
}
|
||||
}
|
||||
|
||||
void CMAObject::updateObjectSize(qint64 size)
|
||||
{
|
||||
if(parent) {
|
||||
parent->updateObjectSize(size);
|
||||
}
|
||||
//FIXME: size should be quint64
|
||||
metadata.size += size;
|
||||
}
|
||||
|
||||
void CMAObject::rename(const QString &newname)
|
||||
{
|
||||
free(metadata.name);
|
||||
metadata.name = strdup(newname.toUtf8().data());
|
||||
|
||||
if(metadata.path) {
|
||||
QStringList metadata_path(QString(metadata.path).split("/"));
|
||||
metadata_path.replace(metadata_path.count() - 1, newname);
|
||||
free(metadata.path);
|
||||
metadata.path = strdup(metadata_path.join("/").toUtf8().data());
|
||||
}
|
||||
|
||||
path = QFileInfo(path).absoluteDir().path() + "/" + newname;
|
||||
}
|
||||
|
||||
void CMAObject::refreshPath()
|
||||
{
|
||||
if(parent) {
|
||||
free(metadata.path);
|
||||
QString newpath(QString(parent->metadata.path) + "/" + metadata.name);
|
||||
metadata.path = strdup(newpath.toUtf8().data());
|
||||
path = parent->path + "/" + metadata.name;
|
||||
}
|
||||
}
|
||||
|
||||
bool CMAObject::hasParent(const CMAObject *obj)
|
||||
{
|
||||
if(parent) {
|
||||
if(metadata.ohfiParent == obj->metadata.ohfi) {
|
||||
return true;
|
||||
} else {
|
||||
return parent->hasParent(obj);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CMAObject::operator==(const CMAObject &obj)
|
||||
{
|
||||
return metadata.ohfi == obj.metadata.ohfi;
|
||||
}
|
||||
|
||||
bool CMAObject::operator!=(const CMAObject &obj)
|
||||
{
|
||||
return metadata.ohfi != obj.metadata.ohfi;
|
||||
}
|
||||
|
||||
bool CMAObject::operator<(const CMAObject &obj)
|
||||
{
|
||||
return metadata.ohfi < obj.metadata.ohfi;
|
||||
}
|
99
src/cmaobject.h
Normal file
99
src/cmaobject.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CMAOBJECT_H
|
||||
#define CMAOBJECT_H
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QString>
|
||||
|
||||
#include <vitamtp.h>
|
||||
|
||||
#define OHFI_OFFSET 256
|
||||
|
||||
#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:
|
||||
explicit CMAObject(CMAObject *parent = 0);
|
||||
~CMAObject();
|
||||
|
||||
void refreshPath();
|
||||
bool removeReferencedObject();
|
||||
void rename(const QString &name);
|
||||
void updateObjectSize(qint64 size);
|
||||
bool hasParent(const CMAObject *obj);
|
||||
void initObject(const QFileInfo &file, int file_type = -1);
|
||||
|
||||
bool operator==(const CMAObject &obj);
|
||||
bool operator!=(const CMAObject &obj);
|
||||
bool operator<(const CMAObject &obj);
|
||||
|
||||
inline void setOhfi(int ohfi) {
|
||||
metadata.ohfi = ohfi;
|
||||
}
|
||||
|
||||
inline static void resetOhfiCounter() {
|
||||
ohfi_count = OHFI_OFFSET;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
protected:
|
||||
static int ohfi_count;
|
||||
|
||||
private:
|
||||
void loadSfoMetadata(const QString &path);
|
||||
void loadMusicMetadata(const QString &path);
|
||||
void loadVideoMetadata(const QString &path);
|
||||
void loadPhotoMetadata(const QString &path);
|
||||
};
|
||||
|
||||
#endif // CMAOBJECT_H
|
147
src/cmarootobject.cpp
Normal file
147
src/cmarootobject.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cmarootobject.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
QString CMARootObject::uuid = "ffffffffffffffff";
|
||||
|
||||
CMARootObject::CMARootObject(int ohfi) :
|
||||
num_filters(0), filters(NULL), root_ohfi(ohfi)
|
||||
{
|
||||
}
|
||||
|
||||
void CMARootObject::initObject(const QString &path)
|
||||
{
|
||||
metadata.ohfi = root_ohfi;
|
||||
metadata.type = VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_REGULAR;
|
||||
|
||||
switch(root_ohfi) {
|
||||
case VITA_OHFI_MUSIC:
|
||||
metadata.dataType = Music;
|
||||
this->path = path;
|
||||
num_filters = 5;
|
||||
filters = new metadata_t[5];
|
||||
createFilter(&filters[0], "Artists", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ARTISTS);
|
||||
createFilter(&filters[1], "Albums", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ALBUMS);
|
||||
createFilter(&filters[2], "Songs", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_SONGS);
|
||||
createFilter(&filters[3], "Genres", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_GENRES);
|
||||
createFilter(&filters[4], "Playlists", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_PLAYLISTS);
|
||||
break;
|
||||
|
||||
case VITA_OHFI_PHOTO:
|
||||
metadata.dataType = Photo;
|
||||
this->path = path;
|
||||
num_filters = 2;
|
||||
filters = new metadata_t[2];
|
||||
createFilter(&filters[0], "Folders", VITA_DIR_TYPE_MASK_PHOTO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_REGULAR);
|
||||
createFilter(&filters[1], "All", VITA_DIR_TYPE_MASK_PHOTO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ALL);
|
||||
break;
|
||||
|
||||
case VITA_OHFI_VIDEO:
|
||||
metadata.dataType = Video;
|
||||
this->path = path;
|
||||
num_filters = 2;
|
||||
filters = new metadata_t[2];
|
||||
createFilter(&filters[0], "Folders", VITA_DIR_TYPE_MASK_VIDEO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_REGULAR);
|
||||
createFilter(&filters[1], "All", VITA_DIR_TYPE_MASK_VIDEO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ALL);
|
||||
break;
|
||||
|
||||
case VITA_OHFI_VITAAPP:
|
||||
metadata.dataType = App;
|
||||
this->path = QDir(QDir(path).absoluteFilePath("APP")).absoluteFilePath(uuid);
|
||||
num_filters = 0;
|
||||
break;
|
||||
|
||||
case VITA_OHFI_PSPAPP:
|
||||
metadata.dataType = App;
|
||||
this->path = QDir(QDir(path).absoluteFilePath("PGAME")).absoluteFilePath(uuid);
|
||||
num_filters = 0;
|
||||
break;
|
||||
|
||||
case VITA_OHFI_PSPSAVE:
|
||||
metadata.dataType = SaveData;
|
||||
this->path = QDir(QDir(path).absoluteFilePath("PSAVEDATA")).absoluteFilePath(uuid);
|
||||
num_filters = 0;
|
||||
break;
|
||||
|
||||
case VITA_OHFI_PSXAPP:
|
||||
metadata.dataType = App;
|
||||
this->path = QDir(QDir(path).absoluteFilePath("PSGAME")).absoluteFilePath(uuid);
|
||||
num_filters = 0;
|
||||
break;
|
||||
|
||||
case VITA_OHFI_PSMAPP:
|
||||
metadata.dataType = App;
|
||||
this->path = QDir(QDir(path).absoluteFilePath("PSM")).absoluteFilePath(uuid);
|
||||
num_filters = 0;
|
||||
break;
|
||||
|
||||
case VITA_OHFI_BACKUP:
|
||||
metadata.dataType = App;
|
||||
this->path = QDir(QDir(path).absoluteFilePath("SYSTEM")).absoluteFilePath(uuid);
|
||||
num_filters = 0;
|
||||
}
|
||||
|
||||
// create the backup directories
|
||||
QDir dir(this->path);
|
||||
dir.mkpath(dir.absolutePath());
|
||||
}
|
||||
|
||||
CMARootObject::~CMARootObject()
|
||||
{
|
||||
for(int i = 0; i < num_filters; i++) {
|
||||
free(filters[i].name);
|
||||
free(filters[i].path);
|
||||
}
|
||||
|
||||
delete[] filters;
|
||||
}
|
||||
|
||||
void CMARootObject::createFilter(metadata_t *filter, const char *name, int type)
|
||||
{
|
||||
filter->ohfiParent = metadata.ohfi;
|
||||
filter->ohfi = ohfi_count++;
|
||||
filter->name = strdup(name);
|
||||
filter->path = strdup(metadata.path ? metadata.path : "");
|
||||
filter->type = type;
|
||||
filter->dateTimeCreated = 0;
|
||||
filter->size = 0;
|
||||
filter->dataType = static_cast<DataType>(Folder | Special);
|
||||
filter->next_metadata = NULL;
|
||||
//qDebug("Added filter %s to database with OHFI %d (%s)", name, filter->ohfi, metadata.name);
|
||||
}
|
||||
|
||||
int CMARootObject::getFilters(metadata_t **p_head)
|
||||
{
|
||||
int numObjects = num_filters;
|
||||
|
||||
for(int i = 0; i < numObjects; i++) {
|
||||
filters[i].next_metadata = &filters[i + 1];
|
||||
}
|
||||
|
||||
filters[numObjects - 1].next_metadata = NULL;
|
||||
|
||||
if(p_head != NULL) {
|
||||
*p_head = filters;
|
||||
}
|
||||
|
||||
return numObjects;
|
||||
}
|
49
src/cmarootobject.h
Normal file
49
src/cmarootobject.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CMAROOTOBJECT_H
|
||||
#define CMAROOTOBJECT_H
|
||||
|
||||
#include "cmaobject.h"
|
||||
|
||||
#include <QList>
|
||||
|
||||
#include <vitamtp.h>
|
||||
|
||||
class CMARootObject : public CMAObject
|
||||
{
|
||||
public:
|
||||
explicit CMARootObject(int ohfi);
|
||||
~CMARootObject();
|
||||
|
||||
void initObject(const QString &path);
|
||||
void remove(const CMAObject *obj);
|
||||
int getFilters(metadata_t **p_head);
|
||||
|
||||
int num_filters;
|
||||
metadata_t *filters;
|
||||
static QString uuid;
|
||||
|
||||
private:
|
||||
void createFilter(metadata_t *filter, const char *name, int type);
|
||||
|
||||
int root_ohfi;
|
||||
};
|
||||
|
||||
#endif // CMAROOTOBJECT_H
|
503
src/database.cpp
Normal file
503
src/database.cpp
Normal file
@@ -0,0 +1,503 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "database.h"
|
||||
#include "cmaobject.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QSettings>
|
||||
#include <QTextStream>
|
||||
#include <QThread>
|
||||
#include <QDebug>
|
||||
|
||||
Database::Database() :
|
||||
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()
|
||||
{
|
||||
if(mutex.tryLock()) {
|
||||
timer->start();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Database::process()
|
||||
{
|
||||
destroy();
|
||||
cancel_operation = false;
|
||||
int count = create();
|
||||
cancel_operation = false;
|
||||
qDebug("Added %i entries to the database", count);
|
||||
if(count < 0) {
|
||||
destroy();
|
||||
}
|
||||
emit updated(count);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void Database::cancelOperation()
|
||||
{
|
||||
QMutexLocker locker(&cancel);
|
||||
cancel_operation = true;
|
||||
}
|
||||
|
||||
bool Database::continueOperation()
|
||||
{
|
||||
QMutexLocker locker(&cancel);
|
||||
return !cancel_operation;
|
||||
}
|
||||
|
||||
int Database::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<CMARootObject *>((*root).takeFirst());
|
||||
delete first;
|
||||
qDeleteAll(*root);
|
||||
}
|
||||
|
||||
object_list.clear();
|
||||
}
|
||||
|
||||
bool Database::removeInternal(root_list &list, const CMAObject *obj)
|
||||
{
|
||||
bool found = false;
|
||||
QList<CMAObject *>::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<CMARootObject *>(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<CMARootObject *>(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)) {
|
||||
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)) {
|
||||
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)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
101
src/database.h
Normal file
101
src/database.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DATABASE_H
|
||||
#define DATABASE_H
|
||||
|
||||
#include "cmarootobject.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include <vitamtp.h>
|
||||
|
||||
class Database : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef struct {
|
||||
QList<CMAObject *>::const_iterator it;
|
||||
QList<CMAObject *>::const_iterator end;
|
||||
} find_data;
|
||||
|
||||
explicit Database();
|
||||
~Database();
|
||||
|
||||
bool reload();
|
||||
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);
|
||||
|
||||
QMutex mutex;
|
||||
|
||||
private:
|
||||
typedef QList<CMAObject *> root_list;
|
||||
typedef QMap<int, root_list> 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);
|
||||
int checkFileType(const QString path, int ohfi_root);
|
||||
void dumpMetadataList(const metadata_t *p_head);
|
||||
bool continueOperation();
|
||||
|
||||
// control variables
|
||||
QMutex cancel;
|
||||
bool cancel_operation;
|
||||
|
||||
QTimer *timer;
|
||||
QThread *thread;
|
||||
map_list object_list;
|
||||
|
||||
signals:
|
||||
void fileAdded(QString);
|
||||
void directoryAdded(QString);
|
||||
void updated(int);
|
||||
|
||||
protected slots:
|
||||
void process();
|
||||
|
||||
public slots:
|
||||
void cancelOperation();
|
||||
};
|
||||
|
||||
#endif // DATABASE_H
|
87
src/forms/backupitem.cpp
Normal file
87
src/forms/backupitem.cpp
Normal file
@@ -0,0 +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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "backupitem.h"
|
||||
#include "ui_backupitem.h"
|
||||
#include "../utils.h"
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QUrl>
|
||||
|
||||
const QString BackupItem::nameTemplate = "<html><head/><body>"
|
||||
"<p><span style=\" font-size:12pt; font-weight:600;\">%1</span></p>"
|
||||
"<p><span style=\" font-size:10pt;\">%2</span></p>"
|
||||
"</body></html>";
|
||||
|
||||
BackupItem::BackupItem(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::BackupItem)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
// connect the buttons
|
||||
connect(ui->openButton, SIGNAL(clicked()), this, SLOT(openDirectory()));
|
||||
connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(removeEntry()));
|
||||
}
|
||||
|
||||
BackupItem::~BackupItem()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void BackupItem::openDirectory()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("file:///" + path));
|
||||
}
|
||||
|
||||
void BackupItem::removeEntry()
|
||||
{
|
||||
emit deleteEntry(this);
|
||||
}
|
||||
|
||||
const QPixmap *BackupItem::getIconPixmap()
|
||||
{
|
||||
return ui->itemPicture->pixmap();
|
||||
}
|
||||
|
||||
void BackupItem::setDirectory(const QString path)
|
||||
{
|
||||
this->path = path;
|
||||
}
|
||||
|
||||
void BackupItem::setItemInfo(const QString name, const QString size)
|
||||
{
|
||||
ui->itemName->setText(nameTemplate.arg(name, size));
|
||||
}
|
||||
|
||||
int BackupItem::getIconWidth()
|
||||
{
|
||||
return ui->itemPicture->width();
|
||||
}
|
||||
|
||||
void BackupItem::setItemIcon(const QString path, int width)
|
||||
{
|
||||
ui->itemPicture->setMinimumWidth(width);
|
||||
ui->itemPicture->setPixmap(QPixmap(path));
|
||||
}
|
||||
|
||||
bool BackupItem::lessThan(const BackupItem *s1, const BackupItem *s2)
|
||||
{
|
||||
return s1->title.compare(s2->title) < 0;
|
||||
}
|
||||
|
62
src/forms/backupitem.h
Normal file
62
src/forms/backupitem.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BACKUPITEM_H
|
||||
#define BACKUPITEM_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class BackupItem;
|
||||
}
|
||||
|
||||
class BackupItem : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BackupItem(QWidget *parent = 0);
|
||||
~BackupItem();
|
||||
|
||||
void setItemInfo(const QString name, const QString size);
|
||||
void setItemIcon(const QString path, int width = 48);
|
||||
void setDirectory(const QString path);
|
||||
const QPixmap *getIconPixmap();
|
||||
int getIconWidth();
|
||||
|
||||
static bool lessThan(const BackupItem *s1, const BackupItem *s2);
|
||||
|
||||
int row;
|
||||
int ohfi;
|
||||
QString title;
|
||||
|
||||
private:
|
||||
QString path;
|
||||
Ui::BackupItem *ui;
|
||||
static const QString nameTemplate;
|
||||
|
||||
signals:
|
||||
void deleteEntry(BackupItem *entry);
|
||||
|
||||
private slots:
|
||||
void openDirectory();
|
||||
void removeEntry();
|
||||
};
|
||||
|
||||
#endif // BACKUPITEM_H
|
102
src/forms/backupitem.ui
Normal file
102
src/forms/backupitem.ui
Normal file
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>BackupItem</class>
|
||||
<widget class="QWidget" name="BackupItem">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>634</width>
|
||||
<height>86</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="itemPicture">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>48</width>
|
||||
<height>48</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>48</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="itemName">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-size:12pt; font-weight:600;">Game Name</span></p><p><span style=" font-size:10pt;">0.00 GiB</span></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="deleteButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete entry</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="openButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
221
src/forms/backupmanagerform.cpp
Normal file
221
src/forms/backupmanagerform.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "backupmanagerform.h"
|
||||
#include "ui_backupmanagerform.h"
|
||||
#include "../cmaobject.h"
|
||||
#include "../sforeader.h"
|
||||
#include "confirmdialog.h"
|
||||
#include "../utils.h"
|
||||
#include "filterlineedit.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDir>
|
||||
#include <QSettings>
|
||||
|
||||
#include <vitamtp.h>
|
||||
|
||||
BackupManagerForm::BackupManagerForm(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::BackupManagerForm)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setupForm();
|
||||
}
|
||||
|
||||
BackupManagerForm::~BackupManagerForm()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void BackupManagerForm::setupForm()
|
||||
{
|
||||
this->resize(800, 480);
|
||||
connect(ui->backupComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(loadBackupListing(int)));
|
||||
ui->tableWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
ui->tableWidget->horizontalHeader()->hide();
|
||||
// the the account name when vitamtp returns this value
|
||||
ui->accountBox->addItem(QSettings().value("lastOnlineId", tr("Default account")).toString());
|
||||
}
|
||||
|
||||
void BackupManagerForm::removeEntry(BackupItem *item)
|
||||
{
|
||||
ConfirmDialog msgBox;
|
||||
|
||||
msgBox.setMessageText(tr("Are you sure to remove the backup of the following entry?"), item->title);
|
||||
msgBox.setMessagePixmap(*item->getIconPixmap(), item->getIconWidth());
|
||||
|
||||
if(msgBox.exec() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&db->mutex);
|
||||
|
||||
CMAObject *obj = db->ohfiToObject(item->ohfi);
|
||||
if(obj) {
|
||||
obj->removeReferencedObject();
|
||||
db->remove(obj);
|
||||
}
|
||||
ui->tableWidget->removeRow(item->row);
|
||||
obj = db->ohfiToObject(obj->metadata.ohfiParent);
|
||||
if(obj) {
|
||||
setBackupUsage(obj->metadata.size);
|
||||
}
|
||||
}
|
||||
|
||||
void BackupManagerForm::setBackupUsage(quint64 size)
|
||||
{
|
||||
ui->usageLabel->setText(tr("Backup disk usage: %1").arg(readable_size(size, true)));
|
||||
}
|
||||
|
||||
void BackupManagerForm::loadBackupListing(int index)
|
||||
{
|
||||
int ohfi;
|
||||
bool sys_dir;
|
||||
int img_width;
|
||||
|
||||
ui->tableWidget->clear();
|
||||
|
||||
switch(index) {
|
||||
case 0:
|
||||
ohfi = VITA_OHFI_VITAAPP;
|
||||
img_width = 48;
|
||||
sys_dir = true;
|
||||
break;
|
||||
case 1:
|
||||
ohfi = VITA_OHFI_PSPAPP;
|
||||
img_width = 80;
|
||||
sys_dir = true;
|
||||
break;
|
||||
case 2:
|
||||
ohfi = VITA_OHFI_PSMAPP;
|
||||
img_width = 48;
|
||||
sys_dir = true;
|
||||
break;
|
||||
case 3:
|
||||
ohfi = VITA_OHFI_PSXAPP;
|
||||
img_width = 48;
|
||||
sys_dir = true;
|
||||
break;
|
||||
case 4:
|
||||
ohfi = VITA_OHFI_PSPSAVE;
|
||||
img_width = 80;
|
||||
sys_dir = false;
|
||||
break;
|
||||
case 5:
|
||||
ohfi = VITA_OHFI_BACKUP;
|
||||
img_width = 48;
|
||||
sys_dir = false;
|
||||
break;
|
||||
default:
|
||||
ohfi = VITA_OHFI_VITAAPP;
|
||||
img_width = 48;
|
||||
sys_dir = true;
|
||||
}
|
||||
|
||||
db->mutex.lock();
|
||||
|
||||
// get the item list
|
||||
metadata_t *meta;
|
||||
int row_count = db->filterObjects(ohfi, &meta);
|
||||
ui->tableWidget->setRowCount(row_count);
|
||||
|
||||
// adjust the table item width to fill all the widget
|
||||
QHeaderView *vert_header = ui->tableWidget->verticalHeader();
|
||||
QHeaderView *horiz_header = ui->tableWidget->horizontalHeader();
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
horiz_header->setSectionResizeMode(QHeaderView::Stretch);
|
||||
#else
|
||||
horiz_header->setResizeMode(QHeaderView::Stretch);
|
||||
#endif
|
||||
CMAObject *obj = db->ohfiToObject(ohfi);
|
||||
setBackupUsage(obj->metadata.size);
|
||||
|
||||
QList<BackupItem *> item_list;
|
||||
|
||||
while(meta) {
|
||||
QString base_path = obj->path + QDir::separator() + meta->name;
|
||||
QString parent_path = sys_dir ? base_path + QDir::separator() + "sce_sys" : base_path;
|
||||
SfoReader reader;
|
||||
QString game_name;
|
||||
|
||||
// retrieve the game name from the SFO
|
||||
if(reader.load(QDir(parent_path).absoluteFilePath(sys_dir ? "param.sfo" : "PARAM.SFO"))) {
|
||||
game_name = QString::fromUtf8(reader.value("TITLE", meta->name));
|
||||
} else {
|
||||
game_name = QString(meta->name);
|
||||
}
|
||||
|
||||
BackupItem *item = new BackupItem();
|
||||
// save the game title and ohfi for sorting/deleting
|
||||
item->ohfi = meta->ohfi;
|
||||
item->title = game_name;
|
||||
|
||||
connect(item, SIGNAL(deleteEntry(BackupItem*)), this, SLOT(removeEntry(BackupItem*)));
|
||||
QString size = readable_size(meta->size);
|
||||
|
||||
// check if the game data is present, else is just a LiveArea launcher
|
||||
if(sys_dir && !(QDir(base_path + QDir::separator() + "app").exists() || QDir(base_path + QDir::separator() + "game").exists())) {
|
||||
size.append(tr(" - (Launcher only)"));
|
||||
}
|
||||
|
||||
item->setItemInfo(game_name, size);
|
||||
item->setItemIcon(QDir(parent_path).absoluteFilePath(sys_dir ? "icon0.png" : "ICON0.PNG"), img_width);
|
||||
item->setDirectory(obj->path + QDir::separator() + meta->name);
|
||||
item->resize(646, 70);
|
||||
|
||||
item_list << item;
|
||||
meta = meta->next_metadata;
|
||||
}
|
||||
|
||||
qSort(item_list.begin(), item_list.end(), BackupItem::lessThan);
|
||||
|
||||
int row;
|
||||
QList<BackupItem *>::iterator it;
|
||||
vert_header->setUpdatesEnabled(false);
|
||||
|
||||
// insert the sorted items into the table
|
||||
for(it = item_list.begin(), row = 0; it != item_list.end(); ++it, ++row) {
|
||||
(*it)->row = row;
|
||||
ui->tableWidget->setCellWidget(row, 0, *it);
|
||||
vert_header->resizeSection(row, 70);
|
||||
}
|
||||
|
||||
vert_header->setUpdatesEnabled(true);
|
||||
db->mutex.unlock();
|
||||
|
||||
// apply filter
|
||||
this->on_filterLineEdit_textChanged(ui->filterLineEdit->text());
|
||||
}
|
||||
|
||||
void BackupManagerForm::on_filterLineEdit_textChanged(const QString &arg1)
|
||||
{
|
||||
if(arg1 != tr("Filter")) {
|
||||
for(int i = 0; i < ui->tableWidget->rowCount(); ++i) {
|
||||
BackupItem *item = (BackupItem*) ui->tableWidget->cellWidget(i, 0);
|
||||
|
||||
if(item->title.contains(arg1, Qt::CaseInsensitive)) {
|
||||
ui->tableWidget->setRowHidden(i, false);
|
||||
} else {
|
||||
ui->tableWidget->setRowHidden(i, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
55
src/forms/backupmanagerform.h
Normal file
55
src/forms/backupmanagerform.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BACKUPMANAGERFORM_H
|
||||
#define BACKUPMANAGERFORM_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "backupitem.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class BackupManagerForm;
|
||||
}
|
||||
|
||||
class BackupManagerForm : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BackupManagerForm(QWidget *parent = 0);
|
||||
~BackupManagerForm();
|
||||
|
||||
Database *db;
|
||||
|
||||
private:
|
||||
void setupForm();
|
||||
void setBackupUsage(quint64 size);
|
||||
|
||||
Ui::BackupManagerForm *ui;
|
||||
|
||||
public slots:
|
||||
void loadBackupListing(int index);
|
||||
void removeEntry(BackupItem *item);
|
||||
private slots:
|
||||
void on_filterLineEdit_textChanged(const QString &arg1);
|
||||
};
|
||||
|
||||
#endif // BACKUPMANAGERFORM_H
|
139
src/forms/backupmanagerform.ui
Normal file
139
src/forms/backupmanagerform.ui
Normal file
@@ -0,0 +1,139 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>BackupManagerForm</class>
|
||||
<widget class="QWidget" name="BackupManagerForm">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>857</width>
|
||||
<height>478</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Backup Manager</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Online ID / Username</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="accountBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Backup Type</string>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="backupComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>PS Vita Games</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>PSP Games</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>PSM Games</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>PSOne Games</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>PSP Savedatas</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Backups</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QTableWidget" name="tableWidget">
|
||||
<property name="columnCount">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<column/>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="usageLabel">
|
||||
<property name="text">
|
||||
<string>Backup disk usage</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="FilterLineEdit" name="filterLineEdit">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color:gray;font-style:italic</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Filter</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>FilterLineEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>filterlineedit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
150
src/forms/configwidget.cpp
Normal file
150
src/forms/configwidget.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "configwidget.h"
|
||||
#include "ui_configwidget.h"
|
||||
|
||||
extern "C" {
|
||||
#include <vitamtp.h>
|
||||
}
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QSettings>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
#include <QStandardPaths>
|
||||
#else
|
||||
#include <QDesktopServices>
|
||||
#define QStandardPaths QDesktopServices
|
||||
#define writableLocation storageLocation
|
||||
#endif
|
||||
|
||||
ConfigWidget::ConfigWidget(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::ConfigWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connectSignals();
|
||||
setDefaultDirs();
|
||||
}
|
||||
|
||||
void ConfigWidget::connectSignals()
|
||||
{
|
||||
QSignalMapper *mapper = new QSignalMapper(this);
|
||||
mapper->setMapping(ui->photoBtn, BTN_PHOTO);
|
||||
mapper->setMapping(ui->musicBtn, BTN_MUSIC);
|
||||
mapper->setMapping(ui->videoBtn, BTN_VIDEO);
|
||||
mapper->setMapping(ui->appBtn, BTN_APPS);
|
||||
mapper->setMapping(ui->urlBtn, BTN_URL);
|
||||
connect(ui->photoBtn, SIGNAL(clicked()), mapper, SLOT(map()));
|
||||
connect(ui->musicBtn, SIGNAL(clicked()), mapper, SLOT(map()));
|
||||
connect(ui->videoBtn, SIGNAL(clicked()), mapper, SLOT(map()));
|
||||
connect(ui->appBtn, SIGNAL(clicked()), mapper, SLOT(map()));
|
||||
connect(ui->urlBtn, SIGNAL(clicked()), mapper, SLOT(map()));
|
||||
connect(mapper, SIGNAL(mapped(int)), this, SLOT(browseBtnPressed(int)));
|
||||
connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
}
|
||||
|
||||
void ConfigWidget::setDefaultDirs()
|
||||
{
|
||||
QString defaultdir;
|
||||
QSettings settings;
|
||||
defaultdir = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
|
||||
ui->photoPath->setText(settings.value("photoPath", defaultdir).toString());
|
||||
defaultdir = QStandardPaths::writableLocation(QStandardPaths::MusicLocation);
|
||||
ui->musicPath->setText(settings.value("musicPath", defaultdir).toString());
|
||||
defaultdir = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);
|
||||
ui->videoPath->setText(settings.value("videoPath", defaultdir).toString());
|
||||
defaultdir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
|
||||
defaultdir.append(QDir::separator()).append("PS Vita");
|
||||
ui->appPath->setText(settings.value("appsPath", defaultdir).toString());
|
||||
defaultdir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
|
||||
defaultdir.append(QDir::separator()).append("PSV Updates");
|
||||
ui->urlPath->setText(settings.value("urlPath", defaultdir).toString());
|
||||
}
|
||||
|
||||
ConfigWidget::~ConfigWidget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ConfigWidget::browseBtnPressed(int btn)
|
||||
{
|
||||
QString msg;
|
||||
QLineEdit *lineedit;
|
||||
|
||||
switch(btn) {
|
||||
case BTN_PHOTO:
|
||||
lineedit = ui->photoPath;
|
||||
msg = tr("Select the folder to be used as a photo source");
|
||||
break;
|
||||
|
||||
case BTN_MUSIC:
|
||||
lineedit = ui->musicPath;
|
||||
msg = tr("Select the folder to be used as a music source");
|
||||
break;
|
||||
|
||||
case BTN_VIDEO:
|
||||
lineedit = ui->videoPath;
|
||||
msg = tr("Select the folder to be used as a video source");
|
||||
break;
|
||||
|
||||
case BTN_APPS:
|
||||
lineedit = ui->appPath;
|
||||
msg = tr("Select the folder to be used to save PS Vita games and backups");
|
||||
break;
|
||||
|
||||
case BTN_URL:
|
||||
lineedit = ui->urlPath;
|
||||
msg = tr("Select the folder to be used to fetch software updates");
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
QString selected = QFileDialog::getExistingDirectory(this, msg, lineedit->text(), QFileDialog::ShowDirsOnly);
|
||||
|
||||
if(!selected.isEmpty()) {
|
||||
lineedit->setText(selected);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigWidget::savePath(QSettings &settings, const QLineEdit *edit, const QString &key)
|
||||
{
|
||||
QString path = edit->text();
|
||||
if(path.endsWith(QDir::separator())) {
|
||||
path.chop(1);
|
||||
}
|
||||
settings.setValue(key, path);
|
||||
QDir(QDir::root()).mkpath(path);
|
||||
}
|
||||
|
||||
void ConfigWidget::accept()
|
||||
{
|
||||
QSettings settings;
|
||||
savePath(settings, ui->photoPath, "photoPath");
|
||||
savePath(settings, ui->musicPath, "musicPath");
|
||||
savePath(settings, ui->videoPath, "videoPath");
|
||||
savePath(settings, ui->appPath, "appsPath");
|
||||
savePath(settings, ui->urlPath, "urlPath");
|
||||
settings.sync();
|
||||
done(Accepted);
|
||||
}
|
54
src/forms/configwidget.h
Normal file
54
src/forms/configwidget.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CONFIGWIDGET_H
|
||||
#define CONFIGWIDGET_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QSettings>
|
||||
#include <QSignalMapper>
|
||||
|
||||
namespace Ui {
|
||||
class ConfigWidget;
|
||||
}
|
||||
|
||||
class ConfigWidget : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigWidget(QWidget *parent = 0);
|
||||
~ConfigWidget();
|
||||
|
||||
private:
|
||||
enum browse_buttons {BTN_PHOTO, BTN_MUSIC, BTN_VIDEO, BTN_APPS, BTN_URL};
|
||||
|
||||
void connectSignals();
|
||||
void setDefaultDirs();
|
||||
void savePath(QSettings &settings, const QLineEdit *edit, const QString &key);
|
||||
|
||||
Ui::ConfigWidget *ui;
|
||||
|
||||
private slots:
|
||||
void browseBtnPressed(int from);
|
||||
void accept();
|
||||
};
|
||||
|
||||
#endif // CONFIGWIDGET_H
|
342
src/forms/configwidget.ui
Normal file
342
src/forms/configwidget.ui
Normal file
@@ -0,0 +1,342 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigWidget</class>
|
||||
<widget class="QWidget" name="ConfigWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>520</width>
|
||||
<height>405</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>QCMA Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Folders</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Specify the folders that the PS Vita will access for each content type</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="whatsThis">
|
||||
<string>This is the location your Screenshots and Pictures are Saved to/Imported from.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Photo Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="photoPath">
|
||||
<property name="toolTip">
|
||||
<string>This is the location your Screenshots and Pictures are Saved to/Imported from.</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="photoBtn">
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="whatsThis">
|
||||
<string>This is the location your Videos are Saved to/Imported from.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Video Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="videoPath">
|
||||
<property name="toolTip">
|
||||
<string>This is the location your Videos are Saved to/Imported from.</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="videoBtn">
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="whatsThis">
|
||||
<string>This is the location your Music is Saved to/Imported from.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Music Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="musicPath">
|
||||
<property name="toolTip">
|
||||
<string>This is the location your Music is Saved to/Imported from.</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="musicBtn">
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="whatsThis">
|
||||
<string>This is the location your Games, Apps, Savegames, and System Backups are Saved to/Imported from.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Applications / Backups</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="appPath">
|
||||
<property name="toolTip">
|
||||
<string>This is the location your Games, Apps, Savegames, and System Backups are Saved to/Imported from.</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="appBtn">
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="whatsThis">
|
||||
<string>This is the location your Software Updates and Browser Data is Saved to/Imported from.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Updates / Web content</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_10">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="urlPath">
|
||||
<property name="toolTip">
|
||||
<string>This is the location your Software Updates and Browser Data is Saved to/Imported from.</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="urlBtn">
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Other</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="2" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p align="center"><span style=" font-size:14pt; font-weight:600;">Advanced settings</span></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Offline Mode</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_2">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Skip metadata extraction</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_3">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Update database automatically when files on the PC are changed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_4">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Disable USB monitoring</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_5">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Disable Wi-Fi monitoring</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Database backend</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="comboBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>In Memory</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
50
src/forms/confirmdialog.cpp
Normal file
50
src/forms/confirmdialog.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "confirmdialog.h"
|
||||
#include "ui_confirmdialog.h"
|
||||
|
||||
const QString ConfirmDialog::messageTemplate = "<html><head/><body>"
|
||||
"<p><span style=\"font-size:10pt;\">%1</span></p>"
|
||||
"<p><span style=\"font-size:12pt; font-weight:600;\">%2</span></p>"
|
||||
"</body></html>";
|
||||
|
||||
ConfirmDialog::ConfirmDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::ConfirmDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
this->layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
}
|
||||
|
||||
void ConfirmDialog::setMessageText(const QString message, const QString game_title)
|
||||
{
|
||||
ui->confirmText->setText(messageTemplate.arg(message, game_title));
|
||||
}
|
||||
|
||||
void ConfirmDialog::setMessagePixmap(const QPixmap &pixmap, int width)
|
||||
{
|
||||
ui->itemPicture->setPixmap(pixmap);
|
||||
ui->itemPicture->setMinimumWidth(width);
|
||||
}
|
||||
|
||||
ConfirmDialog::~ConfirmDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
46
src/forms/confirmdialog.h
Normal file
46
src/forms/confirmdialog.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CONFIRMDIALOG_H
|
||||
#define CONFIRMDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class ConfirmDialog;
|
||||
}
|
||||
|
||||
class ConfirmDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfirmDialog(QWidget *parent = 0);
|
||||
~ConfirmDialog();
|
||||
|
||||
void setMessageText(const QString message, const QString game_title);
|
||||
void setMessagePixmap(const QPixmap &pixmap, int width);
|
||||
|
||||
static const QString messageTemplate;
|
||||
|
||||
private:
|
||||
Ui::ConfirmDialog *ui;
|
||||
};
|
||||
|
||||
#endif // CONFIRMDIALOG_H
|
120
src/forms/confirmdialog.ui
Normal file
120
src/forms/confirmdialog.ui
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfirmDialog</class>
|
||||
<widget class="QDialog" name="ConfirmDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>519</width>
|
||||
<height>106</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Confirmation Message</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="itemPicture">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>48</width>
|
||||
<height>48</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>48</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="confirmText">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-size:10pt;">Are you sure to delete the backup of the following game?</span></p><p><span style=" font-size:12pt; font-weight:600;">Game Name</span></p></body></html>
|
||||
</string>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ConfirmDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ConfirmDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
71
src/forms/pinform.cpp
Normal file
71
src/forms/pinform.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pinform.h"
|
||||
#include "ui_pinform.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDesktopWidget>
|
||||
|
||||
const QString PinForm::pinFormat =
|
||||
"<html><head/><body>"
|
||||
"<p><span style=\"font-size:24pt; font-weight:600;\">%1</span></p>"
|
||||
"</body></html>";
|
||||
|
||||
PinForm::PinForm(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::PinForm)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
move(QApplication::desktop()->screen()->rect().center() - rect().center());
|
||||
setFixedSize(size());
|
||||
setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
|
||||
connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(hide()));
|
||||
}
|
||||
|
||||
void PinForm::setPin(QString name, int pin)
|
||||
{
|
||||
qDebug() << "Got pin from user " << name;
|
||||
ui->deviceLabel->setText(tr("Device: %1 (PS Vita)").arg(name));
|
||||
ui->pinLabel->setText(pinFormat.arg(QString::number(pin), 8, QChar('0')));
|
||||
show();
|
||||
}
|
||||
|
||||
void PinForm::startCountdown()
|
||||
{
|
||||
timer.setInterval(1000);
|
||||
counter = 300;
|
||||
connect(&timer, SIGNAL(timeout()), this, SLOT(decreaseTimer()));
|
||||
timer.start();
|
||||
}
|
||||
|
||||
void PinForm::decreaseTimer()
|
||||
{
|
||||
counter--;
|
||||
if(counter == 0) {
|
||||
timer.stop();
|
||||
hide();
|
||||
}
|
||||
ui->timeLabel->setText(tr("Time remaining: %1 seconds").arg(counter));
|
||||
}
|
||||
|
||||
PinForm::~PinForm()
|
||||
{
|
||||
delete ui;
|
||||
}
|
55
src/forms/pinform.h
Normal file
55
src/forms/pinform.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PINFORM_H
|
||||
#define PINFORM_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class PinForm;
|
||||
}
|
||||
|
||||
class PinForm : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PinForm(QWidget *parent = 0);
|
||||
~PinForm();
|
||||
|
||||
private:
|
||||
Ui::PinForm *ui;
|
||||
|
||||
// pin timeout
|
||||
int counter;
|
||||
QTimer timer;
|
||||
|
||||
static const QString pinFormat;
|
||||
|
||||
public slots:
|
||||
void startCountdown();
|
||||
void setPin(QString name, int pin);
|
||||
|
||||
private slots:
|
||||
void decreaseTimer();
|
||||
};
|
||||
|
||||
#endif // PINFORM_H
|
115
src/forms/pinform.ui
Normal file
115
src/forms/pinform.ui
Normal file
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PinForm</class>
|
||||
<widget class="QWidget" name="PinForm">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>526</width>
|
||||
<height>216</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Device pairing</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>An unregistered PS Vita system is connecting with QCMA via Wi-Fi</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="deviceLabel">
|
||||
<property name="text">
|
||||
<string>Device: PS Vita</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Input the following number in the PS Vita system to register it with QCMA</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="pinLabel">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-size:24pt; font-weight:600;">12345678</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="timeLabel">
|
||||
<property name="text">
|
||||
<string>Time remaining: 300 seconds</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cancelButton">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
64
src/forms/progressform.cpp
Normal file
64
src/forms/progressform.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "progressform.h"
|
||||
#include "ui_progressform.h"
|
||||
|
||||
#include <QDesktopWidget>
|
||||
#include <QMessageBox>
|
||||
|
||||
ProgressForm::ProgressForm(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::ProgressForm)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
move(QApplication::desktop()->screen()->rect().center() - rect().center());
|
||||
setFixedSize(size());
|
||||
setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
|
||||
connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(cancelConfirm()));
|
||||
}
|
||||
|
||||
ProgressForm::~ProgressForm()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ProgressForm::cancelConfirm()
|
||||
{
|
||||
QMessageBox box;
|
||||
box.setText(tr("Database indexing in progress"));
|
||||
box.setInformativeText(tr("Are you sure to cancel the database indexing?"));
|
||||
box.setIcon(QMessageBox::Warning);
|
||||
box.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
|
||||
if(box.exec() == QMessageBox::Ok) {
|
||||
emit canceled();
|
||||
}
|
||||
}
|
||||
|
||||
void ProgressForm::setFileName(QString file)
|
||||
{
|
||||
QString elided = ui->fileLabel->fontMetrics().elidedText(file, Qt::ElideMiddle, ui->fileLabel->width(), 0);
|
||||
ui->fileLabel->setText(elided);
|
||||
}
|
||||
|
||||
void ProgressForm::setDirectoryName(QString dir)
|
||||
{
|
||||
QString elided = ui->directoryLabel->fontMetrics().elidedText(dir, Qt::ElideMiddle, ui->directoryLabel->width(), 0);
|
||||
ui->directoryLabel->setText(elided);
|
||||
}
|
51
src/forms/progressform.h
Normal file
51
src/forms/progressform.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PROGRESSFORM_H
|
||||
#define PROGRESSFORM_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class ProgressForm;
|
||||
}
|
||||
|
||||
class ProgressForm : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ProgressForm(QWidget *parent = 0);
|
||||
~ProgressForm();
|
||||
|
||||
private:
|
||||
Ui::ProgressForm *ui;
|
||||
|
||||
signals:
|
||||
void canceled();
|
||||
|
||||
private slots:
|
||||
void cancelConfirm();
|
||||
|
||||
public slots:
|
||||
void setDirectoryName(QString dir);
|
||||
void setFileName(QString file);
|
||||
};
|
||||
|
||||
#endif // PROGRESSFORM_H
|
80
src/forms/progressform.ui
Normal file
80
src/forms/progressform.ui
Normal file
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ProgressForm</class>
|
||||
<widget class="QWidget" name="ProgressForm">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>602</width>
|
||||
<height>138</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Refreshing database...</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">Reading directory:</span></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="directoryLabel">
|
||||
<property name="text">
|
||||
<string>directory name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">Processing file:</span></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="fileLabel">
|
||||
<property name="text">
|
||||
<string>file name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cancelButton">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
106
src/main.cpp
Normal file
106
src/main.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef Q_OS_WIN32
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <QDebug>
|
||||
#include <QLocale>
|
||||
#include <QThread>
|
||||
#include <QTranslator>
|
||||
|
||||
#include "singleapplication.h"
|
||||
#include "mainwidget.h"
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
void noMessageOutput(QtMsgType type, const QMessageLogContext &, const QString & str)
|
||||
{
|
||||
const char * msg = str.toStdString().c_str();
|
||||
#else
|
||||
void noMessageOutput(QtMsgType type, const char *msg)
|
||||
{
|
||||
#endif
|
||||
Q_UNUSED(type);
|
||||
Q_UNUSED(msg);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if(SingleApplication::sendMessage(QObject::tr("A instance of QCMA is already running"))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SingleApplication app(argc, argv);
|
||||
|
||||
#ifndef Q_OS_WIN32
|
||||
// FIXME: libmtp sends SIGPIPE if a socket write fails crashing the whole app
|
||||
// the proper fix is to libmtp to handle the cancel properly or ignoring
|
||||
// SIGPIPE on the socket
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
if(app.arguments().contains("--with-debug")) {
|
||||
VitaMTP_Set_Logging(VitaMTP_DEBUG);
|
||||
} else if(app.arguments().contains("--verbose")) {
|
||||
VitaMTP_Set_Logging(VitaMTP_VERBOSE);
|
||||
} else {
|
||||
VitaMTP_Set_Logging(VitaMTP_NONE);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
qInstallMessageHandler(noMessageOutput);
|
||||
#else
|
||||
qInstallMsgHandler(noMessageOutput);
|
||||
#endif
|
||||
}
|
||||
|
||||
qDebug("Starting QCMA %s", QCMA_VER);
|
||||
|
||||
QTranslator translator;
|
||||
QString locale = QLocale().system().name();
|
||||
qDebug("Current locale: %s", locale.toUtf8().data());
|
||||
|
||||
if(app.arguments().contains("--set-locale")) {
|
||||
int index = app.arguments().indexOf("--set-locale");
|
||||
if(index + 1 < app.arguments().length()) {
|
||||
qDebug("Enforcing locale: %s", app.arguments().at(index + 1).toUtf8().data());
|
||||
locale = app.arguments().at(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(translator.load("qcma." + locale, ":/main/resources/translations")) {
|
||||
app.installTranslator(&translator);
|
||||
}
|
||||
|
||||
qDebug("Starting main thread: %lu", (unsigned long)QThread::currentThreadId());
|
||||
|
||||
// set the organization/application for QSettings to work properly
|
||||
app.setOrganizationName("qcma");
|
||||
app.setApplicationName("qcma");
|
||||
|
||||
//TODO: check if this is actually needed since we don't have a main window by default
|
||||
QApplication::setQuitOnLastWindowClosed(false);
|
||||
|
||||
MainWidget widget;
|
||||
widget.prepareApplication();
|
||||
|
||||
// receive the message from another process
|
||||
QObject::connect(&app, SIGNAL(messageAvailable(QString)), &widget, SLOT(receiveMessage(QString)));
|
||||
|
||||
return app.exec();
|
||||
}
|
186
src/mainwidget.cpp
Normal file
186
src/mainwidget.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "mainwidget.h"
|
||||
#include "cmaclient.h"
|
||||
#include "cmaevent.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QGridLayout>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
#include <QTimer>
|
||||
#include <QSettings>
|
||||
#include <QSpacerItem>
|
||||
|
||||
const QStringList MainWidget::path_list = QStringList() << "photoPath" << "musicPath" << "videoPath" << "appsPath" << "urlPath";
|
||||
|
||||
MainWidget::MainWidget(QWidget *parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void MainWidget::checkSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
// make sure that all the paths are set, else show the config dialog
|
||||
foreach(const QString &path, path_list) {
|
||||
if(!settings.contains(path)) {
|
||||
first_run = true;
|
||||
dialog.show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
first_run = false;
|
||||
manager.start();
|
||||
}
|
||||
|
||||
void MainWidget::dialogResult(int result)
|
||||
{
|
||||
if(result == QDialog::Accepted) {
|
||||
if(first_run) {
|
||||
first_run = false;
|
||||
manager.start();
|
||||
}
|
||||
} else if(first_run) {
|
||||
qApp->quit();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::stopServer()
|
||||
{
|
||||
setTrayTooltip(tr("Shutting down..."));
|
||||
receiveMessage(tr("Stopping QCMA..."));
|
||||
manager.stop();
|
||||
}
|
||||
|
||||
void MainWidget::deviceDisconnect()
|
||||
{
|
||||
setTrayTooltip(tr("Disconnected"));
|
||||
receiveMessage(tr("The device has been disconnected"));
|
||||
}
|
||||
|
||||
void MainWidget::prepareApplication()
|
||||
{
|
||||
connectSignals();
|
||||
createTrayIcon();
|
||||
checkSettings();
|
||||
}
|
||||
|
||||
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(receiveMessage(QString)));
|
||||
connect(&manager, SIGNAL(deviceConnected(QString)), this, SLOT(setTrayTooltip(QString)));
|
||||
connect(&manager, SIGNAL(deviceDisconnected()), this, SLOT(deviceDisconnect()));
|
||||
connect(&manager, SIGNAL(messageSent(QString)), this, SLOT(receiveMessage(QString)));
|
||||
|
||||
form.db = &manager.db;
|
||||
}
|
||||
|
||||
void MainWidget::setTrayTooltip(QString message)
|
||||
{
|
||||
trayIcon->setToolTip(message);
|
||||
}
|
||||
|
||||
void MainWidget::openManager()
|
||||
{
|
||||
form.loadBackupListing(0);
|
||||
form.show();
|
||||
}
|
||||
|
||||
void MainWidget::showAboutDialog()
|
||||
{
|
||||
QMessageBox about;
|
||||
|
||||
about.setText(QString("QCMA ") + QCMA_VER);
|
||||
about.setWindowTitle(tr("About QCMA"));
|
||||
about.setInformativeText(tr("Copyright (C) 2013 Codestation") + "\n");
|
||||
about.setStandardButtons(QMessageBox::Ok);
|
||||
about.setIconPixmap(QPixmap(":/main/images/qcma.png"));
|
||||
about.setDefaultButton(QMessageBox::Ok);
|
||||
|
||||
// hack to expand the messagebox minimum size
|
||||
QSpacerItem* horizontalSpacer = new QSpacerItem(300, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||
QGridLayout* layout = (QGridLayout*)about.layout();
|
||||
layout->addItem(horizontalSpacer, layout->rowCount(), 0, 1, layout->columnCount());
|
||||
|
||||
about.show();
|
||||
about.exec();
|
||||
}
|
||||
|
||||
void MainWidget::showAboutQt()
|
||||
{
|
||||
QMessageBox::aboutQt(this);
|
||||
}
|
||||
|
||||
void MainWidget::createTrayIcon()
|
||||
{
|
||||
options = new QAction(tr("&Settings"), this);
|
||||
reload = new QAction(tr("&Refresh database"), this);
|
||||
backup = new QAction(tr("&Backup Manager"), this);
|
||||
about = new QAction(tr("&About QCMA"), this);
|
||||
about_qt = new QAction(tr("Abou&t Qt"), this);
|
||||
quit = new QAction(tr("&Quit"), this);
|
||||
|
||||
connect(options, SIGNAL(triggered()), &dialog, SLOT(open()));
|
||||
connect(backup, SIGNAL(triggered()), this, SLOT(openManager()));
|
||||
connect(reload, SIGNAL(triggered()), &manager, SLOT(refreshDatabase()));
|
||||
connect(about, SIGNAL(triggered()), this, SLOT(showAboutDialog()));
|
||||
connect(about_qt, SIGNAL(triggered()), this, SLOT(showAboutQt()));
|
||||
connect(quit, SIGNAL(triggered()), this, SLOT(stopServer()));
|
||||
|
||||
QMenu *trayIconMenu = new QMenu(this);
|
||||
trayIconMenu->addAction(options);
|
||||
trayIconMenu->addAction(reload);
|
||||
trayIconMenu->addAction(backup);
|
||||
trayIconMenu->addSeparator();
|
||||
trayIconMenu->addAction(about);
|
||||
trayIconMenu->addAction(about_qt);
|
||||
trayIconMenu->addAction(quit);
|
||||
|
||||
trayIcon = new QSystemTrayIcon(this);
|
||||
trayIcon->setContextMenu(trayIconMenu);
|
||||
#ifndef Q_OS_WIN32
|
||||
trayIcon->setIcon(QIcon(":/main/images/psv_icon.png"));
|
||||
#else
|
||||
trayIcon->setIcon(QIcon(":/main/images/psv_icon_16.png"));
|
||||
#endif
|
||||
trayIcon->show();
|
||||
// try to avoid the iconTray Qt bug
|
||||
Sleeper::sleep(1);
|
||||
}
|
||||
|
||||
void MainWidget::receiveMessage(QString message)
|
||||
{
|
||||
if(trayIcon->isVisible()) {
|
||||
trayIcon->showMessage(tr("Information"), message);
|
||||
}
|
||||
}
|
||||
|
||||
MainWidget::~MainWidget()
|
||||
{
|
||||
trayIcon->hide();
|
||||
}
|
78
src/mainwidget.h
Normal file
78
src/mainwidget.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MAINWIDGET_H
|
||||
#define MAINWIDGET_H
|
||||
|
||||
#include "cmaclient.h"
|
||||
#include "clientmanager.h"
|
||||
#include "forms/configwidget.h"
|
||||
#include "forms/backupmanagerform.h"
|
||||
#include "forms/progressform.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QWidget>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
#include <vitamtp.h>
|
||||
|
||||
class MainWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainWidget(QWidget *parent = 0);
|
||||
~MainWidget();
|
||||
|
||||
void prepareApplication();
|
||||
|
||||
private:
|
||||
void connectSignals();
|
||||
void createTrayIcon();
|
||||
void checkSettings();
|
||||
|
||||
bool first_run;
|
||||
|
||||
// forms
|
||||
ConfigWidget dialog;
|
||||
ClientManager manager;
|
||||
BackupManagerForm form;
|
||||
|
||||
//system tray
|
||||
QAction *quit;
|
||||
QAction *reload;
|
||||
QAction *options;
|
||||
QAction *backup;
|
||||
QAction *about;
|
||||
QAction *about_qt;
|
||||
QSystemTrayIcon *trayIcon;
|
||||
|
||||
const static QStringList path_list;
|
||||
|
||||
private slots:
|
||||
void stopServer();
|
||||
void openManager();
|
||||
void showAboutQt();
|
||||
void showAboutDialog();
|
||||
void deviceDisconnect();
|
||||
void dialogResult(int result);
|
||||
void receiveMessage(QString message);
|
||||
void setTrayTooltip(QString message);
|
||||
};
|
||||
|
||||
#endif // MAINWIDGET_H
|
51
src/sforeader.cpp
Normal file
51
src/sforeader.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "sforeader.h"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
SfoReader::SfoReader()
|
||||
{
|
||||
}
|
||||
|
||||
bool SfoReader::load(const QString &path)
|
||||
{
|
||||
QFile file(path);
|
||||
if(file.open(QIODevice::ReadOnly)) {
|
||||
data = file.readAll();
|
||||
key_offset = data.constData();
|
||||
header = (sfo_header *)key_offset;
|
||||
index = (sfo_index *)(key_offset + sizeof(sfo_header));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *SfoReader::value(const char *key, const char *defaultValue)
|
||||
{
|
||||
const char *base_key = key_offset + header->key_offset;
|
||||
for(uint i = 0; i < header->pair_count; i++) {
|
||||
const char *curr_key = base_key + index[i].key_offset;
|
||||
if(strcmp(key, curr_key) == 0) {
|
||||
return key_offset + header->value_offset + index[i].data_offset;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
56
src/sforeader.h
Normal file
56
src/sforeader.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SFOREADER_H
|
||||
#define SFOREADER_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
class SfoReader
|
||||
{
|
||||
public:
|
||||
SfoReader();
|
||||
bool load(const QString &path);
|
||||
const char *value(const char *key, const char *defaultValue);
|
||||
|
||||
private:
|
||||
typedef struct {
|
||||
quint16 key_offset;
|
||||
uchar alignment;
|
||||
uchar data_type;
|
||||
quint32 value_size;
|
||||
quint32 value_size_with_padding;
|
||||
quint32 data_offset;
|
||||
} __attribute__((packed)) sfo_index;
|
||||
|
||||
typedef struct {
|
||||
char id[4];
|
||||
quint32 version;
|
||||
quint32 key_offset;
|
||||
quint32 value_offset;
|
||||
quint32 pair_count;
|
||||
} __attribute__((packed)) sfo_header;
|
||||
|
||||
QByteArray data;
|
||||
const char *key_offset;
|
||||
const sfo_header *header;
|
||||
const sfo_index *index;
|
||||
};
|
||||
|
||||
#endif // SFOREADER_H
|
69
src/singleapplication.cpp
Normal file
69
src/singleapplication.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "singleapplication.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
const int SingleApplication::timeout = 500;
|
||||
const QString SingleApplication::SHARED_KEY = "QCMA_KEY";
|
||||
|
||||
SingleApplication::SingleApplication(int &argc, char **argv) :
|
||||
QApplication(argc, argv)
|
||||
{
|
||||
server = new QLocalServer(this);
|
||||
connect(server, SIGNAL(newConnection()), this, SLOT(receiveMessage()));
|
||||
QLocalServer::removeServer(SHARED_KEY);
|
||||
server->listen(SHARED_KEY);
|
||||
}
|
||||
|
||||
void SingleApplication::receiveMessage()
|
||||
{
|
||||
QLocalSocket *socket = server->nextPendingConnection();
|
||||
|
||||
if(!socket->waitForReadyRead(timeout)) {
|
||||
qDebug() << socket->errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray byteArray = socket->readAll();
|
||||
QString message = QString::fromUtf8(byteArray.constData());
|
||||
emit messageAvailable(message);
|
||||
socket->disconnectFromServer();
|
||||
}
|
||||
|
||||
bool SingleApplication::sendMessage(const QString &message)
|
||||
{
|
||||
QLocalSocket socket;
|
||||
socket.connectToServer(SHARED_KEY, QIODevice::WriteOnly);
|
||||
|
||||
if(!socket.waitForConnected(timeout)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
socket.write(message.toUtf8());
|
||||
|
||||
if(!socket.waitForBytesWritten(timeout)) {
|
||||
qDebug() << socket.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
socket.disconnectFromServer();
|
||||
return true;
|
||||
}
|
50
src/singleapplication.h
Normal file
50
src/singleapplication.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SINGLEAPPLICATION_H
|
||||
#define SINGLEAPPLICATION_H
|
||||
|
||||
#include <QApplication>
|
||||
#include <QLocalSocket>
|
||||
#include <QLocalServer>
|
||||
#include <QSharedMemory>
|
||||
|
||||
class SingleApplication : public QApplication
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SingleApplication(int &argc, char **argv);
|
||||
|
||||
static bool sendMessage(const QString &message);
|
||||
|
||||
private:
|
||||
QLocalServer *server;
|
||||
|
||||
static const int timeout;
|
||||
static const QString SHARED_KEY;
|
||||
|
||||
signals:
|
||||
void messageAvailable(QString message);
|
||||
|
||||
|
||||
public slots:
|
||||
void receiveMessage();
|
||||
};
|
||||
|
||||
#endif // SINGLEAPPLICATION_H
|
177
src/utils.cpp
Normal file
177
src/utils.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
#include "avdecoder.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QImage>
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/statvfs.h>
|
||||
#endif
|
||||
|
||||
bool getDiskSpace(const QString &dir, quint64 *free, quint64 *total)
|
||||
{
|
||||
#ifdef Q_OS_WIN32
|
||||
|
||||
if(GetDiskFreeSpaceEx(dir.toStdWString().c_str(), (ULARGE_INTEGER *)free, (ULARGE_INTEGER *)total, NULL) != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
struct statvfs stat;
|
||||
|
||||
if(statvfs(dir.toUtf8().data(), &stat) == 0) {
|
||||
*total = stat.f_frsize * stat.f_blocks;
|
||||
*free = stat.f_frsize * stat.f_bfree;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool removeRecursively(const QString &dirName)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
return QDir(dirName).removeRecursively();
|
||||
#else
|
||||
bool result = false;
|
||||
QDir dir(dirName);
|
||||
|
||||
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(!result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
result = dir.rmdir(dirName);
|
||||
}
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
QByteArray findFolderAlbumArt(const QString path, metadata_t *metadata)
|
||||
{
|
||||
QByteArray data;
|
||||
QDir folder(path);
|
||||
|
||||
QStringList files = folder.entryList(QDir::Files | QDir::Readable);
|
||||
const QStringList cover_list = QStringList() << "album" << "cover" << "front";
|
||||
const QStringList ext_list = QStringList() << "jpg" << "jpeg" << "png" << "gif";
|
||||
|
||||
foreach(const QString &file, files) {
|
||||
foreach(const QString &cover, cover_list) {
|
||||
foreach(const QString &ext, ext_list) {
|
||||
if(file.compare(QString("%1.%2").arg(cover, ext), Qt::CaseInsensitive) == 0) {
|
||||
qDebug() << "Trying to load album art from" << folder.absoluteFilePath(file);
|
||||
QImage img;
|
||||
if(img.load(folder.absoluteFilePath(file))) {
|
||||
QBuffer buffer(&data);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
QImage result = img.scaled(256, 250, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
result.save(&buffer, "JPEG");
|
||||
metadata->data.thumbnail.width = result.width();
|
||||
metadata->data.thumbnail.height = result.height();
|
||||
}
|
||||
// only try with the first match
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
QByteArray getThumbnail(const QString &path, DataType type, metadata_t *metadata)
|
||||
{
|
||||
QByteArray data;
|
||||
|
||||
if(MASK_SET(type, SaveData)) {
|
||||
QFile file(QDir(path).absoluteFilePath("ICON0.PNG"));
|
||||
|
||||
if(file.open(QIODevice::ReadOnly)) {
|
||||
data = file.readAll();
|
||||
}
|
||||
} else if(MASK_SET(type, Photo)) {
|
||||
QImage img;
|
||||
|
||||
if(img.load(path)) {
|
||||
QBuffer buffer(&data);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
QImage result = img.scaled(213, 120, Qt::KeepAspectRatio, Qt::FastTransformation);
|
||||
result.save(&buffer, "JPEG");
|
||||
metadata->data.thumbnail.width = result.width();
|
||||
metadata->data.thumbnail.height = result.height();
|
||||
}
|
||||
} else if(MASK_SET(type, Music)) {
|
||||
if(MASK_SET(type, Folder)) {
|
||||
// TODO: try to load an album cover from one of the audio files.
|
||||
data = findFolderAlbumArt(path, metadata);
|
||||
} else {
|
||||
AVDecoder decoder;
|
||||
|
||||
if(decoder.open(path)) {
|
||||
data = decoder.getAudioThumbnail(256, 256);
|
||||
metadata->data.thumbnail.width = 256;
|
||||
metadata->data.thumbnail.height = 256;
|
||||
}
|
||||
}
|
||||
} else if(MASK_SET(type, Video)) {
|
||||
AVDecoder decoder;
|
||||
if(decoder.open(path)) {
|
||||
data = decoder.getVideoThumbnail(256, 256);
|
||||
metadata->data.thumbnail.width = 256;
|
||||
metadata->data.thumbnail.height = 256;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
QString readable_size(quint64 size, bool use_gib)
|
||||
{
|
||||
QStringList list;
|
||||
list << "KiB" << "MiB";
|
||||
if(use_gib) {
|
||||
list << "GiB";
|
||||
}
|
||||
|
||||
QStringListIterator i(list);
|
||||
QString unit("bytes");
|
||||
|
||||
float size_f = size;
|
||||
|
||||
while(size_f >= 1024.0 && i.hasNext()) {
|
||||
unit = i.next();
|
||||
size_f /= 1024.0;
|
||||
}
|
||||
return QString().setNum(size_f,'f',2) + " " + unit;
|
||||
}
|
53
src/utils.h
Normal file
53
src/utils.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* QCMA: Cross-platform content manager assistant for the PS Vita
|
||||
*
|
||||
* Copyright (C) 2013 Codestation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QThread>
|
||||
|
||||
#include <vitamtp.h>
|
||||
|
||||
// Qt4 doesn't have public methods for Thread::*sleep
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
typedef QThread Sleeper;
|
||||
#else
|
||||
class Sleeper : QThread
|
||||
{
|
||||
public:
|
||||
static void sleep(unsigned long secs) {
|
||||
QThread::sleep(secs);
|
||||
}
|
||||
static void msleep(unsigned long msecs) {
|
||||
QThread::msleep(msecs);
|
||||
}
|
||||
static void usleep(unsigned long usecs) {
|
||||
QThread::usleep(usecs);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
bool removeRecursively(const QString &dirName);
|
||||
QString readable_size(quint64 size, bool use_gib = false);
|
||||
bool getDiskSpace(const QString &dir, quint64 *free, quint64 *total);
|
||||
QByteArray getThumbnail(const QString &path, DataType type, metadata_t *metadata);
|
||||
|
||||
#endif // UTILS_H
|
Reference in New Issue
Block a user