RagePhoto: G5E2P and G5E3P parser added
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Syping 2020-11-08 18:05:44 +01:00
parent a798be0b53
commit b70396f77e
2 changed files with 304 additions and 154 deletions

View file

@ -23,18 +23,24 @@
#include <QDebug> #include <QDebug>
#include <QFile> #include <QFile>
RagePhoto::RagePhoto(const QString &filePath) : p_filePath(filePath) RagePhoto::RagePhoto(const QByteArray &data) : p_fileData(data)
{ {
p_inputMode = 0; p_inputMode = 0;
p_isLoaded = false; p_isLoaded = false;
} }
RagePhoto::RagePhoto(QIODevice *ioDevice) : p_ioDevice(ioDevice) RagePhoto::RagePhoto(const QString &filePath) : p_filePath(filePath)
{ {
p_inputMode = 1; p_inputMode = 1;
p_isLoaded = false; p_isLoaded = false;
} }
RagePhoto::RagePhoto(QIODevice *ioDevice) : p_ioDevice(ioDevice)
{
p_inputMode = 2;
p_isLoaded = false;
}
bool RagePhoto::isLoaded() bool RagePhoto::isLoaded()
{ {
return p_isLoaded; return p_isLoaded;
@ -45,188 +51,317 @@ bool RagePhoto::load()
if (p_isLoaded) if (p_isLoaded)
clear(); clear();
QByteArray readData; if (p_inputMode == 1) {
if (p_inputMode == 0) {
QFile pictureFile(p_filePath); QFile pictureFile(p_filePath);
if (pictureFile.open(QIODevice::ReadOnly)) { if (pictureFile.open(QIODevice::ReadOnly)) {
readData = pictureFile.readAll(); p_fileData = pictureFile.readAll();
} }
pictureFile.close(); pictureFile.close();
} }
else if (p_inputMode == 1) { else if (p_inputMode == 2) {
if (!p_ioDevice->isOpen()) { if (!p_ioDevice->isOpen()) {
if (!p_ioDevice->open(QIODevice::ReadOnly)) if (!p_ioDevice->open(QIODevice::ReadOnly))
return false; return false;
readData = p_ioDevice->readAll();
} }
} p_fileData = p_ioDevice->readAll();
else {
return false;
} }
QBuffer dataBuffer(&readData); QBuffer dataBuffer(&p_fileData);
dataBuffer.open(QIODevice::ReadOnly); dataBuffer.open(QIODevice::ReadOnly);
char formatHeader[4]; char formatHeader[4];
qint64 size = dataBuffer.read(formatHeader, 4); qint64 size = dataBuffer.read(formatHeader, 4);
if (size != 4) if (size != 4)
return false; return false;
quint32 format = charToUInt32(formatHeader); quint32 format = charToUInt32LE(formatHeader);
if (format != PhotoFormat::GTA5)
return false;
char photoHeader[256]; if (format == PhotoFormat::GTA5) {
size = dataBuffer.read(photoHeader, 256); char photoHeader[256];
if (size != 256) size = dataBuffer.read(photoHeader, 256);
return false; if (size != 256)
for (const QChar &photoChar : QTextCodec::codecForName("UTF-16LE")->toUnicode(photoHeader, 256)) { return false;
if (photoChar.isNull()) for (const QChar &photoChar : QTextCodec::codecForName("UTF-16LE")->toUnicode(photoHeader, 256)) {
break; if (photoChar.isNull())
p_photoString += photoChar; break;
p_photoString += photoChar;
}
char checksum[4];
size = dataBuffer.read(checksum, 4);
if (size != 4)
return false;
p_headerSum = charToUInt32LE(checksum);
char endOfFile[4];
size = dataBuffer.read(endOfFile, 4);
if (size != 4)
return false;
p_endOfFile = charToUInt32LE(endOfFile);
char jsonOffset[4];
size = dataBuffer.read(jsonOffset, 4);
if (size != 4)
return false;
p_jsonOffset = charToUInt32LE(jsonOffset);
char titleOffset[4];
size = dataBuffer.read(titleOffset, 4);
if (size != 4)
return false;
p_titlOffset = charToUInt32LE(titleOffset);
char descOffset[4];
size = dataBuffer.read(descOffset, 4);
if (size != 4)
return false;
p_descOffset = charToUInt32LE(descOffset);
char jpegMarker[4];
size = dataBuffer.read(jpegMarker, 4);
if (size != 4)
return false;
if (strncmp(jpegMarker, "JPEG", 4) != 0)
return false;
char jpegBuffer[4];
size = dataBuffer.read(jpegBuffer, 4);
if (size != 4)
return false;
p_jpegBuffer = charToUInt32LE(jpegBuffer);
char photoSize[4];
size = dataBuffer.read(photoSize, 4);
if (size != 4)
return false;
p_photoSize = charToUInt32LE(photoSize);
char photoData[p_photoSize];
size = dataBuffer.read(photoData, p_photoSize);
if (size != p_photoSize)
return false;
p_photoData = QByteArray::fromRawData(photoData, p_photoSize);
dataBuffer.seek(p_jsonOffset + 264);
char jsonMarker[4];
size = dataBuffer.read(jsonMarker, 4);
if (size != 4)
return false;
if (strncmp(jsonMarker, "JSON", 4) != 0)
return false;
char jsonSize[4];
size = dataBuffer.read(jsonSize, 4);
if (size != 4)
return false;
quint32 i_jsonSize = charToUInt32LE(jsonSize);
char jsonBytes[i_jsonSize];
size = dataBuffer.read(jsonBytes, i_jsonSize);
if (size != i_jsonSize)
return false;
QByteArray t_jsonBytes;
for (quint32 i = 0; i != i_jsonSize; i++) {
if (jsonBytes[i] == '\x00')
break;
t_jsonBytes += jsonBytes[i];
}
QJsonDocument t_jsonDocument = QJsonDocument::fromJson(t_jsonBytes);
if (t_jsonDocument.isNull())
return false;
p_jsonObject = t_jsonDocument.object();
dataBuffer.seek(p_titlOffset + 264);
char titlMarker[4];
size = dataBuffer.read(titlMarker, 4);
if (size != 4)
return false;
if (strncmp(titlMarker, "TITL", 4) != 0)
return false;
char titlSize[4];
size = dataBuffer.read(titlSize, 4);
if (size != 4)
return false;
quint32 i_titlSize = charToUInt32LE(titlSize);
char titlBytes[i_titlSize];
size = dataBuffer.read(titlBytes, i_titlSize);
if (size != i_titlSize)
return false;
for (const QChar &titlChar : QString::fromUtf8(titlBytes, i_titlSize)) {
if (titlChar.isNull())
break;
p_titleString += titlChar;
}
dataBuffer.seek(p_descOffset + 264);
char descMarker[4];
size = dataBuffer.read(descMarker, 4);
if (size != 4)
return false;
if (strncmp(descMarker, "DESC", 4) != 0)
return false;
char descSize[4];
size = dataBuffer.read(descSize, 4);
if (size != 4)
return false;
quint32 i_descSize = charToUInt32LE(descSize);
char descBytes[i_descSize];
size = dataBuffer.read(descBytes, i_descSize);
if (size != i_descSize)
return false;
for (const QChar &descChar : QString::fromUtf8(descBytes, i_descSize)) {
if (descChar.isNull())
break;
p_descriptionString += descChar;
}
dataBuffer.seek(p_endOfFile + 260);
char jendMarker[4];
size = dataBuffer.read(jendMarker, 4);
if (size != 4)
return false;
if (strncmp(jendMarker, "JEND", 4) != 0)
return false;
p_fileData.clear();
p_isLoaded = true;
return true;
} }
else if (format == PhotoFormat::G5EX) {
char formatHeader[4];
qint64 size = dataBuffer.read(formatHeader, 4);
if (size != 4)
return false;
quint32 format = charToUInt32LE(formatHeader);
if (format == ExportFormat::G5E3P) {
char photoHeaderSize[4];
size = dataBuffer.peek(photoHeaderSize, 4);
if (size != 4)
return false;
quint32 i_photoHeaderSize = charToUInt32BE(photoHeaderSize) + 4;
char checksum[4]; char compressedPhotoHeader[i_photoHeaderSize];
size = dataBuffer.read(checksum, 4); size = dataBuffer.read(compressedPhotoHeader, i_photoHeaderSize);
if (size != 4) if (size != i_photoHeaderSize)
return false; return false;
p_headerCRC = charToUInt32(checksum); QByteArray t_photoHeaderBytes = QByteArray::fromRawData(compressedPhotoHeader, i_photoHeaderSize);
t_photoHeaderBytes = qUncompress(t_photoHeaderBytes);
p_photoString = QString::fromUtf8(t_photoHeaderBytes);
char endOfFile[4]; char checksum[4];
size = dataBuffer.read(endOfFile, 4); size = dataBuffer.read(checksum, 4);
if (size != 4) if (size != 4)
return false; return false;
p_endOfFile = charToUInt32(endOfFile); p_headerSum = charToUInt32LE(checksum);
char jsonOffset[4]; char jpegBuffer[4];
size = dataBuffer.read(jsonOffset, 4); size = dataBuffer.read(jpegBuffer, 4);
if (size != 4) if (size != 4)
return false; return false;
p_jsonOffset = charToUInt32(jsonOffset); p_jpegBuffer = charToUInt32LE(jpegBuffer);
char titleOffset[4]; char photoSize[4];
size = dataBuffer.read(titleOffset, 4); size = dataBuffer.peek(photoSize, 4);
if (size != 4) if (size != 4)
return false; return false;
p_titlOffset = charToUInt32(titleOffset); quint32 i_photoSize = charToUInt32BE(photoSize) + 4;
char descOffset[4]; char compressedPhoto[i_photoSize];
size = dataBuffer.read(descOffset, 4); size = dataBuffer.read(compressedPhoto, i_photoSize);
if (size != 4) if (size != i_photoSize)
return false; return false;
p_descOffset = charToUInt32(descOffset); QByteArray t_photoData = QByteArray::fromRawData(compressedPhoto, i_photoSize);
p_photoData = qUncompress(t_photoData);
p_photoSize = p_photoData.size();
char jpegMarker[4]; char jsonOffset[4];
size = dataBuffer.read(jpegMarker, 4); size = dataBuffer.read(jsonOffset, 4);
if (size != 4) if (size != 4)
return false; return false;
if (strncmp(jpegMarker, "JPEG", 4) != 0) p_jsonOffset = charToUInt32LE(jsonOffset);
return false;
char jpegBuffer[4]; char jsonSize[4];
size = dataBuffer.read(jpegBuffer, 4); size = dataBuffer.peek(jsonSize, 4);
if (size != 4) if (size != 4)
return false; return false;
p_jpegBuffer = charToUInt32(jpegBuffer); quint32 i_jsonSize = charToUInt32BE(jsonSize) + 4;
char photoSize[4]; char compressedJson[i_jsonSize];
size = dataBuffer.read(photoSize, 4); size = dataBuffer.read(compressedJson, i_jsonSize);
if (size != 4) if (size != i_jsonSize)
return false; return false;
p_photoSize = charToUInt32(photoSize); QByteArray t_jsonBytes = QByteArray::fromRawData(compressedJson, i_jsonSize);
t_jsonBytes = qUncompress(t_jsonBytes);
QJsonDocument t_jsonDocument = QJsonDocument::fromJson(t_jsonBytes);
if (t_jsonDocument.isNull())
return false;
p_jsonObject = t_jsonDocument.object();
char photoData[p_photoSize]; char titleOffset[4];
size = dataBuffer.read(photoData, p_photoSize); size = dataBuffer.read(titleOffset, 4);
if (size != p_photoSize) if (size != 4)
return false; return false;
p_photoData = QByteArray::fromRawData(photoData, p_photoSize); p_titlOffset = charToUInt32LE(titleOffset);
dataBuffer.seek(p_jsonOffset + 264); char titlSize[4];
char jsonMarker[4]; size = dataBuffer.peek(titlSize, 4);
size = dataBuffer.read(jsonMarker, 4); if (size != 4)
if (size != 4) return false;
return false; quint32 i_titlSize = charToUInt32BE(titlSize) + 4;
if (strncmp(jsonMarker, "JSON", 4) != 0)
return false;
char jsonSize[4]; char compressedTitl[i_titlSize];
size = dataBuffer.read(jsonSize, 4); size = dataBuffer.read(compressedTitl, i_titlSize);
if (size != 4) if (size != i_titlSize)
return false; return false;
quint32 i_jsonSize = charToUInt32(jsonSize); QByteArray t_titlBytes = QByteArray::fromRawData(compressedTitl, i_titlSize);
t_titlBytes = qUncompress(t_titlBytes);
p_titleString = QString::fromUtf8(t_titlBytes);
char jsonBytes[i_jsonSize]; char descOffset[4];
size = dataBuffer.read(jsonBytes, i_jsonSize); size = dataBuffer.read(descOffset, 4);
if (size != i_jsonSize) if (size != 4)
return false; return false;
QByteArray t_jsonBytes; p_descOffset = charToUInt32LE(descOffset);
for (quint32 i = 0; i != i_jsonSize; i++) {
if (jsonBytes[i] == '\x00') char descSize[4];
break; size = dataBuffer.peek(descSize, 4);
t_jsonBytes += jsonBytes[i]; if (size != 4)
return false;
quint32 i_descSize = charToUInt32BE(descSize) + 4;
char compressedDesc[i_descSize];
size = dataBuffer.read(compressedDesc, i_descSize);
if (size != i_descSize)
return false;
QByteArray t_descBytes = QByteArray::fromRawData(compressedDesc, i_descSize);
t_descBytes = qUncompress(t_descBytes);
p_descriptionString = QString::fromUtf8(t_descBytes);
char endOfFile[4];
size = dataBuffer.read(endOfFile, 4);
if (size != 4)
return false;
p_endOfFile = charToUInt32LE(endOfFile);
p_fileData.clear();
p_isLoaded = true;
return true;
}
else if (format == ExportFormat::G5E2P) {
p_fileData = dataBuffer.readAll();
p_inputMode = 0;
return load();
}
else {
return false;
}
} }
QJsonDocument t_jsonDocument = QJsonDocument::fromJson(t_jsonBytes); else {
if (t_jsonDocument.isNull())
return false; return false;
p_jsonObject = t_jsonDocument.object();
dataBuffer.seek(p_titlOffset + 264);
char titlMarker[4];
size = dataBuffer.read(titlMarker, 4);
if (size != 4)
return false;
if (strncmp(titlMarker, "TITL", 4) != 0)
return false;
char titlSize[4];
size = dataBuffer.read(titlSize, 4);
if (size != 4)
return false;
quint32 i_titlSize = charToUInt32(titlSize);
char titlBytes[i_titlSize];
size = dataBuffer.read(titlBytes, i_titlSize);
if (size != i_titlSize)
return false;
for (const QChar &titlChar : QString::fromUtf8(titlBytes, i_titlSize)) {
if (titlChar.isNull())
break;
p_titleString += titlChar;
} }
dataBuffer.seek(p_descOffset + 264);
char descMarker[4];
size = dataBuffer.read(descMarker, 4);
if (size != 4)
return false;
if (strncmp(descMarker, "DESC", 4) != 0)
return false;
char descSize[4];
size = dataBuffer.read(descSize, 4);
if (size != 4)
return false;
quint32 i_descSize = charToUInt32(descSize);
char descBytes[i_descSize];
size = dataBuffer.read(descBytes, i_descSize);
if (size != i_descSize)
return false;
for (const QChar &descChar : QString::fromUtf8(descBytes, i_descSize)) {
if (descChar.isNull())
break;
p_descriptionString += descChar;
}
dataBuffer.seek(p_endOfFile + 260);
char jendMarker[4];
size = dataBuffer.read(jendMarker, 4);
if (size != 4)
return false;
if (strncmp(jendMarker, "JEND", 4) != 0)
return false;
p_isLoaded = true;
return true;
} }
void RagePhoto::clear() void RagePhoto::clear()
@ -236,7 +371,7 @@ void RagePhoto::clear()
p_photoData.clear(); p_photoData.clear();
p_photoString.clear(); p_photoString.clear();
p_titleString.clear(); p_titleString.clear();
p_headerCRC = 0; p_headerSum = 0;
p_isLoaded = false; p_isLoaded = false;
} }
@ -293,7 +428,12 @@ RagePhoto* RagePhoto::loadFile(const QString &filePath)
return ragePhoto; return ragePhoto;
} }
quint32 RagePhoto::charToUInt32(char *x) quint32 RagePhoto::charToUInt32BE(char *x)
{
return (((unsigned char)x[0] << 24) | ((unsigned char)x[1] << 16) | ((unsigned char)x[2] << 8) | ((unsigned char)x[3]));
}
quint32 RagePhoto::charToUInt32LE(char *x)
{ {
return (((unsigned char)x[3] << 24) | ((unsigned char)x[2] << 16) | ((unsigned char)x[1] << 8) | ((unsigned char)x[0])); return (((unsigned char)x[3] << 24) | ((unsigned char)x[2] << 16) | ((unsigned char)x[1] << 8) | ((unsigned char)x[0]));
} }

View file

@ -27,10 +27,18 @@ class RagePhoto : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
enum ExportFormat {
G5E2P = 0x01000032U,
G5E2S = 0x02000032U,
G5E3P = 0x01000033U,
G5E3S = 0x02000033U,
};
enum PhotoFormat { enum PhotoFormat {
G5EX = 0x45354700U,
GTA5 = 0x01000000U, GTA5 = 0x01000000U,
RDR2 = 0x04000000U, RDR2 = 0x04000000U,
}; };
explicit RagePhoto(const QByteArray &data);
explicit RagePhoto(const QString &filePath = QString()); explicit RagePhoto(const QString &filePath = QString());
explicit RagePhoto(QIODevice *device); explicit RagePhoto(QIODevice *device);
bool isLoaded(); bool isLoaded();
@ -48,8 +56,10 @@ public:
static RagePhoto* loadFile(const QString &filePath); static RagePhoto* loadFile(const QString &filePath);
private: private:
inline quint32 charToUInt32(char *x); inline quint32 charToUInt32BE(char *x);
inline quint32 charToUInt32LE(char *x);
QJsonObject p_jsonObject; QJsonObject p_jsonObject;
QByteArray p_fileData;
QByteArray p_photoData; QByteArray p_photoData;
QIODevice *p_ioDevice; QIODevice *p_ioDevice;
QString p_descriptionString; QString p_descriptionString;
@ -58,7 +68,7 @@ private:
QString p_titleString; QString p_titleString;
quint32 p_descOffset; quint32 p_descOffset;
quint32 p_endOfFile; quint32 p_endOfFile;
quint32 p_headerCRC; quint32 p_headerSum;
quint32 p_jpegBuffer; quint32 p_jpegBuffer;
quint32 p_jsonOffset; quint32 p_jsonOffset;
quint32 p_photoSize; quint32 p_photoSize;