diff --git a/CMakeLists.txt b/CMakeLists.txt index 3783304fb74..5a8cd923cd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -939,8 +939,6 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL src/library/recording/dlgrecording.cpp src/library/recording/dlgrecording.ui src/library/recording/recordingfeature.cpp - src/library/rekordbox/kaitaistructs/rekordbox_anlz.cpp - src/library/rekordbox/kaitaistructs/rekordbox_pdb.cpp src/library/rekordbox/rekordboxfeature.cpp src/library/rhythmbox/rhythmboxfeature.cpp src/library/scanner/importfilestask.cpp @@ -1576,26 +1574,6 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") message(STATUS "Enabling QML Debugging! This poses a security risk as Mixxx will open a TCP port for debugging") endif() -# Disable warnings in generated source files -if(GNU_GCC OR LLVM_CLANG) - set_property( - SOURCE src/library/rekordbox/kaitaistructs/rekordbox_anlz.cpp - APPEND_STRING - PROPERTY COMPILE_OPTIONS -Wno-unused-parameter - ) - set_property( - SOURCE src/library/rekordbox/kaitaistructs/rekordbox_pdb.cpp - APPEND_STRING - PROPERTY COMPILE_OPTIONS -Wno-unused-parameter -Wno-switch - ) -elseif(MSVC) - set_property( - SOURCE src/library/rekordbox/kaitaistructs/rekordbox_pdb.cpp - APPEND_STRING - PROPERTY COMPILE_OPTIONS /wd4244 - ) -endif() - option(WARNINGS_PEDANTIC "Let the compiler show even more warnings" OFF) if(MSVC) if(WARNINGS_PEDANTIC) @@ -2508,12 +2486,20 @@ target_link_libraries(mixxx-lib PRIVATE FpClassify) find_package(mp3lame REQUIRED) target_link_libraries(mixxx-lib PRIVATE mp3lame::mp3lame) +add_library(rekordbox_metadata STATIC EXCLUDE_FROM_ALL + lib/rekordbox-metadata/rekordbox_pdb.cpp + lib/rekordbox-metadata/rekordbox_anlz.cpp +) +target_include_directories(rekordbox_metadata SYSTEM PUBLIC lib/rekordbox-metadata) +target_link_libraries(mixxx-lib PRIVATE rekordbox_metadata) + # Kaitai for reading Rekordbox libraries add_library(Kaitai STATIC EXCLUDE_FROM_ALL - lib/kaitai/kaitaistream.cpp + lib/kaitai/kaitai/kaitaistream.cpp ) target_include_directories(Kaitai SYSTEM PUBLIC lib/kaitai) target_compile_definitions(Kaitai PRIVATE KS_STR_ENCODING_NONE) +target_link_libraries(rekordbox_metadata PRIVATE Kaitai) target_link_libraries(mixxx-lib PRIVATE Kaitai) # For determining MP3 timing offset cases in Rekordbox library feature diff --git a/lib/kaitai/CMakeLists.txt b/lib/kaitai/CMakeLists.txt new file mode 100644 index 00000000000..7371aba0d08 --- /dev/null +++ b/lib/kaitai/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required (VERSION 3.11) +project (kaitai_struct_cpp_stl_runtime CXX) +enable_testing() + +option(BUILD_SHARED_LIBS "Build using shared libraries" ON) +option(BUILD_TESTS "Build tests" ON) + +set (CMAKE_INCLUDE_CURRENT_DIR ON) + +find_package(ZLIB) +find_package(Iconv) + +set (HEADERS + kaitai/kaitaistream.h + kaitai/kaitaistruct.h + kaitai/exceptions.h +) + +set (SOURCES + kaitai/kaitaistream.cpp +) + +set(STRING_ENCODING_TYPE "ICONV" CACHE STRING "Set the way strings have to be encoded (ICONV|WIN32API|NONE|...)") + +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +add_library (${PROJECT_NAME} ${HEADERS} ${SOURCES}) +set_property(TARGET ${PROJECT_NAME} PROPERTY PUBLIC_HEADER ${HEADERS}) +target_include_directories(${PROJECT_NAME} INTERFACE ${PROJECT_SOURCE_DIR}) + +if (ZLIB_FOUND) + target_link_libraries(${PROJECT_NAME} PRIVATE ZLIB::ZLIB) + target_compile_definitions(${PROJECT_NAME} PRIVATE -DKS_ZLIB) +endif() + +if(Iconv_FOUND) + target_link_libraries(${PROJECT_NAME} PRIVATE Iconv::Iconv) +endif() + +include(Common.cmake) + +install(TARGETS ${PROJECT_NAME} + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + PUBLIC_HEADER DESTINATION include/kaitai +) + +# Add the tests +if(BUILD_TESTS) + add_subdirectory(tests) +endif() \ No newline at end of file diff --git a/lib/kaitai/Common.cmake b/lib/kaitai/Common.cmake new file mode 100644 index 00000000000..161e702c952 --- /dev/null +++ b/lib/kaitai/Common.cmake @@ -0,0 +1,17 @@ +if (STRING_ENCODING_TYPE STREQUAL "ICONV") + target_compile_definitions(${PROJECT_NAME} PRIVATE -DKS_STR_ENCODING_ICONV) +elseif (STRING_ENCODING_TYPE STREQUAL "WIN32API") + target_compile_definitions(${PROJECT_NAME} PRIVATE -DKS_STR_ENCODING_WIN32API) +elseif (STRING_ENCODING_TYPE STREQUAL "NONE") + target_compile_definitions(${PROJECT_NAME} PRIVATE -DKS_STR_ENCODING_NONE) +else() + # User action requested +endif() + +# Maximum warnings emission, treat all warnings as errors +# +# This method was taken from https://www.pragmaticlinux.com/2022/07/enable-compiler-warnings-with-cmake/ +target_compile_options(${PROJECT_NAME} PRIVATE + $<$:/W4 /WX> + $<$>:-Wall -Wextra -Wpedantic -Werror> +) \ No newline at end of file diff --git a/lib/kaitai/LICENSE b/lib/kaitai/LICENSE index a1867624b1f..827e2f620ca 100644 --- a/lib/kaitai/LICENSE +++ b/lib/kaitai/LICENSE @@ -1,7 +1,21 @@ -Copyright 2016-2019 Kaitai Project: MIT license +MIT License -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Copyright (c) 2016-2022 Kaitai Project -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/kaitai/custom_decoder.h b/lib/kaitai/kaitai/custom_decoder.h similarity index 100% rename from lib/kaitai/custom_decoder.h rename to lib/kaitai/kaitai/custom_decoder.h diff --git a/lib/kaitai/kaitai/exceptions.h b/lib/kaitai/kaitai/exceptions.h new file mode 100644 index 00000000000..8033b9838b9 --- /dev/null +++ b/lib/kaitai/kaitai/exceptions.h @@ -0,0 +1,224 @@ +#ifndef KAITAI_EXCEPTIONS_H +#define KAITAI_EXCEPTIONS_H + +#include + +#include +#include + +// We need to use "noexcept" in virtual destructor of our exceptions +// subclasses. Different compilers have different ideas on how to +// achieve that: C++98 compilers prefer `throw()`, C++11 and later +// use `noexcept`. We define KS_NOEXCEPT macro for that. + +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) +#define KS_NOEXCEPT noexcept +#else +#define KS_NOEXCEPT throw() +#endif + +namespace kaitai { + +/** + * Common ancestor for all errors related to `bytes_to_str` operation. Also used + * to signal misc non-specific `bytes_to_str` failures. + */ +class bytes_to_str_error: public std::runtime_error { +public: + bytes_to_str_error(const std::string what): + std::runtime_error(std::string("bytes_to_str error: ") + what) {} + + virtual ~bytes_to_str_error() KS_NOEXCEPT {}; +}; + +/** + * Exception to signal that `bytes_to_str` operation was requested to use some encoding + * that is not available in given runtime environment. + */ +class unknown_encoding: public bytes_to_str_error { +public: + unknown_encoding(const std::string enc_name): + bytes_to_str_error(std::string("unknown encoding: `") + enc_name + std::string("`")) {} + + virtual ~unknown_encoding() KS_NOEXCEPT {}; +}; + +/** + * Exception to signal that `bytes_to_str` operation failed to decode given byte sequence. + */ +class illegal_seq_in_encoding: public bytes_to_str_error { +public: + illegal_seq_in_encoding(const std::string what): + bytes_to_str_error("illegal sequence: " + what) {} + + virtual ~illegal_seq_in_encoding() KS_NOEXCEPT {}; +}; + +/** + * Common ancestor for all error originating from Kaitai Struct usage. + * Stores KSY source path, pointing to an element supposedly guilty of + * an error. + */ +class kstruct_error: public std::runtime_error { +public: + kstruct_error(const std::string what, const std::string src_path): + std::runtime_error(src_path + ": " + what), + m_src_path(src_path) + { + } + + virtual ~kstruct_error() KS_NOEXCEPT {}; + +protected: + const std::string m_src_path; +}; + +/** + * Error that occurs when default endianness should be decided with + * a switch, but nothing matches (although using endianness expression + * implies that there should be some positive result). + */ +class undecided_endianness_error: public kstruct_error { +public: + undecided_endianness_error(const std::string src_path): + kstruct_error("unable to decide on endianness for a type", src_path) + { + } + + virtual ~undecided_endianness_error() KS_NOEXCEPT {}; +}; + +/** + * Common ancestor for all validation failures. Stores pointer to + * KaitaiStream IO object which was involved in an error. + */ +class validation_failed_error: public kstruct_error { +public: + validation_failed_error(const std::string what, kstream* io, const std::string src_path): + kstruct_error("at pos " + kstream::to_string(io->pos()) + ": validation failed: " + what, src_path), + m_io(io) + { + } + +// "at pos #{io.pos}: validation failed: #{msg}" + + virtual ~validation_failed_error() KS_NOEXCEPT {}; + +protected: + kstream* m_io; +}; + +/** + * Signals validation failure: we required "actual" value to be equal to + * "expected", but it turned out that it's not. + */ +template +class validation_not_equal_error: public validation_failed_error { +public: + validation_not_equal_error(const T& expected, const T& actual, kstream* io, const std::string src_path): + validation_failed_error("not equal", io, src_path), + m_expected(expected), + m_actual(actual) + { + } + + // "not equal, expected #{expected.inspect}, but got #{actual.inspect}" + + virtual ~validation_not_equal_error() KS_NOEXCEPT {}; + +protected: + const T& m_expected; + const T& m_actual; +}; + +/** + * Signals validation failure: we required "actual" value to be greater + * than or equal to "min", but it turned out that it's not. + */ +template +class validation_less_than_error: public validation_failed_error { +public: + validation_less_than_error(const T& min, const T& actual, kstream* io, const std::string src_path): + validation_failed_error("not in range", io, src_path), + m_min(min), + m_actual(actual) + { + } + + // "not in range, min #{min.inspect}, but got #{actual.inspect}" + + virtual ~validation_less_than_error() KS_NOEXCEPT {}; + +protected: + const T& m_min; + const T& m_actual; +}; + +/** + * Signals validation failure: we required "actual" value to be less + * than or equal to "max", but it turned out that it's not. + */ +template +class validation_greater_than_error: public validation_failed_error { +public: + validation_greater_than_error(const T& max, const T& actual, kstream* io, const std::string src_path): + validation_failed_error("not in range", io, src_path), + m_max(max), + m_actual(actual) + { + } + + // "not in range, max #{max.inspect}, but got #{actual.inspect}" + + virtual ~validation_greater_than_error() KS_NOEXCEPT {}; + +protected: + const T& m_max; + const T& m_actual; +}; + +/** + * Signals validation failure: we required "actual" value to be from + * the list, but it turned out that it's not. + */ +template +class validation_not_any_of_error: public validation_failed_error { +public: + validation_not_any_of_error(const T& actual, kstream* io, const std::string src_path): + validation_failed_error("not any of the list", io, src_path), + m_actual(actual) + { + } + + // "not any of the list, got #{actual.inspect}" + + virtual ~validation_not_any_of_error() KS_NOEXCEPT {}; + +protected: + const T& m_actual; +}; + +/** + * Signals validation failure: we required "actual" value to match + * the expression, but it turned out that it doesn't. + */ +template +class validation_expr_error: public validation_failed_error { +public: + validation_expr_error(const T& actual, kstream* io, const std::string src_path): + validation_failed_error("not matching the expression", io, src_path), + m_actual(actual) + { + } + + // "not matching the expression, got #{actual.inspect}" + + virtual ~validation_expr_error() KS_NOEXCEPT {}; + +protected: + const T& m_actual; +}; + +} + +#endif diff --git a/lib/kaitai/kaitaistream.cpp b/lib/kaitai/kaitai/kaitaistream.cpp similarity index 55% rename from lib/kaitai/kaitaistream.cpp rename to lib/kaitai/kaitai/kaitaistream.cpp index 2a9f082e957..625b89022a1 100644 --- a/lib/kaitai/kaitaistream.cpp +++ b/lib/kaitai/kaitai/kaitaistream.cpp @@ -1,4 +1,5 @@ -#include +#include +#include #if defined(__APPLE__) #include @@ -17,7 +18,16 @@ #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 +#elif defined(__QNX__) // __QNX__ +#include +#include +#define bswap_16(x) ENDIAN_RET16(x) +#define bswap_32(x) ENDIAN_RET32(x) +#define bswap_64(x) ENDIAN_RET64(x) +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#else // !__APPLE__ or !_MSC_VER or !__QNX__ #include #include #endif @@ -26,12 +36,12 @@ #include #include -kaitai::kstream::kstream(std::istream* io) { +kaitai::kstream::kstream(std::istream *io) { m_io = io; init(); } -kaitai::kstream::kstream(std::string& data): m_io_str(data) { +kaitai::kstream::kstream(const std::string &data) : m_io_str(data) { m_io = &m_io_str; init(); } @@ -62,9 +72,7 @@ bool kaitai::kstream::is_eof() const { return false; } char t; - m_io->exceptions( - std::istream::badbit - ); + m_io->exceptions(std::istream::badbit); m_io->get(t); if (m_io->eof()) { m_io->clear(); @@ -255,7 +263,7 @@ float kaitai::kstream::read_f4be() { #if __BYTE_ORDER == __LITTLE_ENDIAN t = bswap_32(t); #endif - return reinterpret_cast(t); + return reinterpret_cast(t); } double kaitai::kstream::read_f8be() { @@ -264,7 +272,7 @@ double kaitai::kstream::read_f8be() { #if __BYTE_ORDER == __LITTLE_ENDIAN t = bswap_64(t); #endif - return reinterpret_cast(t); + return reinterpret_cast(t); } // ........................................................................ @@ -277,7 +285,7 @@ float kaitai::kstream::read_f4le() { #if __BYTE_ORDER == __BIG_ENDIAN t = bswap_32(t); #endif - return reinterpret_cast(t); + return reinterpret_cast(t); } double kaitai::kstream::read_f8le() { @@ -286,7 +294,7 @@ double kaitai::kstream::read_f8le() { #if __BYTE_ORDER == __BIG_ENDIAN t = bswap_64(t); #endif - return reinterpret_cast(t); + return reinterpret_cast(t); } // ======================================================================== @@ -298,46 +306,81 @@ void kaitai::kstream::align_to_byte() { m_bits = 0; } -uint64_t kaitai::kstream::read_bits_int(int n) { +uint64_t kaitai::kstream::read_bits_int_be(int n) { + uint64_t res = 0; + int bits_needed = n - m_bits_left; + m_bits_left = -bits_needed & 7; // `-bits_needed mod 8` + if (bits_needed > 0) { // 1 bit => 1 byte // 8 bits => 1 byte // 9 bits => 2 bytes - int bytes_needed = ((bits_needed - 1) / 8) + 1; + int bytes_needed = ((bits_needed - 1) / 8) + 1; // `ceil(bits_needed / 8)` 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); + throw std::runtime_error("read_bits_int_be: more than 8 bytes requested"); + uint8_t buf[8]; + m_io->read(reinterpret_cast(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; + res = res << 8 | buf[i]; } + + uint64_t new_bits = res; + res = res >> m_bits_left | (bits_needed < 64 ? m_bits << bits_needed : 0); // avoid undefined behavior of `x << 64` + m_bits = new_bits; // will be masked at the end of the function + } else { + res = m_bits >> -bits_needed; // shift unneeded bits out } - // 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); + uint64_t mask = (static_cast(1) << m_bits_left) - 1; // `m_bits_left` is in range 0..7, so `(1 << 64)` does not have to be considered m_bits &= mask; return res; } -uint64_t kaitai::kstream::get_mask_ones(int n) { - if (n == 64) { - return 0xFFFFFFFFFFFFFFFF; +// 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) { + uint64_t res = 0; + 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; // `ceil(bits_needed / 8)` + if (bytes_needed > 8) + throw std::runtime_error("read_bits_int_le: more than 8 bytes requested"); + uint8_t buf[8]; + m_io->read(reinterpret_cast(buf), bytes_needed); + for (int i = 0; i < bytes_needed; i++) { + res |= static_cast(buf[i]) << (i * 8); + } + + // NB: for bit shift operators in C++, "if the value of the right operand is + // negative or is greater or equal to the number of bits in the promoted left + // operand, the behavior is undefined." (see + // https://en.cppreference.com/w/cpp/language/operator_arithmetic#Bitwise_shift_operators) + // So we define our desired behavior here. + uint64_t new_bits = bits_needed < 64 ? res >> bits_needed : 0; + res = res << m_bits_left | m_bits; + m_bits = new_bits; } else { - return ((uint64_t) 1 << n) - 1; + res = m_bits; + m_bits >>= n; + } + + m_bits_left = -bits_needed & 7; // `-bits_needed mod 8` + + if (n < 64) { + uint64_t mask = (static_cast(1) << n) - 1; + res &= mask; } + // if `n == 64`, do nothing + return res; } // ======================================================================== @@ -399,7 +442,9 @@ 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. + // NOTE: I think printing it outright is not best idea, it could contain non-ASCII characters + // 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"); } @@ -476,7 +521,7 @@ std::string kaitai::kstream::process_rotate_left(std::string data, int amount) { std::string kaitai::kstream::process_zlib(std::string data) { int ret; - unsigned char *src_ptr = reinterpret_cast(&data[0]); + unsigned char *src_ptr = reinterpret_cast(&data[0]); std::stringstream dst_strm; z_stream strm; @@ -496,16 +541,16 @@ std::string kaitai::kstream::process_zlib(std::string data) { // get the decompressed bytes blockwise using repeated calls to inflate do { - strm.next_out = reinterpret_cast(outbuffer); + strm.next_out = reinterpret_cast(outbuffer); strm.avail_out = sizeof(outbuffer); ret = inflate(&strm, 0); if (outstring.size() < strm.total_out) - outstring.append(reinterpret_cast(outbuffer), strm.total_out - outstring.size()); + outstring.append(reinterpret_cast(outbuffer), strm.total_out - outstring.size()); } while (ret == Z_OK); - if (ret != Z_STREAM_END) { // an error occurred that was not EOF + 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()); @@ -531,29 +576,71 @@ int kaitai::kstream::mod(int a, int b) { return r; } -#include -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); +#include +void kaitai::kstream::unsigned_to_decimal(uint64_t number, char *buffer) { + // Implementation from https://ideone.com/nrQfA8 by Alf P. Steinbach + // (see https://www.zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html#comment-1033931478) + if (number == 0) { + *buffer++ = '0'; + } else { + char *p_first = buffer; + while (number != 0) { + *buffer++ = static_cast('0' + number % 10); + number /= 10; + } + std::reverse(p_first, buffer); + } + *buffer = '\0'; +} - // should never happen, but check nonetheless - if (got_len > sizeof(buf)) - throw std::invalid_argument("to_string: integer is longer than string buffer"); +int64_t kaitai::kstream::string_to_int(const std::string& str, int base) { + char *str_end; - return std::string(buf); + errno = 0; + int64_t res = strtoll(str.c_str(), &str_end, base); + + // Check for successful conversion and throw an exception if the entire string was not converted + if (str_end != str.c_str() + str.size()) { + throw std::invalid_argument("string_to_int"); + } + + if (errno == ERANGE) { + throw std::out_of_range("string_to_int"); + } + + return res; } -#include 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(*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(*it); + if (cur > max) { + max = cur; + } + } + return max; +} + // ======================================================================== // Other internal methods // ======================================================================== @@ -568,14 +655,14 @@ std::string kaitai::kstream::reverse(std::string val) { #include #include -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()); +std::string kaitai::kstream::bytes_to_str(const std::string src, const char *src_enc) { + iconv_t cd = iconv_open(KS_STR_DEFAULT_ENCODING, src_enc); - if (cd == (iconv_t) -1) { + if (cd == (iconv_t)-1) { if (errno == EINVAL) { - throw std::runtime_error("bytes_to_str: invalid encoding pair conversion requested"); + throw unknown_encoding(src_enc); } else { - throw std::runtime_error("bytes_to_str: error opening iconv"); + throw bytes_to_str_error("error opening iconv"); } } @@ -587,13 +674,15 @@ std::string kaitai::kstream::bytes_to_str(std::string src, std::string src_enc) std::string dst(dst_len, ' '); size_t dst_left = dst_len; - char *src_ptr = &src[0]; + // NB: this should be const char *, but for some reason iconv() requires non-const in its 2nd argument, + // so we force it with a cast. + char *src_ptr = const_cast(src.data()); 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 (res == (size_t)-1) { if (errno == E2BIG) { // dst buffer is not enough to accomodate whole string // enlarge the buffer and try again @@ -606,8 +695,12 @@ std::string kaitai::kstream::bytes_to_str(std::string src, std::string src_enc) // of memory, thus our previous pointer "dst" will be invalid; re-point // it using "dst_used". dst_ptr = &dst[dst_used]; + } else if (errno == EILSEQ) { + throw illegal_seq_in_encoding("EILSEQ"); + } else if (errno == EINVAL) { + throw illegal_seq_in_encoding("EINVAL"); } else { - throw std::runtime_error("bytes_to_str: iconv error"); + throw bytes_to_str_error(to_string(errno)); } } else { // conversion successful @@ -617,15 +710,168 @@ std::string kaitai::kstream::bytes_to_str(std::string src, std::string src_enc) } if (iconv_close(cd) != 0) { - throw std::runtime_error("bytes_to_str: iconv close error"); + throw bytes_to_str_error("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) { +std::string kaitai::kstream::bytes_to_str(const std::string src, const char *src_enc) { return src; } +#elif defined(KS_STR_ENCODING_WIN32API) +#include +#include + +// Unbreak std::numeric_limits::max, as otherwise MSVC substitutes "useful" max() macro. +#undef max + +int kaitai::kstream::encoding_to_win_codepage(const char *src_enc) { + std::string enc(src_enc); + if (enc == "UTF-8") { + return CP_UTF8; + } else if (enc == "UTF-16LE") { + return KAITAI_CP_UTF16LE; + } else if (enc == "UTF-16BE") { + return KAITAI_CP_UTF16BE; + } else if (enc == "IBM437") { + return 437; + } else if (enc == "IBM850") { + return 850; + } else if (enc == "SHIFT_JIS") { + return 932; + } else if (enc == "GB2312") { + return 936; + } else if (enc == "ASCII") { + return 20127; + } else if (enc == "EUC-JP") { + return 20932; + } else if (enc == "ISO-8859-1") { + return 28591; + } else if (enc == "ISO-8859-2") { + return 28592; + } else if (enc == "ISO-8859-3") { + return 28593; + } else if (enc == "ISO-8859-4") { + return 28594; + } else if (enc == "ISO-8859-5") { + return 28595; + } else if (enc == "ISO-8859-6") { + return 28596; + } else if (enc == "ISO-8859-7") { + return 28597; + } else if (enc == "ISO-8859-8") { + return 28598; + } else if (enc == "ISO-8859-9") { + return 28599; + } else if (enc == "ISO-8859-10") { + return 28600; + } else if (enc == "ISO-8859-11") { + return 28601; + } else if (enc == "ISO-8859-13") { + return 28603; + } else if (enc == "ISO-8859-14") { + return 28604; + } else if (enc == "ISO-8859-15") { + return 28605; + } else if (enc == "ISO-8859-16") { + return 28606; + } + + return KAITAI_CP_UNSUPPORTED; +} + +std::string kaitai::kstream::bytes_to_str(const std::string src, const char *src_enc) { + // Step 1: convert encoding name to codepage number + int codepage = encoding_to_win_codepage(src_enc); + if (codepage == KAITAI_CP_UNSUPPORTED) { + throw unknown_encoding(src_enc); + } + return bytes_to_str(src, codepage); +} + +std::string kaitai::kstream::bytes_to_str(const std::string src, int codepage) { + // Shortcut: if we're already in UTF-8, no need to convert anything + if (codepage == CP_UTF8) { + return src; + } + + // Step 2: convert bytes to UTF-16 ("wide char") string + std::wstring utf16; + int32_t utf16_len; + int32_t src_len; + if (src.length() > std::numeric_limits::max()) { + throw bytes_to_str_error("buffers longer than int32_t are unsupported"); + } else { + src_len = static_cast(src.length()); + } + + switch (codepage) { + case KAITAI_CP_UTF16LE: + // If our source is already UTF-16LE, just copy it + + if (src_len % 2 != 0) { + throw illegal_seq_in_encoding("incomplete"); + } + + utf16_len = src_len / 2; + utf16 = std::wstring((wchar_t*)src.c_str(), utf16_len); + break; + case KAITAI_CP_UTF16BE: + // If our source is in UTF-16BE, convert it to UTF-16LE by swapping bytes + + if (src_len % 2 != 0) { + throw illegal_seq_in_encoding("incomplete"); + } + + utf16_len = src_len / 2; + + utf16 = std::wstring(utf16_len, L'\0'); + for (int32_t i = 0; i < utf16_len; i++) { + utf16[i] = (static_cast(src[i * 2]) << 8) | static_cast(src[i * 2 + 1]); + } + break; + default: + // Calculate the length of the UTF-16 string + utf16_len = MultiByteToWideChar(codepage, 0, src.c_str(), src_len, 0, 0); + if (utf16_len == 0) { + throw bytes_to_str_error("MultiByteToWideChar length calculation error"); + } + + // Convert to UTF-16 string + utf16 = std::wstring(utf16_len, L'\0'); + if (MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src.c_str(), src_len, &utf16[0], utf16_len) == 0) { + auto err = GetLastError(); + if (err == ERROR_NO_UNICODE_TRANSLATION) { + throw illegal_seq_in_encoding("MultiByteToWideChar"); + } else { + throw bytes_to_str_error("MultiByteToWideChar conversion error"); + } + } + } + + // Step 3: convert UTF-16 string to UTF-8 string + + // Calculate the length of the UTF-8 string + int utf8_len = WideCharToMultiByte(CP_UTF8, 0, &utf16[0], utf16_len, 0, 0, 0, 0); + if (utf8_len == 0) { + throw bytes_to_str_error("WideCharToMultiByte length calculation error"); + } + + // Convert to UTF-8 string + std::string utf8(utf8_len, '\0'); + if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &utf16[0], utf16_len, &utf8[0], utf8_len, 0, 0) == 0) { + auto err = GetLastError(); + if (err == ERROR_NO_UNICODE_TRANSLATION) { + throw illegal_seq_in_encoding("WideCharToMultiByte"); + } else { + throw bytes_to_str_error("WideCharToMultiByte conversion error"); + } + } + + return utf8; +} + #else -#error Need to decide how to handle strings: please define one of: KS_STR_ENCODING_ICONV, KS_STR_ENCODING_NONE -#endif +#error Need to decide how to handle strings: please define one of: KS_STR_ENCODING_ICONV, KS_STR_ENCODING_WIN32API, KS_STR_ENCODING_NONE +#endif \ No newline at end of file diff --git a/lib/kaitai/kaitaistream.h b/lib/kaitai/kaitai/kaitaistream.h similarity index 51% rename from lib/kaitai/kaitaistream.h rename to lib/kaitai/kaitai/kaitaistream.h index 9592771db04..06d68f083f2 100644 --- a/lib/kaitai/kaitaistream.h +++ b/lib/kaitai/kaitai/kaitaistream.h @@ -2,18 +2,21 @@ #define KAITAI_STREAM_H // Kaitai Struct runtime API version: x.y.z = 'xxxyyyzzz' decimal -#define KAITAI_STRUCT_VERSION 7000L +#define KAITAI_STRUCT_VERSION 10000L #include #include #include #include +#include +#include +#include namespace kaitai { /** * Kaitai Stream class (kaitai::kstream) is an implementation of - * Kaitai Struct stream API + * Kaitai Struct stream API * for C++/STL. It's implemented as a wrapper over generic STL std::istream. * * It provides a wide variety of simple methods to read (parse) binary @@ -41,7 +44,7 @@ class kstream { * buffer. * \param data data buffer to use for this Kaitai Stream */ - kstream(std::string& data); + kstream(const std::string& data); void close(); @@ -148,7 +151,9 @@ class kstream { //@{ void align_to_byte(); + uint64_t read_bits_int_be(int n); uint64_t read_bits_int(int n); + uint64_t read_bits_int_le(int n); //@} @@ -162,7 +167,11 @@ class kstream { static std::string bytes_strip_right(std::string src, char pad_byte); static std::string bytes_terminate(std::string src, char term, bool include); - static std::string bytes_to_str(std::string src, std::string src_enc); + static std::string bytes_to_str(const std::string src, const char *src_enc); + // workaround for https://github.com/kaitai-io/kaitai_struct_cpp_stl_runtime/issues/67 + static std::string bytes_to_str(const std::string src, const std::string& enc) { + return bytes_to_str(src, enc.c_str()); + } //@} @@ -222,7 +231,84 @@ class kstream { * Should be used in place of std::to_string() (which is available only * since C++11) in older C++ implementations. */ - static std::string to_string(int val); + template +// check for C++11 support - https://stackoverflow.com/a/40512515 +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) + // https://stackoverflow.com/a/27913885 + typename std::enable_if< + std::is_integral::value && + // check if we don't have something too large like GCC's `__int128_t` + std::numeric_limits::max() >= 0 && + std::numeric_limits::max() <= std::numeric_limits::max(), + std::string + >::type +#else + std::string +#endif + static to_string(I val) { + // in theory, `digits10 + 3` would be enough (minus sign + leading digit + // + null terminator), but let's add a little more to be safe + char buf[std::numeric_limits::digits10 + 5]; + if (val < 0) { + buf[0] = '-'; + + // NB: `val` is negative and we need to get its absolute value (i.e. minus `val`). However, since + // `int64_t` uses two's complement representation, its range is `[-2**63, 2**63 - 1] = + // [-0x8000_0000_0000_0000, 0x7fff_ffff_ffff_ffff]` (both ends inclusive) and thus the naive + // `-val` operation will overflow for `val = std::numeric_limits::min() = + // -0x8000_0000_0000_0000` (because the result of `-val` is mathematically + // `-(-0x8000_0000_0000_0000) = 0x8000_0000_0000_0000`, but the `int64_t` type can represent at + // most `0x7fff_ffff_ffff_ffff`). And signed integer overflow is undefined behavior in C++. + // + // To avoid undefined behavior for `val = -0x8000_0000_0000_0000 = -2**63`, we do the following + // steps for all negative `val`s: + // + // 1. Convert the signed (and negative) `val` to an unsigned `uint64_t` type. This is a + // well-defined operation in C++: the resulting `uint64_t` value will be `val mod 2**64` (`mod` + // is modulo). The maximum `val` we can have here is `-1` (because `val < 0`), a theoretical + // minimum we are able to support would be `-2**64 + 1 = -0xffff_ffff_ffff_ffff` (even though + // in practice the widest standard type is `int64_t` with the minimum of `-2**63`): + // + // * `static_cast(-1) = -1 mod 2**64 = 2**64 + (-1) = 0xffff_ffff_ffff_ffff = 2**64 - 1` + // * `static_cast(-2**64 + 1) = (-2**64 + 1) mod 2**64 = 2**64 + (-2**64 + 1) = 1` + // + // 2. Subtract `static_cast(val)` from `2**64 - 1 = 0xffff_ffff_ffff_ffff`. Since + // `static_cast(val)` is in range `[1, 2**64 - 1]` (see step 1), the result of this + // subtraction will be mathematically in range `[0, (2**64 - 1) - 1] = [0, 2**64 - 2]`. So the + // mathematical result cannot be negative, hence this unsigned integer subtraction can never + // wrap around (which wouldn't be a good thing to rely upon because it confuses programmers and + // code analysis tools). + // + // 3. Since we did mathematically `(2**64 - 1) - (2**64 + val) = -val - 1` so far (and we wanted + // to do `-val`), we add `1` to correct that. From step 2 we know that the result of `-val - 1` + // is in range `[0, 2**64 - 2]`, so adding `1` will not wrap (at most we could get `2**64 - 1 = + // 0xffff_ffff_ffff_ffff`, which is still in the valid range of `uint64_t`). + + unsigned_to_decimal((std::numeric_limits::max() - static_cast(val)) + 1, &buf[1]); + } else { + unsigned_to_decimal(val, buf); + } + return std::string(buf); + } + + /** + * Converts string `str` to an integer value. Throws an exception if the + * string is not a valid integer. + * + * This one is supposed to mirror `std::stoll()` (which is available only + * since C++11) in older C++ implementations. + * + * Major difference between standard `std::stoll()` and `string_to_int()` + * is that this one does not perform any partial conversions and always + * throws `std::invalid_argument` if the string is not a valid integer. + * + * @param str String to convert + * @param base Base of the integer (default: 10) + * @throws std::invalid_argument if the string is not a valid integer + * @throws std::out_of_range if the integer is out of range + * @return Integer value of the string + */ + static int64_t string_to_int(const std::string& str, int base = 10); /** * Reverses given string `val`, so that the first character becomes the @@ -231,6 +317,22 @@ class kstream { */ static std::string reverse(std::string val); + /** + * Finds the minimal byte in a byte array, treating bytes as + * unsigned values. + * @param val byte array to scan + * @return minimal byte in byte array as integer + */ + static uint8_t byte_array_min(const std::string val); + + /** + * Finds the maximal byte in a byte array, treating bytes as + * unsigned values. + * @param val byte array to scan + * @return maximal byte in byte array as integer + */ + static uint8_t byte_array_max(const std::string val); + private: std::istream* m_io; std::istringstream m_io_str; @@ -240,7 +342,33 @@ class kstream { void init(); void exceptions_enable() const; - static uint64_t get_mask_ones(int n); + static void unsigned_to_decimal(uint64_t number, char *buffer); + +#ifdef KS_STR_ENCODING_WIN32API + enum { + KAITAI_CP_UNSUPPORTED = -1, + KAITAI_CP_UTF16LE = -2, + KAITAI_CP_UTF16BE = -3, + }; + + /** + * Converts string name of the encoding into a Windows codepage number. We extend standard Windows codepage list + * with a few special meanings (see KAITAI_CP_* enum), reserving negative values of integer for that. + * @param src_enc string name of the encoding; this should match canonical name of the encoding as per discussion + * in https://github.com/kaitai-io/kaitai_struct/issues/116 + * @return Windows codepage number or member of KAITAI_CP_* enum. + * @ref https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers + */ + static int encoding_to_win_codepage(const char *src_enc); + + /** + * Converts bytes packed in std::string into a UTF-8 string, based on given source encoding indicated by `codepage`. + * @param src bytes to be converted + * @param codepage Windows codepage number or member of KAITAI_CP_* enum. + * @return UTF-8 string + */ + static std::string bytes_to_str(const std::string src, int codepage); +#endif static const int ZLIB_BUF_SIZE = 128 * 1024; }; diff --git a/lib/kaitai/kaitaistruct.h b/lib/kaitai/kaitai/kaitaistruct.h similarity index 88% rename from lib/kaitai/kaitaistruct.h rename to lib/kaitai/kaitai/kaitaistruct.h index f8b848fdd1a..8172ede6c98 100644 --- a/lib/kaitai/kaitaistruct.h +++ b/lib/kaitai/kaitai/kaitaistruct.h @@ -1,7 +1,7 @@ #ifndef KAITAI_STRUCT_H #define KAITAI_STRUCT_H -#include +#include namespace kaitai { diff --git a/lib/rekordbox-metadata/rekordbox_anlz.cpp b/lib/rekordbox-metadata/rekordbox_anlz.cpp new file mode 100644 index 00000000000..9311cfa8198 --- /dev/null +++ b/lib/rekordbox-metadata/rekordbox_anlz.cpp @@ -0,0 +1,727 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "rekordbox_anlz.h" + +#include "kaitai/exceptions.h" + +rekordbox_anlz_t::rekordbox_anlz_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = this; + m_sections = nullptr; + _read(); +} + +void rekordbox_anlz_t::_read() { + m_magic = m__io->read_bytes(4); + if (!(magic() == std::string("\x50\x4D\x41\x49", 4))) { + throw kaitai::validation_not_equal_error( + std::string("\x50\x4D\x41\x49", 4), + magic(), + _io(), + std::string("/seq/0")); + } + m_len_header = m__io->read_u4be(); + m_len_file = m__io->read_u4be(); + m__unnamed3 = m__io->read_bytes((len_header() - _io()->pos())); + m_sections = + std::unique_ptr>>( + new std::vector>()); + { + int i = 0; + while (!m__io->is_eof()) { + m_sections->push_back(std::move(std::unique_ptr( + new tagged_section_t(m__io, this, m__root)))); + i++; + } + } +} + +rekordbox_anlz_t::~rekordbox_anlz_t() { + _clean_up(); +} + +void rekordbox_anlz_t::_clean_up() { +} + +rekordbox_anlz_t::phrase_mid_t::phrase_mid_t(kaitai::kstream* p__io, + rekordbox_anlz_t::song_structure_entry_t* p__parent, + rekordbox_anlz_t* p__root) + : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_anlz_t::phrase_mid_t::_read() { + m_id = static_cast(m__io->read_u2be()); +} + +rekordbox_anlz_t::phrase_mid_t::~phrase_mid_t() { + _clean_up(); +} + +void rekordbox_anlz_t::phrase_mid_t::_clean_up() { +} + +rekordbox_anlz_t::path_tag_t::path_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_anlz_t::path_tag_t::_read() { + m_len_path = m__io->read_u4be(); + n_path = true; + if (len_path() > 1) { + n_path = false; + m_path = kaitai::kstream::bytes_to_str(m__io->read_bytes((len_path() - 2)), std::string("utf-16be")); + } +} + +rekordbox_anlz_t::path_tag_t::~path_tag_t() { + _clean_up(); +} + +void rekordbox_anlz_t::path_tag_t::_clean_up() { + if (!n_path) { + } +} + +rekordbox_anlz_t::wave_preview_tag_t::wave_preview_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_anlz_t::wave_preview_tag_t::_read() { + m_len_data = m__io->read_u4be(); + m__unnamed1 = m__io->read_u4be(); + n_data = true; + if (_parent()->len_tag() > _parent()->len_header()) { + n_data = false; + m_data = m__io->read_bytes(len_data()); + } +} + +rekordbox_anlz_t::wave_preview_tag_t::~wave_preview_tag_t() { + _clean_up(); +} + +void rekordbox_anlz_t::wave_preview_tag_t::_clean_up() { + if (!n_data) { + } +} + +rekordbox_anlz_t::beat_grid_tag_t::beat_grid_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m_beats = nullptr; + _read(); +} + +void rekordbox_anlz_t::beat_grid_tag_t::_read() { + m__unnamed0 = m__io->read_u4be(); + m__unnamed1 = m__io->read_u4be(); + m_num_beats = m__io->read_u4be(); + m_beats = std::unique_ptr>>( + new std::vector>()); + const int l_beats = num_beats(); + for (int i = 0; i < l_beats; i++) { + m_beats->push_back(std::move(std::unique_ptr( + new beat_grid_beat_t(m__io, this, m__root)))); + } +} + +rekordbox_anlz_t::beat_grid_tag_t::~beat_grid_tag_t() { + _clean_up(); +} + +void rekordbox_anlz_t::beat_grid_tag_t::_clean_up() { +} + +rekordbox_anlz_t::song_structure_body_t::song_structure_body_t( + kaitai::kstream* p__io, + rekordbox_anlz_t::song_structure_tag_t* p__parent, + rekordbox_anlz_t* p__root) + : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m_entries = nullptr; + _read(); +} + +void rekordbox_anlz_t::song_structure_body_t::_read() { + m_mood = static_cast(m__io->read_u2be()); + m__unnamed1 = m__io->read_bytes(6); + m_end_beat = m__io->read_u2be(); + m__unnamed3 = m__io->read_bytes(2); + m_bank = static_cast(m__io->read_u1()); + m__unnamed5 = m__io->read_bytes(1); + m_entries = std::unique_ptr< + std::vector>>( + new std::vector>()); + const int l_entries = _parent()->len_entries(); + for (int i = 0; i < l_entries; i++) { + m_entries->push_back(std::move(std::unique_ptr( + new song_structure_entry_t(m__io, this, m__root)))); + } +} + +rekordbox_anlz_t::song_structure_body_t::~song_structure_body_t() { + _clean_up(); +} + +void rekordbox_anlz_t::song_structure_body_t::_clean_up() { +} + +rekordbox_anlz_t::wave_color_preview_tag_t::wave_color_preview_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_anlz_t::wave_color_preview_tag_t::_read() { + m_len_entry_bytes = m__io->read_u4be(); + m_len_entries = m__io->read_u4be(); + m__unnamed2 = m__io->read_u4be(); + m_entries = m__io->read_bytes((len_entries() * len_entry_bytes())); +} + +rekordbox_anlz_t::wave_color_preview_tag_t::~wave_color_preview_tag_t() { + _clean_up(); +} + +void rekordbox_anlz_t::wave_color_preview_tag_t::_clean_up() { +} + +rekordbox_anlz_t::phrase_high_t::phrase_high_t(kaitai::kstream* p__io, + rekordbox_anlz_t::song_structure_entry_t* p__parent, + rekordbox_anlz_t* p__root) + : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_anlz_t::phrase_high_t::_read() { + m_id = static_cast(m__io->read_u2be()); +} + +rekordbox_anlz_t::phrase_high_t::~phrase_high_t() { + _clean_up(); +} + +void rekordbox_anlz_t::phrase_high_t::_clean_up() { +} + +rekordbox_anlz_t::wave_scroll_tag_t::wave_scroll_tag_t(kaitai::kstream* p__io, + rekordbox_anlz_t::tagged_section_t* p__parent, + rekordbox_anlz_t* p__root) + : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_anlz_t::wave_scroll_tag_t::_read() { + m_len_entry_bytes = m__io->read_u4be(); + m_len_entries = m__io->read_u4be(); + m__unnamed2 = m__io->read_u4be(); + m_entries = m__io->read_bytes((len_entries() * len_entry_bytes())); +} + +rekordbox_anlz_t::wave_scroll_tag_t::~wave_scroll_tag_t() { + _clean_up(); +} + +void rekordbox_anlz_t::wave_scroll_tag_t::_clean_up() { +} + +rekordbox_anlz_t::song_structure_tag_t::song_structure_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m_body = nullptr; + m__io__raw_body = nullptr; + f_c = false; + f_raw_mood = false; + f_is_masked = false; + _read(); +} + +void rekordbox_anlz_t::song_structure_tag_t::_read() { + m_len_entry_bytes = m__io->read_u4be(); + m_len_entries = m__io->read_u2be(); + m__raw_body = m__io->read_bytes_full(); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr( + new song_structure_body_t(m__io__raw_body.get(), this, m__root)); +} + +rekordbox_anlz_t::song_structure_tag_t::~song_structure_tag_t() { + _clean_up(); +} + +void rekordbox_anlz_t::song_structure_tag_t::_clean_up() { + if (f_raw_mood) { + } +} + +uint16_t rekordbox_anlz_t::song_structure_tag_t::c() { + if (f_c) + return m_c; + m_c = len_entries(); + f_c = true; + return m_c; +} + +uint16_t rekordbox_anlz_t::song_structure_tag_t::raw_mood() { + if (f_raw_mood) + return m_raw_mood; + std::streampos _pos = m__io->pos(); + m__io->seek(6); + m_raw_mood = m__io->read_u2be(); + m__io->seek(_pos); + f_raw_mood = true; + return m_raw_mood; +} + +bool rekordbox_anlz_t::song_structure_tag_t::is_masked() { + if (f_is_masked) + return m_is_masked; + m_is_masked = raw_mood() > 20; + f_is_masked = true; + return m_is_masked; +} + +rekordbox_anlz_t::cue_extended_entry_t::cue_extended_entry_t(kaitai::kstream* p__io, rekordbox_anlz_t::cue_extended_tag_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_anlz_t::cue_extended_entry_t::_read() { + m_magic = m__io->read_bytes(4); + if (!(magic() == std::string("\x50\x43\x50\x32", 4))) { + throw kaitai::validation_not_equal_error( + std::string("\x50\x43\x50\x32", 4), + magic(), + _io(), + std::string("/types/cue_extended_entry/seq/0")); + } + m_len_header = m__io->read_u4be(); + m_len_entry = m__io->read_u4be(); + m_hot_cue = m__io->read_u4be(); + m_type = static_cast(m__io->read_u1()); + m__unnamed5 = m__io->read_bytes(3); + m_time = m__io->read_u4be(); + m_loop_time = m__io->read_u4be(); + m_color_id = m__io->read_u1(); + m__unnamed9 = m__io->read_bytes(7); + m_loop_numerator = m__io->read_u2be(); + m_loop_denominator = m__io->read_u2be(); + n_len_comment = true; + if (len_entry() > 43) { + n_len_comment = false; + m_len_comment = m__io->read_u4be(); + } + n_comment = true; + if (len_entry() > 43) { + n_comment = false; + m_comment = kaitai::kstream::bytes_to_str(m__io->read_bytes(len_comment()), std::string("utf-16be")); + } + n_color_code = true; + if ((len_entry() - len_comment()) > 44) { + n_color_code = false; + m_color_code = m__io->read_u1(); + } + n_color_red = true; + if ((len_entry() - len_comment()) > 45) { + n_color_red = false; + m_color_red = m__io->read_u1(); + } + n_color_green = true; + if ((len_entry() - len_comment()) > 46) { + n_color_green = false; + m_color_green = m__io->read_u1(); + } + n_color_blue = true; + if ((len_entry() - len_comment()) > 47) { + n_color_blue = false; + m_color_blue = m__io->read_u1(); + } + n__unnamed18 = true; + if ((len_entry() - len_comment()) > 48) { + n__unnamed18 = false; + m__unnamed18 = m__io->read_bytes(((len_entry() - 48) - len_comment())); + } +} + +rekordbox_anlz_t::cue_extended_entry_t::~cue_extended_entry_t() { + _clean_up(); +} + +void rekordbox_anlz_t::cue_extended_entry_t::_clean_up() { + if (!n_len_comment) { + } + if (!n_comment) { + } + if (!n_color_code) { + } + if (!n_color_red) { + } + if (!n_color_green) { + } + if (!n_color_blue) { + } + if (!n__unnamed18) { + } +} + +rekordbox_anlz_t::vbr_tag_t::vbr_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m_index = nullptr; + _read(); +} + +void rekordbox_anlz_t::vbr_tag_t::_read() { + m__unnamed0 = m__io->read_u4be(); + m_index = std::unique_ptr>(new std::vector()); + const int l_index = 400; + for (int i = 0; i < l_index; i++) { + m_index->push_back(std::move(m__io->read_u4be())); + } +} + +rekordbox_anlz_t::vbr_tag_t::~vbr_tag_t() { + _clean_up(); +} + +void rekordbox_anlz_t::vbr_tag_t::_clean_up() { +} + +rekordbox_anlz_t::song_structure_entry_t::song_structure_entry_t( + kaitai::kstream* p__io, + rekordbox_anlz_t::song_structure_body_t* p__parent, + rekordbox_anlz_t* p__root) + : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_anlz_t::song_structure_entry_t::_read() { + m_index = m__io->read_u2be(); + m_beat = m__io->read_u2be(); + switch (_parent()->mood()) { + case rekordbox_anlz_t::TRACK_MOOD_HIGH: { + m_kind = std::unique_ptr(new phrase_high_t(m__io, this, m__root)); + break; + } + case rekordbox_anlz_t::TRACK_MOOD_MID: { + m_kind = std::unique_ptr(new phrase_mid_t(m__io, this, m__root)); + break; + } + case rekordbox_anlz_t::TRACK_MOOD_LOW: { + m_kind = std::unique_ptr(new phrase_low_t(m__io, this, m__root)); + break; + } + default: { + m_kind = std::unique_ptr(new phrase_mid_t(m__io, this, m__root)); + break; + } + } + m__unnamed3 = m__io->read_bytes(1); + m_k1 = m__io->read_u1(); + m__unnamed5 = m__io->read_bytes(1); + m_k2 = m__io->read_u1(); + m__unnamed7 = m__io->read_bytes(1); + m_b = m__io->read_u1(); + m_beat2 = m__io->read_u2be(); + m_beat3 = m__io->read_u2be(); + m_beat4 = m__io->read_u2be(); + m__unnamed12 = m__io->read_bytes(1); + m_k3 = m__io->read_u1(); + m__unnamed14 = m__io->read_bytes(1); + m_fill = m__io->read_u1(); + m_beat_fill = m__io->read_u2be(); +} + +rekordbox_anlz_t::song_structure_entry_t::~song_structure_entry_t() { + _clean_up(); +} + +void rekordbox_anlz_t::song_structure_entry_t::_clean_up() { +} + +rekordbox_anlz_t::cue_entry_t::cue_entry_t(kaitai::kstream* p__io, rekordbox_anlz_t::cue_tag_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_anlz_t::cue_entry_t::_read() { + m_magic = m__io->read_bytes(4); + if (!(magic() == std::string("\x50\x43\x50\x54", 4))) { + throw kaitai::validation_not_equal_error( + std::string("\x50\x43\x50\x54", 4), + magic(), + _io(), + std::string("/types/cue_entry/seq/0")); + } + m_len_header = m__io->read_u4be(); + m_len_entry = m__io->read_u4be(); + m_hot_cue = m__io->read_u4be(); + m_status = static_cast(m__io->read_u4be()); + m__unnamed5 = m__io->read_u4be(); + m_order_first = m__io->read_u2be(); + m_order_last = m__io->read_u2be(); + m_type = static_cast(m__io->read_u1()); + m__unnamed9 = m__io->read_bytes(3); + m_time = m__io->read_u4be(); + m_loop_time = m__io->read_u4be(); + m__unnamed12 = m__io->read_bytes(16); +} + +rekordbox_anlz_t::cue_entry_t::~cue_entry_t() { + _clean_up(); +} + +void rekordbox_anlz_t::cue_entry_t::_clean_up() { +} + +rekordbox_anlz_t::beat_grid_beat_t::beat_grid_beat_t(kaitai::kstream* p__io, rekordbox_anlz_t::beat_grid_tag_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_anlz_t::beat_grid_beat_t::_read() { + m_beat_number = m__io->read_u2be(); + m_tempo = m__io->read_u2be(); + m_time = m__io->read_u4be(); +} + +rekordbox_anlz_t::beat_grid_beat_t::~beat_grid_beat_t() { + _clean_up(); +} + +void rekordbox_anlz_t::beat_grid_beat_t::_clean_up() { +} + +rekordbox_anlz_t::cue_extended_tag_t::cue_extended_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m_cues = nullptr; + _read(); +} + +void rekordbox_anlz_t::cue_extended_tag_t::_read() { + m_type = static_cast(m__io->read_u4be()); + m_num_cues = m__io->read_u2be(); + m__unnamed2 = m__io->read_bytes(2); + m_cues = + std::unique_ptr>>( + new std::vector>()); + const int l_cues = num_cues(); + for (int i = 0; i < l_cues; i++) { + m_cues->push_back(std::move(std::unique_ptr( + new cue_extended_entry_t(m__io, this, m__root)))); + } +} + +rekordbox_anlz_t::cue_extended_tag_t::~cue_extended_tag_t() { + _clean_up(); +} + +void rekordbox_anlz_t::cue_extended_tag_t::_clean_up() { +} + +rekordbox_anlz_t::phrase_low_t::phrase_low_t(kaitai::kstream* p__io, + rekordbox_anlz_t::song_structure_entry_t* p__parent, + rekordbox_anlz_t* p__root) + : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_anlz_t::phrase_low_t::_read() { + m_id = static_cast(m__io->read_u2be()); +} + +rekordbox_anlz_t::phrase_low_t::~phrase_low_t() { + _clean_up(); +} + +void rekordbox_anlz_t::phrase_low_t::_clean_up() { +} + +rekordbox_anlz_t::unknown_tag_t::unknown_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_anlz_t::unknown_tag_t::_read() { +} + +rekordbox_anlz_t::unknown_tag_t::~unknown_tag_t() { + _clean_up(); +} + +void rekordbox_anlz_t::unknown_tag_t::_clean_up() { +} + +rekordbox_anlz_t::tagged_section_t::tagged_section_t(kaitai::kstream* p__io, rekordbox_anlz_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__io__raw_body = nullptr; + _read(); +} + +void rekordbox_anlz_t::tagged_section_t::_read() { + m_fourcc = static_cast(m__io->read_s4be()); + m_len_header = m__io->read_u4be(); + m_len_tag = m__io->read_u4be(); + switch (fourcc()) { + case rekordbox_anlz_t::SECTION_TAGS_WAVE_COLOR_SCROLL: { + m__raw_body = m__io->read_bytes((len_tag() - 12)); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr( + new wave_color_scroll_tag_t( + m__io__raw_body.get(), this, m__root)); + break; + } + case rekordbox_anlz_t::SECTION_TAGS_WAVE_SCROLL: { + m__raw_body = m__io->read_bytes((len_tag() - 12)); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr( + new wave_scroll_tag_t(m__io__raw_body.get(), this, m__root)); + break; + } + case rekordbox_anlz_t::SECTION_TAGS_VBR: { + m__raw_body = m__io->read_bytes((len_tag() - 12)); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr(new vbr_tag_t(m__io__raw_body.get(), this, m__root)); + break; + } + case rekordbox_anlz_t::SECTION_TAGS_CUES_2: { + m__raw_body = m__io->read_bytes((len_tag() - 12)); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr( + new cue_extended_tag_t(m__io__raw_body.get(), this, m__root)); + break; + } + case rekordbox_anlz_t::SECTION_TAGS_CUES: { + m__raw_body = m__io->read_bytes((len_tag() - 12)); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr(new cue_tag_t(m__io__raw_body.get(), this, m__root)); + break; + } + case rekordbox_anlz_t::SECTION_TAGS_SONG_STRUCTURE: { + m__raw_body = m__io->read_bytes((len_tag() - 12)); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr( + new song_structure_tag_t(m__io__raw_body.get(), this, m__root)); + break; + } + case rekordbox_anlz_t::SECTION_TAGS_BEAT_GRID: { + m__raw_body = m__io->read_bytes((len_tag() - 12)); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr( + new beat_grid_tag_t(m__io__raw_body.get(), this, m__root)); + break; + } + case rekordbox_anlz_t::SECTION_TAGS_WAVE_PREVIEW: { + m__raw_body = m__io->read_bytes((len_tag() - 12)); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr( + new wave_preview_tag_t(m__io__raw_body.get(), this, m__root)); + break; + } + case rekordbox_anlz_t::SECTION_TAGS_WAVE_COLOR_PREVIEW: { + m__raw_body = m__io->read_bytes((len_tag() - 12)); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr( + new wave_color_preview_tag_t( + m__io__raw_body.get(), this, m__root)); + break; + } + case rekordbox_anlz_t::SECTION_TAGS_PATH: { + m__raw_body = m__io->read_bytes((len_tag() - 12)); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr(new path_tag_t(m__io__raw_body.get(), this, m__root)); + break; + } + case rekordbox_anlz_t::SECTION_TAGS_WAVE_TINY: { + m__raw_body = m__io->read_bytes((len_tag() - 12)); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr( + new wave_preview_tag_t(m__io__raw_body.get(), this, m__root)); + break; + } + default: { + m__raw_body = m__io->read_bytes((len_tag() - 12)); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr( + new unknown_tag_t(m__io__raw_body.get(), this, m__root)); + break; + } + } +} + +rekordbox_anlz_t::tagged_section_t::~tagged_section_t() { + _clean_up(); +} + +void rekordbox_anlz_t::tagged_section_t::_clean_up() { +} + +rekordbox_anlz_t::wave_color_scroll_tag_t::wave_color_scroll_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_anlz_t::wave_color_scroll_tag_t::_read() { + m_len_entry_bytes = m__io->read_u4be(); + m_len_entries = m__io->read_u4be(); + m__unnamed2 = m__io->read_u4be(); + m_entries = m__io->read_bytes((len_entries() * len_entry_bytes())); +} + +rekordbox_anlz_t::wave_color_scroll_tag_t::~wave_color_scroll_tag_t() { + _clean_up(); +} + +void rekordbox_anlz_t::wave_color_scroll_tag_t::_clean_up() { +} + +rekordbox_anlz_t::cue_tag_t::cue_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m_cues = nullptr; + _read(); +} + +void rekordbox_anlz_t::cue_tag_t::_read() { + m_type = static_cast(m__io->read_u4be()); + m__unnamed1 = m__io->read_bytes(2); + m_num_cues = m__io->read_u2be(); + m_memory_count = m__io->read_u4be(); + m_cues = std::unique_ptr>>( + new std::vector>()); + const int l_cues = num_cues(); + for (int i = 0; i < l_cues; i++) { + m_cues->push_back(std::move(std::unique_ptr( + new cue_entry_t(m__io, this, m__root)))); + } +} + +rekordbox_anlz_t::cue_tag_t::~cue_tag_t() { + _clean_up(); +} + +void rekordbox_anlz_t::cue_tag_t::_clean_up() { +} diff --git a/src/library/rekordbox/kaitaistructs/rekordbox_anlz.h b/lib/rekordbox-metadata/rekordbox_anlz.h similarity index 65% rename from src/library/rekordbox/kaitaistructs/rekordbox_anlz.h rename to lib/rekordbox-metadata/rekordbox_anlz.h index 0cd43827d81..44d378c450c 100644 --- a/src/library/rekordbox/kaitaistructs/rekordbox_anlz.h +++ b/lib/rekordbox-metadata/rekordbox_anlz.h @@ -2,13 +2,14 @@ // This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild -#include "kaitaistruct.h" +#include "kaitai/kaitaistruct.h" #include +#include #include -#if KAITAI_STRUCT_VERSION < 7000L -#error "Incompatible Kaitai Struct C++/STL API: version 0.7 or later is required" +#if KAITAI_STRUCT_VERSION < 9000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required" #endif /** @@ -19,25 +20,26 @@ * inside variable bit-rate audio streams, and lists of memory cues * and loop points. They are used by Pioneer professional DJ * equipment. - * + * * The format has been reverse-engineered to facilitate sophisticated * integrations with light and laser shows, videos, and other musical * instruments, by supporting deep knowledge of what is playing and * what is coming next through monitoring the network communications * of the players. - * \sa Source + * \sa https://reverseengineering.stackexchange.com/questions/4311/help-reversing-a-edb-database-file-for-pioneers-rekordbox-software Source */ class rekordbox_anlz_t : public kaitai::kstruct { public: - class phrase_up_down_t; + class phrase_mid_t; class path_tag_t; class wave_preview_tag_t; class beat_grid_tag_t; + class song_structure_body_t; class wave_color_preview_tag_t; + class phrase_high_t; class wave_scroll_tag_t; - class phrase_verse_bridge_t; class song_structure_tag_t; class cue_extended_entry_t; class vbr_tag_t; @@ -45,6 +47,7 @@ class rekordbox_anlz_t : public kaitai::kstruct { class cue_entry_t; class beat_grid_beat_t; class cue_extended_tag_t; + class phrase_low_t; class unknown_tag_t; class tagged_section_t; class wave_color_scroll_tag_t; @@ -52,7 +55,8 @@ class rekordbox_anlz_t : public kaitai::kstruct { enum cue_entry_status_t { CUE_ENTRY_STATUS_DISABLED = 0, - CUE_ENTRY_STATUS_ENABLED = 1 + CUE_ENTRY_STATUS_ENABLED = 1, + CUE_ENTRY_STATUS_ACTIVE_LOOP = 4 }; enum cue_list_type_t { @@ -60,10 +64,24 @@ class rekordbox_anlz_t : public kaitai::kstruct { CUE_LIST_TYPE_HOT_CUES = 1 }; - enum phrase_style_t { - PHRASE_STYLE_UP_DOWN = 1, - PHRASE_STYLE_VERSE_BRIDGE = 2, - PHRASE_STYLE_VERSE_BRIDGE_2 = 3 + enum mood_high_phrase_t { + MOOD_HIGH_PHRASE_INTRO = 1, + MOOD_HIGH_PHRASE_UP = 2, + MOOD_HIGH_PHRASE_DOWN = 3, + MOOD_HIGH_PHRASE_CHORUS = 5, + MOOD_HIGH_PHRASE_OUTRO = 6 + }; + + enum track_bank_t { + TRACK_BANK_DEFAULT = 0, + TRACK_BANK_COOL = 1, + TRACK_BANK_NATURAL = 2, + TRACK_BANK_HOT = 3, + TRACK_BANK_SUBTLE = 4, + TRACK_BANK_WARM = 5, + TRACK_BANK_VIVID = 6, + TRACK_BANK_CLUB_1 = 7, + TRACK_BANK_CLUB_2 = 8 }; enum cue_entry_type_t { @@ -85,54 +103,67 @@ class rekordbox_anlz_t : public kaitai::kstruct { SECTION_TAGS_WAVE_COLOR_SCROLL = 1347900981 }; - enum phrase_verse_bridge_id_t { - PHRASE_VERSE_BRIDGE_ID_INTRO = 1, - PHRASE_VERSE_BRIDGE_ID_VERSE1 = 2, - PHRASE_VERSE_BRIDGE_ID_VERSE2 = 3, - PHRASE_VERSE_BRIDGE_ID_VERSE3 = 4, - PHRASE_VERSE_BRIDGE_ID_VERSE4 = 5, - PHRASE_VERSE_BRIDGE_ID_VERSE5 = 6, - PHRASE_VERSE_BRIDGE_ID_VERSE6 = 7, - PHRASE_VERSE_BRIDGE_ID_BRIDGE = 8, - PHRASE_VERSE_BRIDGE_ID_CHORUS = 9, - PHRASE_VERSE_BRIDGE_ID_OUTRO = 10 + enum track_mood_t { + TRACK_MOOD_HIGH = 1, + TRACK_MOOD_MID = 2, + TRACK_MOOD_LOW = 3 + }; + + enum mood_mid_phrase_t { + MOOD_MID_PHRASE_INTRO = 1, + MOOD_MID_PHRASE_VERSE_1 = 2, + MOOD_MID_PHRASE_VERSE_2 = 3, + MOOD_MID_PHRASE_VERSE_3 = 4, + MOOD_MID_PHRASE_VERSE_4 = 5, + MOOD_MID_PHRASE_VERSE_5 = 6, + MOOD_MID_PHRASE_VERSE_6 = 7, + MOOD_MID_PHRASE_BRIDGE = 8, + MOOD_MID_PHRASE_CHORUS = 9, + MOOD_MID_PHRASE_OUTRO = 10 }; - enum phrase_up_down_id_t { - PHRASE_UP_DOWN_ID_INTRO = 1, - PHRASE_UP_DOWN_ID_UP = 2, - PHRASE_UP_DOWN_ID_DOWN = 3, - PHRASE_UP_DOWN_ID_CHORUS = 5, - PHRASE_UP_DOWN_ID_OUTRO = 6 + enum mood_low_phrase_t { + MOOD_LOW_PHRASE_INTRO = 1, + MOOD_LOW_PHRASE_VERSE_1 = 2, + MOOD_LOW_PHRASE_VERSE_1B = 3, + MOOD_LOW_PHRASE_VERSE_1C = 4, + MOOD_LOW_PHRASE_VERSE_2 = 5, + MOOD_LOW_PHRASE_VERSE_2B = 6, + MOOD_LOW_PHRASE_VERSE_2C = 7, + MOOD_LOW_PHRASE_BRIDGE = 8, + MOOD_LOW_PHRASE_CHORUS = 9, + MOOD_LOW_PHRASE_OUTRO = 10 }; - rekordbox_anlz_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, rekordbox_anlz_t* p__root = 0); + rekordbox_anlz_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~rekordbox_anlz_t(); - class phrase_up_down_t : public kaitai::kstruct { + class phrase_mid_t : public kaitai::kstruct { public: - phrase_up_down_t(kaitai::kstream* p__io, rekordbox_anlz_t::song_structure_entry_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + phrase_mid_t(kaitai::kstream* p__io, rekordbox_anlz_t::song_structure_entry_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: - ~phrase_up_down_t(); + ~phrase_mid_t(); private: - phrase_up_down_id_t m_id; + mood_mid_phrase_t m_id; rekordbox_anlz_t* m__root; rekordbox_anlz_t::song_structure_entry_t* m__parent; public: - phrase_up_down_id_t id() const { return m_id; } + mood_mid_phrase_t id() const { return m_id; } rekordbox_anlz_t* _root() const { return m__root; } rekordbox_anlz_t::song_structure_entry_t* _parent() const { return m__parent; } }; @@ -146,10 +177,11 @@ class rekordbox_anlz_t : public kaitai::kstruct { public: - path_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + path_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~path_tag_t(); @@ -182,16 +214,17 @@ class rekordbox_anlz_t : public kaitai::kstruct { public: - wave_preview_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + wave_preview_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~wave_preview_tag_t(); private: - uint32_t m_len_preview; + uint32_t m_len_data; uint32_t m__unnamed1; std::string m_data; bool n_data; @@ -210,7 +243,7 @@ class rekordbox_anlz_t : public kaitai::kstruct { * slightly redundant because it can be computed from the * length of the tag. */ - uint32_t len_preview() const { return m_len_preview; } + uint32_t len_data() const { return m_len_data; } uint32_t _unnamed1() const { return m__unnamed1; } /** @@ -231,10 +264,11 @@ class rekordbox_anlz_t : public kaitai::kstruct { public: - beat_grid_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + beat_grid_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~beat_grid_tag_t(); @@ -242,8 +276,8 @@ class rekordbox_anlz_t : public kaitai::kstruct { private: uint32_t m__unnamed0; uint32_t m__unnamed1; - uint32_t m_len_beats; - std::vector* m_beats; + uint32_t m_num_beats; + std::unique_ptr>> m_beats; rekordbox_anlz_t* m__root; rekordbox_anlz_t::tagged_section_t* m__parent; @@ -254,16 +288,71 @@ class rekordbox_anlz_t : public kaitai::kstruct { /** * The number of beat entries which follow. */ - uint32_t len_beats() const { return m_len_beats; } + uint32_t num_beats() const { return m_num_beats; } /** * The entries of the beat grid. */ - std::vector* beats() const { return m_beats; } + std::vector>* beats() const { return m_beats.get(); } rekordbox_anlz_t* _root() const { return m__root; } rekordbox_anlz_t::tagged_section_t* _parent() const { return m__parent; } }; + /** + * Stores the rest of the song structure tag, which can only be + * parsed after unmasking. + */ + + class song_structure_body_t : public kaitai::kstruct { + + public: + + song_structure_body_t(kaitai::kstream* p__io, rekordbox_anlz_t::song_structure_tag_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); + + private: + void _read(); + void _clean_up(); + + public: + ~song_structure_body_t(); + + private: + track_mood_t m_mood; + std::string m__unnamed1; + uint16_t m_end_beat; + std::string m__unnamed3; + track_bank_t m_bank; + std::string m__unnamed5; + std::unique_ptr>> m_entries; + rekordbox_anlz_t* m__root; + rekordbox_anlz_t::song_structure_tag_t* m__parent; + + public: + + /** + * The mood which rekordbox assigns the track as a whole during phrase analysis. + */ + track_mood_t mood() const { return m_mood; } + std::string _unnamed1() const { return m__unnamed1; } + + /** + * The beat number at which the last phrase ends. The track may + * continue after the last phrase ends. If this is the case, it will + * mostly be silence. + */ + uint16_t end_beat() const { return m_end_beat; } + std::string _unnamed3() const { return m__unnamed3; } + + /** + * The stylistic bank which can be assigned to the track in rekordbox Lighting mode. + */ + track_bank_t bank() const { return m_bank; } + std::string _unnamed5() const { return m__unnamed5; } + std::vector>* entries() const { return m_entries.get(); } + rekordbox_anlz_t* _root() const { return m__root; } + rekordbox_anlz_t::song_structure_tag_t* _parent() const { return m__parent; } + }; + /** * A larger, colorful waveform preview image suitable for display * above the touch strip for jumping to a track position on newer @@ -274,10 +363,11 @@ class rekordbox_anlz_t : public kaitai::kstruct { public: - wave_color_preview_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + wave_color_preview_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~wave_color_preview_tag_t(); @@ -308,6 +398,30 @@ class rekordbox_anlz_t : public kaitai::kstruct { rekordbox_anlz_t::tagged_section_t* _parent() const { return m__parent; } }; + class phrase_high_t : public kaitai::kstruct { + + public: + + phrase_high_t(kaitai::kstream* p__io, rekordbox_anlz_t::song_structure_entry_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); + + private: + void _read(); + void _clean_up(); + + public: + ~phrase_high_t(); + + private: + mood_high_phrase_t m_id; + rekordbox_anlz_t* m__root; + rekordbox_anlz_t::song_structure_entry_t* m__parent; + + public: + mood_high_phrase_t id() const { return m_id; } + rekordbox_anlz_t* _root() const { return m__root; } + rekordbox_anlz_t::song_structure_entry_t* _parent() const { return m__parent; } + }; + /** * A larger waveform image suitable for scrolling along as a track * plays. @@ -317,10 +431,11 @@ class rekordbox_anlz_t : public kaitai::kstruct { public: - wave_scroll_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + wave_scroll_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~wave_scroll_tag_t(); @@ -351,56 +466,58 @@ class rekordbox_anlz_t : public kaitai::kstruct { rekordbox_anlz_t::tagged_section_t* _parent() const { return m__parent; } }; - class phrase_verse_bridge_t : public kaitai::kstruct { + /** + * Stores the song structure, also known as phrases (intro, verse, + * bridge, chorus, up, down, outro). + */ + + class song_structure_tag_t : public kaitai::kstruct { public: - phrase_verse_bridge_t(kaitai::kstream* p__io, rekordbox_anlz_t::song_structure_entry_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + song_structure_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: - ~phrase_verse_bridge_t(); + ~song_structure_tag_t(); private: - phrase_verse_bridge_id_t m_id; - rekordbox_anlz_t* m__root; - rekordbox_anlz_t::song_structure_entry_t* m__parent; + bool f_c; + uint16_t m_c; public: - phrase_verse_bridge_id_t id() const { return m_id; } - rekordbox_anlz_t* _root() const { return m__root; } - rekordbox_anlz_t::song_structure_entry_t* _parent() const { return m__parent; } - }; - - /** - * Stores the song structure, also known as phrases (intro, verse, - * bridge, chorus, up, down, outro). - */ + uint16_t c(); - class song_structure_tag_t : public kaitai::kstruct { + private: + bool f_raw_mood; + uint16_t m_raw_mood; public: - song_structure_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + /** + * This is a way to tell whether the rest of the tag has been masked. The value is supposed + * to range from 1 to 3, but in masked files it will be much larger. + */ + uint16_t raw_mood(); private: - void _read(); + bool f_is_masked; + bool m_is_masked; public: - ~song_structure_tag_t(); + bool is_masked(); private: uint32_t m_len_entry_bytes; uint16_t m_len_entries; - uint16_t m_style; - std::string m__unnamed3; - uint16_t m_end_beat; - std::string m__unnamed5; - std::vector* m_entries; + std::unique_ptr m_body; rekordbox_anlz_t* m__root; rekordbox_anlz_t::tagged_section_t* m__parent; + std::string m__raw_body; + std::unique_ptr m__io__raw_body; public: @@ -415,27 +532,14 @@ class rekordbox_anlz_t : public kaitai::kstruct { uint16_t len_entries() const { return m_len_entries; } /** - * The phrase style. 1 is the up-down style - * (white label text in rekordbox) where the main phrases consist - * of up, down, and chorus. 2 is the bridge-verse style - * (black label text in rekordbox) where the main phrases consist - * of verse, chorus, and bridge. Style 3 is mostly identical to - * bridge-verse style except verses 1-3 are labeled VERSE1 and verses - * 4-6 are labeled VERSE2 in rekordbox. + * The rest of the tag, which needs to be unmasked before it + * can be parsed. */ - uint16_t style() const { return m_style; } - std::string _unnamed3() const { return m__unnamed3; } - - /** - * The beat number at which the last phrase ends. The track may - * continue after the last phrase ends. If this is the case, it will - * mostly be silence. - */ - uint16_t end_beat() const { return m_end_beat; } - std::string _unnamed5() const { return m__unnamed5; } - std::vector* entries() const { return m_entries; } + song_structure_body_t* body() const { return m_body.get(); } rekordbox_anlz_t* _root() const { return m__root; } rekordbox_anlz_t::tagged_section_t* _parent() const { return m__parent; } + std::string _raw_body() const { return m__raw_body; } + kaitai::kstream* _io__raw_body() const { return m__io__raw_body.get(); } }; /** @@ -447,16 +551,17 @@ class rekordbox_anlz_t : public kaitai::kstruct { public: - cue_extended_entry_t(kaitai::kstream* p__io, rekordbox_anlz_t::cue_extended_tag_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + cue_extended_entry_t(kaitai::kstream* p__io, rekordbox_anlz_t::cue_extended_tag_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~cue_extended_entry_t(); private: - std::string m__unnamed0; + std::string m_magic; uint32_t m_len_header; uint32_t m_len_entry; uint32_t m_hot_cue; @@ -466,6 +571,8 @@ class rekordbox_anlz_t : public kaitai::kstruct { uint32_t m_loop_time; uint8_t m_color_id; std::string m__unnamed9; + uint16_t m_loop_numerator; + uint16_t m_loop_denominator; uint32_t m_len_comment; bool n_len_comment; @@ -508,18 +615,22 @@ class rekordbox_anlz_t : public kaitai::kstruct { bool _is_null_color_blue() { color_blue(); return n_color_blue; }; private: - std::string m__unnamed16; - bool n__unnamed16; + std::string m__unnamed18; + bool n__unnamed18; public: - bool _is_null__unnamed16() { _unnamed16(); return n__unnamed16; }; + bool _is_null__unnamed18() { _unnamed18(); return n__unnamed18; }; private: rekordbox_anlz_t* m__root; rekordbox_anlz_t::cue_extended_tag_t* m__parent; public: - std::string _unnamed0() const { return m__unnamed0; } + + /** + * Identifies this as an extended cue list entry (cue point). + */ + std::string magic() const { return m_magic; } uint32_t len_header() const { return m_len_header; } uint32_t len_entry() const { return m_len_entry; } @@ -530,7 +641,7 @@ class rekordbox_anlz_t : public kaitai::kstruct { uint32_t hot_cue() const { return m_hot_cue; } /** - * Indicates whether this is a memory cue or a loop. + * Indicates whether this is a regular cue point or a loop. */ cue_entry_type_t type() const { return m_type; } std::string _unnamed5() const { return m__unnamed5; } @@ -548,11 +659,23 @@ class rekordbox_anlz_t : public kaitai::kstruct { uint32_t loop_time() const { return m_loop_time; } /** - * References a row in the colors table if the memory cue or loop - * has been assigned a color + * References a row in the colors table if this is a memory cue or loop + * and has been assigned a color. */ uint8_t color_id() const { return m_color_id; } std::string _unnamed9() const { return m__unnamed9; } + + /** + * The numerator of the loop length in beats. + * Zero if the loop is not quantized. + */ + uint16_t loop_numerator() const { return m_loop_numerator; } + + /** + * The denominator of the loop length in beats. + * Zero if the loop is not quantized. + */ + uint16_t loop_denominator() const { return m_loop_denominator; } uint32_t len_comment() const { return m_len_comment; } /** @@ -561,25 +684,25 @@ class rekordbox_anlz_t : public kaitai::kstruct { std::string comment() const { return m_comment; } /** - * A lookup value for a color table? We use this to index to the colors shown in rekordbox. + * A lookup value for a color table? We use this to index to the hot cue colors shown in rekordbox. */ uint8_t color_code() const { return m_color_code; } /** - * The red component of the color to be displayed. + * The red component of the hot cue color to be displayed. */ uint8_t color_red() const { return m_color_red; } /** - * The green component of the color to be displayed. + * The green component of the hot cue color to be displayed. */ uint8_t color_green() const { return m_color_green; } /** - * The blue component of the color to be displayed. + * The blue component of the hot cue color to be displayed. */ uint8_t color_blue() const { return m_color_blue; } - std::string _unnamed16() const { return m__unnamed16; } + std::string _unnamed18() const { return m__unnamed18; } rekordbox_anlz_t* _root() const { return m__root; } rekordbox_anlz_t::cue_extended_tag_t* _parent() const { return m__parent; } }; @@ -593,23 +716,24 @@ class rekordbox_anlz_t : public kaitai::kstruct { public: - vbr_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + vbr_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~vbr_tag_t(); private: uint32_t m__unnamed0; - std::vector* m_index; + std::unique_ptr> m_index; rekordbox_anlz_t* m__root; rekordbox_anlz_t::tagged_section_t* m__parent; public: uint32_t _unnamed0() const { return m__unnamed0; } - std::vector* index() const { return m_index; } + std::vector* index() const { return m_index.get(); } rekordbox_anlz_t* _root() const { return m__root; } rekordbox_anlz_t::tagged_section_t* _parent() const { return m__parent; } }; @@ -622,53 +746,106 @@ class rekordbox_anlz_t : public kaitai::kstruct { public: - song_structure_entry_t(kaitai::kstream* p__io, rekordbox_anlz_t::song_structure_tag_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + song_structure_entry_t(kaitai::kstream* p__io, rekordbox_anlz_t::song_structure_body_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~song_structure_entry_t(); private: - uint16_t m_phrase_number; - uint16_t m_beat_number; - kaitai::kstruct* m_phrase_id; + uint16_t m_index; + uint16_t m_beat; + std::unique_ptr m_kind; std::string m__unnamed3; - uint8_t m_fill_in; - uint16_t m_fill_in_beat_number; + uint8_t m_k1; + std::string m__unnamed5; + uint8_t m_k2; + std::string m__unnamed7; + uint8_t m_b; + uint16_t m_beat2; + uint16_t m_beat3; + uint16_t m_beat4; + std::string m__unnamed12; + uint8_t m_k3; + std::string m__unnamed14; + uint8_t m_fill; + uint16_t m_beat_fill; rekordbox_anlz_t* m__root; - rekordbox_anlz_t::song_structure_tag_t* m__parent; + rekordbox_anlz_t::song_structure_body_t* m__parent; public: /** * The absolute number of the phrase, starting at one. */ - uint16_t phrase_number() const { return m_phrase_number; } + uint16_t index() const { return m_index; } /** * The beat number at which the phrase starts. */ - uint16_t beat_number() const { return m_beat_number; } + uint16_t beat() const { return m_beat; } /** - * Identifier of the phrase label. + * The kind of phrase as displayed in rekordbox. */ - kaitai::kstruct* phrase_id() const { return m_phrase_id; } + kaitai::kstruct* kind() const { return m_kind.get(); } std::string _unnamed3() const { return m__unnamed3; } /** - * If nonzero, fill-in is present. + * One of three flags that identify phrase kind variants in high-mood tracks. + */ + uint8_t k1() const { return m_k1; } + std::string _unnamed5() const { return m__unnamed5; } + + /** + * One of three flags that identify phrase kind variants in high-mood tracks. */ - uint8_t fill_in() const { return m_fill_in; } + uint8_t k2() const { return m_k2; } + std::string _unnamed7() const { return m__unnamed7; } + + /** + * Flags how many more beat numbers are in a high-mood "Up 3" phrase. + */ + uint8_t b() const { return m_b; } + + /** + * Extra beat number (falling within phrase) always present in high-mood "Up 3" phrases. + */ + uint16_t beat2() const { return m_beat2; } + + /** + * Extra beat number (falling within phrase, larger than beat2) + * present in high-mood "Up 3" phrases when b has value 1. + */ + uint16_t beat3() const { return m_beat3; } + + /** + * Extra beat number (falling within phrase, larger than beat3) + * present in high-mood "Up 3" phrases when b has value 1. + */ + uint16_t beat4() const { return m_beat4; } + std::string _unnamed12() const { return m__unnamed12; } + + /** + * One of three flags that identify phrase kind variants in high-mood tracks. + */ + uint8_t k3() const { return m_k3; } + std::string _unnamed14() const { return m__unnamed14; } + + /** + * If nonzero, fill-in is present at end of phrase. + */ + uint8_t fill() const { return m_fill; } /** * The beat number at which fill-in starts. */ - uint16_t fill_in_beat_number() const { return m_fill_in_beat_number; } + uint16_t beat_fill() const { return m_beat_fill; } rekordbox_anlz_t* _root() const { return m__root; } - rekordbox_anlz_t::song_structure_tag_t* _parent() const { return m__parent; } + rekordbox_anlz_t::song_structure_body_t* _parent() const { return m__parent; } }; /** @@ -679,16 +856,17 @@ class rekordbox_anlz_t : public kaitai::kstruct { public: - cue_entry_t(kaitai::kstream* p__io, rekordbox_anlz_t::cue_tag_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + cue_entry_t(kaitai::kstream* p__io, rekordbox_anlz_t::cue_tag_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~cue_entry_t(); private: - std::string m__unnamed0; + std::string m_magic; uint32_t m_len_header; uint32_t m_len_entry; uint32_t m_hot_cue; @@ -705,7 +883,11 @@ class rekordbox_anlz_t : public kaitai::kstruct { rekordbox_anlz_t::cue_tag_t* m__parent; public: - std::string _unnamed0() const { return m__unnamed0; } + + /** + * Identifies this as a cue list entry (cue point). + */ + std::string magic() const { return m_magic; } uint32_t len_header() const { return m_len_header; } uint32_t len_entry() const { return m_len_entry; } @@ -716,7 +898,7 @@ class rekordbox_anlz_t : public kaitai::kstruct { uint32_t hot_cue() const { return m_hot_cue; } /** - * If zero, this entry should be ignored. + * Indicates if this is an active loop. */ cue_entry_status_t status() const { return m_status; } uint32_t _unnamed5() const { return m__unnamed5; } @@ -761,10 +943,11 @@ class rekordbox_anlz_t : public kaitai::kstruct { public: - beat_grid_beat_t(kaitai::kstream* p__io, rekordbox_anlz_t::beat_grid_tag_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + beat_grid_beat_t(kaitai::kstream* p__io, rekordbox_anlz_t::beat_grid_tag_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~beat_grid_beat_t(); @@ -810,19 +993,20 @@ class rekordbox_anlz_t : public kaitai::kstruct { public: - cue_extended_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + cue_extended_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~cue_extended_tag_t(); private: cue_list_type_t m_type; - uint16_t m_len_cues; + uint16_t m_num_cues; std::string m__unnamed2; - std::vector* m_cues; + std::unique_ptr>> m_cues; rekordbox_anlz_t* m__root; rekordbox_anlz_t::tagged_section_t* m__parent; @@ -836,21 +1020,46 @@ class rekordbox_anlz_t : public kaitai::kstruct { /** * The length of the cue comment list. */ - uint16_t len_cues() const { return m_len_cues; } + uint16_t num_cues() const { return m_num_cues; } std::string _unnamed2() const { return m__unnamed2; } - std::vector* cues() const { return m_cues; } + std::vector>* cues() const { return m_cues.get(); } rekordbox_anlz_t* _root() const { return m__root; } rekordbox_anlz_t::tagged_section_t* _parent() const { return m__parent; } }; + class phrase_low_t : public kaitai::kstruct { + + public: + + phrase_low_t(kaitai::kstream* p__io, rekordbox_anlz_t::song_structure_entry_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); + + private: + void _read(); + void _clean_up(); + + public: + ~phrase_low_t(); + + private: + mood_low_phrase_t m_id; + rekordbox_anlz_t* m__root; + rekordbox_anlz_t::song_structure_entry_t* m__parent; + + public: + mood_low_phrase_t id() const { return m_id; } + rekordbox_anlz_t* _root() const { return m__root; } + rekordbox_anlz_t::song_structure_entry_t* _parent() const { return m__parent; } + }; + class unknown_tag_t : public kaitai::kstruct { public: - unknown_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + unknown_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~unknown_tag_t(); @@ -874,30 +1083,31 @@ class rekordbox_anlz_t : public kaitai::kstruct { public: - tagged_section_t(kaitai::kstream* p__io, rekordbox_anlz_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + tagged_section_t(kaitai::kstream* p__io, rekordbox_anlz_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~tagged_section_t(); private: - int32_t m_fourcc; + section_tags_t m_fourcc; uint32_t m_len_header; uint32_t m_len_tag; - kaitai::kstruct* m_body; + std::unique_ptr m_body; rekordbox_anlz_t* m__root; rekordbox_anlz_t* m__parent; std::string m__raw_body; - kaitai::kstream* m__io__raw_body; + std::unique_ptr m__io__raw_body; public: /** * A tag value indicating what kind of section this is. */ - int32_t fourcc() const { return m_fourcc; } + section_tags_t fourcc() const { return m_fourcc; } /** * The size, in bytes, of the header portion of the tag. @@ -908,11 +1118,11 @@ class rekordbox_anlz_t : public kaitai::kstruct { * The size, in bytes, of this entire tag, counting the header. */ uint32_t len_tag() const { return m_len_tag; } - kaitai::kstruct* body() const { return m_body; } + kaitai::kstruct* body() const { return m_body.get(); } rekordbox_anlz_t* _root() const { return m__root; } rekordbox_anlz_t* _parent() const { return m__parent; } std::string _raw_body() const { return m__raw_body; } - kaitai::kstream* _io__raw_body() const { return m__io__raw_body; } + kaitai::kstream* _io__raw_body() const { return m__io__raw_body.get(); } }; /** @@ -925,10 +1135,11 @@ class rekordbox_anlz_t : public kaitai::kstruct { public: - wave_color_scroll_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + wave_color_scroll_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~wave_color_scroll_tag_t(); @@ -968,10 +1179,11 @@ class rekordbox_anlz_t : public kaitai::kstruct { public: - cue_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = 0, rekordbox_anlz_t* p__root = 0); + cue_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent = nullptr, rekordbox_anlz_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~cue_tag_t(); @@ -979,9 +1191,9 @@ class rekordbox_anlz_t : public kaitai::kstruct { private: cue_list_type_t m_type; std::string m__unnamed1; - uint16_t m_len_cues; + uint16_t m_num_cues; uint32_t m_memory_count; - std::vector* m_cues; + std::unique_ptr>> m_cues; rekordbox_anlz_t* m__root; rekordbox_anlz_t::tagged_section_t* m__parent; @@ -996,28 +1208,32 @@ class rekordbox_anlz_t : public kaitai::kstruct { /** * The length of the cue list. */ - uint16_t len_cues() const { return m_len_cues; } + uint16_t num_cues() const { return m_num_cues; } /** * Unsure what this means. */ uint32_t memory_count() const { return m_memory_count; } - std::vector* cues() const { return m_cues; } + std::vector>* cues() const { return m_cues.get(); } rekordbox_anlz_t* _root() const { return m__root; } rekordbox_anlz_t::tagged_section_t* _parent() const { return m__parent; } }; private: - std::string m__unnamed0; + std::string m_magic; uint32_t m_len_header; uint32_t m_len_file; std::string m__unnamed3; - std::vector* m_sections; + std::unique_ptr>> m_sections; rekordbox_anlz_t* m__root; kaitai::kstruct* m__parent; public: - std::string _unnamed0() const { return m__unnamed0; } + + /** + * Identifies this as an analysis file. + */ + std::string magic() const { return m_magic; } /** * The number of bytes of this header section. @@ -1034,7 +1250,7 @@ class rekordbox_anlz_t : public kaitai::kstruct { * The remainder of the file is a sequence of type-tagged sections, * identified by a four-byte magic sequence. */ - std::vector* sections() const { return m_sections; } + std::vector>* sections() const { return m_sections.get(); } rekordbox_anlz_t* _root() const { return m__root; } kaitai::kstruct* _parent() const { return m__parent; } }; diff --git a/src/library/rekordbox/kaitaistructs/rekordbox_anlz.ksy b/lib/rekordbox-metadata/rekordbox_anlz.ksy similarity index 65% rename from src/library/rekordbox/kaitaistructs/rekordbox_anlz.ksy rename to lib/rekordbox-metadata/rekordbox_anlz.ksy index 15fe3c2d803..08e97a46456 100644 --- a/src/library/rekordbox/kaitaistructs/rekordbox_anlz.ksy +++ b/lib/rekordbox-metadata/rekordbox_anlz.ksy @@ -26,7 +26,9 @@ doc: | doc-ref: https://reverseengineering.stackexchange.com/questions/4311/help-reversing-a-edb-database-file-for-pioneers-rekordbox-software seq: - - contents: "PMAI" + - id: magic + contents: "PMAI" + doc: Identifies this as an analysis file. - id: len_header type: u4 doc: | @@ -52,7 +54,7 @@ types: seq: - id: fourcc type: s4 - # enum: section_tags Can't use this until enums support default/unmatched value + enum: section_tags doc: | A tag value indicating what kind of section this is. - id: len_header @@ -68,17 +70,17 @@ types: type: switch-on: fourcc cases: - 0x50434f32: cue_extended_tag #'section_tags::cues_2' (PCO2) - 0x50434f42: cue_tag #'section_tags::cues' (PCOB) - 0x50505448: path_tag #'section_tags::path' (PPTH) - 0x5051545a: beat_grid_tag #'section_tags::beat_grid' (PQTZ) - 0x50564252: vbr_tag #'section_tags::vbr' (PVBR) - 0x50574156: wave_preview_tag #'section_tags::wave_preview' (PWAV) - 0x50575632: wave_preview_tag #'section_tags::wave_tiny' (PWV2) - 0x50575633: wave_scroll_tag #'section_tags::wave_scroll' (PWV3, seen in .EXT) - 0x50575634: wave_color_preview_tag #'section_tags::wave_color_preview' (PWV4, in .EXT) - 0x50575635: wave_color_scroll_tag #'section_tags::wave_color_scroll' (PWV5, in .EXT) - 0x50535349: song_structure_tag #'section_tags::song_structure' (PSSI, in .EXT) + 'section_tags::cues_2': cue_extended_tag # PCO2 + 'section_tags::cues': cue_tag # PCOB + 'section_tags::path': path_tag # PPTH + 'section_tags::beat_grid': beat_grid_tag # PQTZ + 'section_tags::vbr': vbr_tag # PVBR + 'section_tags::wave_preview': wave_preview_tag # PWAV + 'section_tags::wave_tiny': wave_preview_tag # PWV2 + 'section_tags::wave_scroll': wave_scroll_tag # PWV3, seen in .EXT + 'section_tags::wave_color_preview': wave_color_preview_tag # PWV4, in .EXT + 'section_tags::wave_color_scroll': wave_color_scroll_tag # PWV5, in .EXT + 'section_tags::song_structure': song_structure_tag # PSSI, in .EXT _: unknown_tag -webide-representation: '{fourcc}' @@ -91,14 +93,14 @@ types: seq: - type: u4 - type: u4 # @flesniak says this is always 0x80000 - - id: len_beats + - id: num_beats type: u4 doc: | The number of beat entries which follow. - id: beats type: beat_grid_beat repeat: expr - repeat-expr: len_beats + repeat-expr: num_beats doc: The entries of the beat grid. beat_grid_beat: @@ -132,7 +134,7 @@ types: doc: | Identifies whether this tag stores ordinary or hot cues. - size: 2 - - id: len_cues + - id: num_cues type: u2 doc: | The length of the cue list. @@ -143,13 +145,15 @@ types: - id: cues type: cue_entry repeat: expr - repeat-expr: len_cues + repeat-expr: num_cues cue_entry: doc: | A cue list entry. Can either represent a memory cue or a loop. seq: - - contents: "PCPT" + - id: magic + contents: "PCPT" + doc: Identifies this as a cue list entry (cue point). - id: len_header type: u4 - id: len_entry @@ -163,7 +167,7 @@ types: type: u4 enum: cue_entry_status doc: | - If zero, this entry should be ignored. + Indicates if this is an active loop. - type: u4 # Seems to always be 0x10000 - id: order_first type: u2 @@ -203,7 +207,7 @@ types: enum: cue_list_type doc: | Identifies whether this tag stores ordinary or hot cues. - - id: len_cues + - id: num_cues type: u2 doc: | The length of the cue comment list. @@ -211,14 +215,16 @@ types: - id: cues type: cue_extended_entry repeat: expr - repeat-expr: len_cues + repeat-expr: num_cues cue_extended_entry: doc: | A cue extended list entry. Can either describe a memory cue or a loop. seq: - - contents: "PCP2" + - id: magic + contents: "PCP2" + doc: Identifies this as an extended cue list entry (cue point). - id: len_header type: u4 - id: len_entry @@ -232,7 +238,7 @@ types: type: u1 enum: cue_entry_type doc: | - Indicates whether this is a memory cue or a loop. + Indicates whether this is a regular cue point or a loop. - size: 3 # seems to always be 1000 - id: time type: u4 @@ -247,8 +253,19 @@ types: - id: color_id type: u1 doc: | - Color ID of memory cues and loops, same color IDs as track colors - - size: 11 # Loops seem to have some non-zero values in the last four bytes of this. + References a row in the colors table if this is a memory cue or loop + and has been assigned a color. + - size: 7 + - id: loop_numerator + type: u2 + doc: | + The numerator of the loop length in beats. + Zero if the loop is not quantized. + - id: loop_denominator + type: u2 + doc: | + The denominator of the loop length in beats. + Zero if the loop is not quantized. - id: len_comment type: u4 if: len_entry > 43 @@ -262,22 +279,22 @@ types: - id: color_code type: u1 doc: | - A lookup value for a color table? We use this to index to the colors shown in rekordbox. + A lookup value for a color table? We use this to index to the hot cue colors shown in rekordbox. if: (len_entry - len_comment) > 44 - id: color_red type: u1 doc: | - The red component of the color to be displayed. + The red component of the hot cue color to be displayed. if: (len_entry - len_comment) > 45 - id: color_green type: u1 doc: | - The green component of the color to be displayed. + The green component of the hot cue color to be displayed. if: (len_entry - len_comment) > 46 - id: color_blue type: u1 doc: | - The blue component of the color to be displayed. + The blue component of the hot cue color to be displayed. if: (len_entry - len_comment) > 47 - size: len_entry - 48 - len_comment # The remainder after the color if: (len_entry - len_comment) > 48 @@ -294,6 +311,7 @@ types: size: len_path - 2 encoding: utf-16be if: len_path > 1 + -webide-representation: '{path}' vbr_tag: doc: | @@ -311,7 +329,7 @@ types: Stores a waveform preview image suitable for display above the touch strip for jumping to a track position. seq: - - id: len_preview + - id: len_data type: u4 doc: | The length, in bytes, of the preview data itself. This is @@ -319,7 +337,7 @@ types: length of the tag. - type: u4 # This seems to always have the value 0x10000 - id: data - size: len_preview + size: len_data doc: | The actual bytes of the waveform preview. if: _parent.len_tag > _parent.len_header @@ -393,17 +411,46 @@ types: type: u2 doc: | The number of phrases. - - id: style + - id: body + type: song_structure_body + doc: | + The rest of the tag, which needs to be unmasked before it + can be parsed. + size-eos: true + # NOTE: unmasking is disabled because the C++ backend of kaitai_struct + # doesn't seem to support typecasting as of kaitai version 0.10? + # process: 'xor(is_masked ? mask : [0])' + instances: + c: + value: len_entries + # mask: + # value: | + # [ + # (0xCB+c).as, (0xE1+c).as, (0xEE+c).as, (0xFA+c).as, (0xE5+c).as, (0xEE+c).as, (0xAD+c).as, (0xEE+c).as, + # (0xE9+c).as, (0xD2+c).as, (0xE9+c).as, (0xEB+c).as, (0xE1+c).as, (0xE9+c).as, (0xF3+c).as, (0xE8+c).as, + # (0xE9+c).as, (0xF4+c).as, (0xE1+c).as + # ].as + raw_mood: type: u2 - enum: phrase_style - doc: | - The phrase style. 1 is the up-down style - (white label text in rekordbox) where the main phrases consist - of up, down, and chorus. 2 is the bridge-verse style - (black label text in rekordbox) where the main phrases consist - of verse, chorus, and bridge. Style 3 is mostly identical to - bridge-verse style except verses 1-3 are labeled VERSE1 and verses - 4-6 are labeled VERSE2 in rekordbox. + pos: 6 + doc: | + This is a way to tell whether the rest of the tag has been masked. The value is supposed + to range from 1 to 3, but in masked files it will be much larger. + is_masked: + value: 'raw_mood > 20' # This is almost certainly not true for an unmasked file. + -webide-representation: '{body.mood}' + + + song_structure_body: + doc: | + Stores the rest of the song structure tag, which can only be + parsed after unmasking. + seq: + - id: mood + type: u2 + enum: track_mood + doc: | + The mood which rekordbox assigns the track as a whole during phrase analysis. - size: 6 - id: end_beat type: u2 @@ -411,59 +458,104 @@ types: The beat number at which the last phrase ends. The track may continue after the last phrase ends. If this is the case, it will mostly be silence. - - size: 4 + - size: 2 + - id: bank + type: u1 + enum: track_bank + doc: | + The stylistic bank which can be assigned to the track in rekordbox Lighting mode. + - size: 1 - id: entries type: song_structure_entry repeat: expr - repeat-expr: len_entries + repeat-expr: _parent.len_entries song_structure_entry: doc: | A song structure entry, represents a single phrase. seq: - - id: phrase_number + - id: index type: u2 doc: | The absolute number of the phrase, starting at one. - - id: beat_number + - id: beat type: u2 doc: | The beat number at which the phrase starts. - - id: phrase_id + - id: kind type: - switch-on: _parent.style + switch-on: _parent.mood cases: - 'phrase_style::up_down': phrase_up_down - 'phrase_style::verse_bridge': phrase_verse_bridge - _: phrase_verse_bridge + 'track_mood::high': phrase_high + 'track_mood::mid': phrase_mid + 'track_mood::low': phrase_low + _: phrase_mid # We don't recognize this mood, so pick a generic interpretation. + doc: | + The kind of phrase as displayed in rekordbox. + - size: 1 + - id: k1 + type: u1 + doc: One of three flags that identify phrase kind variants in high-mood tracks. + - size: 1 + - id: k2 + type: u1 + doc: One of three flags that identify phrase kind variants in high-mood tracks. + - size: 1 + - id: b + type: u1 + doc: | + Flags how many more beat numbers are in a high-mood "Up 3" phrase. + - id: beat2 + type: u2 + doc: | + Extra beat number (falling within phrase) always present in high-mood "Up 3" phrases. + - id: beat3 + type: u2 + doc: | + Extra beat number (falling within phrase, larger than beat2) + present in high-mood "Up 3" phrases when b has value 1. + - id: beat4 + type: u2 doc: | - Identifier of the phrase label. - - size: _parent.len_entry_bytes - 9 - - id: fill_in + Extra beat number (falling within phrase, larger than beat3) + present in high-mood "Up 3" phrases when b has value 1. + - size: 1 + - id: k3 + type: u1 + doc: One of three flags that identify phrase kind variants in high-mood tracks. + - size: 1 + - id: fill type: u1 doc: | - If nonzero, fill-in is present. - - id: fill_in_beat_number + If nonzero, fill-in is present at end of phrase. + - id: beat_fill type: u2 doc: | The beat number at which fill-in starts. + -webide-representation: '{kind.id}' - phrase_up_down: + phrase_high: seq: - id: id type: u2 - enum: phrase_up_down_id + enum: mood_high_phrase - phrase_verse_bridge: + phrase_mid: seq: - id: id type: u2 - enum: phrase_verse_bridge_id + enum: mood_mid_phrase + + phrase_low: + seq: + - id: id + type: u2 + enum: mood_low_phrase unknown_tag: {} enums: - section_tags: # We can't use this enum until KSC supports default/unmatched values + section_tags: 0x50434f42: cues # PCOB 0x50434f32: cues_2 # PCO2 (seen in .EXT) 0x50505448: path # PPTH @@ -487,27 +579,51 @@ enums: cue_entry_status: 0: disabled 1: enabled + 4: active_loop - phrase_style: - 1: up_down - 2: verse_bridge - 3: verse_bridge_2 + track_mood: + 1: high + 2: mid + 3: low - phrase_verse_bridge_id: + mood_low_phrase: 1: intro - 2: verse1 - 3: verse2 - 4: verse3 - 5: verse4 - 6: verse5 - 7: verse6 + 2: verse_1 + 3: verse_1b # Just displayed as "Verse 1" in rekordbox. + 4: verse_1c # Just displayed as "Verse 1" in rekordbox. + 5: verse_2 + 6: verse_2b # Just displayed as "Verse 2" in rekordbox. + 7: verse_2c # Just displayed as "Verse 2" in rekordbox. 8: bridge 9: chorus 10: outro - phrase_up_down_id: + mood_mid_phrase: + 1: intro + 2: verse_1 + 3: verse_2 + 4: verse_3 + 5: verse_4 + 6: verse_5 + 7: verse_6 + 8: bridge + 9: chorus + 10: outro + + mood_high_phrase: 1: intro 2: up 3: down 5: chorus 6: outro + + track_bank: + 0: default + 1: cool + 2: natural + 3: hot + 4: subtle + 5: warm + 6: vivid + 7: club_1 + 8: club_2 diff --git a/src/library/rekordbox/kaitaistructs/rekordbox_pdb.cpp b/lib/rekordbox-metadata/rekordbox_pdb.cpp similarity index 61% rename from src/library/rekordbox/kaitaistructs/rekordbox_pdb.cpp rename to lib/rekordbox-metadata/rekordbox_pdb.cpp index 6ad2d48c064..89f355eebc7 100644 --- a/src/library/rekordbox/kaitaistructs/rekordbox_pdb.cpp +++ b/lib/rekordbox-metadata/rekordbox_pdb.cpp @@ -1,12 +1,12 @@ // This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild #include "rekordbox_pdb.h" - - +#include "kaitai/exceptions.h" rekordbox_pdb_t::rekordbox_pdb_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = this; + m_tables = nullptr; _read(); } @@ -17,20 +17,22 @@ void rekordbox_pdb_t::_read() { m_next_unused_page = m__io->read_u4le(); m__unnamed4 = m__io->read_u4le(); m_sequence = m__io->read_u4le(); - m__unnamed6 = m__io->ensure_fixed_contents(std::string("\x00\x00\x00\x00", 4)); - int l_tables = num_tables(); - m_tables = new std::vector(); - m_tables->reserve(l_tables); + m_gap = m__io->read_bytes(4); + if (!(gap() == std::string("\x00\x00\x00\x00", 4))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00\x00", 4), gap(), _io(), std::string("/seq/6")); + } + m_tables = std::unique_ptr>>(new std::vector>()); + const int l_tables = num_tables(); for (int i = 0; i < l_tables; i++) { - m_tables->push_back(new table_t(m__io, this, m__root)); + m_tables->push_back(std::move(std::unique_ptr(new table_t(m__io, this, m__root)))); } } rekordbox_pdb_t::~rekordbox_pdb_t() { - for (std::vector::iterator it = m_tables->begin(); it != m_tables->end(); ++it) { - delete *it; - } - delete m_tables; + _clean_up(); +} + +void rekordbox_pdb_t::_clean_up() { } rekordbox_pdb_t::device_sql_string_t::device_sql_string_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { @@ -43,27 +45,50 @@ void rekordbox_pdb_t::device_sql_string_t::_read() { m_length_and_kind = m__io->read_u1(); switch (length_and_kind()) { case 64: { - m_body = new device_sql_long_ascii_t(m__io, this, m__root); + m_body = std::unique_ptr(new device_sql_long_ascii_t(m__io, this, m__root)); break; } case 144: { - m_body = new device_sql_long_utf16be_t(m__io, this, m__root); + m_body = std::unique_ptr(new device_sql_long_utf16le_t(m__io, this, m__root)); break; } default: { - m_body = new device_sql_short_ascii_t(length_and_kind(), m__io, this, m__root); + m_body = std::unique_ptr(new device_sql_short_ascii_t(length_and_kind(), m__io, this, m__root)); break; } } } rekordbox_pdb_t::device_sql_string_t::~device_sql_string_t() { - delete m_body; + _clean_up(); +} + +void rekordbox_pdb_t::device_sql_string_t::_clean_up() { +} + +rekordbox_pdb_t::history_playlist_row_t::history_playlist_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m_name = nullptr; + _read(); +} + +void rekordbox_pdb_t::history_playlist_row_t::_read() { + m_id = m__io->read_u4le(); + m_name = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); +} + +rekordbox_pdb_t::history_playlist_row_t::~history_playlist_row_t() { + _clean_up(); +} + +void rekordbox_pdb_t::history_playlist_row_t::_clean_up() { } rekordbox_pdb_t::playlist_tree_row_t::playlist_tree_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + m_name = nullptr; f_is_folder = false; _read(); } @@ -74,11 +99,14 @@ void rekordbox_pdb_t::playlist_tree_row_t::_read() { m_sort_order = m__io->read_u4le(); m_id = m__io->read_u4le(); m_raw_is_folder = m__io->read_u4le(); - m_name = new device_sql_string_t(m__io, this, m__root); + m_name = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); } rekordbox_pdb_t::playlist_tree_row_t::~playlist_tree_row_t() { - delete m_name; + _clean_up(); +} + +void rekordbox_pdb_t::playlist_tree_row_t::_clean_up() { } bool rekordbox_pdb_t::playlist_tree_row_t::is_folder() { @@ -92,6 +120,7 @@ bool rekordbox_pdb_t::playlist_tree_row_t::is_folder() { rekordbox_pdb_t::color_row_t::color_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + m_name = nullptr; _read(); } @@ -99,38 +128,39 @@ void rekordbox_pdb_t::color_row_t::_read() { m__unnamed0 = m__io->read_bytes(5); m_id = m__io->read_u2le(); m__unnamed2 = m__io->read_u1(); - m_name = new device_sql_string_t(m__io, this, m__root); + m_name = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); } rekordbox_pdb_t::color_row_t::~color_row_t() { - delete m_name; + _clean_up(); } -rekordbox_pdb_t::device_sql_short_ascii_t::device_sql_short_ascii_t(uint8_t p_mangled_length, kaitai::kstream* p__io, rekordbox_pdb_t::device_sql_string_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { +void rekordbox_pdb_t::color_row_t::_clean_up() { +} + +rekordbox_pdb_t::device_sql_short_ascii_t::device_sql_short_ascii_t(uint8_t p_length_and_kind, kaitai::kstream* p__io, rekordbox_pdb_t::device_sql_string_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; - m_mangled_length = p_mangled_length; + m_length_and_kind = p_length_and_kind; f_length = false; _read(); } void rekordbox_pdb_t::device_sql_short_ascii_t::_read() { - n_text = true; - if ( ((kaitai::kstream::mod(mangled_length(), 2) > 0) && (length() >= 0)) ) { - n_text = false; - m_text = kaitai::kstream::bytes_to_str(m__io->read_bytes(length()), std::string("ascii")); - } + m_text = kaitai::kstream::bytes_to_str(m__io->read_bytes((length() - 1)), std::string("ASCII")); } rekordbox_pdb_t::device_sql_short_ascii_t::~device_sql_short_ascii_t() { - if (!n_text) { - } + _clean_up(); +} + +void rekordbox_pdb_t::device_sql_short_ascii_t::_clean_up() { } int32_t rekordbox_pdb_t::device_sql_short_ascii_t::length() { if (f_length) return m_length; - m_length = (((mangled_length() - 1) / 2) - 1); + m_length = (length_and_kind() >> 1); f_length = true; return m_length; } @@ -138,6 +168,7 @@ int32_t rekordbox_pdb_t::device_sql_short_ascii_t::length() { rekordbox_pdb_t::album_row_t::album_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + m_name = nullptr; f_name = false; _read(); } @@ -154,27 +185,32 @@ void rekordbox_pdb_t::album_row_t::_read() { } rekordbox_pdb_t::album_row_t::~album_row_t() { + _clean_up(); +} + +void rekordbox_pdb_t::album_row_t::_clean_up() { if (f_name) { - delete m_name; } } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::album_row_t::name() { if (f_name) - return m_name; + return m_name.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_name())); - m_name = new device_sql_string_t(m__io, this, m__root); + m_name = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_name = true; - return m_name; + return m_name.get(); } rekordbox_pdb_t::page_t::page_t(kaitai::kstream* p__io, rekordbox_pdb_t::page_ref_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + m_next_page = nullptr; + m_row_groups = nullptr; f_num_rows = false; - f_num_groups = false; + f_num_row_groups = false; f_row_groups = false; f_heap_pos = false; f_is_data_page = false; @@ -182,10 +218,13 @@ rekordbox_pdb_t::page_t::page_t(kaitai::kstream* p__io, rekordbox_pdb_t::page_re } void rekordbox_pdb_t::page_t::_read() { - m__unnamed0 = m__io->ensure_fixed_contents(std::string("\x00\x00\x00\x00", 4)); + m_gap = m__io->read_bytes(4); + if (!(gap() == std::string("\x00\x00\x00\x00", 4))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00\x00", 4), gap(), _io(), std::string("/types/page/seq/0")); + } m_page_index = m__io->read_u4le(); m_type = static_cast(m__io->read_u4le()); - m_next_page = new page_ref_t(m__io, this, m__root); + m_next_page = std::unique_ptr(new page_ref_t(m__io, this, m__root)); m__unnamed4 = m__io->read_u4le(); m__unnamed5 = m__io->read_bytes(4); m_num_rows_small = m__io->read_u1(); @@ -206,14 +245,13 @@ void rekordbox_pdb_t::page_t::_read() { } rekordbox_pdb_t::page_t::~page_t() { - delete m_next_page; + _clean_up(); +} + +void rekordbox_pdb_t::page_t::_clean_up() { if (!n_heap) { } if (f_row_groups && !n_row_groups) { - for (std::vector::iterator it = m_row_groups->begin(); it != m_row_groups->end(); ++it) { - delete *it; - } - delete m_row_groups; } } @@ -225,29 +263,28 @@ uint16_t rekordbox_pdb_t::page_t::num_rows() { return m_num_rows; } -int32_t rekordbox_pdb_t::page_t::num_groups() { - if (f_num_groups) - return m_num_groups; - m_num_groups = (((num_rows() - 1) / 16) + 1); - f_num_groups = true; - return m_num_groups; +int32_t rekordbox_pdb_t::page_t::num_row_groups() { + if (f_num_row_groups) + return m_num_row_groups; + m_num_row_groups = (((num_rows() - 1) / 16) + 1); + f_num_row_groups = true; + return m_num_row_groups; } -std::vector* rekordbox_pdb_t::page_t::row_groups() { +std::vector>* rekordbox_pdb_t::page_t::row_groups() { if (f_row_groups) - return m_row_groups; + return m_row_groups.get(); n_row_groups = true; if (is_data_page()) { n_row_groups = false; - int l_row_groups = num_groups(); - m_row_groups = new std::vector(); - m_row_groups->reserve(l_row_groups); + m_row_groups = std::unique_ptr>>(new std::vector>()); + const int l_row_groups = num_row_groups(); for (int i = 0; i < l_row_groups; i++) { - m_row_groups->push_back(new row_group_t(i, m__io, this, m__root)); + m_row_groups->push_back(std::move(std::unique_ptr(new row_group_t(i, m__io, this, m__root)))); } + f_row_groups = true; } - f_row_groups = true; - return m_row_groups; + return m_row_groups.get(); } int32_t rekordbox_pdb_t::page_t::heap_pos() { @@ -270,6 +307,7 @@ rekordbox_pdb_t::row_group_t::row_group_t(uint16_t p_group_index, kaitai::kstrea m__parent = p__parent; m__root = p__root; m_group_index = p_group_index; + m_rows = nullptr; f_base = false; f_row_present_flags = false; f_rows = false; @@ -280,13 +318,13 @@ void rekordbox_pdb_t::row_group_t::_read() { } rekordbox_pdb_t::row_group_t::~row_group_t() { + _clean_up(); +} + +void rekordbox_pdb_t::row_group_t::_clean_up() { if (f_row_present_flags) { } if (f_rows) { - for (std::vector::iterator it = m_rows->begin(); it != m_rows->end(); ++it) { - delete *it; - } - delete m_rows; } } @@ -309,47 +347,73 @@ uint16_t rekordbox_pdb_t::row_group_t::row_present_flags() { return m_row_present_flags; } -std::vector* rekordbox_pdb_t::row_group_t::rows() { +std::vector>* rekordbox_pdb_t::row_group_t::rows() { if (f_rows) - return m_rows; - int l_rows = ((group_index() < (_parent()->num_groups() - 1)) ? (16) : ((kaitai::kstream::mod((_parent()->num_rows() - 1), 16) + 1))); - m_rows = new std::vector(); - m_rows->reserve(l_rows); + return m_rows.get(); + m_rows = std::unique_ptr>>(new std::vector>()); + const int l_rows = 16; for (int i = 0; i < l_rows; i++) { - m_rows->push_back(new row_ref_t(i, m__io, this, m__root)); + m_rows->push_back(std::move(std::unique_ptr(new row_ref_t(i, m__io, this, m__root)))); } f_rows = true; - return m_rows; + return m_rows.get(); } rekordbox_pdb_t::genre_row_t::genre_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + m_name = nullptr; _read(); } void rekordbox_pdb_t::genre_row_t::_read() { m_id = m__io->read_u4le(); - m_name = new device_sql_string_t(m__io, this, m__root); + m_name = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); } rekordbox_pdb_t::genre_row_t::~genre_row_t() { - delete m_name; + _clean_up(); +} + +void rekordbox_pdb_t::genre_row_t::_clean_up() { +} + +rekordbox_pdb_t::history_entry_row_t::history_entry_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_pdb_t::history_entry_row_t::_read() { + m_track_id = m__io->read_u4le(); + m_playlist_id = m__io->read_u4le(); + m_entry_index = m__io->read_u4le(); +} + +rekordbox_pdb_t::history_entry_row_t::~history_entry_row_t() { + _clean_up(); +} + +void rekordbox_pdb_t::history_entry_row_t::_clean_up() { } rekordbox_pdb_t::artwork_row_t::artwork_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + m_path = nullptr; _read(); } void rekordbox_pdb_t::artwork_row_t::_read() { m_id = m__io->read_u4le(); - m_path = new device_sql_string_t(m__io, this, m__root); + m_path = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); } rekordbox_pdb_t::artwork_row_t::~artwork_row_t() { - delete m_path; + _clean_up(); +} + +void rekordbox_pdb_t::artwork_row_t::_clean_up() { } rekordbox_pdb_t::device_sql_long_ascii_t::device_sql_long_ascii_t(kaitai::kstream* p__io, rekordbox_pdb_t::device_sql_string_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { @@ -360,15 +424,21 @@ rekordbox_pdb_t::device_sql_long_ascii_t::device_sql_long_ascii_t(kaitai::kstrea void rekordbox_pdb_t::device_sql_long_ascii_t::_read() { m_length = m__io->read_u2le(); - m_text = kaitai::kstream::bytes_to_str(m__io->read_bytes(length()), std::string("ascii")); + m__unnamed1 = m__io->read_u1(); + m_text = kaitai::kstream::bytes_to_str(m__io->read_bytes((length() - 4)), std::string("ASCII")); } rekordbox_pdb_t::device_sql_long_ascii_t::~device_sql_long_ascii_t() { + _clean_up(); +} + +void rekordbox_pdb_t::device_sql_long_ascii_t::_clean_up() { } rekordbox_pdb_t::artist_row_t::artist_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + m_name = nullptr; f_ofs_name_far = false; f_name = false; _read(); @@ -383,10 +453,13 @@ void rekordbox_pdb_t::artist_row_t::_read() { } rekordbox_pdb_t::artist_row_t::~artist_row_t() { + _clean_up(); +} + +void rekordbox_pdb_t::artist_row_t::_clean_up() { if (f_ofs_name_far && !n_ofs_name_far) { } if (f_name) { - delete m_name; } } @@ -400,25 +473,27 @@ uint16_t rekordbox_pdb_t::artist_row_t::ofs_name_far() { m__io->seek((_parent()->row_base() + 10)); m_ofs_name_far = m__io->read_u2le(); m__io->seek(_pos); + f_ofs_name_far = true; } - f_ofs_name_far = true; return m_ofs_name_far; } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::artist_row_t::name() { if (f_name) - return m_name; + return m_name.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ((subtype() == 100) ? (ofs_name_far()) : (ofs_name_near())))); - m_name = new device_sql_string_t(m__io, this, m__root); + m_name = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_name = true; - return m_name; + return m_name.get(); } rekordbox_pdb_t::page_ref_t::page_ref_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + m_body = nullptr; + m__io__raw_body = nullptr; f_body = false; _read(); } @@ -428,48 +503,57 @@ void rekordbox_pdb_t::page_ref_t::_read() { } rekordbox_pdb_t::page_ref_t::~page_ref_t() { + _clean_up(); +} + +void rekordbox_pdb_t::page_ref_t::_clean_up() { if (f_body) { - delete m__io__raw_body; - delete m_body; } } rekordbox_pdb_t::page_t* rekordbox_pdb_t::page_ref_t::body() { if (f_body) - return m_body; + return m_body.get(); kaitai::kstream *io = _root()->_io(); std::streampos _pos = io->pos(); io->seek((_root()->len_page() * index())); m__raw_body = io->read_bytes(_root()->len_page()); - m__io__raw_body = new kaitai::kstream(m__raw_body); - m_body = new page_t(m__io__raw_body, this, m__root); + m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); + m_body = std::unique_ptr(new page_t(m__io__raw_body.get(), this, m__root)); io->seek(_pos); f_body = true; - return m_body; -} - -rekordbox_pdb_t::device_sql_long_utf16be_t::device_sql_long_utf16be_t(kaitai::kstream* p__io, rekordbox_pdb_t::device_sql_string_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_pdb_t::device_sql_long_utf16be_t::_read() { - m_length = m__io->read_u2le(); - m_text = kaitai::kstream::bytes_to_str(m__io->read_bytes((length() - 4)), std::string("utf-16be")); -} - -rekordbox_pdb_t::device_sql_long_utf16be_t::~device_sql_long_utf16be_t() { + return m_body.get(); } rekordbox_pdb_t::track_row_t::track_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + m_ofs_strings = nullptr; + m_unknown_string_8 = nullptr; + m_unknown_string_6 = nullptr; + m_analyze_date = nullptr; + m_file_path = nullptr; + m_date_added = nullptr; + m_unknown_string_3 = nullptr; + m_texter = nullptr; + m_kuvo_public = nullptr; + m_mix_name = nullptr; + m_unknown_string_5 = nullptr; + m_unknown_string_4 = nullptr; + m_message = nullptr; + m_unknown_string_2 = nullptr; + m_isrc = nullptr; + m_unknown_string_7 = nullptr; + m_filename = nullptr; + m_analyze_path = nullptr; + m_comment = nullptr; + m_release_date = nullptr; + m_autoload_hot_cues = nullptr; + m_title = nullptr; f_unknown_string_8 = false; f_unknown_string_6 = false; f_analyze_date = false; f_file_path = false; - f_autoload_hotcues = false; f_date_added = false; f_unknown_string_3 = false; f_texter = false; @@ -479,12 +563,13 @@ rekordbox_pdb_t::track_row_t::track_row_t(kaitai::kstream* p__io, rekordbox_pdb_ f_unknown_string_4 = false; f_message = false; f_unknown_string_2 = false; - f_unknown_string_1 = false; + f_isrc = false; f_unknown_string_7 = false; f_filename = false; f_analyze_path = false; f_comment = false; f_release_date = false; + f_autoload_hot_cues = false; f_title = false; _read(); } @@ -521,326 +606,311 @@ void rekordbox_pdb_t::track_row_t::_read() { m_rating = m__io->read_u1(); m__unnamed29 = m__io->read_u2le(); m__unnamed30 = m__io->read_u2le(); - int l_ofs_strings = 21; - m_ofs_strings = new std::vector(); - m_ofs_strings->reserve(l_ofs_strings); + m_ofs_strings = std::unique_ptr>(new std::vector()); + const int l_ofs_strings = 21; for (int i = 0; i < l_ofs_strings; i++) { - m_ofs_strings->push_back(m__io->read_u2le()); + m_ofs_strings->push_back(std::move(m__io->read_u2le())); } } rekordbox_pdb_t::track_row_t::~track_row_t() { - delete m_ofs_strings; + _clean_up(); +} + +void rekordbox_pdb_t::track_row_t::_clean_up() { if (f_unknown_string_8) { - delete m_unknown_string_8; } if (f_unknown_string_6) { - delete m_unknown_string_6; } if (f_analyze_date) { - delete m_analyze_date; } if (f_file_path) { - delete m_file_path; - } - if (f_autoload_hotcues) { - delete m_autoload_hotcues; } if (f_date_added) { - delete m_date_added; } if (f_unknown_string_3) { - delete m_unknown_string_3; } if (f_texter) { - delete m_texter; } if (f_kuvo_public) { - delete m_kuvo_public; } if (f_mix_name) { - delete m_mix_name; } if (f_unknown_string_5) { - delete m_unknown_string_5; } if (f_unknown_string_4) { - delete m_unknown_string_4; } if (f_message) { - delete m_message; } if (f_unknown_string_2) { - delete m_unknown_string_2; } - if (f_unknown_string_1) { - delete m_unknown_string_1; + if (f_isrc) { } if (f_unknown_string_7) { - delete m_unknown_string_7; } if (f_filename) { - delete m_filename; } if (f_analyze_path) { - delete m_analyze_path; } if (f_comment) { - delete m_comment; } if (f_release_date) { - delete m_release_date; + } + if (f_autoload_hot_cues) { } if (f_title) { - delete m_title; } } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::unknown_string_8() { if (f_unknown_string_8) - return m_unknown_string_8; + return m_unknown_string_8.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(18))); - m_unknown_string_8 = new device_sql_string_t(m__io, this, m__root); + m_unknown_string_8 = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_unknown_string_8 = true; - return m_unknown_string_8; + return m_unknown_string_8.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::unknown_string_6() { if (f_unknown_string_6) - return m_unknown_string_6; + return m_unknown_string_6.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(9))); - m_unknown_string_6 = new device_sql_string_t(m__io, this, m__root); + m_unknown_string_6 = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_unknown_string_6 = true; - return m_unknown_string_6; + return m_unknown_string_6.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::analyze_date() { if (f_analyze_date) - return m_analyze_date; + return m_analyze_date.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(15))); - m_analyze_date = new device_sql_string_t(m__io, this, m__root); + m_analyze_date = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_analyze_date = true; - return m_analyze_date; + return m_analyze_date.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::file_path() { if (f_file_path) - return m_file_path; + return m_file_path.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(20))); - m_file_path = new device_sql_string_t(m__io, this, m__root); + m_file_path = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_file_path = true; - return m_file_path; -} - -rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::autoload_hotcues() { - if (f_autoload_hotcues) - return m_autoload_hotcues; - std::streampos _pos = m__io->pos(); - m__io->seek((_parent()->row_base() + ofs_strings()->at(7))); - m_autoload_hotcues = new device_sql_string_t(m__io, this, m__root); - m__io->seek(_pos); - f_autoload_hotcues = true; - return m_autoload_hotcues; + return m_file_path.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::date_added() { if (f_date_added) - return m_date_added; + return m_date_added.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(10))); - m_date_added = new device_sql_string_t(m__io, this, m__root); + m_date_added = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_date_added = true; - return m_date_added; + return m_date_added.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::unknown_string_3() { if (f_unknown_string_3) - return m_unknown_string_3; + return m_unknown_string_3.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(3))); - m_unknown_string_3 = new device_sql_string_t(m__io, this, m__root); + m_unknown_string_3 = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_unknown_string_3 = true; - return m_unknown_string_3; + return m_unknown_string_3.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::texter() { if (f_texter) - return m_texter; + return m_texter.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(1))); - m_texter = new device_sql_string_t(m__io, this, m__root); + m_texter = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_texter = true; - return m_texter; + return m_texter.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::kuvo_public() { if (f_kuvo_public) - return m_kuvo_public; + return m_kuvo_public.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(6))); - m_kuvo_public = new device_sql_string_t(m__io, this, m__root); + m_kuvo_public = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_kuvo_public = true; - return m_kuvo_public; + return m_kuvo_public.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::mix_name() { if (f_mix_name) - return m_mix_name; + return m_mix_name.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(12))); - m_mix_name = new device_sql_string_t(m__io, this, m__root); + m_mix_name = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_mix_name = true; - return m_mix_name; + return m_mix_name.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::unknown_string_5() { if (f_unknown_string_5) - return m_unknown_string_5; + return m_unknown_string_5.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(8))); - m_unknown_string_5 = new device_sql_string_t(m__io, this, m__root); + m_unknown_string_5 = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_unknown_string_5 = true; - return m_unknown_string_5; + return m_unknown_string_5.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::unknown_string_4() { if (f_unknown_string_4) - return m_unknown_string_4; + return m_unknown_string_4.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(4))); - m_unknown_string_4 = new device_sql_string_t(m__io, this, m__root); + m_unknown_string_4 = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_unknown_string_4 = true; - return m_unknown_string_4; + return m_unknown_string_4.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::message() { if (f_message) - return m_message; + return m_message.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(5))); - m_message = new device_sql_string_t(m__io, this, m__root); + m_message = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_message = true; - return m_message; + return m_message.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::unknown_string_2() { if (f_unknown_string_2) - return m_unknown_string_2; + return m_unknown_string_2.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(2))); - m_unknown_string_2 = new device_sql_string_t(m__io, this, m__root); + m_unknown_string_2 = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_unknown_string_2 = true; - return m_unknown_string_2; + return m_unknown_string_2.get(); } -rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::unknown_string_1() { - if (f_unknown_string_1) - return m_unknown_string_1; +rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::isrc() { + if (f_isrc) + return m_isrc.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(0))); - m_unknown_string_1 = new device_sql_string_t(m__io, this, m__root); + m_isrc = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); - f_unknown_string_1 = true; - return m_unknown_string_1; + f_isrc = true; + return m_isrc.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::unknown_string_7() { if (f_unknown_string_7) - return m_unknown_string_7; + return m_unknown_string_7.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(13))); - m_unknown_string_7 = new device_sql_string_t(m__io, this, m__root); + m_unknown_string_7 = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_unknown_string_7 = true; - return m_unknown_string_7; + return m_unknown_string_7.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::filename() { if (f_filename) - return m_filename; + return m_filename.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(19))); - m_filename = new device_sql_string_t(m__io, this, m__root); + m_filename = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_filename = true; - return m_filename; + return m_filename.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::analyze_path() { if (f_analyze_path) - return m_analyze_path; + return m_analyze_path.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(14))); - m_analyze_path = new device_sql_string_t(m__io, this, m__root); + m_analyze_path = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_analyze_path = true; - return m_analyze_path; + return m_analyze_path.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::comment() { if (f_comment) - return m_comment; + return m_comment.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(16))); - m_comment = new device_sql_string_t(m__io, this, m__root); + m_comment = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_comment = true; - return m_comment; + return m_comment.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::release_date() { if (f_release_date) - return m_release_date; + return m_release_date.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(11))); - m_release_date = new device_sql_string_t(m__io, this, m__root); + m_release_date = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_release_date = true; - return m_release_date; + return m_release_date.get(); +} + +rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::autoload_hot_cues() { + if (f_autoload_hot_cues) + return m_autoload_hot_cues.get(); + std::streampos _pos = m__io->pos(); + m__io->seek((_parent()->row_base() + ofs_strings()->at(7))); + m_autoload_hot_cues = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); + m__io->seek(_pos); + f_autoload_hot_cues = true; + return m_autoload_hot_cues.get(); } rekordbox_pdb_t::device_sql_string_t* rekordbox_pdb_t::track_row_t::title() { if (f_title) - return m_title; + return m_title.get(); std::streampos _pos = m__io->pos(); m__io->seek((_parent()->row_base() + ofs_strings()->at(17))); - m_title = new device_sql_string_t(m__io, this, m__root); + m_title = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); m__io->seek(_pos); f_title = true; - return m_title; + return m_title.get(); } rekordbox_pdb_t::key_row_t::key_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + m_name = nullptr; _read(); } void rekordbox_pdb_t::key_row_t::_read() { m_id = m__io->read_u4le(); m_id2 = m__io->read_u4le(); - m_name = new device_sql_string_t(m__io, this, m__root); + m_name = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); } rekordbox_pdb_t::key_row_t::~key_row_t() { - delete m_name; + _clean_up(); +} + +void rekordbox_pdb_t::key_row_t::_clean_up() { } rekordbox_pdb_t::playlist_entry_row_t::playlist_entry_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { @@ -856,39 +926,70 @@ void rekordbox_pdb_t::playlist_entry_row_t::_read() { } rekordbox_pdb_t::playlist_entry_row_t::~playlist_entry_row_t() { + _clean_up(); +} + +void rekordbox_pdb_t::playlist_entry_row_t::_clean_up() { } rekordbox_pdb_t::label_row_t::label_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + m_name = nullptr; _read(); } void rekordbox_pdb_t::label_row_t::_read() { m_id = m__io->read_u4le(); - m_name = new device_sql_string_t(m__io, this, m__root); + m_name = std::unique_ptr(new device_sql_string_t(m__io, this, m__root)); } rekordbox_pdb_t::label_row_t::~label_row_t() { - delete m_name; + _clean_up(); +} + +void rekordbox_pdb_t::label_row_t::_clean_up() { +} + +rekordbox_pdb_t::device_sql_long_utf16le_t::device_sql_long_utf16le_t(kaitai::kstream* p__io, rekordbox_pdb_t::device_sql_string_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + _read(); +} + +void rekordbox_pdb_t::device_sql_long_utf16le_t::_read() { + m_length = m__io->read_u2le(); + m__unnamed1 = m__io->read_u1(); + m_text = kaitai::kstream::bytes_to_str(m__io->read_bytes((length() - 4)), std::string("UTF-16LE")); +} + +rekordbox_pdb_t::device_sql_long_utf16le_t::~device_sql_long_utf16le_t() { + _clean_up(); +} + +void rekordbox_pdb_t::device_sql_long_utf16le_t::_clean_up() { } rekordbox_pdb_t::table_t::table_t(kaitai::kstream* p__io, rekordbox_pdb_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + m_first_page = nullptr; + m_last_page = nullptr; _read(); } void rekordbox_pdb_t::table_t::_read() { m_type = static_cast(m__io->read_u4le()); m_empty_candidate = m__io->read_u4le(); - m_first_page = new page_ref_t(m__io, this, m__root); - m_last_page = new page_ref_t(m__io, this, m__root); + m_first_page = std::unique_ptr(new page_ref_t(m__io, this, m__root)); + m_last_page = std::unique_ptr(new page_ref_t(m__io, this, m__root)); } rekordbox_pdb_t::table_t::~table_t() { - delete m_first_page; - delete m_last_page; + _clean_up(); +} + +void rekordbox_pdb_t::table_t::_clean_up() { } rekordbox_pdb_t::row_ref_t::row_ref_t(uint16_t p_row_index, kaitai::kstream* p__io, rekordbox_pdb_t::row_group_t* p__parent, rekordbox_pdb_t* p__root) : kaitai::kstruct(p__io) { @@ -906,10 +1007,13 @@ void rekordbox_pdb_t::row_ref_t::_read() { } rekordbox_pdb_t::row_ref_t::~row_ref_t() { + _clean_up(); +} + +void rekordbox_pdb_t::row_ref_t::_clean_up() { if (f_ofs_row) { } if (f_body && !n_body) { - delete m_body; } } @@ -942,7 +1046,7 @@ bool rekordbox_pdb_t::row_ref_t::present() { kaitai::kstruct* rekordbox_pdb_t::row_ref_t::body() { if (f_body) - return m_body; + return m_body.get(); n_body = true; if (present()) { n_body = false; @@ -950,59 +1054,69 @@ kaitai::kstruct* rekordbox_pdb_t::row_ref_t::body() { m__io->seek(row_base()); n_body = true; switch (_parent()->_parent()->type()) { - case PAGE_TYPE_KEYS: { + case rekordbox_pdb_t::PAGE_TYPE_PLAYLIST_TREE: { n_body = false; - m_body = new key_row_t(m__io, this, m__root); + m_body = std::unique_ptr(new playlist_tree_row_t(m__io, this, m__root)); break; } - case PAGE_TYPE_GENRES: { + case rekordbox_pdb_t::PAGE_TYPE_KEYS: { n_body = false; - m_body = new genre_row_t(m__io, this, m__root); + m_body = std::unique_ptr(new key_row_t(m__io, this, m__root)); break; } - case PAGE_TYPE_PLAYLIST_ENTRIES: { + case rekordbox_pdb_t::PAGE_TYPE_ARTISTS: { n_body = false; - m_body = new playlist_entry_row_t(m__io, this, m__root); + m_body = std::unique_ptr(new artist_row_t(m__io, this, m__root)); break; } - case PAGE_TYPE_TRACKS: { + case rekordbox_pdb_t::PAGE_TYPE_ALBUMS: { n_body = false; - m_body = new track_row_t(m__io, this, m__root); + m_body = std::unique_ptr(new album_row_t(m__io, this, m__root)); break; } - case PAGE_TYPE_PLAYLIST_TREE: { + case rekordbox_pdb_t::PAGE_TYPE_GENRES: { n_body = false; - m_body = new playlist_tree_row_t(m__io, this, m__root); + m_body = std::unique_ptr(new genre_row_t(m__io, this, m__root)); break; } - case PAGE_TYPE_LABELS: { + case rekordbox_pdb_t::PAGE_TYPE_HISTORY_PLAYLISTS: { n_body = false; - m_body = new label_row_t(m__io, this, m__root); + m_body = std::unique_ptr(new history_playlist_row_t(m__io, this, m__root)); break; } - case PAGE_TYPE_ALBUMS: { + case rekordbox_pdb_t::PAGE_TYPE_ARTWORK: { n_body = false; - m_body = new album_row_t(m__io, this, m__root); + m_body = std::unique_ptr(new artwork_row_t(m__io, this, m__root)); break; } - case PAGE_TYPE_COLORS: { + case rekordbox_pdb_t::PAGE_TYPE_PLAYLIST_ENTRIES: { n_body = false; - m_body = new color_row_t(m__io, this, m__root); + m_body = std::unique_ptr(new playlist_entry_row_t(m__io, this, m__root)); break; } - case PAGE_TYPE_ARTISTS: { + case rekordbox_pdb_t::PAGE_TYPE_LABELS: { n_body = false; - m_body = new artist_row_t(m__io, this, m__root); + m_body = std::unique_ptr(new label_row_t(m__io, this, m__root)); break; } - case PAGE_TYPE_ARTWORK: { + case rekordbox_pdb_t::PAGE_TYPE_TRACKS: { n_body = false; - m_body = new artwork_row_t(m__io, this, m__root); + m_body = std::unique_ptr(new track_row_t(m__io, this, m__root)); + break; + } + case rekordbox_pdb_t::PAGE_TYPE_HISTORY_ENTRIES: { + n_body = false; + m_body = std::unique_ptr(new history_entry_row_t(m__io, this, m__root)); + break; + } + case rekordbox_pdb_t::PAGE_TYPE_COLORS: { + n_body = false; + m_body = std::unique_ptr(new color_row_t(m__io, this, m__root)); break; } } m__io->seek(_pos); + f_body = true; } - f_body = true; - return m_body; + return m_body.get(); } diff --git a/src/library/rekordbox/kaitaistructs/rekordbox_pdb.h b/lib/rekordbox-metadata/rekordbox_pdb.h similarity index 81% rename from src/library/rekordbox/kaitaistructs/rekordbox_pdb.h rename to lib/rekordbox-metadata/rekordbox_pdb.h index 9b251b59364..76f7b517480 100644 --- a/src/library/rekordbox/kaitaistructs/rekordbox_pdb.h +++ b/lib/rekordbox-metadata/rekordbox_pdb.h @@ -2,13 +2,13 @@ // This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild -#include "kaitaistruct.h" - +#include "kaitai/kaitaistruct.h" #include +#include #include -#if KAITAI_STRUCT_VERSION < 7000L -#error "Incompatible Kaitai Struct C++/STL API: version 0.7 or later is required" +#if KAITAI_STRUCT_VERSION < 9000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required" #endif /** @@ -18,18 +18,18 @@ * the Pioneer Professional DJ ecosystem, because it is the format that * their rekordbox software uses to write USB and SD media which can be * mounted in DJ controllers and used to play and mix music. - * + * * It has been reverse-engineered to facilitate sophisticated * integrations with light and laser shows, videos, and other musical * instruments, by supporting deep knowledge of what is playing and * what is coming next through monitoring the network communications of * the players. - * + * * The file is divided into fixed-size blocks. The first block has a * header that establishes the block size, and lists the tables * available in the database, identifying their types and the index of * the first of the series of linked pages that make up that table. - * + * * Each table is made up of a series of rows which may be spread across * any number of pages. The pages start with a header describing the * page and linking to the next page. The rest of the page is used as a @@ -37,25 +37,26 @@ * structure that builds backwards from the end of the page. Each row * of a given type has a fixed size structure which links to any * variable-sized strings by their offsets within the page. - * + * * As changes are made to the table, some records may become unused, * and there may be gaps within the heap that are too small to be used * by other data. There is a bit map in the row index that identifies * which rows are actually present. Rows that are not present must be * ignored: they do not contain valid (or even necessarily well-formed) * data. - * + * * The majority of the work in reverse-engineering this format was * performed by @henrybetts and @flesniak, for which I am hugely * grateful. @GreyCat helped me learn the intricacies (and best * practices) of Kaitai far faster than I would have managed on my own. - * \sa Source + * \sa https://github.com/Deep-Symmetry/crate-digger/blob/master/doc/Analysis.pdf Source */ class rekordbox_pdb_t : public kaitai::kstruct { public: class device_sql_string_t; + class history_playlist_row_t; class playlist_tree_row_t; class color_row_t; class device_sql_short_ascii_t; @@ -63,15 +64,16 @@ class rekordbox_pdb_t : public kaitai::kstruct { class page_t; class row_group_t; class genre_row_t; + class history_entry_row_t; class artwork_row_t; class device_sql_long_ascii_t; class artist_row_t; class page_ref_t; - class device_sql_long_utf16be_t; class track_row_t; class key_row_t; class playlist_entry_row_t; class label_row_t; + class device_sql_long_utf16le_t; class table_t; class row_ref_t; @@ -87,8 +89,8 @@ class rekordbox_pdb_t : public kaitai::kstruct { PAGE_TYPE_PLAYLIST_ENTRIES = 8, PAGE_TYPE_UNKNOWN_9 = 9, PAGE_TYPE_UNKNOWN_10 = 10, - PAGE_TYPE_UNKNOWN_11 = 11, - PAGE_TYPE_UNKNOWN_12 = 12, + PAGE_TYPE_HISTORY_PLAYLISTS = 11, + PAGE_TYPE_HISTORY_ENTRIES = 12, PAGE_TYPE_ARTWORK = 13, PAGE_TYPE_UNKNOWN_14 = 14, PAGE_TYPE_UNKNOWN_15 = 15, @@ -98,10 +100,11 @@ class rekordbox_pdb_t : public kaitai::kstruct { PAGE_TYPE_HISTORY = 19 }; - rekordbox_pdb_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, rekordbox_pdb_t* p__root = 0); + rekordbox_pdb_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~rekordbox_pdb_t(); @@ -115,17 +118,18 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - device_sql_string_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, rekordbox_pdb_t* p__root = 0); + device_sql_string_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~device_sql_string_t(); private: uint8_t m_length_and_kind; - kaitai::kstruct* m_body; + std::unique_ptr m_body; rekordbox_pdb_t* m__root; kaitai::kstruct* m__parent; @@ -137,11 +141,51 @@ class rekordbox_pdb_t : public kaitai::kstruct { * follow. */ uint8_t length_and_kind() const { return m_length_and_kind; } - kaitai::kstruct* body() const { return m_body; } + kaitai::kstruct* body() const { return m_body.get(); } rekordbox_pdb_t* _root() const { return m__root; } kaitai::kstruct* _parent() const { return m__parent; } }; + /** + * A row that holds a history playlist ID and name, linking to + * the track IDs captured during a performance on the player. + */ + + class history_playlist_row_t : public kaitai::kstruct { + + public: + + history_playlist_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); + + private: + void _read(); + void _clean_up(); + + public: + ~history_playlist_row_t(); + + private: + uint32_t m_id; + std::unique_ptr m_name; + rekordbox_pdb_t* m__root; + rekordbox_pdb_t::row_ref_t* m__parent; + + public: + + /** + * The unique identifier by which this history playlist can + * be requested. + */ + uint32_t id() const { return m_id; } + + /** + * The variable-length string naming the playlist. + */ + device_sql_string_t* name() const { return m_name.get(); } + rekordbox_pdb_t* _root() const { return m__root; } + rekordbox_pdb_t::row_ref_t* _parent() const { return m__parent; } + }; + /** * A row that holds a playlist name, ID, indication of whether it * is an ordinary playlist or a folder of other playlists, a link @@ -152,10 +196,11 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - playlist_tree_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + playlist_tree_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~playlist_tree_row_t(); @@ -173,7 +218,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { uint32_t m_sort_order; uint32_t m_id; uint32_t m_raw_is_folder; - device_sql_string_t* m_name; + std::unique_ptr m_name; rekordbox_pdb_t* m__root; rekordbox_pdb_t::row_ref_t* m__parent; @@ -206,7 +251,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { /** * The variable-length string naming the playlist. */ - device_sql_string_t* name() const { return m_name; } + device_sql_string_t* name() const { return m_name.get(); } rekordbox_pdb_t* _root() const { return m__root; } rekordbox_pdb_t::row_ref_t* _parent() const { return m__parent; } }; @@ -219,10 +264,11 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - color_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + color_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~color_row_t(); @@ -231,7 +277,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { std::string m__unnamed0; uint16_t m_id; uint8_t m__unnamed2; - device_sql_string_t* m_name; + std::unique_ptr m_name; rekordbox_pdb_t* m__root; rekordbox_pdb_t::row_ref_t* m__parent; @@ -248,7 +294,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { /** * The variable-length string naming the color. */ - device_sql_string_t* name() const { return m_name; } + device_sql_string_t* name() const { return m_name.get(); } rekordbox_pdb_t* _root() const { return m__root; } rekordbox_pdb_t::row_ref_t* _parent() const { return m__parent; } }; @@ -261,10 +307,11 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - device_sql_short_ascii_t(uint8_t p_mangled_length, kaitai::kstream* p__io, rekordbox_pdb_t::device_sql_string_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + device_sql_short_ascii_t(uint8_t p_length_and_kind, kaitai::kstream* p__io, rekordbox_pdb_t::device_sql_string_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~device_sql_short_ascii_t(); @@ -276,19 +323,13 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: /** - * The un-mangled length of the string, in bytes. + * the length extracted of the entire device_sql_short_ascii type */ int32_t length(); private: std::string m_text; - bool n_text; - - public: - bool _is_null_text() { text(); return n_text; }; - - private: - uint8_t m_mangled_length; + uint8_t m_length_and_kind; rekordbox_pdb_t* m__root; rekordbox_pdb_t::device_sql_string_t* m__parent; @@ -303,7 +344,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { * Contains the actual length, incremented, doubled, and * incremented again. Go figure. */ - uint8_t mangled_length() const { return m_mangled_length; } + uint8_t length_and_kind() const { return m_length_and_kind; } rekordbox_pdb_t* _root() const { return m__root; } rekordbox_pdb_t::device_sql_string_t* _parent() const { return m__parent; } }; @@ -316,17 +357,18 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - album_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + album_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~album_row_t(); private: bool f_name; - device_sql_string_t* m_name; + std::unique_ptr m_name; public: @@ -373,7 +415,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { uint32_t _unnamed5() const { return m__unnamed5; } /** - * @flesniak says: "alwayx 0x03, maybe an unindexed empty string" + * @flesniak says: "always 0x03, maybe an unindexed empty string" */ uint8_t _unnamed6() const { return m__unnamed6; } @@ -398,10 +440,11 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - page_t(kaitai::kstream* p__io, rekordbox_pdb_t::page_ref_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + page_t(kaitai::kstream* p__io, rekordbox_pdb_t::page_ref_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~page_t(); @@ -420,8 +463,8 @@ class rekordbox_pdb_t : public kaitai::kstruct { uint16_t num_rows(); private: - bool f_num_groups; - int32_t m_num_groups; + bool f_num_row_groups; + int32_t m_num_row_groups; public: @@ -430,11 +473,11 @@ class rekordbox_pdb_t : public kaitai::kstruct { * group can hold up to sixteen rows. All but the final one * will hold sixteen rows. */ - int32_t num_groups(); + int32_t num_row_groups(); private: bool f_row_groups; - std::vector* m_row_groups; + std::unique_ptr>> m_row_groups; bool n_row_groups; public: @@ -449,7 +492,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { * can hold up to sixteen rows. Non-data pages do not have * actual rows, and attempting to parse them can crash. */ - std::vector* row_groups(); + std::vector>* row_groups(); private: bool f_heap_pos; @@ -466,10 +509,10 @@ class rekordbox_pdb_t : public kaitai::kstruct { bool is_data_page(); private: - std::string m__unnamed0; + std::string m_gap; uint32_t m_page_index; page_type_t m_type; - page_ref_t* m_next_page; + std::unique_ptr m_next_page; uint32_t m__unnamed4; std::string m__unnamed5; uint8_t m_num_rows_small; @@ -493,7 +536,13 @@ class rekordbox_pdb_t : public kaitai::kstruct { rekordbox_pdb_t::page_ref_t* m__parent; public: - std::string _unnamed0() const { return m__unnamed0; } + + /** + * Only exposed until + * https://github.com/kaitai-io/kaitai_struct/issues/825 can be + * fixed. + */ + std::string gap() const { return m_gap; } /** * Matches the index we used to look up the page, sanity check? @@ -509,7 +558,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { * Index of the next page containing this type of rows. Points past * the end of the file if there are no more. */ - page_ref_t* next_page() const { return m_next_page; } + page_ref_t* next_page() const { return m_next_page.get(); } /** * @flesniak said: "sequence number (0->1: 8->13, 1->2: 22, 2->3: 27)" @@ -597,10 +646,11 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - row_group_t(uint16_t p_group_index, kaitai::kstream* p__io, rekordbox_pdb_t::page_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + row_group_t(uint16_t p_group_index, kaitai::kstream* p__io, rekordbox_pdb_t::page_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~row_group_t(); @@ -633,14 +683,14 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_rows; - std::vector* m_rows; + std::unique_ptr>> m_rows; public: /** * The row offsets in this group. */ - std::vector* rows(); + std::vector>* rows(); private: uint16_t m_group_index; @@ -666,17 +716,18 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - genre_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + genre_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~genre_row_t(); private: uint32_t m_id; - device_sql_string_t* m_name; + std::unique_ptr m_name; rekordbox_pdb_t* m__root; rekordbox_pdb_t::row_ref_t* m__parent; @@ -691,7 +742,51 @@ class rekordbox_pdb_t : public kaitai::kstruct { /** * The variable-length string naming the genre. */ - device_sql_string_t* name() const { return m_name; } + device_sql_string_t* name() const { return m_name.get(); } + rekordbox_pdb_t* _root() const { return m__root; } + rekordbox_pdb_t::row_ref_t* _parent() const { return m__parent; } + }; + + /** + * A row that associates a track with a position in a history playlist. + */ + + class history_entry_row_t : public kaitai::kstruct { + + public: + + history_entry_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); + + private: + void _read(); + void _clean_up(); + + public: + ~history_entry_row_t(); + + private: + uint32_t m_track_id; + uint32_t m_playlist_id; + uint32_t m_entry_index; + rekordbox_pdb_t* m__root; + rekordbox_pdb_t::row_ref_t* m__parent; + + public: + + /** + * The track found at this position in the playlist. + */ + uint32_t track_id() const { return m_track_id; } + + /** + * The history playlist to which this entry belongs. + */ + uint32_t playlist_id() const { return m_playlist_id; } + + /** + * The position within the playlist represented by this entry. + */ + uint32_t entry_index() const { return m_entry_index; } rekordbox_pdb_t* _root() const { return m__root; } rekordbox_pdb_t::row_ref_t* _parent() const { return m__parent; } }; @@ -705,17 +800,18 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - artwork_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + artwork_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~artwork_row_t(); private: uint32_t m_id; - device_sql_string_t* m_path; + std::unique_ptr m_path; rekordbox_pdb_t* m__root; rekordbox_pdb_t::row_ref_t* m__parent; @@ -731,29 +827,31 @@ class rekordbox_pdb_t : public kaitai::kstruct { * The variable-length file path string at which the art file * can be found. */ - device_sql_string_t* path() const { return m_path; } + device_sql_string_t* path() const { return m_path.get(); } rekordbox_pdb_t* _root() const { return m__root; } rekordbox_pdb_t::row_ref_t* _parent() const { return m__parent; } }; /** - * An ASCII-encoded string preceded by a two-byte length field. + * An ASCII-encoded string preceded by a two-byte length field in a four-byte header. */ class device_sql_long_ascii_t : public kaitai::kstruct { public: - device_sql_long_ascii_t(kaitai::kstream* p__io, rekordbox_pdb_t::device_sql_string_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + device_sql_long_ascii_t(kaitai::kstream* p__io, rekordbox_pdb_t::device_sql_string_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~device_sql_long_ascii_t(); private: uint16_t m_length; + uint8_t m__unnamed1; std::string m_text; rekordbox_pdb_t* m__root; rekordbox_pdb_t::device_sql_string_t* m__parent; @@ -764,6 +862,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { * Contains the length of the string in bytes. */ uint16_t length() const { return m_length; } + uint8_t _unnamed1() const { return m__unnamed1; } /** * The content of the string. @@ -781,10 +880,11 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - artist_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + artist_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~artist_row_t(); @@ -810,7 +910,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_name; - device_sql_string_t* m_name; + std::unique_ptr m_name; public: @@ -871,17 +971,18 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - page_ref_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, rekordbox_pdb_t* p__root = 0); + page_ref_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~page_ref_t(); private: bool f_body; - page_t* m_body; + std::unique_ptr m_body; public: @@ -896,7 +997,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { rekordbox_pdb_t* m__root; kaitai::kstruct* m__parent; std::string m__raw_body; - kaitai::kstream* m__io__raw_body; + std::unique_ptr m__io__raw_body; public: @@ -907,44 +1008,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { rekordbox_pdb_t* _root() const { return m__root; } kaitai::kstruct* _parent() const { return m__parent; } std::string _raw_body() const { return m__raw_body; } - kaitai::kstream* _io__raw_body() const { return m__io__raw_body; } - }; - - /** - * A UTF-16BE-encoded string preceded by a two-byte length field. - */ - - class device_sql_long_utf16be_t : public kaitai::kstruct { - - public: - - device_sql_long_utf16be_t(kaitai::kstream* p__io, rekordbox_pdb_t::device_sql_string_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); - - private: - void _read(); - - public: - ~device_sql_long_utf16be_t(); - - private: - uint16_t m_length; - std::string m_text; - rekordbox_pdb_t* m__root; - rekordbox_pdb_t::device_sql_string_t* m__parent; - - public: - - /** - * Contains the length of the string in bytes, including two trailing nulls. - */ - uint16_t length() const { return m_length; } - - /** - * The content of the string. - */ - std::string text() const { return m_text; } - rekordbox_pdb_t* _root() const { return m__root; } - rekordbox_pdb_t::device_sql_string_t* _parent() const { return m__parent; } + kaitai::kstream* _io__raw_body() const { return m__io__raw_body.get(); } }; /** @@ -957,17 +1021,18 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - track_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + track_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~track_row_t(); private: bool f_unknown_string_8; - device_sql_string_t* m_unknown_string_8; + std::unique_ptr m_unknown_string_8; public: @@ -978,7 +1043,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_unknown_string_6; - device_sql_string_t* m_unknown_string_6; + std::unique_ptr m_unknown_string_6; public: @@ -989,7 +1054,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_analyze_date; - device_sql_string_t* m_analyze_date; + std::unique_ptr m_analyze_date; public: @@ -1000,7 +1065,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_file_path; - device_sql_string_t* m_file_path; + std::unique_ptr m_file_path; public: @@ -1009,23 +1074,9 @@ class rekordbox_pdb_t : public kaitai::kstruct { */ device_sql_string_t* file_path(); - private: - bool f_autoload_hotcues; - device_sql_string_t* m_autoload_hotcues; - - public: - - /** - * A string whose value is always either empty or "ON", and - * which apparently for some insane reason is used, rather than - * a single bit somewhere, to control whether hot-cues are - * auto-loaded for the track. - */ - device_sql_string_t* autoload_hotcues(); - private: bool f_date_added; - device_sql_string_t* m_date_added; + std::unique_ptr m_date_added; public: @@ -1036,7 +1087,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_unknown_string_3; - device_sql_string_t* m_unknown_string_3; + std::unique_ptr m_unknown_string_3; public: @@ -1049,18 +1100,18 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_texter; - device_sql_string_t* m_texter; + std::unique_ptr m_texter; public: /** - * A string of unknown purpose, which @flesnik named. + * A string of unknown purpose, which @flesniak named. */ device_sql_string_t* texter(); private: bool f_kuvo_public; - device_sql_string_t* m_kuvo_public; + std::unique_ptr m_kuvo_public; public: @@ -1074,7 +1125,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_mix_name; - device_sql_string_t* m_mix_name; + std::unique_ptr m_mix_name; public: @@ -1085,7 +1136,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_unknown_string_5; - device_sql_string_t* m_unknown_string_5; + std::unique_ptr m_unknown_string_5; public: @@ -1096,7 +1147,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_unknown_string_4; - device_sql_string_t* m_unknown_string_4; + std::unique_ptr m_unknown_string_4; public: @@ -1109,42 +1160,42 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_message; - device_sql_string_t* m_message; + std::unique_ptr m_message; public: /** - * A string of unknown purpose, which @flesnik named. + * A string of unknown purpose, which @flesniak named. */ device_sql_string_t* message(); private: bool f_unknown_string_2; - device_sql_string_t* m_unknown_string_2; + std::unique_ptr m_unknown_string_2; public: /** * A string of unknown purpose; @flesniak said "thought - * tracknumber -> wrong!" + * track number -> wrong!" */ device_sql_string_t* unknown_string_2(); private: - bool f_unknown_string_1; - device_sql_string_t* m_unknown_string_1; + bool f_isrc; + std::unique_ptr m_isrc; public: /** - * A string of unknown purpose, which has so far only been - * empty. + * International Standard Recording Code of track + * when known (in mangled format). */ - device_sql_string_t* unknown_string_1(); + device_sql_string_t* isrc(); private: bool f_unknown_string_7; - device_sql_string_t* m_unknown_string_7; + std::unique_ptr m_unknown_string_7; public: @@ -1155,7 +1206,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_filename; - device_sql_string_t* m_filename; + std::unique_ptr m_filename; public: @@ -1166,7 +1217,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_analyze_path; - device_sql_string_t* m_analyze_path; + std::unique_ptr m_analyze_path; public: @@ -1180,7 +1231,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_comment; - device_sql_string_t* m_comment; + std::unique_ptr m_comment; public: @@ -1191,7 +1242,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_release_date; - device_sql_string_t* m_release_date; + std::unique_ptr m_release_date; public: @@ -1200,9 +1251,23 @@ class rekordbox_pdb_t : public kaitai::kstruct { */ device_sql_string_t* release_date(); + private: + bool f_autoload_hot_cues; + std::unique_ptr m_autoload_hot_cues; + + public: + + /** + * A string whose value is always either empty or "ON", and + * which apparently for some insane reason is used, rather than + * a single bit somewhere, to control whether hot-cues are + * auto-loaded for the track. + */ + device_sql_string_t* autoload_hot_cues(); + private: bool f_title; - device_sql_string_t* m_title; + std::unique_ptr m_title; public: @@ -1243,7 +1308,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { uint8_t m_rating; uint16_t m__unnamed29; uint16_t m__unnamed30; - std::vector* m_ofs_strings; + std::unique_ptr> m_ofs_strings; rekordbox_pdb_t* m__root; rekordbox_pdb_t::row_ref_t* m__parent; @@ -1422,7 +1487,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { * The location, relative to the start of this row, of a * variety of variable-length strings. */ - std::vector* ofs_strings() const { return m_ofs_strings; } + std::vector* ofs_strings() const { return m_ofs_strings.get(); } rekordbox_pdb_t* _root() const { return m__root; } rekordbox_pdb_t::row_ref_t* _parent() const { return m__parent; } }; @@ -1435,10 +1500,11 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - key_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + key_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~key_row_t(); @@ -1446,7 +1512,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: uint32_t m_id; uint32_t m_id2; - device_sql_string_t* m_name; + std::unique_ptr m_name; rekordbox_pdb_t* m__root; rekordbox_pdb_t::row_ref_t* m__parent; @@ -1466,7 +1532,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { /** * The variable-length string naming the key. */ - device_sql_string_t* name() const { return m_name; } + device_sql_string_t* name() const { return m_name.get(); } rekordbox_pdb_t* _root() const { return m__root; } rekordbox_pdb_t::row_ref_t* _parent() const { return m__parent; } }; @@ -1479,10 +1545,11 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - playlist_entry_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + playlist_entry_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~playlist_entry_row_t(); @@ -1522,17 +1589,18 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - label_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + label_row_t(kaitai::kstream* p__io, rekordbox_pdb_t::row_ref_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~label_row_t(); private: uint32_t m_id; - device_sql_string_t* m_name; + std::unique_ptr m_name; rekordbox_pdb_t* m__root; rekordbox_pdb_t::row_ref_t* m__parent; @@ -1547,11 +1615,51 @@ class rekordbox_pdb_t : public kaitai::kstruct { /** * The variable-length string naming the label. */ - device_sql_string_t* name() const { return m_name; } + device_sql_string_t* name() const { return m_name.get(); } rekordbox_pdb_t* _root() const { return m__root; } rekordbox_pdb_t::row_ref_t* _parent() const { return m__parent; } }; + /** + * A UTF-16LE-encoded string preceded by a two-byte length field in a four-byte header. + */ + + class device_sql_long_utf16le_t : public kaitai::kstruct { + + public: + + device_sql_long_utf16le_t(kaitai::kstream* p__io, rekordbox_pdb_t::device_sql_string_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); + + private: + void _read(); + void _clean_up(); + + public: + ~device_sql_long_utf16le_t(); + + private: + uint16_t m_length; + uint8_t m__unnamed1; + std::string m_text; + rekordbox_pdb_t* m__root; + rekordbox_pdb_t::device_sql_string_t* m__parent; + + public: + + /** + * Contains the length of the string in bytes, plus four trailing bytes that must be ignored. + */ + uint16_t length() const { return m_length; } + uint8_t _unnamed1() const { return m__unnamed1; } + + /** + * The content of the string. + */ + std::string text() const { return m_text; } + rekordbox_pdb_t* _root() const { return m__root; } + rekordbox_pdb_t::device_sql_string_t* _parent() const { return m__parent; } + }; + /** * Each table is a linked list of pages containing rows of a single * type. This header describes the nature of the table and links to @@ -1562,10 +1670,11 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - table_t(kaitai::kstream* p__io, rekordbox_pdb_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + table_t(kaitai::kstream* p__io, rekordbox_pdb_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~table_t(); @@ -1573,8 +1682,8 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: page_type_t m_type; uint32_t m_empty_candidate; - page_ref_t* m_first_page; - page_ref_t* m_last_page; + std::unique_ptr m_first_page; + std::unique_ptr m_last_page; rekordbox_pdb_t* m__root; rekordbox_pdb_t* m__parent; @@ -1592,7 +1701,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { * zero rows, but the next page it links to contains the start * of the meaningful data rows. */ - page_ref_t* first_page() const { return m_first_page; } + page_ref_t* first_page() const { return m_first_page.get(); } /** * Holds the index of the last page that makes up this table. @@ -1601,7 +1710,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { * notice that the `next_page` link you followed took you to a * page of a different `type`. */ - page_ref_t* last_page() const { return m_last_page; } + page_ref_t* last_page() const { return m_last_page.get(); } rekordbox_pdb_t* _root() const { return m__root; } rekordbox_pdb_t* _parent() const { return m__parent; } }; @@ -1618,10 +1727,11 @@ class rekordbox_pdb_t : public kaitai::kstruct { public: - row_ref_t(uint16_t p_row_index, kaitai::kstream* p__io, rekordbox_pdb_t::row_group_t* p__parent = 0, rekordbox_pdb_t* p__root = 0); + row_ref_t(uint16_t p_row_index, kaitai::kstream* p__io, rekordbox_pdb_t::row_group_t* p__parent = nullptr, rekordbox_pdb_t* p__root = nullptr); private: void _read(); + void _clean_up(); public: ~row_ref_t(); @@ -1666,7 +1776,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { private: bool f_body; - kaitai::kstruct* m_body; + std::unique_ptr m_body; bool n_body; public: @@ -1705,8 +1815,8 @@ class rekordbox_pdb_t : public kaitai::kstruct { uint32_t m_next_unused_page; uint32_t m__unnamed4; uint32_t m_sequence; - std::string m__unnamed6; - std::vector* m_tables; + std::string m_gap; + std::unique_ptr>> m_tables; rekordbox_pdb_t* m__root; kaitai::kstruct* m__parent; @@ -1735,7 +1845,7 @@ class rekordbox_pdb_t : public kaitai::kstruct { uint32_t num_tables() const { return m_num_tables; } /** - * @flesinak said: "Not used as any `empty_candidate`, points + * @flesniak said: "Not used as any `empty_candidate`, points * past the end of the file." */ uint32_t next_unused_page() const { return m_next_unused_page; } @@ -1746,12 +1856,18 @@ class rekordbox_pdb_t : public kaitai::kstruct { * sometimes by two or three." */ uint32_t sequence() const { return m_sequence; } - std::string _unnamed6() const { return m__unnamed6; } + + /** + * Only exposed until + * https://github.com/kaitai-io/kaitai_struct/issues/825 can be + * fixed. + */ + std::string gap() const { return m_gap; } /** * Describes and links to the tables present in the database. */ - std::vector* tables() const { return m_tables; } + std::vector>* tables() const { return m_tables.get(); } rekordbox_pdb_t* _root() const { return m__root; } kaitai::kstruct* _parent() const { return m__parent; } }; diff --git a/src/library/rekordbox/kaitaistructs/rekordbox_pdb.ksy b/lib/rekordbox-metadata/rekordbox_pdb.ksy similarity index 91% rename from src/library/rekordbox/kaitaistructs/rekordbox_pdb.ksy rename to lib/rekordbox-metadata/rekordbox_pdb.ksy index ba9eca0a2a4..d591c3cebf9 100644 --- a/src/library/rekordbox/kaitaistructs/rekordbox_pdb.ksy +++ b/lib/rekordbox-metadata/rekordbox_pdb.ksy @@ -70,7 +70,7 @@ seq: - id: next_unused_page type: u4 doc: | - @flesinak said: "Not used as any `empty_candidate`, points + @flesniak said: "Not used as any `empty_candidate`, points past the end of the file." - type: u4 - id: sequence @@ -78,7 +78,12 @@ seq: doc: | @flesniak said: "Always incremented by at least one, sometimes by two or three." - - contents: [0, 0, 0, 0] + - id: gap + contents: [0, 0, 0, 0] + doc: | + Only exposed until + https://github.com/kaitai-io/kaitai_struct/issues/825 can be + fixed. - id: tables type: table repeat: expr @@ -145,7 +150,12 @@ types: an index which locates all rows present in the heap via their offsets past the end of the page header. seq: - - contents: [0, 0, 0, 0] + - id: gap + contents: [0, 0, 0, 0] + doc: | + Only exposed until + https://github.com/kaitai-io/kaitai_struct/issues/825 can be + fixed. - id: page_index doc: Matches the index we used to look up the page, sanity check? type: u4 @@ -230,7 +240,7 @@ types: index entries there are, but some of those may not be marked as present in the table due to deletion). -webide-parse-mode: eager - num_groups: + num_row_groups: value: '(num_rows - 1) / 16 + 1' doc: | The number of row groups that are present in the index. Each @@ -239,7 +249,7 @@ types: row_groups: type: 'row_group(_index)' repeat: expr - repeat-expr: num_groups + repeat-expr: num_row_groups doc: | The actual row groups making up the row index. Each group can hold up to sixteen rows. Non-data pages do not have @@ -276,7 +286,7 @@ types: rows: type: row_ref(_index) repeat: expr - repeat-expr: '(group_index < (_parent.num_groups - 1)) ? 16 : ((_parent.num_rows - 1) % 16 + 1)' + repeat-expr: 16 doc: | The row offsets in this group. @@ -328,6 +338,8 @@ types: 'page_type::labels': label_row 'page_type::playlist_tree': playlist_tree_row 'page_type::playlist_entries': playlist_entry_row + 'page_type::history_playlists': history_playlist_row + 'page_type::history_entries': history_entry_row 'page_type::tracks': track_row if: present doc: | @@ -358,7 +370,7 @@ types: - type: u4 - type: u1 doc: | - @flesniak says: "alwayx 0x03, maybe an unindexed empty string" + @flesniak says: "always 0x03, maybe an unindexed empty string" - id: ofs_name type: u1 doc: | @@ -544,6 +556,38 @@ types: doc: | The playlist to which this entry belongs. + history_playlist_row: + doc: | + A row that holds a history playlist ID and name, linking to + the track IDs captured during a performance on the player. + seq: + - id: id + type: u4 + doc: | + The unique identifier by which this history playlist can + be requested. + - id: name + type: device_sql_string + doc: | + The variable-length string naming the playlist. + + history_entry_row: + doc: | + A row that associates a track with a position in a history playlist. + seq: + - id: track_id + type: u4 + doc: | + The track found at this position in the playlist. + - id: playlist_id + type: u4 + doc: | + The history playlist to which this entry belongs. + - id: entry_index + type: u4 + doc: | + The position within the playlist represented by this entry. + track_row: doc: | A row that describes a track that can be played, with many @@ -687,25 +731,25 @@ types: The location, relative to the start of this row, of a variety of variable-length strings. instances: - unknown_string_1: + isrc: type: device_sql_string pos: _parent.row_base + ofs_strings[0] doc: | - A string of unknown purpose, which has so far only been - empty. + International Standard Recording Code of track + when known (in mangled format). -webide-parse-mode: eager texter: type: device_sql_string pos: _parent.row_base + ofs_strings[1] doc: | - A string of unknown purpose, which @flesnik named. + A string of unknown purpose, which @flesniak named. -webide-parse-mode: eager unknown_string_2: type: device_sql_string pos: _parent.row_base + ofs_strings[2] doc: | A string of unknown purpose; @flesniak said "thought - tracknumber -> wrong!" + track number -> wrong!" unknown_string_3: type: device_sql_string pos: _parent.row_base + ofs_strings[3] @@ -725,7 +769,7 @@ types: type: device_sql_string pos: _parent.row_base + ofs_strings[5] doc: | - A string of unknown purpose, which @flesnik named. + A string of unknown purpose, which @flesniak named. -webide-parse-mode: eager kuvo_public: type: device_sql_string @@ -736,7 +780,7 @@ types: a single bit somewhere, to control whether the track information is visible on Kuvo. -webide-parse-mode: eager - autoload_hotcues: + autoload_hot_cues: type: device_sql_string pos: _parent.row_base + ofs_strings[7] doc: | @@ -843,7 +887,7 @@ types: switch-on: length_and_kind cases: 0x40: device_sql_long_ascii - 0x90: device_sql_long_utf16be + 0x90: device_sql_long_utf16le _: device_sql_short_ascii(length_and_kind) -webide-parse-mode: eager -webide-representation: '{body.text}' @@ -852,7 +896,7 @@ types: doc: | An ASCII-encoded string up to 127 bytes long. params: - - id: mangled_length + - id: length_and_kind type: u1 doc: | Contains the actual length, incremented, doubled, and @@ -860,45 +904,46 @@ types: seq: - id: text type: str - size: length - encoding: ascii - if: '(mangled_length % 2 > 0) and (length >= 0)' # Skip invalid strings + size: length - 1 + encoding: ASCII doc: | The content of the string. instances: length: - value: '((mangled_length - 1) / 2) - 1' + value: '(length_and_kind >> 1)' doc: | - The un-mangled length of the string, in bytes. + the length extracted of the entire device_sql_short_ascii type -webide-parse-mode: eager device_sql_long_ascii: doc: | - An ASCII-encoded string preceded by a two-byte length field. + An ASCII-encoded string preceded by a two-byte length field in a four-byte header. seq: - id: length type: u2 doc: | Contains the length of the string in bytes. + - type: u1 - id: text type: str - size: length - encoding: ascii + size: length - 4 + encoding: ASCII doc: | The content of the string. - device_sql_long_utf16be: + device_sql_long_utf16le: doc: | - A UTF-16BE-encoded string preceded by a two-byte length field. + A UTF-16LE-encoded string preceded by a two-byte length field in a four-byte header. seq: - id: length type: u2 doc: | - Contains the length of the string in bytes, including two trailing nulls. + Contains the length of the string in bytes, plus four trailing bytes that must be ignored. + - type: u1 - id: text type: str size: length - 4 - encoding: utf-16be + encoding: UTF-16LE doc: | The content of the string. @@ -948,11 +993,15 @@ enums: 10: id: unknown_10 11: - id: unknown_11 + id: history_playlists doc: | - The rows all seem to have history file names in them, such as "HISTORY 001". + Holds rows that assign IDs and give names to the history playlists + that have been captured by the player, such as "HISTORY 001". 12: - id: unknown_12 + id: history_entries + doc: | + Holds rows that enumerate the tracks found in history playlists + and the playlists they belong to. 13: id: artwork doc: | @@ -972,4 +1021,4 @@ enums: 19: id: history doc: | - Holds rows listing tracks played in performance sessions. + Holds information to help rekordbox sync history playlists. diff --git a/src/library/rekordbox/kaitaistructs/.clang-tidy b/src/library/rekordbox/kaitaistructs/.clang-tidy deleted file mode 100644 index 9a90eccedd5..00000000000 --- a/src/library/rekordbox/kaitaistructs/.clang-tidy +++ /dev/null @@ -1,6 +0,0 @@ -# Disable all clang-tidy checks generated code in this folder -# clang-tidy errors if you remove all checks, so this is one workaround -# https://stackoverflow.com/a/58379342/251068 -Checks: "-*,misc-definitions-in-headers" -CheckOptions: - - { key: HeaderFileExtensions, value: "x" } diff --git a/src/library/rekordbox/kaitaistructs/rekordbox_anlz.cpp b/src/library/rekordbox/kaitaistructs/rekordbox_anlz.cpp deleted file mode 100644 index 681d89e95e3..00000000000 --- a/src/library/rekordbox/kaitaistructs/rekordbox_anlz.cpp +++ /dev/null @@ -1,516 +0,0 @@ -// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild - -#include "rekordbox_anlz.h" - - - -rekordbox_anlz_t::rekordbox_anlz_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = this; - _read(); -} - -void rekordbox_anlz_t::_read() { - m__unnamed0 = m__io->ensure_fixed_contents(std::string("\x50\x4D\x41\x49", 4)); - m_len_header = m__io->read_u4be(); - m_len_file = m__io->read_u4be(); - m__unnamed3 = m__io->read_bytes((len_header() - _io()->pos())); - m_sections = new std::vector(); - { - while (!m__io->is_eof()) { - m_sections->push_back(new tagged_section_t(m__io, this, m__root)); - } - } -} - -rekordbox_anlz_t::~rekordbox_anlz_t() { - for (std::vector::iterator it = m_sections->begin(); it != m_sections->end(); ++it) { - delete *it; - } - delete m_sections; -} - -rekordbox_anlz_t::phrase_up_down_t::phrase_up_down_t(kaitai::kstream* p__io, rekordbox_anlz_t::song_structure_entry_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::phrase_up_down_t::_read() { - m_id = static_cast(m__io->read_u2be()); -} - -rekordbox_anlz_t::phrase_up_down_t::~phrase_up_down_t() { -} - -rekordbox_anlz_t::path_tag_t::path_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::path_tag_t::_read() { - m_len_path = m__io->read_u4be(); - n_path = true; - if (len_path() > 1) { - n_path = false; - m_path = kaitai::kstream::bytes_to_str(m__io->read_bytes((len_path() - 2)), std::string("utf-16be")); - } -} - -rekordbox_anlz_t::path_tag_t::~path_tag_t() { - if (!n_path) { - } -} - -rekordbox_anlz_t::wave_preview_tag_t::wave_preview_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::wave_preview_tag_t::_read() { - m_len_preview = m__io->read_u4be(); - m__unnamed1 = m__io->read_u4be(); - n_data = true; - if (_parent()->len_tag() > _parent()->len_header()) { - n_data = false; - m_data = m__io->read_bytes(len_preview()); - } -} - -rekordbox_anlz_t::wave_preview_tag_t::~wave_preview_tag_t() { - if (!n_data) { - } -} - -rekordbox_anlz_t::beat_grid_tag_t::beat_grid_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::beat_grid_tag_t::_read() { - m__unnamed0 = m__io->read_u4be(); - m__unnamed1 = m__io->read_u4be(); - m_len_beats = m__io->read_u4be(); - int l_beats = len_beats(); - m_beats = new std::vector(); - m_beats->reserve(l_beats); - for (int i = 0; i < l_beats; i++) { - m_beats->push_back(new beat_grid_beat_t(m__io, this, m__root)); - } -} - -rekordbox_anlz_t::beat_grid_tag_t::~beat_grid_tag_t() { - for (std::vector::iterator it = m_beats->begin(); it != m_beats->end(); ++it) { - delete *it; - } - delete m_beats; -} - -rekordbox_anlz_t::wave_color_preview_tag_t::wave_color_preview_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::wave_color_preview_tag_t::_read() { - m_len_entry_bytes = m__io->read_u4be(); - m_len_entries = m__io->read_u4be(); - m__unnamed2 = m__io->read_u4be(); - m_entries = m__io->read_bytes((len_entries() * len_entry_bytes())); -} - -rekordbox_anlz_t::wave_color_preview_tag_t::~wave_color_preview_tag_t() { -} - -rekordbox_anlz_t::wave_scroll_tag_t::wave_scroll_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::wave_scroll_tag_t::_read() { - m_len_entry_bytes = m__io->read_u4be(); - m_len_entries = m__io->read_u4be(); - m__unnamed2 = m__io->read_u4be(); - m_entries = m__io->read_bytes((len_entries() * len_entry_bytes())); -} - -rekordbox_anlz_t::wave_scroll_tag_t::~wave_scroll_tag_t() { -} - -rekordbox_anlz_t::phrase_verse_bridge_t::phrase_verse_bridge_t(kaitai::kstream* p__io, rekordbox_anlz_t::song_structure_entry_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::phrase_verse_bridge_t::_read() { - m_id = static_cast(m__io->read_u2be()); -} - -rekordbox_anlz_t::phrase_verse_bridge_t::~phrase_verse_bridge_t() { -} - -rekordbox_anlz_t::song_structure_tag_t::song_structure_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::song_structure_tag_t::_read() { - m_len_entry_bytes = m__io->read_u4be(); - m_len_entries = m__io->read_u2be(); - m_style = m__io->read_u2be(); - m__unnamed3 = m__io->read_bytes(6); - m_end_beat = m__io->read_u2be(); - m__unnamed5 = m__io->read_bytes(4); - int l_entries = len_entries(); - m_entries = new std::vector(); - m_entries->reserve(l_entries); - for (int i = 0; i < l_entries; i++) { - m_entries->push_back(new song_structure_entry_t(m__io, this, m__root)); - } -} - -rekordbox_anlz_t::song_structure_tag_t::~song_structure_tag_t() { - for (std::vector::iterator it = m_entries->begin(); it != m_entries->end(); ++it) { - delete *it; - } - delete m_entries; -} - -rekordbox_anlz_t::cue_extended_entry_t::cue_extended_entry_t(kaitai::kstream* p__io, rekordbox_anlz_t::cue_extended_tag_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::cue_extended_entry_t::_read() { - m__unnamed0 = m__io->ensure_fixed_contents(std::string("\x50\x43\x50\x32", 4)); - m_len_header = m__io->read_u4be(); - m_len_entry = m__io->read_u4be(); - m_hot_cue = m__io->read_u4be(); - m_type = static_cast(m__io->read_u1()); - m__unnamed5 = m__io->read_bytes(3); - m_time = m__io->read_u4be(); - m_loop_time = m__io->read_u4be(); - m_color_id = m__io->read_u1(); - m__unnamed9 = m__io->read_bytes(11); - n_len_comment = true; - if (len_entry() > 43) { - n_len_comment = false; - m_len_comment = m__io->read_u4be(); - } - n_comment = true; - if (len_entry() > 43) { - n_comment = false; - m_comment = kaitai::kstream::bytes_to_str(m__io->read_bytes(len_comment()), std::string("utf-16be")); - } - n_color_code = true; - if ((len_entry() - len_comment()) > 44) { - n_color_code = false; - m_color_code = m__io->read_u1(); - } - n_color_red = true; - if ((len_entry() - len_comment()) > 45) { - n_color_red = false; - m_color_red = m__io->read_u1(); - } - n_color_green = true; - if ((len_entry() - len_comment()) > 46) { - n_color_green = false; - m_color_green = m__io->read_u1(); - } - n_color_blue = true; - if ((len_entry() - len_comment()) > 47) { - n_color_blue = false; - m_color_blue = m__io->read_u1(); - } - n__unnamed16 = true; - if ((len_entry() - len_comment()) > 48) { - n__unnamed16 = false; - m__unnamed16 = m__io->read_bytes(((len_entry() - 48) - len_comment())); - } -} - -rekordbox_anlz_t::cue_extended_entry_t::~cue_extended_entry_t() { - if (!n_len_comment) { - } - if (!n_comment) { - } - if (!n_color_code) { - } - if (!n_color_red) { - } - if (!n_color_green) { - } - if (!n_color_blue) { - } - if (!n__unnamed16) { - } -} - -rekordbox_anlz_t::vbr_tag_t::vbr_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::vbr_tag_t::_read() { - m__unnamed0 = m__io->read_u4be(); - int l_index = 400; - m_index = new std::vector(); - m_index->reserve(l_index); - for (int i = 0; i < l_index; i++) { - m_index->push_back(m__io->read_u4be()); - } -} - -rekordbox_anlz_t::vbr_tag_t::~vbr_tag_t() { - delete m_index; -} - -rekordbox_anlz_t::song_structure_entry_t::song_structure_entry_t(kaitai::kstream* p__io, rekordbox_anlz_t::song_structure_tag_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::song_structure_entry_t::_read() { - m_phrase_number = m__io->read_u2be(); - m_beat_number = m__io->read_u2be(); - switch (_parent()->style()) { - case 1: { - m_phrase_id = new phrase_up_down_t(m__io, this, m__root); - break; - } - case 2: { - m_phrase_id = new phrase_verse_bridge_t(m__io, this, m__root); - break; - } - default: { - m_phrase_id = new phrase_verse_bridge_t(m__io, this, m__root); - break; - } - } - m__unnamed3 = m__io->read_bytes((_parent()->len_entry_bytes() - 9)); - m_fill_in = m__io->read_u1(); - m_fill_in_beat_number = m__io->read_u2be(); -} - -rekordbox_anlz_t::song_structure_entry_t::~song_structure_entry_t() { - delete m_phrase_id; -} - -rekordbox_anlz_t::cue_entry_t::cue_entry_t(kaitai::kstream* p__io, rekordbox_anlz_t::cue_tag_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::cue_entry_t::_read() { - m__unnamed0 = m__io->ensure_fixed_contents(std::string("\x50\x43\x50\x54", 4)); - m_len_header = m__io->read_u4be(); - m_len_entry = m__io->read_u4be(); - m_hot_cue = m__io->read_u4be(); - m_status = static_cast(m__io->read_u4be()); - m__unnamed5 = m__io->read_u4be(); - m_order_first = m__io->read_u2be(); - m_order_last = m__io->read_u2be(); - m_type = static_cast(m__io->read_u1()); - m__unnamed9 = m__io->read_bytes(3); - m_time = m__io->read_u4be(); - m_loop_time = m__io->read_u4be(); - m__unnamed12 = m__io->read_bytes(16); -} - -rekordbox_anlz_t::cue_entry_t::~cue_entry_t() { -} - -rekordbox_anlz_t::beat_grid_beat_t::beat_grid_beat_t(kaitai::kstream* p__io, rekordbox_anlz_t::beat_grid_tag_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::beat_grid_beat_t::_read() { - m_beat_number = m__io->read_u2be(); - m_tempo = m__io->read_u2be(); - m_time = m__io->read_u4be(); -} - -rekordbox_anlz_t::beat_grid_beat_t::~beat_grid_beat_t() { -} - -rekordbox_anlz_t::cue_extended_tag_t::cue_extended_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::cue_extended_tag_t::_read() { - m_type = static_cast(m__io->read_u4be()); - m_len_cues = m__io->read_u2be(); - m__unnamed2 = m__io->read_bytes(2); - int l_cues = len_cues(); - m_cues = new std::vector(); - m_cues->reserve(l_cues); - for (int i = 0; i < l_cues; i++) { - m_cues->push_back(new cue_extended_entry_t(m__io, this, m__root)); - } -} - -rekordbox_anlz_t::cue_extended_tag_t::~cue_extended_tag_t() { - for (std::vector::iterator it = m_cues->begin(); it != m_cues->end(); ++it) { - delete *it; - } - delete m_cues; -} - -rekordbox_anlz_t::unknown_tag_t::unknown_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::unknown_tag_t::_read() { -} - -rekordbox_anlz_t::unknown_tag_t::~unknown_tag_t() { -} - -rekordbox_anlz_t::tagged_section_t::tagged_section_t(kaitai::kstream* p__io, rekordbox_anlz_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::tagged_section_t::_read() { - m_fourcc = m__io->read_s4be(); - m_len_header = m__io->read_u4be(); - m_len_tag = m__io->read_u4be(); - switch (fourcc()) { - case 1346588482: { - m__raw_body = m__io->read_bytes((len_tag() - 12)); - m__io__raw_body = new kaitai::kstream(m__raw_body); - m_body = new cue_tag_t(m__io__raw_body, this, m__root); - break; - } - case 1347900978: { - m__raw_body = m__io->read_bytes((len_tag() - 12)); - m__io__raw_body = new kaitai::kstream(m__raw_body); - m_body = new wave_preview_tag_t(m__io__raw_body, this, m__root); - break; - } - case 1347900980: { - m__raw_body = m__io->read_bytes((len_tag() - 12)); - m__io__raw_body = new kaitai::kstream(m__raw_body); - m_body = new wave_color_preview_tag_t(m__io__raw_body, this, m__root); - break; - } - case 1347895638: { - m__raw_body = m__io->read_bytes((len_tag() - 12)); - m__io__raw_body = new kaitai::kstream(m__raw_body); - m_body = new wave_preview_tag_t(m__io__raw_body, this, m__root); - break; - } - case 1347900979: { - m__raw_body = m__io->read_bytes((len_tag() - 12)); - m__io__raw_body = new kaitai::kstream(m__raw_body); - m_body = new wave_scroll_tag_t(m__io__raw_body, this, m__root); - break; - } - case 1347638089: { - m__raw_body = m__io->read_bytes((len_tag() - 12)); - m__io__raw_body = new kaitai::kstream(m__raw_body); - m_body = new song_structure_tag_t(m__io__raw_body, this, m__root); - break; - } - case 1347507290: { - m__raw_body = m__io->read_bytes((len_tag() - 12)); - m__io__raw_body = new kaitai::kstream(m__raw_body); - m_body = new beat_grid_tag_t(m__io__raw_body, this, m__root); - break; - } - case 1347830354: { - m__raw_body = m__io->read_bytes((len_tag() - 12)); - m__io__raw_body = new kaitai::kstream(m__raw_body); - m_body = new vbr_tag_t(m__io__raw_body, this, m__root); - break; - } - case 1347900981: { - m__raw_body = m__io->read_bytes((len_tag() - 12)); - m__io__raw_body = new kaitai::kstream(m__raw_body); - m_body = new wave_color_scroll_tag_t(m__io__raw_body, this, m__root); - break; - } - case 1346588466: { - m__raw_body = m__io->read_bytes((len_tag() - 12)); - m__io__raw_body = new kaitai::kstream(m__raw_body); - m_body = new cue_extended_tag_t(m__io__raw_body, this, m__root); - break; - } - case 1347441736: { - m__raw_body = m__io->read_bytes((len_tag() - 12)); - m__io__raw_body = new kaitai::kstream(m__raw_body); - m_body = new path_tag_t(m__io__raw_body, this, m__root); - break; - } - default: { - m__raw_body = m__io->read_bytes((len_tag() - 12)); - m__io__raw_body = new kaitai::kstream(m__raw_body); - m_body = new unknown_tag_t(m__io__raw_body, this, m__root); - break; - } - } -} - -rekordbox_anlz_t::tagged_section_t::~tagged_section_t() { - delete m__io__raw_body; - delete m_body; -} - -rekordbox_anlz_t::wave_color_scroll_tag_t::wave_color_scroll_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::wave_color_scroll_tag_t::_read() { - m_len_entry_bytes = m__io->read_u4be(); - m_len_entries = m__io->read_u4be(); - m__unnamed2 = m__io->read_u4be(); - m_entries = m__io->read_bytes((len_entries() * len_entry_bytes())); -} - -rekordbox_anlz_t::wave_color_scroll_tag_t::~wave_color_scroll_tag_t() { -} - -rekordbox_anlz_t::cue_tag_t::cue_tag_t(kaitai::kstream* p__io, rekordbox_anlz_t::tagged_section_t* p__parent, rekordbox_anlz_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - _read(); -} - -void rekordbox_anlz_t::cue_tag_t::_read() { - m_type = static_cast(m__io->read_u4be()); - m__unnamed1 = m__io->read_bytes(2); - m_len_cues = m__io->read_u2be(); - m_memory_count = m__io->read_u4be(); - int l_cues = len_cues(); - m_cues = new std::vector(); - m_cues->reserve(l_cues); - for (int i = 0; i < l_cues; i++) { - m_cues->push_back(new cue_entry_t(m__io, this, m__root)); - } -} - -rekordbox_anlz_t::cue_tag_t::~cue_tag_t() { - for (std::vector::iterator it = m_cues->begin(); it != m_cues->end(); ++it) { - delete *it; - } - delete m_cues; -} diff --git a/src/library/rekordbox/rekordboxfeature.cpp b/src/library/rekordbox/rekordboxfeature.cpp index d8d45020a50..470060c8d14 100644 --- a/src/library/rekordbox/rekordboxfeature.cpp +++ b/src/library/rekordbox/rekordboxfeature.cpp @@ -1,10 +1,13 @@ #include "library/rekordbox/rekordboxfeature.h" #include +#include +#include #include #include #include +#include #include #include @@ -12,8 +15,6 @@ #include "library/dao/trackschema.h" #include "library/library.h" #include "library/queryutil.h" -#include "library/rekordbox/kaitaistructs/rekordbox_anlz.h" -#include "library/rekordbox/kaitaistructs/rekordbox_pdb.h" #include "library/rekordbox/rekordboxconstants.h" #include "library/trackcollection.h" #include "library/trackcollectionmanager.h" @@ -255,7 +256,7 @@ inline bool instanceof (const T* ptr) { } QString toUnicode(const std::string& toConvert) { - return QTextCodec::codecForName("UTF-16BE") + return QTextCodec::codecForName("UTF-16LE") ->toUnicode(toConvert.data(), static_cast(toConvert.length())); } @@ -274,10 +275,10 @@ QString getText(rekordbox_pdb_t::device_sql_string_t* deviceString) { rekordbox_pdb_t::device_sql_long_ascii_t* longAsciiString = static_cast(deviceString->body()); text = QString::fromStdString(longAsciiString->text()); - } else if (instanceof (deviceString->body())) { - rekordbox_pdb_t::device_sql_long_utf16be_t* longUtf16beString = - static_cast(deviceString->body()); - text = toUnicode(longUtf16beString->text()); + } else if (instanceof (deviceString->body())) { + rekordbox_pdb_t::device_sql_long_utf16le_t* longUtf16leString = + static_cast(deviceString->body()); + text = toUnicode(longUtf16leString->text()); } // Some strings read from Rekordbox *.PDB files contain random null characters @@ -490,7 +491,7 @@ QString parseDeviceDB(mixxx::DbConnectionPoolPtr dbConnectionPool, TreeItem* dev std::ifstream ifs(dbPath.toStdString(), std::ifstream::binary); kaitai::kstream ks(&ifs); - rekordbox_pdb_t reckordboxDB = rekordbox_pdb_t(&ks); + rekordbox_pdb_t rekordboxDB = rekordbox_pdb_t(&ks); // There are other types of tables (eg. COLOR), these are the only ones we are // interested at the moment. Perhaps when/if @@ -523,70 +524,47 @@ QString parseDeviceDB(mixxx::DbConnectionPoolPtr dbConnectionPool, TreeItem* dev bool folderOrPlaylistFound = false; for (int tableOrderIndex = 0; tableOrderIndex < totalTables; tableOrderIndex++) { - for (std::vector::iterator table = - reckordboxDB.tables()->begin(); - table != reckordboxDB.tables()->end(); - ++table) { - if ((*table)->type() == tableOrder[tableOrderIndex]) { - uint16_t lastIndex = (*table)->last_page()->index(); - rekordbox_pdb_t::page_ref_t* currentRef = (*table)->first_page(); + for (const auto& table : *rekordboxDB.tables()) { + if (table->type() == tableOrder[tableOrderIndex]) { + uint16_t lastIndex = table->last_page()->index(); + rekordbox_pdb_t::page_ref_t* currentRef = table->first_page(); while (true) { rekordbox_pdb_t::page_t* page = currentRef->body(); if (page->is_data_page()) { - for (std::vector:: - iterator rowGroup = - page->row_groups()->begin(); - rowGroup != page->row_groups()->end(); - ++rowGroup) { - for (std::vector:: - iterator rowRef = (*rowGroup) - ->rows() - ->begin(); - rowRef != (*rowGroup)->rows()->end(); - ++rowRef) { - if ((*rowRef)->present()) { + for (const auto& rowgroup : *page->row_groups()) { + for (const auto& rowRef : *rowgroup->rows()) { + if (rowRef->present()) { switch (tableOrder[tableOrderIndex]) { case rekordbox_pdb_t::PAGE_TYPE_KEYS: { - // Key found, update map - rekordbox_pdb_t::key_row_t* key = - static_cast( - (*rowRef)->body()); + auto* key = + static_cast( + rowRef->body()); keysMap[key->id()] = getText(key->name()); } break; case rekordbox_pdb_t::PAGE_TYPE_GENRES: { - // Genre found, update map - rekordbox_pdb_t::genre_row_t* genre = - static_cast( - (*rowRef)->body()); + auto* genre = + static_cast( + rowRef->body()); genresMap[genre->id()] = getText(genre->name()); } break; case rekordbox_pdb_t::PAGE_TYPE_ARTISTS: { - // Artist found, update map - rekordbox_pdb_t::artist_row_t* artist = - static_cast( - (*rowRef)->body()); + auto* artist = + static_cast( + rowRef->body()); artistsMap[artist->id()] = getText(artist->name()); } break; case rekordbox_pdb_t::PAGE_TYPE_ALBUMS: { - // Album found, update map - rekordbox_pdb_t::album_row_t* album = - static_cast( - (*rowRef)->body()); + auto* album = + static_cast( + rowRef->body()); albumsMap[album->id()] = getText(album->name()); } break; case rekordbox_pdb_t::PAGE_TYPE_PLAYLIST_ENTRIES: { - // Playlist to track mapping found, update map - rekordbox_pdb_t::playlist_entry_row_t* - playlistEntry = static_cast< - rekordbox_pdb_t:: - playlist_entry_row_t*>( - (*rowRef)->body()); + auto* playlistEntry = + static_cast( + rowRef->body()); playlistTrackMap [playlistEntry->playlist_id()] [playlistEntry->entry_index()] = @@ -594,11 +572,9 @@ QString parseDeviceDB(mixxx::DbConnectionPoolPtr dbConnectionPool, TreeItem* dev ->track_id(); } break; case rekordbox_pdb_t::PAGE_TYPE_TRACKS: { - // Track found, insert into database insertTrack(database, - static_cast( - (*rowRef)->body()), + static_cast( + rowRef->body()), query, queryInsertIntoDevicePlaylistTracks, artistsMap, @@ -612,12 +588,9 @@ QString parseDeviceDB(mixxx::DbConnectionPoolPtr dbConnectionPool, TreeItem* dev audioFilesCount++; } break; case rekordbox_pdb_t::PAGE_TYPE_PLAYLIST_TREE: { - // Playlist tree node found, update map - rekordbox_pdb_t::playlist_tree_row_t* - playlistTree = static_cast< - rekordbox_pdb_t:: - playlist_tree_row_t*>( - (*rowRef)->body()); + auto* playlistTree = + static_cast( + rowRef->body()); playlistNameMap[playlistTree->id()] = getText(playlistTree->name()); @@ -632,6 +605,8 @@ QString parseDeviceDB(mixxx::DbConnectionPoolPtr dbConnectionPool, TreeItem* dev folderOrPlaylistFound = true; } break; default: + // we currently don't handle any other + // data, even though there is more. break; } } @@ -909,27 +884,21 @@ void readAnalyze(TrackPointer track, QList memoryCuesAndLoops; int lastHotCueIndex = 0; - for (std::vector::iterator section = - anlz.sections()->begin(); - section != anlz.sections()->end(); - ++section) { - switch ((*section)->fourcc()) { + for (const auto& section : *anlz.sections()) { + switch (section->fourcc()) { case rekordbox_anlz_t::SECTION_TAGS_BEAT_GRID: { if (!ignoreCues) { break; } - rekordbox_anlz_t::beat_grid_tag_t* beatGridTag = + auto* beatGridTag = static_cast( - (*section)->body()); + section->body()); QVector beats; - for (std::vector::iterator - beat = beatGridTag->beats()->begin(); - beat != beatGridTag->beats()->end(); - ++beat) { - int time = static_cast((*beat)->time()) - timingOffset; + for (const auto& beat : *beatGridTag->beats()) { + int time = static_cast(beat->time()) - timingOffset; // Ensure no offset times are less than 1 if (time < 1) { time = 1; @@ -948,15 +917,12 @@ void readAnalyze(TrackPointer track, break; } - rekordbox_anlz_t::cue_tag_t* cuesTag = + auto* cuesTag = static_cast( - (*section)->body()); + section->body()); - for (std::vector::iterator - cueEntry = cuesTag->cues()->begin(); - cueEntry != cuesTag->cues()->end(); - ++cueEntry) { - int time = static_cast((*cueEntry)->time()) - timingOffset; + for (const auto& cueEntry : *cuesTag->cues()) { + int time = static_cast(cueEntry->time()) - timingOffset; // Ensure no offset times are less than 1 if (time < 1) { time = 1; @@ -966,7 +932,7 @@ void readAnalyze(TrackPointer track, switch (cuesTag->type()) { case rekordbox_anlz_t::CUE_LIST_TYPE_MEMORY_CUES: { - switch ((*cueEntry)->type()) { + switch (cueEntry->type()) { case rekordbox_anlz_t::CUE_ENTRY_TYPE_MEMORY_CUE: { memory_cue_loop_t memoryCue; memoryCue.startPosition = position; @@ -975,7 +941,7 @@ void readAnalyze(TrackPointer track, memoryCuesAndLoops << memoryCue; } break; case rekordbox_anlz_t::CUE_ENTRY_TYPE_LOOP: { - int endTime = static_cast((*cueEntry)->loop_time()) - timingOffset; + int endTime = static_cast(cueEntry->loop_time()) - timingOffset; // Ensure no offset times are less than 1 if (endTime < 1) { endTime = 1; @@ -991,7 +957,7 @@ void readAnalyze(TrackPointer track, } } break; case rekordbox_anlz_t::CUE_LIST_TYPE_HOT_CUES: { - int hotCueIndex = static_cast((*cueEntry)->hot_cue() - 1); + int hotCueIndex = static_cast(cueEntry->hot_cue() - 1); if (hotCueIndex > lastHotCueIndex) { lastHotCueIndex = hotCueIndex; } @@ -1011,15 +977,12 @@ void readAnalyze(TrackPointer track, break; } - rekordbox_anlz_t::cue_extended_tag_t* cuesExtendedTag = + auto* cuesExtendedTag = static_cast( - (*section)->body()); + section->body()); - for (std::vector::iterator - cueExtendedEntry = cuesExtendedTag->cues()->begin(); - cueExtendedEntry != cuesExtendedTag->cues()->end(); - ++cueExtendedEntry) { - int time = static_cast((*cueExtendedEntry)->time()) - timingOffset; + for (const auto& cueExtendedEntry : *cuesExtendedTag->cues()) { + int time = static_cast(cueExtendedEntry->time()) - timingOffset; // Ensure no offset times are less than 1 if (time < 1) { time = 1; @@ -1029,20 +992,20 @@ void readAnalyze(TrackPointer track, switch (cuesExtendedTag->type()) { case rekordbox_anlz_t::CUE_LIST_TYPE_MEMORY_CUES: { - switch ((*cueExtendedEntry)->type()) { + switch (cueExtendedEntry->type()) { case rekordbox_anlz_t::CUE_ENTRY_TYPE_MEMORY_CUE: { memory_cue_loop_t memoryCue; memoryCue.startPosition = position; memoryCue.endPosition = mixxx::audio::kInvalidFramePos; - memoryCue.comment = toUnicode((*cueExtendedEntry)->comment()); + memoryCue.comment = toUnicode(cueExtendedEntry->comment()); memoryCue.color = colorFromID(static_cast( - (*cueExtendedEntry)->color_id())); + cueExtendedEntry->color_id())); memoryCuesAndLoops << memoryCue; } break; case rekordbox_anlz_t::CUE_ENTRY_TYPE_LOOP: { int endTime = static_cast( - (*cueExtendedEntry)->loop_time()) - + cueExtendedEntry->loop_time()) - timingOffset; // Ensure no offset times are less than 1 if (endTime < 1) { @@ -1053,14 +1016,14 @@ void readAnalyze(TrackPointer track, loop.startPosition = position; loop.endPosition = mixxx::audio::FramePos( sampleRateKhz * static_cast(endTime)); - loop.comment = toUnicode((*cueExtendedEntry)->comment()); - loop.color = colorFromID(static_cast((*cueExtendedEntry)->color_id())); + loop.comment = toUnicode(cueExtendedEntry->comment()); + loop.color = colorFromID(static_cast(cueExtendedEntry->color_id())); memoryCuesAndLoops << loop; } break; } } break; case rekordbox_anlz_t::CUE_LIST_TYPE_HOT_CUES: { - int hotCueIndex = static_cast((*cueExtendedEntry)->hot_cue() - 1); + int hotCueIndex = static_cast(cueExtendedEntry->hot_cue() - 1); if (hotCueIndex > lastHotCueIndex) { lastHotCueIndex = hotCueIndex; } @@ -1068,13 +1031,13 @@ void readAnalyze(TrackPointer track, position, mixxx::audio::kInvalidFramePos, hotCueIndex, - toUnicode((*cueExtendedEntry)->comment()), + toUnicode(cueExtendedEntry->comment()), mixxx::RgbColor(qRgb( static_cast( - (*cueExtendedEntry)->color_red()), + cueExtendedEntry->color_red()), static_cast( - (*cueExtendedEntry)->color_green()), - static_cast((*cueExtendedEntry) + cueExtendedEntry->color_green()), + static_cast(cueExtendedEntry ->color_blue())))); } break; }