123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689 |
- #include <kaitai/kaitaistream.h>
- #if defined(__APPLE__)
- #include <machine/endian.h>
- #include <libkern/OSByteOrder.h>
- #define bswap_16(x) OSSwapInt16(x)
- #define bswap_32(x) OSSwapInt32(x)
- #define bswap_64(x) OSSwapInt64(x)
- #define __BYTE_ORDER BYTE_ORDER
- #define __BIG_ENDIAN BIG_ENDIAN
- #define __LITTLE_ENDIAN LITTLE_ENDIAN
- #elif defined(_MSC_VER) // !__APPLE__
- #include <stdlib.h>
- #define __LITTLE_ENDIAN 1234
- #define __BIG_ENDIAN 4321
- #define __BYTE_ORDER __LITTLE_ENDIAN
- #define bswap_16(x) _byteswap_ushort(x)
- #define bswap_32(x) _byteswap_ulong(x)
- #define bswap_64(x) _byteswap_uint64(x)
- #else // !__APPLE__ or !_MSC_VER
- #include <endian.h>
- #include <byteswap.h>
- #endif
- #include <iostream>
- #include <vector>
- #include <stdexcept>
- kaitai::kstream::kstream(std::istream* io) {
- m_io = io;
- init();
- }
- kaitai::kstream::kstream(std::string& data): m_io_str(data) {
- m_io = &m_io_str;
- init();
- }
- void kaitai::kstream::init() {
- exceptions_enable();
- align_to_byte();
- }
- void kaitai::kstream::close() {
- // m_io->close();
- }
- void kaitai::kstream::exceptions_enable() const {
- m_io->exceptions(
- std::istream::eofbit |
- std::istream::failbit |
- std::istream::badbit
- );
- }
- // ========================================================================
- // Stream positioning
- // ========================================================================
- bool kaitai::kstream::is_eof() const {
- if (m_bits_left > 0) {
- return false;
- }
- char t;
- m_io->exceptions(
- std::istream::badbit
- );
- m_io->get(t);
- if (m_io->eof()) {
- m_io->clear();
- exceptions_enable();
- return true;
- } else {
- m_io->unget();
- exceptions_enable();
- return false;
- }
- }
- void kaitai::kstream::seek(uint64_t pos) {
- m_io->seekg(pos);
- }
- uint64_t kaitai::kstream::pos() {
- return m_io->tellg();
- }
- uint64_t kaitai::kstream::size() {
- std::iostream::pos_type cur_pos = m_io->tellg();
- m_io->seekg(0, std::ios::end);
- std::iostream::pos_type len = m_io->tellg();
- m_io->seekg(cur_pos);
- return len;
- }
- // ========================================================================
- // Integer numbers
- // ========================================================================
- // ------------------------------------------------------------------------
- // Signed
- // ------------------------------------------------------------------------
- int8_t kaitai::kstream::read_s1() {
- char t;
- m_io->get(t);
- return t;
- }
- // ........................................................................
- // Big-endian
- // ........................................................................
- int16_t kaitai::kstream::read_s2be() {
- int16_t t;
- m_io->read(reinterpret_cast<char *>(&t), 2);
- #if __BYTE_ORDER == __LITTLE_ENDIAN
- t = bswap_16(t);
- #endif
- return t;
- }
- int32_t kaitai::kstream::read_s4be() {
- int32_t t;
- m_io->read(reinterpret_cast<char *>(&t), 4);
- #if __BYTE_ORDER == __LITTLE_ENDIAN
- t = bswap_32(t);
- #endif
- return t;
- }
- int64_t kaitai::kstream::read_s8be() {
- int64_t t;
- m_io->read(reinterpret_cast<char *>(&t), 8);
- #if __BYTE_ORDER == __LITTLE_ENDIAN
- t = bswap_64(t);
- #endif
- return t;
- }
- // ........................................................................
- // Little-endian
- // ........................................................................
- int16_t kaitai::kstream::read_s2le() {
- int16_t t;
- m_io->read(reinterpret_cast<char *>(&t), 2);
- #if __BYTE_ORDER == __BIG_ENDIAN
- t = bswap_16(t);
- #endif
- return t;
- }
- int32_t kaitai::kstream::read_s4le() {
- int32_t t;
- m_io->read(reinterpret_cast<char *>(&t), 4);
- #if __BYTE_ORDER == __BIG_ENDIAN
- t = bswap_32(t);
- #endif
- return t;
- }
- int64_t kaitai::kstream::read_s8le() {
- int64_t t;
- m_io->read(reinterpret_cast<char *>(&t), 8);
- #if __BYTE_ORDER == __BIG_ENDIAN
- t = bswap_64(t);
- #endif
- return t;
- }
- // ------------------------------------------------------------------------
- // Unsigned
- // ------------------------------------------------------------------------
- uint8_t kaitai::kstream::read_u1() {
- char t;
- m_io->get(t);
- return t;
- }
- // ........................................................................
- // Big-endian
- // ........................................................................
- uint16_t kaitai::kstream::read_u2be() {
- uint16_t t;
- m_io->read(reinterpret_cast<char *>(&t), 2);
- #if __BYTE_ORDER == __LITTLE_ENDIAN
- t = bswap_16(t);
- #endif
- return t;
- }
- uint32_t kaitai::kstream::read_u4be() {
- uint32_t t;
- m_io->read(reinterpret_cast<char *>(&t), 4);
- #if __BYTE_ORDER == __LITTLE_ENDIAN
- t = bswap_32(t);
- #endif
- return t;
- }
- uint64_t kaitai::kstream::read_u8be() {
- uint64_t t;
- m_io->read(reinterpret_cast<char *>(&t), 8);
- #if __BYTE_ORDER == __LITTLE_ENDIAN
- t = bswap_64(t);
- #endif
- return t;
- }
- // ........................................................................
- // Little-endian
- // ........................................................................
- uint16_t kaitai::kstream::read_u2le() {
- uint16_t t;
- m_io->read(reinterpret_cast<char *>(&t), 2);
- #if __BYTE_ORDER == __BIG_ENDIAN
- t = bswap_16(t);
- #endif
- return t;
- }
- uint32_t kaitai::kstream::read_u4le() {
- uint32_t t;
- m_io->read(reinterpret_cast<char *>(&t), 4);
- #if __BYTE_ORDER == __BIG_ENDIAN
- t = bswap_32(t);
- #endif
- return t;
- }
- uint64_t kaitai::kstream::read_u8le() {
- uint64_t t;
- m_io->read(reinterpret_cast<char *>(&t), 8);
- #if __BYTE_ORDER == __BIG_ENDIAN
- t = bswap_64(t);
- #endif
- return t;
- }
- // ========================================================================
- // Floating point numbers
- // ========================================================================
- // ........................................................................
- // Big-endian
- // ........................................................................
- float kaitai::kstream::read_f4be() {
- uint32_t t;
- m_io->read(reinterpret_cast<char *>(&t), 4);
- #if __BYTE_ORDER == __LITTLE_ENDIAN
- t = bswap_32(t);
- #endif
- return reinterpret_cast<float&>(t);
- }
- double kaitai::kstream::read_f8be() {
- uint64_t t;
- m_io->read(reinterpret_cast<char *>(&t), 8);
- #if __BYTE_ORDER == __LITTLE_ENDIAN
- t = bswap_64(t);
- #endif
- return reinterpret_cast<double&>(t);
- }
- // ........................................................................
- // Little-endian
- // ........................................................................
- float kaitai::kstream::read_f4le() {
- uint32_t t;
- m_io->read(reinterpret_cast<char *>(&t), 4);
- #if __BYTE_ORDER == __BIG_ENDIAN
- t = bswap_32(t);
- #endif
- return reinterpret_cast<float&>(t);
- }
- double kaitai::kstream::read_f8le() {
- uint64_t t;
- m_io->read(reinterpret_cast<char *>(&t), 8);
- #if __BYTE_ORDER == __BIG_ENDIAN
- t = bswap_64(t);
- #endif
- return reinterpret_cast<double&>(t);
- }
- // ========================================================================
- // Unaligned bit values
- // ========================================================================
- void kaitai::kstream::align_to_byte() {
- m_bits_left = 0;
- m_bits = 0;
- }
- uint64_t kaitai::kstream::read_bits_int_be(int n) {
- int bits_needed = n - m_bits_left;
- if (bits_needed > 0) {
- // 1 bit => 1 byte
- // 8 bits => 1 byte
- // 9 bits => 2 bytes
- int bytes_needed = ((bits_needed - 1) / 8) + 1;
- if (bytes_needed > 8)
- throw std::runtime_error("read_bits_int: more than 8 bytes requested");
- char buf[8];
- m_io->read(buf, bytes_needed);
- for (int i = 0; i < bytes_needed; i++) {
- uint8_t b = buf[i];
- m_bits <<= 8;
- m_bits |= b;
- m_bits_left += 8;
- }
- }
- // raw mask with required number of 1s, starting from lowest bit
- uint64_t mask = get_mask_ones(n);
- // shift mask to align with highest bits available in @bits
- int shift_bits = m_bits_left - n;
- mask <<= shift_bits;
- // derive reading result
- uint64_t res = (m_bits & mask) >> shift_bits;
- // clear top bits that we've just read => AND with 1s
- m_bits_left -= n;
- mask = get_mask_ones(m_bits_left);
- m_bits &= mask;
- return res;
- }
- // Deprecated, use read_bits_int_be() instead.
- uint64_t kaitai::kstream::read_bits_int(int n) {
- return read_bits_int_be(n);
- }
- uint64_t kaitai::kstream::read_bits_int_le(int n) {
- int bits_needed = n - m_bits_left;
- if (bits_needed > 0) {
- // 1 bit => 1 byte
- // 8 bits => 1 byte
- // 9 bits => 2 bytes
- int bytes_needed = ((bits_needed - 1) / 8) + 1;
- if (bytes_needed > 8)
- throw std::runtime_error("read_bits_int_le: more than 8 bytes requested");
- char buf[8];
- m_io->read(buf, bytes_needed);
- for (int i = 0; i < bytes_needed; i++) {
- uint8_t b = buf[i];
- m_bits |= (static_cast<uint64_t>(b) << m_bits_left);
- m_bits_left += 8;
- }
- }
- // raw mask with required number of 1s, starting from lowest bit
- uint64_t mask = get_mask_ones(n);
- // derive reading result
- uint64_t res = m_bits & mask;
- // remove bottom bits that we've just read by shifting
- m_bits >>= n;
- m_bits_left -= n;
- return res;
- }
- uint64_t kaitai::kstream::get_mask_ones(int n) {
- if (n == 64) {
- return 0xFFFFFFFFFFFFFFFF;
- } else {
- return ((uint64_t) 1 << n) - 1;
- }
- }
- // ========================================================================
- // Byte arrays
- // ========================================================================
- std::string kaitai::kstream::read_bytes(std::streamsize len) {
- std::vector<char> result(len);
- // NOTE: streamsize type is signed, negative values are only *supposed* to not be used.
- // http://en.cppreference.com/w/cpp/io/streamsize
- if (len < 0) {
- throw std::runtime_error("read_bytes: requested a negative amount");
- }
- if (len > 0) {
- m_io->read(&result[0], len);
- }
- return std::string(result.begin(), result.end());
- }
- std::string kaitai::kstream::read_bytes_full() {
- std::iostream::pos_type p1 = m_io->tellg();
- m_io->seekg(0, std::ios::end);
- std::iostream::pos_type p2 = m_io->tellg();
- size_t len = p2 - p1;
- // Note: this requires a std::string to be backed with a
- // contiguous buffer. Officially, it's a only requirement since
- // C++11 (C++98 and C++03 didn't have this requirement), but all
- // major implementations had contiguous buffers anyway.
- std::string result(len, ' ');
- m_io->seekg(p1);
- m_io->read(&result[0], len);
- return result;
- }
- std::string kaitai::kstream::read_bytes_term(char term, bool include, bool consume, bool eos_error) {
- std::string result;
- std::getline(*m_io, result, term);
- if (m_io->eof()) {
- // encountered EOF
- if (eos_error) {
- throw std::runtime_error("read_bytes_term: encountered EOF");
- }
- } else {
- // encountered terminator
- if (include)
- result.push_back(term);
- if (!consume)
- m_io->unget();
- }
- return result;
- }
- std::string kaitai::kstream::ensure_fixed_contents(std::string expected) {
- std::string actual = read_bytes(expected.length());
- if (actual != expected) {
- // NOTE: I think printing it outright is not best idea, it could contain non-ascii charactes like backspace and beeps and whatnot. It would be better to print hexlified version, and also to redirect it to stderr.
- throw std::runtime_error("ensure_fixed_contents: actual data does not match expected data");
- }
- return actual;
- }
- std::string kaitai::kstream::bytes_strip_right(std::string src, char pad_byte) {
- std::size_t new_len = src.length();
- while (new_len > 0 && src[new_len - 1] == pad_byte)
- new_len--;
- return src.substr(0, new_len);
- }
- std::string kaitai::kstream::bytes_terminate(std::string src, char term, bool include) {
- std::size_t new_len = 0;
- std::size_t max_len = src.length();
- while (new_len < max_len && src[new_len] != term)
- new_len++;
- if (include && new_len < max_len)
- new_len++;
- return src.substr(0, new_len);
- }
- // ========================================================================
- // Byte array processing
- // ========================================================================
- std::string kaitai::kstream::process_xor_one(std::string data, uint8_t key) {
- size_t len = data.length();
- std::string result(len, ' ');
- for (size_t i = 0; i < len; i++)
- result[i] = data[i] ^ key;
- return result;
- }
- std::string kaitai::kstream::process_xor_many(std::string data, std::string key) {
- size_t len = data.length();
- size_t kl = key.length();
- std::string result(len, ' ');
- size_t ki = 0;
- for (size_t i = 0; i < len; i++) {
- result[i] = data[i] ^ key[ki];
- ki++;
- if (ki >= kl)
- ki = 0;
- }
- return result;
- }
- std::string kaitai::kstream::process_rotate_left(std::string data, int amount) {
- size_t len = data.length();
- std::string result(len, ' ');
- for (size_t i = 0; i < len; i++) {
- uint8_t bits = data[i];
- result[i] = (bits << amount) | (bits >> (8 - amount));
- }
- return result;
- }
- #ifdef KS_ZLIB
- #include <zlib.h>
- std::string kaitai::kstream::process_zlib(std::string data) {
- int ret;
- unsigned char *src_ptr = reinterpret_cast<unsigned char*>(&data[0]);
- std::stringstream dst_strm;
- z_stream strm;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- ret = inflateInit(&strm);
- if (ret != Z_OK)
- throw std::runtime_error("process_zlib: inflateInit error");
- strm.next_in = src_ptr;
- strm.avail_in = data.length();
- unsigned char outbuffer[ZLIB_BUF_SIZE];
- std::string outstring;
- // get the decompressed bytes blockwise using repeated calls to inflate
- do {
- strm.next_out = reinterpret_cast<Bytef*>(outbuffer);
- strm.avail_out = sizeof(outbuffer);
- ret = inflate(&strm, 0);
- if (outstring.size() < strm.total_out)
- outstring.append(reinterpret_cast<char*>(outbuffer), strm.total_out - outstring.size());
- } while (ret == Z_OK);
- if (ret != Z_STREAM_END) { // an error occurred that was not EOF
- std::ostringstream exc_msg;
- exc_msg << "process_zlib: error #" << ret << "): " << strm.msg;
- throw std::runtime_error(exc_msg.str());
- }
- if (inflateEnd(&strm) != Z_OK)
- throw std::runtime_error("process_zlib: inflateEnd error");
- return outstring;
- }
- #endif
- // ========================================================================
- // Misc utility methods
- // ========================================================================
- int kaitai::kstream::mod(int a, int b) {
- if (b <= 0)
- throw std::invalid_argument("mod: divisor b <= 0");
- int r = a % b;
- if (r < 0)
- r += b;
- return r;
- }
- #include <stdio.h>
- std::string kaitai::kstream::to_string(int val) {
- // if int is 32 bits, "-2147483648" is the longest string representation
- // => 11 chars + zero => 12 chars
- // if int is 64 bits, "-9223372036854775808" is the longest
- // => 20 chars + zero => 21 chars
- char buf[25];
- int got_len = snprintf(buf, sizeof(buf), "%d", val);
- // should never happen, but check nonetheless
- if (got_len > sizeof(buf))
- throw std::invalid_argument("to_string: integer is longer than string buffer");
- return std::string(buf);
- }
- #include <algorithm>
- std::string kaitai::kstream::reverse(std::string val) {
- std::reverse(val.begin(), val.end());
- return val;
- }
- uint8_t kaitai::kstream::byte_array_min(const std::string val) {
- uint8_t min = 0xff; // UINT8_MAX
- std::string::const_iterator end = val.end();
- for (std::string::const_iterator it = val.begin(); it != end; ++it) {
- uint8_t cur = static_cast<uint8_t>(*it);
- if (cur < min) {
- min = cur;
- }
- }
- return min;
- }
- uint8_t kaitai::kstream::byte_array_max(const std::string val) {
- uint8_t max = 0; // UINT8_MIN
- std::string::const_iterator end = val.end();
- for (std::string::const_iterator it = val.begin(); it != end; ++it) {
- uint8_t cur = static_cast<uint8_t>(*it);
- if (cur > max) {
- max = cur;
- }
- }
- return max;
- }
- // ========================================================================
- // Other internal methods
- // ========================================================================
- #ifndef KS_STR_DEFAULT_ENCODING
- #define KS_STR_DEFAULT_ENCODING "UTF-8"
- #endif
- #ifdef KS_STR_ENCODING_ICONV
- #include <iconv.h>
- #include <cerrno>
- #include <stdexcept>
- std::string kaitai::kstream::bytes_to_str(std::string src, std::string src_enc) {
- iconv_t cd = iconv_open(KS_STR_DEFAULT_ENCODING, src_enc.c_str());
- if (cd == (iconv_t) -1) {
- if (errno == EINVAL) {
- throw std::runtime_error("bytes_to_str: invalid encoding pair conversion requested");
- } else {
- throw std::runtime_error("bytes_to_str: error opening iconv");
- }
- }
- size_t src_len = src.length();
- size_t src_left = src_len;
- // Start with a buffer length of double the source length.
- size_t dst_len = src_len * 2;
- std::string dst(dst_len, ' ');
- size_t dst_left = dst_len;
- char *src_ptr = &src[0];
- char *dst_ptr = &dst[0];
- while (true) {
- size_t res = iconv(cd, &src_ptr, &src_left, &dst_ptr, &dst_left);
- if (res == (size_t) -1) {
- if (errno == E2BIG) {
- // dst buffer is not enough to accomodate whole string
- // enlarge the buffer and try again
- size_t dst_used = dst_len - dst_left;
- dst_left += dst_len;
- dst_len += dst_len;
- dst.resize(dst_len);
- // dst.resize might have allocated destination buffer in another area
- // of memory, thus our previous pointer "dst" will be invalid; re-point
- // it using "dst_used".
- dst_ptr = &dst[dst_used];
- } else {
- throw std::runtime_error("bytes_to_str: iconv error");
- }
- } else {
- // conversion successful
- dst.resize(dst_len - dst_left);
- break;
- }
- }
- if (iconv_close(cd) != 0) {
- throw std::runtime_error("bytes_to_str: iconv close error");
- }
- return dst;
- }
- #elif defined(KS_STR_ENCODING_NONE)
- std::string kaitai::kstream::bytes_to_str(std::string src, std::string src_enc) {
- return src;
- }
- #else
- #error Need to decide how to handle strings: please define one of: KS_STR_ENCODING_ICONV, KS_STR_ENCODING_NONE
- #endif
|