SnapmaticPicture: add G5EX RagePhotoFormatParser

This commit is contained in:
Syping 2023-02-20 14:04:17 +01:00
parent bb62cc438d
commit 83e133ddf2
2 changed files with 140 additions and 50 deletions

View File

@ -61,8 +61,10 @@ inline void gta5view_uInt32ToCharLE(quint32 x, char *y)
y[3] = x >> 24; y[3] = x >> 24;
} }
inline bool gta5view_export_load(const QByteArray &fileData, RagePhoto *ragePhoto) ragephoto_bool_t gta5view_export_load(RagePhotoData *rp_data, const char *data, size_t length)
{ {
const QByteArray fileData = QByteArray::fromRawData(data, length);
QBuffer dataBuffer; QBuffer dataBuffer;
dataBuffer.setData(fileData); dataBuffer.setData(fileData);
if (!dataBuffer.open(QIODevice::ReadOnly)) if (!dataBuffer.open(QIODevice::ReadOnly))
@ -70,181 +72,268 @@ inline bool gta5view_export_load(const QByteArray &fileData, RagePhoto *ragePhot
dataBuffer.seek(4); dataBuffer.seek(4);
char uInt32Buffer[4]; char uInt32Buffer[4];
qint64 size = dataBuffer.read(uInt32Buffer, 4); size_t size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::NoFormatIdentifier;
return false; return false;
}
quint32 format = gta5view_charToUInt32LE(uInt32Buffer); uint32_t format = gta5view_charToUInt32LE(uInt32Buffer);
if (format == G5EExportFormat::G5E3P) { if (format == G5EExportFormat::G5E3P) {
size = dataBuffer.read(uInt32Buffer, 4); size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::IncompleteHeader;
return false; return false;
}
quint32 compressedSize = gta5view_charToUInt32LE(uInt32Buffer); quint32 compressedSize = gta5view_charToUInt32LE(uInt32Buffer);
char *compressedPhotoHeader = static_cast<char*>(std::malloc(compressedSize)); char *compressedPhotoHeader = static_cast<char*>(std::malloc(compressedSize));
if (!compressedPhotoHeader) if (!compressedPhotoHeader) {
rp_data->error = RagePhoto::HeaderMallocError;
return false; return false;
}
size = dataBuffer.read(compressedPhotoHeader, compressedSize); size = dataBuffer.read(compressedPhotoHeader, compressedSize);
if (size != compressedSize) { if (size != compressedSize) {
std::free(compressedPhotoHeader); std::free(compressedPhotoHeader);
rp_data->error = RagePhoto::UnicodeHeaderError;
return false; return false;
} }
QByteArray t_photoHeader = QByteArray::fromRawData(compressedPhotoHeader, compressedSize); QByteArray t_photoHeader = QByteArray::fromRawData(compressedPhotoHeader, compressedSize);
t_photoHeader = qUncompress(t_photoHeader); t_photoHeader = qUncompress(t_photoHeader);
std::free(compressedPhotoHeader); std::free(compressedPhotoHeader);
if (t_photoHeader.isEmpty()) if (t_photoHeader.isEmpty()) {
rp_data->error = RagePhoto::IncompleteHeader;
return false; return false;
}
size = dataBuffer.read(uInt32Buffer, 4); size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::IncompleteHeader;
return false; return false;
quint32 t_headerSum = gta5view_charToUInt32LE(uInt32Buffer); }
ragePhoto->setHeader(t_photoHeader.constData(), t_headerSum); rp_data->headerSum = gta5view_charToUInt32LE(uInt32Buffer);
rp_data->header = static_cast<char*>(std::malloc(t_photoHeader.size() + 1));
if (!rp_data->header) {
rp_data->error = RagePhoto::HeaderMallocError;
return false;
}
std::memcpy(rp_data->header, t_photoHeader.constData(), t_photoHeader.size() + 1);
size = dataBuffer.read(uInt32Buffer, 4); size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::IncompletePhotoBuffer;
return false; return false;
quint32 t_photoBuffer = gta5view_charToUInt32LE(uInt32Buffer); }
rp_data->jpegBuffer = gta5view_charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4); size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::IncompletePhotoBuffer;
return false; return false;
}
compressedSize = gta5view_charToUInt32LE(uInt32Buffer); compressedSize = gta5view_charToUInt32LE(uInt32Buffer);
char *compressedPhoto = static_cast<char*>(std::malloc(compressedSize)); char *compressedPhoto = static_cast<char*>(std::malloc(compressedSize));
if (!compressedPhoto) if (!compressedPhoto) {
rp_data->error = RagePhoto::PhotoMallocError;
return false; return false;
}
size = dataBuffer.read(compressedPhoto, compressedSize); size = dataBuffer.read(compressedPhoto, compressedSize);
if (size != compressedSize) { if (size != compressedSize) {
std::free(compressedPhoto); std::free(compressedPhoto);
rp_data->error = RagePhoto::PhotoReadError;
return false; return false;
} }
QByteArray t_photoData = QByteArray::fromRawData(compressedPhoto, compressedSize); QByteArray t_photoData = QByteArray::fromRawData(compressedPhoto, compressedSize);
t_photoData = qUncompress(t_photoData); t_photoData = qUncompress(t_photoData);
std::free(compressedPhoto); std::free(compressedPhoto);
ragePhoto->setPhoto(t_photoData.constData(), t_photoData.size(), t_photoBuffer); rp_data->jpeg = static_cast<char*>(std::malloc(t_photoData.size()));
if (!rp_data->jpeg) {
rp_data->error = RagePhoto::PhotoMallocError;
return false;
}
std::memcpy(rp_data->jpeg, t_photoData.constData(), t_photoData.size());
rp_data->jpegSize = t_photoData.size();
// JSON offset will be calculated later, offsets will be removed in G5E4P // JSON offset will be calculated later, offsets will be removed in G5E4P
size = dataBuffer.skip(4); size = dataBuffer.skip(4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::IncompleteJsonOffset;
return false; return false;
}
size = dataBuffer.read(uInt32Buffer, 4); size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::IncompleteJsonBuffer;
return false; return false;
quint32 t_jsonBuffer = gta5view_charToUInt32LE(uInt32Buffer); }
rp_data->jsonBuffer = gta5view_charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4); size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::IncompleteJsonBuffer;
return false; return false;
}
compressedSize = gta5view_charToUInt32LE(uInt32Buffer); compressedSize = gta5view_charToUInt32LE(uInt32Buffer);
char *compressedJson = static_cast<char*>(std::malloc(compressedSize)); char *compressedJson = static_cast<char*>(std::malloc(compressedSize));
if (!compressedJson) if (!compressedJson) {
rp_data->error = RagePhoto::JsonMallocError;
return false; return false;
}
size = dataBuffer.read(compressedJson, compressedSize); size = dataBuffer.read(compressedJson, compressedSize);
if (size != compressedSize) { if (size != compressedSize) {
std::free(compressedJson); std::free(compressedJson);
rp_data->error = RagePhoto::JsonReadError;
return false; return false;
} }
QByteArray t_jsonData = QByteArray::fromRawData(compressedJson, compressedSize); QByteArray t_jsonData = QByteArray::fromRawData(compressedJson, compressedSize);
t_jsonData = qUncompress(t_jsonData); t_jsonData = qUncompress(t_jsonData);
std::free(compressedJson); std::free(compressedJson);
if (t_jsonData.isEmpty()) if (t_jsonData.isEmpty()) {
rp_data->error = RagePhoto::JsonReadError;
return false; return false;
ragePhoto->setJson(t_jsonData.constData(), t_jsonBuffer); }
rp_data->json = static_cast<char*>(std::malloc(t_jsonData.size() + 1));
if (!rp_data->json) {
rp_data->error = RagePhoto::JsonMallocError;
return false;
}
std::memcpy(rp_data->json, t_jsonData.constData(), t_jsonData.size() + 1);
// TITL offset will be calculated later, offsets will be removed in G5E4P // TITL offset will be calculated later, offsets will be removed in G5E4P
size = dataBuffer.skip(4); size = dataBuffer.skip(4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::IncompleteTitleOffset;
return false; return false;
}
size = dataBuffer.read(uInt32Buffer, 4); size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::IncompleteTitleBuffer;
return false; return false;
quint32 t_titlBuffer = gta5view_charToUInt32LE(uInt32Buffer); }
rp_data->titlBuffer = gta5view_charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4); size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::IncompleteTitleBuffer;
return false; return false;
}
compressedSize = gta5view_charToUInt32LE(uInt32Buffer); compressedSize = gta5view_charToUInt32LE(uInt32Buffer);
char *compressedTitl = static_cast<char*>(std::malloc(compressedSize)); char *compressedTitl = static_cast<char*>(std::malloc(compressedSize));
if (!compressedTitl) if (!compressedTitl) {
rp_data->error = RagePhoto::TitleMallocError;
return false; return false;
}
size = dataBuffer.read(compressedTitl, compressedSize); size = dataBuffer.read(compressedTitl, compressedSize);
if (size != compressedSize) { if (size != compressedSize) {
std::free(compressedTitl); std::free(compressedTitl);
rp_data->error = RagePhoto::TitleReadError;
return false; return false;
} }
QByteArray t_titlData = QByteArray::fromRawData(compressedTitl, compressedSize); QByteArray t_titlData = QByteArray::fromRawData(compressedTitl, compressedSize);
t_titlData = qUncompress(t_titlData); t_titlData = qUncompress(t_titlData);
std::free(compressedTitl); std::free(compressedTitl);
ragePhoto->setTitle(t_titlData.constData(), t_titlBuffer); rp_data->title = static_cast<char*>(std::malloc(t_titlData.size() + 1));
if (!rp_data->title) {
rp_data->error = RagePhoto::TitleMallocError;
return false;
}
std::memcpy(rp_data->title, t_titlData.constData(), t_titlData.size() + 1);
// DESC offset will be calculated later, offsets will be removed in G5E4P // DESC offset will be calculated later, offsets will be removed in G5E4P
size = dataBuffer.skip(4); size = dataBuffer.skip(4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::IncompleteDescOffset;
return false; return false;
}
size = dataBuffer.read(uInt32Buffer, 4); size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::IncompleteDescBuffer;
return false; return false;
quint32 t_descBuffer = gta5view_charToUInt32LE(uInt32Buffer); }
rp_data->descBuffer = gta5view_charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4); size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::IncompleteDescBuffer;
return false; return false;
}
compressedSize = gta5view_charToUInt32LE(uInt32Buffer); compressedSize = gta5view_charToUInt32LE(uInt32Buffer);
char *compressedDesc = static_cast<char*>(std::malloc(compressedSize)); char *compressedDesc = static_cast<char*>(std::malloc(compressedSize));
if (!compressedDesc) if (!compressedDesc) {
rp_data->error = RagePhoto::DescMallocError;
return false; return false;
}
size = dataBuffer.read(compressedDesc, compressedSize); size = dataBuffer.read(compressedDesc, compressedSize);
if (size != compressedSize) { if (size != compressedSize) {
std::free(compressedDesc); std::free(compressedDesc);
rp_data->error = RagePhoto::DescReadError;
return false; return false;
} }
QByteArray t_descData = QByteArray::fromRawData(compressedDesc, compressedSize); QByteArray t_descData = QByteArray::fromRawData(compressedDesc, compressedSize);
t_descData = qUncompress(t_descData); t_descData = qUncompress(t_descData);
std::free(compressedDesc); std::free(compressedDesc);
ragePhoto->setDescription(t_descData.constData(), t_descBuffer); rp_data->description = static_cast<char*>(std::malloc(t_descData.size() + 1));
if (!rp_data->description) {
rp_data->error = RagePhoto::DescMallocError;
return false;
}
std::memcpy(rp_data->description, t_descData.constData(), t_descData.size() + 1);
// EOF will be calculated later, EOF marker will be removed in G5E4P // EOF will be calculated later, EOF marker will be removed in G5E4P
size = dataBuffer.skip(4); size = dataBuffer.skip(4);
if (size != 4) if (size != 4) {
rp_data->error = RagePhoto::IncompleteEOF;
return false; return false;
}
// libragephoto needs to know we gave it a GTA V Snapmatic // libragephoto needs to know we gave it a GTA V Snapmatic
ragePhoto->setFormat(RagePhoto::GTA5); rp_data->photoFormat = RagePhoto::GTA5;
rp_data->error = RagePhoto::NoError;
RagePhoto::setBufferOffsets(rp_data);
return true; return true;
} }
else if (format == G5EExportFormat::G5E2P) { else if (format == G5EExportFormat::G5E2P) {
const QByteArray t_fileData = qUncompress(dataBuffer.readAll()); const QByteArray t_fileData = qUncompress(dataBuffer.readAll());
if (t_fileData.isEmpty()) if (t_fileData.isEmpty()) {
rp_data->error = RagePhoto::IncompatibleFormat;
return false; return false;
return ragePhoto->load(t_fileData.constData(), t_fileData.size()); }
return RagePhoto::load(rp_data, nullptr, t_fileData.constData(), t_fileData.size());
} }
else if (format == G5EExportFormat::G5E1P) { else if (format == G5EExportFormat::G5E1P) {
size = dataBuffer.skip(1); size = dataBuffer.skip(1);
if (size != 1) if (size != 1) {
rp_data->error = RagePhoto::IncompatibleFormat;
return false; return false;
}
char length[1]; char length[1];
size = dataBuffer.read(length, 1); size = dataBuffer.read(length, 1);
if (size != 1) if (size != 1) {
rp_data->error = RagePhoto::IncompatibleFormat;
return false; return false;
}
int i_length = QByteArray::number(static_cast<int>(length[0]), 16).toInt() + 6; int i_length = QByteArray::number(static_cast<int>(length[0]), 16).toInt() + 6;
size = dataBuffer.skip(i_length); size = dataBuffer.skip(i_length);
if (size != i_length) if (size != i_length) {
rp_data->error = RagePhoto::IncompatibleFormat;
return false; return false;
}
const QByteArray t_fileData = qUncompress(dataBuffer.readAll()); const QByteArray t_fileData = qUncompress(dataBuffer.readAll());
if (t_fileData.isEmpty()) if (t_fileData.isEmpty()) {
rp_data->error = RagePhoto::IncompatibleFormat;
return false; return false;
return ragePhoto->load(t_fileData.constData(), t_fileData.size()); }
return RagePhoto::load(rp_data, nullptr, t_fileData.constData(), t_fileData.size());
} }
return false; return false;
} }
@ -320,6 +409,11 @@ inline void gta5view_export_save(QIODevice *ioDevice, RagePhotoData *data)
// SNAPMATIC PICTURE CLASS // SNAPMATIC PICTURE CLASS
SnapmaticPicture::SnapmaticPicture(const QString &fileName, QObject *parent) : QObject(parent), picFilePath(fileName) SnapmaticPicture::SnapmaticPicture(const QString &fileName, QObject *parent) : QObject(parent), picFilePath(fileName)
{ {
RagePhotoFormatParser g5eParser[1]{};
g5eParser[0].photoFormat = G5EPhotoFormat::G5EX;
g5eParser[0].funcLoad = &gta5view_export_load;
p_ragePhoto.addParser(&g5eParser[0]);
reset(); reset();
} }
@ -372,12 +466,8 @@ bool SnapmaticPicture::preloadFile()
bool ok = p_ragePhoto.load(fileData.constData(), fileData.size()); bool ok = p_ragePhoto.load(fileData.constData(), fileData.size());
picFormat = p_ragePhoto.format(); picFormat = p_ragePhoto.format();
// libragephoto doesn't support modules yet
if (picFormat == G5EPhotoFormat::G5EX)
ok = gta5view_export_load(fileData, &p_ragePhoto);
if (!ok) { if (!ok) {
const RagePhoto::Error error = static_cast<RagePhoto::Error>(p_ragePhoto.error()); const int32_t error = p_ragePhoto.error();
switch (error) { switch (error) {
case RagePhoto::Uninitialised: case RagePhoto::Uninitialised:
lastStep = "1;/1,OpenFile," % convertDrawStringForLog(picFilePath); lastStep = "1;/1,OpenFile," % convertDrawStringForLog(picFilePath);

@ -1 +1 @@
Subproject commit b2e765c2af5fad634bdc2bbb1eb48ccaeb6a509a Subproject commit e51d50f77e38a39151c9418bb9a3359971fa5454