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