From 5bdbb09c7a0c649653f787442b1bfff3d0f988e4 Mon Sep 17 00:00:00 2001 From: Syping Date: Fri, 17 Sep 2021 07:22:49 +0200 Subject: [PATCH] add save feature --- src/RagePhoto.cpp | 238 ++++++++++++++++++++++++++++++++++++++++++++-- src/RagePhoto.h | 28 +++++- 2 files changed, 258 insertions(+), 8 deletions(-) diff --git a/src/RagePhoto.cpp b/src/RagePhoto.cpp index dc5d348..28aa86e 100644 --- a/src/RagePhoto.cpp +++ b/src/RagePhoto.cpp @@ -94,7 +94,7 @@ bool RagePhoto::load(const char *data, size_t length) } #ifdef CODECVT_COMPATIBLE - std::wstring_convert,char16_t> convert; + std::wstring_convert,char16_t> convert; p_photoString = convert.to_bytes(reinterpret_cast(photoHeader)); #elif defined ICONV_COMPATIBLE iconv_t iconv_in = iconv_open("UTF-8", "UTF-16LE"); @@ -114,6 +114,10 @@ bool RagePhoto::load(const char *data, size_t length) return false; } p_photoString = std::string(photoString); +#else + std::cout << "UTF-16LE decoding support missing" << std::endl; + p_error = Error::UnicodeInitError; // 4 + return false; #endif size = readBuffer(data, uInt32Buffer, &pos, 4, length); @@ -410,7 +414,7 @@ uint32_t RagePhoto::photoSize() if (p_photoLoaded) return p_photoSize; else - return 0; + return 0UL; } const std::string RagePhoto::description() @@ -433,17 +437,226 @@ const std::string RagePhoto::title() return p_titleString; } -uint32_t RagePhoto::saveSize(PhotoFormat photoFormat) +bool RagePhoto::save(char *data, PhotoFormat photoFormat) +{ + if (photoFormat == PhotoFormat::GTA5 || photoFormat == PhotoFormat::RDR2) { +#ifdef CODECVT_COMPATIBLE + std::wstring_convert,char16_t> convert; + std::u16string photoString = convert.from_bytes(p_photoString); + uint32_t photoString_size = photoString.size() * 2 + 1; + if (photoString_size > 256) { + p_error = Error::HeaderBufferTight; // 34 + return false; + } + char photoHeader[256]; + memcpy(photoHeader, photoString.data(), photoString_size); +#else + std::cout << "UTF-16LE encoding support missing" << std::endl; + p_error = Error::UnicodeInitError; // 4 + return false; +#endif + + if (p_photoSize > p_photoBuffer) { + p_error = Error::PhotoBufferTight; // 35 + return false; + } + + uint32_t jsonString_size = p_jsonString.size() + 1; + if (jsonString_size > p_jsonBuffer) { + p_error = Error::JsonBufferTight; // 36 + return false; + } + + uint32_t titlString_size = p_titleString.size() + 1; + if (titlString_size > p_titlBuffer) { + p_error = Error::TitleBufferTight; // 37 + return false; + } + + uint32_t descString_size = p_descriptionString.size() + 1; + if (descString_size > p_descBuffer) { + p_error = Error::DescBufferTight; // 38 + return false; + } + + size_t length = saveSize(photoFormat); + size_t pos = 0; + char uInt32Buffer[4]; + +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy(uInt32Buffer, &photoFormat, 4); +#else + uInt32ToCharLE(static_cast(photoFormat), uInt32Buffer); +#endif + writeBuffer(uInt32Buffer, data, &pos, length, 4); + + writeBuffer(photoHeader, data, &pos, length, photoString_size); + for (size_t i = photoString_size; i < 256; i++) { + writeBuffer("\0", data, &pos, length, 1); + } + +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy(uInt32Buffer, &p_headerSum, 4); +#else + uInt32ToCharLE(p_headerSum, uInt32Buffer); +#endif + writeBuffer(uInt32Buffer, data, &pos, length, 4); + + if (photoFormat == PhotoFormat::RDR2) { + for (size_t i = 0; i < 8; i++) { + writeBuffer("\0", data, &pos, length, 1); + } + } + uint32_t headerSize = pos; + +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy(uInt32Buffer, &p_endOfFile, 4); +#else + uInt32ToCharLE(p_endOfFile, uInt32Buffer); +#endif + writeBuffer(uInt32Buffer, data, &pos, length, 4); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy(uInt32Buffer, &p_jsonOffset, 4); +#else + uInt32ToCharLE(p_jsonOffset, uInt32Buffer); +#endif + writeBuffer(uInt32Buffer, data, &pos, length, 4); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy(uInt32Buffer, &p_titlOffset, 4); +#else + uInt32ToCharLE(p_titlOffset, uInt32Buffer); +#endif + writeBuffer(uInt32Buffer, data, &pos, length, 4); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy(uInt32Buffer, &p_descOffset, 4); +#else + uInt32ToCharLE(p_descOffset, uInt32Buffer); +#endif + writeBuffer(uInt32Buffer, data, &pos, length, 4); + + writeBuffer("JPEG", data, &pos, length, 4); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy(uInt32Buffer, &p_photoBuffer, 4); +#else + uInt32ToCharLE(p_photoBuffer, uInt32Buffer); +#endif + writeBuffer(uInt32Buffer, data, &pos, length, 4); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy(uInt32Buffer, &p_photoSize, 4); +#else + uInt32ToCharLE(p_photoSize, uInt32Buffer); +#endif + writeBuffer(uInt32Buffer, data, &pos, length, 4); + + writeBuffer(p_photoData, data, &pos, length, p_photoSize); + for (size_t i = p_photoSize; i < p_photoBuffer; i++) { + writeBuffer("\0", data, &pos, length, 1); + } + + pos = p_jsonOffset + headerSize; + writeBuffer("JSON", data, &pos, length, 4); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy(uInt32Buffer, &p_jsonBuffer, 4); +#else + uInt32ToCharLE(p_jsonBuffer, uInt32Buffer); +#endif + writeBuffer(uInt32Buffer, data, &pos, length, 4); + + writeBuffer(p_jsonString.data(), data, &pos, length, jsonString_size); + for (size_t i = jsonString_size; i < p_jsonBuffer; i++) { + writeBuffer("\0", data, &pos, length, 1); + } + + pos = p_titlOffset + headerSize; + writeBuffer("TITL", data, &pos, length, 4); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy(uInt32Buffer, &p_titlBuffer, 4); +#else + uInt32ToCharLE(p_titlBuffer, uInt32Buffer); +#endif + writeBuffer(uInt32Buffer, data, &pos, length, 4); + + writeBuffer(p_titleString.data(), data, &pos, length, titlString_size); + for (size_t i = titlString_size; i < p_titlBuffer; i++) { + writeBuffer("\0", data, &pos, length, 1); + } + + pos = p_descOffset + headerSize; + writeBuffer("DESC", data, &pos, length, 4); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy(uInt32Buffer, &p_descBuffer, 4); +#else + uInt32ToCharLE(p_descBuffer, uInt32Buffer); +#endif + writeBuffer(uInt32Buffer, data, &pos, length, 4); + + writeBuffer(p_descriptionString.data(), data, &pos, length, descString_size); + for (size_t i = descString_size; i < p_descBuffer; i++) { + writeBuffer("\0", data, &pos, length, 1); + } + + pos = p_endOfFile + headerSize - 4; + writeBuffer("JEND", data, &pos, length, 4); + + p_error = Error::NoError; // 255 + return true; + } + + p_error = Error::IncompatibleFormat; // 2 + return false; +} + +bool RagePhoto::save(char *data) +{ + return save(data, p_photoFormat); +} + +const std::string RagePhoto::save(PhotoFormat photoFormat, bool *ok) +{ + size_t size = saveSize(photoFormat); + if (size == 0) { + if (ok) + *ok = false; + return std::string(); + } + char *data = static_cast(malloc(size)); + if (!data) { + if (ok) + *ok = false; + return std::string(); + } + const bool saved = save(data, photoFormat); + if (ok) + *ok = saved; + const std::string sdata = std::string(data, size); + free(data); + return sdata; +} + +const std::string RagePhoto::save(bool *ok) +{ + return save(p_photoFormat, ok); +} + +size_t RagePhoto::saveSize(PhotoFormat photoFormat) { if (photoFormat == PhotoFormat::GTA5) return (p_photoBuffer + p_jsonBuffer + p_titlBuffer + p_descBuffer + GTA5_HEADERSIZE + 56UL); else if (photoFormat == PhotoFormat::RDR2) return (p_photoBuffer + p_jsonBuffer + p_titlBuffer + p_descBuffer + RDR2_HEADERSIZE + 56UL); else - return 0UL; + return 0; } -uint32_t RagePhoto::saveSize() +size_t RagePhoto::saveSize() { return saveSize(p_photoFormat); } @@ -556,7 +769,7 @@ size_t RagePhoto::readBuffer(const char *input, char *output, size_t *pos, size_ { size_t readLen = 0; if (*pos >= inputLen) - return readLen; + return 0; readLen = inputLen - *pos; if (readLen > len) readLen = len; @@ -565,6 +778,19 @@ size_t RagePhoto::readBuffer(const char *input, char *output, size_t *pos, size_ return readLen; } +size_t RagePhoto::writeBuffer(const char *input, char *output, size_t *pos, size_t len, size_t inputLen) +{ + const size_t maxLen = len - *pos; + size_t writeLen = inputLen; + if (*pos >= len) + return 0; + if (inputLen > maxLen) + writeLen = maxLen; + memcpy(&output[*pos], input, writeLen); + *pos = *pos + writeLen; + return writeLen; +} + uint32_t RagePhoto::charToUInt32LE(char *x) { return (static_cast(x[3]) << 24 | diff --git a/src/RagePhoto.h b/src/RagePhoto.h index 094a7d6..92cb6c5 100644 --- a/src/RagePhoto.h +++ b/src/RagePhoto.h @@ -37,8 +37,10 @@ class LIBRAGEPHOTO_EXPORT RagePhoto public: /** Parsing and set errors */ enum class Error : uint8_t { + DescBufferTight = 38, /**< Description Buffer is too tight */ DescMallocError = 30, /**< Description Buffer can't be allocated */ DescReadError = 31, /**< Description can't be read successfully */ + HeaderBufferTight = 34, /**< Header Buffer is too tight */ IncompatibleFormat = 2, /**< Format is incompatible */ IncompleteChecksum = 6, /**< Header checksum is incomplete */ IncompleteDescBuffer = 29, /**< Description Buffer Size is incomplete */ @@ -61,12 +63,15 @@ public: IncorrectJpegMarker = 12, /**< JPEG Marker is incorrect */ IncorrectJsonMarker = 18, /**< JSON Marker is incorrect */ IncorrectTitleMarker = 23, /**< Title Marker is incorrect */ + JsonBufferTight = 36, /**< JSON Buffer is too tight */ JsonMallocError = 20, /**< JSON Buffer can't be allocated */ JsonReadError = 21, /**< JSON can't be read successfully */ NoError = 255, /**< Finished without errors */ NoFormatIdentifier = 1, /**< No format detected, empty file */ + PhotoBufferTight = 35, /**< Photo Buffer is too tight */ PhotoMallocError = 15, /**< Photo Buffer can't be allocated */ PhotoReadError = 16, /**< Photo can't be read */ + TitleBufferTight = 37, /**< Title Buffer is too tight */ TitleMallocError = 25, /**< Title Buffer can't be allocated */ TitleReadError = 26, /**< Title can't be read */ UnicodeHeaderError = 5, /**< Header can't be decoded */ @@ -100,8 +105,26 @@ public: const std::string json(); /**< Returns the Photo JSON data. */ const std::string header(); /**< Returns the Photo header. */ const std::string title(); /**< Returns the Photo title. */ - uint32_t saveSize(PhotoFormat photoFormat); /**< Returns the save file size. */ - uint32_t saveSize(); /**< Returns the save file size. */ + /** Saves a Photo to a char*. + * \param data Photo data + * \param photoFormat Photo Format (GTA V or RDR 2) + */ + bool save(char *data, PhotoFormat photoFormat); + /** Saves a Photo to a char*. + * \param data Photo data + */ + bool save(char *data); + /** Saves a Photo to a std::string. + * \param photoFormat Photo Format (GTA V or RDR 2) + * \param ok \p true when saved successfully + */ + const std::string save(PhotoFormat photoFormat, bool *ok = nullptr); + /** Saves a Photo to a std::string. + * \param ok \p true when saved successfully + */ + const std::string save(bool *ok = nullptr); + size_t saveSize(PhotoFormat photoFormat); /**< Returns the save file size. */ + size_t saveSize(); /**< Returns the save file size. */ void setBufferDefault(); /**< Sets all cross-format Buffer to default size. */ void setDescription(const std::string &description, uint32_t bufferSize = 0); /**< Sets the Photo description. */ void setFormat(PhotoFormat photoFormat); /**< Sets the Photo Format (GTA V or RDR 2). */ @@ -123,6 +146,7 @@ public: protected: inline void moveOffsets(); inline size_t readBuffer(const char *input, char *output, size_t *pos, size_t len, size_t inputLen); + inline size_t writeBuffer(const char *input, char *output, size_t *pos, size_t len, size_t inputLen); inline uint32_t charToUInt32LE(char *x); inline void uInt32ToCharLE(uint32_t x, char *y); bool p_photoLoaded;