diff --git a/CMakeLists.txt b/CMakeLists.txt index d97c2480..8267ba5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,8 @@ if(DEV_MODE) add_cxx_flag_if_supported(-Wno-error=unused-command-line-argument -Wall -Wextra -Wpedantic -Wfatal-errors -fstack-protector-strong -Wno-self-assign - -Wcast-align) + -Wcast-align + -W4) endif() set(libmatroska_SOURCES @@ -100,6 +101,10 @@ if(NOT BUILD_SHARED_LIBS) endif() if(BUILD_EXAMPLES) + add_executable(mkvtree test/mkvtree/mkvtree.cpp) + target_link_libraries(mkvtree matroska) + target_include_directories(mkvtree PRIVATE $) + add_executable(test00 test/ebml/test00.cpp) target_link_libraries(test00 matroska) target_include_directories(test00 PRIVATE $) diff --git a/test/Makefile b/test/Makefile deleted file mode 100644 index a99616f4..00000000 --- a/test/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -## libmatroska Makefile -## (c) 2002 Steve Lhomme -## This software is licensed under the LGPL, see LICENSE.LGPL - -all: endian - echo "Making endianess tests" - (cd endian; make) -clean: - echo "Cleaning all tests" - (cd endian; make $@) diff --git a/test/mkvtree/mkvtree.cpp b/test/mkvtree/mkvtree.cpp new file mode 100644 index 00000000..ee0e88d8 --- /dev/null +++ b/test/mkvtree/mkvtree.cpp @@ -0,0 +1,306 @@ +// Copyright © 2008-2024 Matroska (non-profit organisation). +// SPDX-License-Identifier: BSD-3-Clause + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace libebml; + +#ifdef _WIN32 +# if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define localtime(x) nullptr +# endif // UWP, Xbox, etc +#endif // win32 + +static int ShowPos = 0; + +#if 0 // TODO allowing find top level elements using an EbmlSemanticContextMaster +DEFINE_START_SEMANTIC(KaxStream) +DEFINE_SEMANTIC_ITEM(true, true, libebml::EbmlHead) +DEFINE_SEMANTIC_ITEM(true, true, libmatroska::KaxSegment) +DEFINE_END_SEMANTIC(KaxStream) + +DEFINE_xxx_CONTEXT(KaxStream,libmatroska::GetKaxGlobal_Context) +#endif + +struct datepack_t +{ + intptr_t Year; // 2001, 2007, etc + intptr_t Month; // 1-12 + intptr_t Day; // 1-31 + intptr_t Hour; // 24-hour + intptr_t Minute; // 0-59 + intptr_t Second; // 0-59 + intptr_t WeekDay; // 1 = Sunday, 2 = Monday, etc +}; + + +static void EndLine(EbmlElement *Element) +{ + if (ShowPos && (Element)->GetElementPosition()!=INVALID_FILEPOS_T) + fprintf(stdout," at %" PRId64 "\r\n",(Element)->GetElementPosition()); + else + fprintf(stdout,"\r\n"); +} + +static EbmlElement *OutputElement(EbmlElement *Element, const EbmlSemanticContext *Context, EbmlStream *Input, unsigned int *Level) +{ + assert(*Level < 10); // safety check + unsigned int LevelPrint; + for (LevelPrint=0;LevelPrint<*Level;++LevelPrint) + fprintf(stdout,"+ "); + fprintf(stdout,"%s: ", EBML_NAME(Element)); + if (dynamic_cast(Element) != nullptr) + { + int UpperElement = 0; + EbmlElement *SubElement,*NewElement; + // EbmlSemanticContext SubContext; + + if (Element->GetSize() == INVALID_FILEPOS_T) + fprintf(stdout,"(master)"); + else if (!(Element)->IsFiniteSize()) + fprintf(stdout,"(master) [unknown size]"); + else + fprintf(stdout,"(master) [%" PRId64 " bytes]",Element->GetSize()); + EndLine(Element); + // SubContext.UpContext = Context; + // SubContext.Context = EBML_CONTEXT(Element); + // SubContext.EndPosition = Element->GetEndPosition(); + // SubContext.Profile = Context->Profile; + // SubElement = EBML_FindNextElement(Input, &SubContext, &UpperElement, 1); + SubElement = Input->FindNextElement(EBML_CONTEXT(Element), UpperElement, UINT64_MAX, true); + while (SubElement != NULL && UpperElement<=0 && (!(Element)->IsFiniteSize() || (SubElement)->GetElementPosition() <= Element->GetEndPosition())) + { + // a sub element == not higher level and contained inside the current element + (*Level)++; + NewElement = OutputElement(SubElement, &EBML_CONTEXT(Element), Input, Level); + delete SubElement; + if (NewElement) + SubElement = NewElement; + else + { + if ((Element)->IsFiniteSize() && Input->I_O().getFilePointer() >= Element->GetEndPosition()) + { + SubElement = nullptr; + // break; + } + else + SubElement = Input->FindNextElement(EBML_CONTEXT(Element), UpperElement, UINT64_MAX, true); + } + assert(*Level > 0); + (*Level)--; + + if (UpperElement < 0) + { + // global element + // assert(*Level + UpperElement >= 0); + // *Level += UpperElement; + } + else if (UpperElement != 0 && *Level>0) + { + assert((int)*Level >= UpperElement-1); + *Level -= UpperElement-1; + } + } + return SubElement; + //EBML_ElementSkipData(Element, Input, Element->Context, NULL, 0); + } + else if (dynamic_cast(Element) != nullptr) + { + fprintf(stdout,"[%" PRId64 " bytes]",Element->GetSize()); + Element->SkipData(*Input, *Context, nullptr, 0); + EndLine(Element); + } + else if (dynamic_cast(Element) != nullptr) + { + // TODO: handle crc32 + fprintf(stdout,"[%" PRId64 " bytes]",Element->GetSize()); + Element->SkipData(*Input, *Context, nullptr, 0); + EndLine(Element); + } + else if (dynamic_cast(Element) != nullptr || dynamic_cast(Element) != nullptr) + { + //tchar_t UnicodeString[MAXDATA]; + if (Element->ReadData(Input->I_O(),SCOPE_ALL_DATA) != INVALID_FILEPOS_T) + { + if (dynamic_cast(Element) != nullptr) + fprintf(stdout,"'%s'",static_cast(Element)->GetValue().c_str()); + else + fprintf(stdout,"'%s'",static_cast(Element)->GetValueUTF8().c_str()); + } else + fprintf(stdout,""); + EndLine(Element); + } + else if (dynamic_cast(Element) != nullptr) + { + if (Element->ReadData(Input->I_O(),SCOPE_ALL_DATA) != INVALID_FILEPOS_T) + { + auto DateTime = static_cast(Element)->GetEpochDate(); + datepack_t Date; + time_t ot; + struct tm *date; + + ot = DateTime; + date = localtime(&ot); + + if (date) + { + Date.Second = date->tm_sec; + Date.Minute = date->tm_min; + Date.Hour = date->tm_hour; + Date.Day = date->tm_mday; + Date.Month = date->tm_mon + 1; + Date.Year = date->tm_year + 1900; + Date.WeekDay = date->tm_wday + 1; + } + + fprintf(stdout,"%04" PRIdPTR "-%02" PRIdPTR "-%02" PRIdPTR " %02" PRIdPTR ":%02" PRIdPTR ":%02" PRIdPTR " UTC",Date.Year,Date.Month,Date.Day,Date.Hour,Date.Minute,Date.Second); + } + else + fprintf(stdout,""); + EndLine(Element); + } + else if (dynamic_cast(Element) != nullptr || dynamic_cast(Element) != nullptr) + { + if (Element->ReadData(Input->I_O(),SCOPE_ALL_DATA) != INVALID_FILEPOS_T) + { + if (dynamic_cast(Element) != nullptr) + fprintf(stdout,"%" PRId64,static_cast(Element)->GetValue()); + else + fprintf(stdout,"%" PRIu64,static_cast(Element)->GetValue()); + } + else + fprintf(stdout,""); + EndLine(Element); + } + else if (dynamic_cast(Element) != nullptr) + { + if (Element->ReadData(Input->I_O(),SCOPE_ALL_DATA) != INVALID_FILEPOS_T) + fprintf(stdout,"%f",static_cast(Element)->GetValue()); + else + fprintf(stdout,""); + EndLine(Element); + } + else if (Element->IsDummy()) + { + fprintf(stdout,"[%X] [%" PRId64 " bytes]",static_cast(Element)->GetClassId().GetValue(),Element->GetSize()); + Element->SkipData(*Input, *Context, nullptr, 0); + EndLine(Element); + } + else if (dynamic_cast(Element) != nullptr) + { + if (Element->ReadData(Input->I_O(),SCOPE_PARTIAL_DATA) != INVALID_FILEPOS_T) + { + const uint8_t *Data = static_cast(Element)->GetBuffer(); + if (Element->GetSize() != 0) + { + if (Data==NULL) + fprintf(stdout,"[data too large] (%" PRId64 ")",Element->GetSize()); + else if (Element->GetSize() == 1) + fprintf(stdout,"%02X (%" PRId64 ")",Data[0],Element->GetSize()); + else if (Element->GetSize() == 2) + fprintf(stdout,"%02X %02X (%" PRId64 ")",Data[0],Data[1],Element->GetSize()); + else if (Element->GetSize() == 3) + fprintf(stdout,"%02X %02X %02X (%" PRId64 ")",Data[0],Data[1],Data[2],Element->GetSize()); + else if (Element->GetSize() == 4) + fprintf(stdout,"%02X %02X %02X %02X (%" PRId64 ")",Data[0],Data[1],Data[2],Data[3],Element->GetSize()); + else + fprintf(stdout,"%02X %02X %02X %02X.. (%" PRId64 ")",Data[0],Data[1],Data[2],Data[3],Element->GetSize()); + } + } + else + fprintf(stdout,""); + Element->SkipData(*Input, *Context, nullptr, 0); + EndLine(Element); + } + else + { +#ifdef IS_BIG_ENDIAN + fprintf(stdout,"\r\n",EBML_ElementClassID(Element)); +#else + auto Id = Element->GetClassId().GetValue(); + fprintf(stdout,">= 8; + } + fprintf(stdout,"%X>",Element->GetClassId().GetValue()); +#endif + Element->SkipData(*Input, *Context, nullptr, 0); + EndLine(Element); + } + return NULL; +} + +static void OutputEbmlhead(EbmlStream *Input) +{ + EbmlElement *Element = Input->FindNextID(UINT64_MAX); + if (Element) + { + unsigned int Level = 0; + OutputElement(Element, NULL, Input, &Level); + delete Element; + } +} + +static void OutputTree(EbmlStream *Input) +{ + EbmlElement *Element = Input->FindNextID(UINT64_MAX); + if (Element) + { + unsigned int Level = 0; + OutputElement(Element, NULL, Input, &Level); + delete Element; + } +} + +int main(int argc, const char *argv[]) +{ + EbmlStream *Input; + + if ((argc!=2 && argc!=3) || (argc==3 && strcmp(argv[1],"--pos"))) + { + fprintf(stderr, "Usage: mkvtree --pos [matroska_file]\r\n"); + fprintf(stderr, "Options:\r\n"); + fprintf(stderr, " --pos output the position of elements\r\n"); + return 1; + } + + if (argc==3) + ShowPos = 1; + + // open the file to parse + Input = new EbmlStream(*(new StdIOCallback(argv[argc-1], MODE_READ))); + if (Input == NULL) + fprintf(stderr, "error: mkvtree cannot open file \"%s\"\r\n",argv[1]); + else + { + OutputEbmlhead(Input); + OutputTree(Input); + + Input->I_O().close(); + delete &(Input->I_O()); + delete Input; + } + + return 0; +}