File indexing completed on 2025-01-05 04:01:13

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 #pragma once
0007 
0008 #include <variant>
0009 #include <cstdint>
0010 #include <vector>
0011 #include <cstring>
0012 #include <memory>
0013 
0014 #include <QByteArray>
0015 #include <QSysInfo>
0016 #include <QBuffer>
0017 
0018 #include <KLocalizedString>
0019 
0020 namespace glaxnimate::io::aep {
0021 
0022 template<int size> struct IntSize;
0023 template<> struct IntSize<1> { using uint = std::uint8_t;  using sint = std::int8_t; };
0024 template<> struct IntSize<2> { using uint = std::uint16_t; using sint = std::int16_t; };
0025 template<> struct IntSize<3> { using uint = std::uint32_t; using sint = std::int32_t; };
0026 template<> struct IntSize<4> { using uint = std::uint32_t; using sint = std::int32_t; };
0027 template<> struct IntSize<8> { using uint = std::uint64_t; using sint = std::int64_t; };
0028 
0029 class Endianness
0030 {
0031 public:
0032     template<class T>
0033     constexpr T read_uint(const QByteArray& arr) const noexcept
0034     {
0035         if constexpr ( sizeof(T) == 1 )
0036         {
0037             return arr[0];
0038         }
0039         else
0040         {
0041             T v = 0;
0042 
0043             for ( int i = 0; i < arr.size(); i++ )
0044             {
0045                 int j = swap() ? arr.size() - i - 1 : i;
0046                 v <<= 8;
0047                 v |= std::uint8_t(arr[j]);
0048             }
0049 
0050             return v;
0051         }
0052     }
0053 
0054     template<int size>
0055     constexpr typename IntSize<size>::uint read_uint(const QByteArray& arr) const noexcept
0056     {
0057         return read_uint<typename IntSize<size>::uint>(arr);
0058     }
0059 
0060     template<int size>
0061     constexpr typename IntSize<size>::sint read_sint(const QByteArray& arr) const noexcept
0062     {
0063         using uint_t = typename IntSize<size>::uint;
0064         using sint_t = typename IntSize<size>::uint;
0065         uint_t uint = read_uint<size>(arr);
0066         constexpr const uint_t sbit = 1ull << (size * 8 - 1);
0067 
0068         if ( !(uint & sbit) )
0069             return uint;
0070 
0071         return -sint_t(~uint + 1);
0072     }
0073 
0074     template<class T>
0075     constexpr T read_sint(const QByteArray& arr) const noexcept
0076     {
0077         return read_uint<sizeof(T)>(arr);
0078     }
0079 
0080     /**
0081      * \note Expects IEEE 754 floats
0082      */
0083     constexpr float read_float32(const QByteArray& arr) const noexcept
0084     {
0085         union {
0086             std::uint32_t vali;
0087             float valf;
0088         } x {read_uint<std::uint32_t>(arr)};
0089         return x.valf;
0090     }
0091 
0092     /**
0093      * \note Expects IEEE 754 floats
0094      */
0095     constexpr double read_float64(const QByteArray& arr) const noexcept
0096     {
0097         union {
0098             std::uint64_t vali;
0099             double valf;
0100         } x {read_uint<std::uint64_t>(arr)};
0101         return x.valf;
0102     }
0103 
0104     template<class T>
0105     QByteArray write_uint(T val) const
0106     {
0107         QByteArray out(sizeof(T), 0);
0108         for ( int i = 0; i < out.size(); i++ )
0109         {
0110             int j = i;
0111             if ( byte_order == QSysInfo::Endian::BigEndian )
0112                 j = sizeof(T) - 1 - i;
0113 
0114             out[j] = val & 0xff;
0115             val >>= 8;
0116         }
0117 
0118         return out;
0119     }
0120 
0121     /**
0122      * \note Expects IEEE 754 floats
0123      */
0124     QByteArray write_float32(float val) const noexcept
0125     {
0126         union {
0127             float valf;
0128             std::uint32_t vali;
0129         } x {val};
0130         return write_uint(x.vali);
0131     }
0132 
0133     /**
0134      * \note Expects IEEE 754 floats
0135      */
0136     QByteArray write_float64(double val) const noexcept
0137     {
0138         union {
0139             double valf;
0140             std::uint64_t vali;
0141         } x {val};
0142         return write_uint(x.vali);
0143     }
0144 
0145     static constexpr const Endianness Big() noexcept
0146     {
0147         return {QSysInfo::BigEndian};
0148     }
0149 
0150     static constexpr const Endianness Little() noexcept
0151     {
0152         return {QSysInfo::LittleEndian};
0153     }
0154 
0155 private:
0156     constexpr bool swap() const noexcept
0157     {
0158         return QSysInfo::ByteOrder == byte_order;
0159     }
0160     constexpr Endianness(QSysInfo::Endian byte_order) noexcept : byte_order(byte_order) {}
0161     QSysInfo::Endian byte_order;
0162 };
0163 
0164 class RiffError : public std::runtime_error
0165 {
0166 public:
0167     RiffError(QString message) : runtime_error(message.toStdString()), message(std::move(message)) {}
0168 
0169 
0170     QString message;
0171 };
0172 
0173 class Flags
0174 {
0175 public:
0176     constexpr Flags(std::uint32_t data) noexcept
0177     : data(data) {}
0178 
0179     constexpr bool get(int byte, int bit) const noexcept
0180     {
0181         return (data >> (8*byte)) & (1 << bit);
0182     }
0183 
0184 private:
0185     std::uint32_t data;
0186 };
0187 
0188 
0189 class BinaryReader
0190 {
0191 public:
0192     BinaryReader()
0193         : endian(Endianness::Big()),
0194         file(nullptr),
0195         file_pos(0),
0196         length_left(0)
0197     {}
0198 
0199     BinaryReader(Endianness endian, QIODevice* file, std::uint32_t length, qint64 pos)
0200         : endian(endian),
0201         file(file),
0202         file_pos(pos),
0203         length_left(length)
0204     {}
0205 
0206     BinaryReader(Endianness endian, QIODevice* file, std::uint32_t length)
0207         : endian(endian),
0208         file(file),
0209         file_pos(file->pos()),
0210         length_left(length)
0211     {}
0212 /*
0213     BinaryReader(Endianness endian, QByteArray& data, std::uint32_t length)
0214         : endian(endian), buffer(std::make_unique<QBuffer>(&data)), file(buffer.get()), length_left(length)
0215     {}
0216 */
0217     BinaryReader sub_reader(std::uint32_t length)
0218     {
0219         if ( length > length_left )
0220             throw RiffError(i18n("Not enough data"));
0221         length_left -= length;
0222         BinaryReader reader{endian, file, length, file_pos};
0223         file_pos += length;
0224         return reader;
0225     }
0226 
0227     /**
0228      * \brief Creates a sub-reader without affecting the current reader
0229      */
0230     BinaryReader sub_reader(std::uint32_t length, std::uint32_t offset) const
0231     {
0232         if ( length + offset > length_left )
0233             throw RiffError(i18n("Not enough data"));
0234 
0235         return {endian, file, length, file_pos + offset};
0236     }
0237 
0238     void set_endianness(const Endianness& endian)
0239     {
0240         this->endian = endian;
0241     }
0242 
0243     QByteArray read()
0244     {
0245         return read(length_left);
0246     }
0247 
0248     QByteArray read(std::uint32_t length)
0249     {
0250         length_left -= length;
0251         file_pos += length;
0252         auto data = file->read(length);
0253         if ( std::uint32_t(data.size()) < length )
0254             throw RiffError(i18n("Not enough data"));
0255         return data;
0256     }
0257 
0258     template<int size>
0259     typename IntSize<size>::uint read_uint()
0260     {
0261         return endian.read_uint<size>(read(size));
0262     }
0263 
0264     template<int size>
0265     typename IntSize<size>::sint read_sint()
0266     {
0267         return endian.read_sint<size>(read(size));
0268     }
0269 
0270     std::uint8_t read_uint8() { return read_uint<1>(); }
0271     std::uint16_t read_uint16() { return read_uint<2>(); }
0272     std::uint32_t read_uint32() { return read_uint<4>(); }
0273 
0274     std::int16_t read_sint16() { return read_sint<2>(); }
0275     std::uint32_t read_sint32() { return read_sint<4>(); }
0276 
0277     float read_float32()
0278     {
0279         return endian.read_float32(read(4));
0280     }
0281 
0282     double read_float64()
0283     {
0284         return endian.read_float64(read(8));
0285     }
0286 
0287     void skip(std::uint32_t length)
0288     {
0289         length_left -= length;
0290         file_pos += length;
0291         if ( file->skip(length) < length )
0292             throw RiffError(i18n("Not enough data"));
0293     }
0294 
0295     std::int64_t available() const
0296     {
0297         return length_left;
0298     }
0299 
0300     QString read_utf8(std::uint32_t length)
0301     {
0302         return QString::fromUtf8(read(length));
0303     }
0304 
0305     /**
0306      * \brief Read a NUL-terminated UTF-8 string
0307      */
0308     QString read_utf8_nul(std::uint32_t length)
0309     {
0310         auto data = read(length);
0311         int str_len = data.indexOf('\0');
0312         return QString::fromUtf8(data.data(), str_len == -1 ? length : str_len);
0313     }
0314 
0315     QString read_utf8_nul()
0316     {
0317         return read_utf8_nul(length_left);
0318     }
0319 
0320     std::uint32_t size() const
0321     {
0322         return length_left;
0323     }
0324 
0325     void prepare() const
0326     {
0327         file->seek(file_pos);
0328     }
0329 
0330     /**
0331      * \brief Defer data reading to a later point
0332      */
0333     void defer()
0334     {
0335         file->skip(length_left);
0336     }
0337 
0338     template<class T>
0339     std::vector<T> read_array(T (BinaryReader::*read_fn)(), int count)
0340     {
0341         std::vector<T> out;
0342         out.reserve(count);
0343         for ( int i = 0; i < count; i++ )
0344             out.push_back((this->*read_fn)());
0345         return out;
0346     }
0347 
0348     QIODevice* device() const
0349     {
0350         return file;
0351     }
0352 
0353 private:
0354     Endianness endian;
0355 //     std::unique_ptr<QBuffer> buffer;
0356     QIODevice* file;
0357     qint64 file_pos;
0358     std::int64_t length_left;
0359 };
0360 
0361 struct ChunkId
0362 {
0363     char name[4] = "";
0364 
0365     ChunkId(const QByteArray& arr)
0366     {
0367         std::memcpy(name, (void*)arr.data(), std::min<std::size_t>(4, arr.size()));
0368     }
0369 
0370     bool operator==(const char* ch) const {
0371         return std::strncmp(name, ch, 4) == 0;
0372     }
0373 
0374     bool operator!=(const char* ch) const {
0375         return std::strncmp(name, ch, 4) != 0;
0376     }
0377 
0378     QString to_string() const
0379     {
0380         return QString::fromLatin1(QByteArray(name, 4));
0381     }
0382 };
0383 
0384 struct RiffChunk
0385 {
0386     ChunkId header;
0387     std::uint32_t length = 0;
0388     ChunkId subheader = {""};
0389     BinaryReader reader = {};
0390     std::vector<std::unique_ptr<RiffChunk>> children = {};
0391 
0392     using iterator = std::vector<std::unique_ptr<RiffChunk>>::const_iterator;
0393 
0394     struct RangeIterator
0395     {
0396     public:
0397         constexpr RangeIterator(const iterator& internal, const char* name, const RiffChunk* chunk)
0398             : internal(internal), name(name), chunk(chunk)
0399         {}
0400 
0401         RangeIterator& operator++()
0402         {
0403             internal = chunk->find(name, internal + 1);
0404             return *this;
0405         }
0406 
0407         const RiffChunk& operator*() const
0408         {
0409             return **internal;
0410         }
0411 
0412         const RiffChunk* operator->() const
0413         {
0414             return internal->get();
0415         }
0416 
0417         bool operator==(const RangeIterator& other) const
0418         {
0419             return other.internal == internal;
0420         }
0421 
0422         bool operator!=(const RangeIterator& other) const
0423         {
0424             return other.internal != internal;
0425         }
0426 
0427     private:
0428         iterator internal;
0429         const char* name;
0430         const RiffChunk* chunk;
0431     };
0432 
0433     struct FindRange
0434     {
0435         RangeIterator a, b;
0436         RangeIterator begin() const { return a; }
0437         RangeIterator end() const { return b; }
0438     };
0439 
0440     bool operator==(const char* name) const
0441     {
0442         if ( header == name )
0443             return true;
0444 
0445         if ( header == "LIST" )
0446             return subheader == name;
0447 
0448         return false;
0449     }
0450 
0451     bool operator!=(const char* name) const
0452     {
0453         return !(*this == name);
0454     }
0455 
0456     BinaryReader data() const
0457     {
0458         BinaryReader data = reader;
0459         data.prepare();
0460         return data;
0461     }
0462 
0463     iterator find(const char* name) const
0464     {
0465         return find(name, children.begin());
0466     }
0467 
0468     iterator find(const char* name, iterator from) const
0469     {
0470         return std::find_if(from, children.end(), [name](const std::unique_ptr<RiffChunk>& c){ return *c == name; });
0471     }
0472 
0473     const RiffChunk* child(const char* name) const
0474     {
0475         auto it = find(name);
0476         if ( it == children.end() )
0477             return nullptr;
0478         return it->get();
0479     }
0480 
0481     FindRange find_all(const char* name) const
0482     {
0483         return {{find(name), name, this}, {children.end(), name, this}};
0484     }
0485 
0486     void find_multiple(
0487         const std::vector<const RiffChunk**>& out,
0488         const std::vector<const char*> names
0489     ) const
0490     {
0491         std::size_t found = 0;
0492         for ( const auto& child: children )
0493         {
0494             for ( std::size_t i = 0; i < names.size(); i++ )
0495             {
0496                 if ( !*out[i] && *child == names[i] )
0497                 {
0498                     *out[i] = child.get();
0499                     found++;
0500                     if ( found == names.size() )
0501                         return;
0502                 }
0503             }
0504         }
0505     }
0506 
0507     const ChunkId& name() const
0508     {
0509         if ( header == "LIST" )
0510             return subheader;
0511         return header;
0512     }
0513 };
0514 
0515 class RiffReader
0516 {
0517 public:
0518     virtual ~RiffReader() = default;
0519 
0520     RiffChunk parse(QIODevice* file)
0521     {
0522         auto headerraw = file->read(4);
0523         ChunkId header = headerraw;
0524         Endianness endian = Endianness::Big();
0525         if ( header == "RIFF" )
0526             endian = Endianness::Little();
0527         else if ( header != "RIFX" )
0528             throw RiffError(i18n("Unknown format %1", QString(headerraw)));
0529 
0530         auto length = endian.read_uint<4>(file->read(4));
0531 
0532         BinaryReader reader = BinaryReader(endian, file, length);
0533         ChunkId format = reader.read(4);
0534         RiffChunk chunk{header, length, format};
0535         chunk.reader = reader;
0536         on_root(chunk);
0537         return chunk;
0538     }
0539 
0540 protected:
0541     RiffChunk read_chunk(BinaryReader& reader)
0542     {
0543         ChunkId header = reader.read(4);
0544         auto length = reader.read_uint<4>();
0545         RiffChunk chunk{header, length};
0546 
0547         chunk.reader = reader.sub_reader(length);
0548 
0549         on_chunk(chunk);
0550 
0551         if ( length % 2 )
0552             reader.skip(1);
0553 
0554         return chunk;
0555     }
0556 
0557     std::vector<std::unique_ptr<RiffChunk>> read_chunks(BinaryReader& reader)
0558     {
0559         std::vector<std::unique_ptr<RiffChunk>> chunks;
0560         while ( reader.available() )
0561             chunks.push_back(std::make_unique<RiffChunk>(read_chunk(reader)));
0562         return chunks;
0563     }
0564 
0565     virtual void on_root(RiffChunk& chunk)
0566     {
0567         chunk.children = read_chunks(chunk.reader);
0568     }
0569 
0570     virtual void on_chunk(RiffChunk& chunk)
0571     {
0572         if ( chunk.header == "LIST" )
0573         {
0574             chunk.subheader = chunk.reader.read(4);
0575             chunk.children = read_chunks(chunk.reader);
0576         }
0577         else
0578         {
0579             chunk.reader.defer();
0580         }
0581     }
0582 };
0583 
0584 } // namespace glaxnimate::io::aep