File indexing completed on 2024-06-23 04:23:33

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
0003  *  SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #ifndef __KIS_ASL_READER_UTILS_H
0009 #define __KIS_ASL_READER_UTILS_H
0010 
0011 #include "psd.h"
0012 #include "psd_utils.h"
0013 
0014 #include <algorithm>
0015 #include <stdexcept>
0016 #include <string>
0017 
0018 #include <QtEndian>
0019 
0020 #include <kis_debug.h>
0021 
0022 /**
0023  * Default value for variable read from a file
0024  */
0025 
0026 #define GARBAGE_VALUE_MARK 999
0027 
0028 namespace KisAslReaderUtils
0029 {
0030 /**
0031  * Exception that is emitted when any parse error appear.
0032  * Thanks to KisOffsetOnExitVerifier parsing can be continued
0033  * most of the time, based on the offset values written in PSD.
0034  */
0035 
0036 struct KRITAPSDUTILS_EXPORT ASLParseException : public std::runtime_error {
0037     ASLParseException(const QString &msg)
0038         : std::runtime_error(msg.toLatin1().data())
0039     {
0040     }
0041 };
0042 
0043 }
0044 
0045 #define SAFE_READ_EX(byteOrder, device, varname)                                                                                                               \
0046     if (!psdread<byteOrder>(device, varname)) {                                                                                                                \
0047         QString msg = QString("Failed to read \'%1\' tag!").arg(#varname);                                                                                     \
0048         throw KisAslReaderUtils::ASLParseException(msg);                                                                                                       \
0049     }
0050 
0051 #define SAFE_READ_SIGNATURE_EX(byteOrder, device, varname, expected)                                                                                           \
0052     if (!psdread<byteOrder>(device, varname) || varname != expected) {                                                                                         \
0053         QString msg = QString(                                                                                                                                 \
0054                           "Failed to check signature \'%1\' tag!\n"                                                                                            \
0055                           "Value: \'%2\' Expected: \'%3\'")                                                                                                    \
0056                           .arg(#varname)                                                                                                                       \
0057                           .arg(varname)                                                                                                                        \
0058                           .arg(expected);                                                                                                                      \
0059         throw KisAslReaderUtils::ASLParseException(msg);                                                                                                       \
0060     }
0061 
0062 template<psd_byte_order byteOrder, typename T, size_t S>
0063 inline bool TRY_READ_SIGNATURE_2OPS_EX(QIODevice &device, const std::array<T, S> &expected1, const std::array<T, S> &expected2)
0064 {
0065     QByteArray bytes = device.peek(S);
0066 
0067     if (byteOrder == psd_byte_order::psdLittleEndian) {
0068         std::reverse(bytes.begin(), bytes.end());
0069     }
0070 
0071     if (bytes.size() != S) {
0072         return false;
0073     }
0074 
0075     // If read successfully, adjust current position of the io device
0076 
0077     if (std::equal(bytes.constBegin(), bytes.constEnd(), expected1.begin()) 
0078         || std::equal(bytes.constBegin(), bytes.constEnd(), expected2.begin())) {
0079         // read, not seek, to support sequential devices
0080         auto bytesRead = psdreadBytes(device, S);
0081         if (bytesRead.size() == S) {
0082             return true;
0083         }
0084     }
0085 
0086     dbgFile << "Photoshop signature verification failed! Got: " << bytes.toHex() << "(" << QString(bytes) << ")";
0087     return false;
0088 }
0089 
0090 template<typename T, size_t S>
0091 inline bool TRY_READ_SIGNATURE_2OPS_EX(psd_byte_order byteOrder, QIODevice &device, const std::array<T, S> &expected1, const std::array<T, S> &expected2)
0092 {
0093     switch (byteOrder) {
0094     case psd_byte_order::psdLittleEndian:
0095         return TRY_READ_SIGNATURE_2OPS_EX<psd_byte_order::psdLittleEndian>(device, expected1, expected2);
0096     default:
0097         return TRY_READ_SIGNATURE_2OPS_EX<psd_byte_order::psdBigEndian>(device, expected1, expected2);
0098     }
0099 }
0100 
0101 namespace KisAslReaderUtils
0102 {
0103 /**
0104  * String fetch functions
0105  *
0106  * ASL has 4 types of strings:
0107  *
0108  * - fixed length (4 bytes)
0109  * - variable length (length (4 bytes) + string (var))
0110  * - pascal (length (1 byte) + string (var))
0111  * - unicode string (length (4 bytes) + null-terminated unicode string (var)
0112  */
0113 
0114 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0115 inline QString readStringCommon(QIODevice &device, int length)
0116 {
0117     QByteArray data = psdreadBytes<byteOrder>(device, length);
0118 
0119     if (data.size() != length) {
0120         QString msg = QString(
0121                           "Failed to read a string! "
0122                           "Bytes read: %1 Expected: %2")
0123                           .arg(data.size())
0124                           .arg(length);
0125         throw ASLParseException(msg);
0126     }
0127 
0128     return QString(data);
0129 }
0130 
0131 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0132 inline QString readFixedString(QIODevice &device)
0133 {
0134     return readStringCommon<byteOrder>(device, 4);
0135 }
0136 
0137 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0138 inline QString readVarString(QIODevice &device)
0139 {
0140     quint32 length = 0;
0141     SAFE_READ_EX(byteOrder, device, length);
0142 
0143     if (!length) {
0144         length = 4;
0145     }
0146 
0147     return readStringCommon<byteOrder>(device, length);
0148 }
0149 
0150 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0151 inline QString readPascalString(QIODevice &device)
0152 {
0153     quint8 length = 0;
0154     SAFE_READ_EX(byteOrder, device, length);
0155 
0156     return readStringCommon(device, length);
0157 }
0158 
0159 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0160 inline QString readUnicodeString(QIODevice &device)
0161 {
0162     QString string;
0163 
0164     if (!psdread_unicodestring<byteOrder>(device, string)) {
0165         QString msg = QString("Failed to read a unicode string!");
0166         throw ASLParseException(msg);
0167     }
0168 
0169     return string;
0170 }
0171 }
0172 
0173 #endif /* __KIS_ASL_READER_UTILS_H */