From 480640f014921d3cf833c43b2d683e030eb62e74 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Wed, 30 Jul 2003 20:28:14 +0000 Subject: [PATCH] Added a tool for encoding/decoding Base64. --- doc/mkvmerge.1 | 2 +- src/Makefile.am | 9 ++- src/base64tool.cpp | 139 ++++++++++++++++++++++++++++++++++++++++ src/chapters.cpp | 154 +++++++++++++++++++++++++++++++++++++++++++++ src/chapters.h | 1 + src/mkvextract.cpp | 25 ++++++-- 6 files changed, 323 insertions(+), 7 deletions(-) create mode 100644 src/base64tool.cpp diff --git a/doc/mkvmerge.1 b/doc/mkvmerge.1 index c849527a1..3ad320f18 100644 --- a/doc/mkvmerge.1 +++ b/doc/mkvmerge.1 @@ -686,7 +686,7 @@ Matroska element. Otherwise the data is expected to be \fIBase64\fR encoded. This is an encoding that transforms binary data into a limited set of ASCII characters and is used e.g. in email programs. \fBmkvtoolnix\fR comes -with a utility, \fBbase64util\fR, that can be used to encode to and +with a utility, \fBbase64tool\fR, that can be used to encode to and decode from Base64. \fBmkvextract\fR will output Base64 encoded data for binary elements. diff --git a/src/Makefile.am b/src/Makefile.am index c788e3a13..7f5580130 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ AUTOMAKE_OPTIONS = dist-zip foreign -bin_PROGRAMS = mkvmerge mkvinfo mkvextract +bin_PROGRAMS = mkvmerge mkvinfo mkvextract base64tool mkvmerge_SOURCES = mkvmerge.cpp mkvmerge.h \ aac_common.cpp aac_common.h \ @@ -53,6 +53,11 @@ mkvextract_SOURCES = mkvextract.cpp \ mm_io.cpp mm_io.h \ tagwriter.cpp tagwriter.h +base64tool_SOURCES = base64tool.cpp \ + base64.cpp base64.h \ + common.cpp common.h \ + mm_io.cpp mm_io.h + mkvmerge_LDADD = @AVILIB_LIBS@ @PROFILING_LIBS@ \ @MATROSKA_LIBS@ @EBML_LIBS@ @VORBIS_LIBS@ @OGG_LIBS@ \ @ICONV_LIBS@ @MINGW_LIBS@ @EXPAT_LIBS@ -lexpat @@ -63,3 +68,5 @@ mkvinfo_LDADD = @PROFILING_LIBS@ @MATROSKA_LIBS@ @EBML_LIBS@ @ICONV_LIBS@ \ mkvextract_LDADD = @AVILIB_LIBS@ @PROFILING_LIBS@ @MATROSKA_LIBS@ @EBML_LIBS@ \ @VORBIS_LIBS@ @OGG_LIBS@ @ICONV_LIBS@ +base64tool_LDADD = @EBML_LIBS@ @ICONV_LIBS@ + diff --git a/src/base64tool.cpp b/src/base64tool.cpp new file mode 100644 index 000000000..b8d0208a5 --- /dev/null +++ b/src/base64tool.cpp @@ -0,0 +1,139 @@ +/* + base64util - Utility for encoding and decoding Base64 files. + + base64util.cpp + + Written by Moritz Bunkus + + Distributed under the GPL + see the file COPYING for details + or visit http://www.gnu.org/copyleft/gpl.html +*/ + +/*! + \file + \version $Id$ + \brief command line parameter parsing, looping, output handling + \author Moritz Bunkus +*/ + +#include +#include + +#include + +#include "os.h" + +#include "base64.h" +#include "common.h" +#include "mm_io.h" + +using namespace std; + +void usage(int retval) { + mxprint(stdout, + "base64util [maxlen]\n" + "\n" + " encode - Read from , encode to Base64 and write to .\n" + " Max line length can be specified and is 72 if left out.\n" + " decode - Read from , decode to binary and write to .\n"); + + exit(retval); +} + +int main(int argc, char *argv[]) { + int maxlen; + int64_t size; + unsigned char *buffer; + char mode; + string s, line; + mm_io_c *in, *out; + mm_text_io_c *intext; + + if (argc < 4) + usage(0); + + if (!strcmp(argv[1], "encode")) + mode = 'e'; + else if (!strcmp(argv[1], "decode")) + mode = 'd'; + else { + mxprint(stdout, "Error: invalid mode '%s'.\n\n", argv[1]); + usage(1); + } + + maxlen = 72; + if ((argc == 5) && (mode == 'e')) { + if (!parse_int(argv[4], maxlen) || (maxlen < 4)) { + mxprint(stdout, "Error: Max line length must be >= 4.\n\n"); + exit(1); + } + } else if ((argc > 5) || ((argc > 4) && (mode == 'd'))) + usage(1); + + maxlen = ((maxlen + 3) / 4) * 4; + + try { + if (mode == 'e') + in = new mm_io_c(argv[2], MODE_READ); + else { + intext = new mm_text_io_c(argv[2]); + in = intext; + } + } catch(...) { + mxprint(stdout, "Error: Could not open '%s' for reading (%d, %s).\n", + argv[2], errno, strerror(errno)); + exit(1); + } + + try { + out = new mm_io_c(argv[3], MODE_WRITE); + } catch(...) { + mxprint(stdout, "Error: Could not open '%s' for writing (%d, %s).\n", + argv[3], errno, strerror(errno)); + exit(1); + } + + in->save_pos(); + in->setFilePointer(0, seek_end); + size = in->getFilePointer(); + in->restore_pos(); + + if (mode == 'e') { + buffer = (unsigned char *)safemalloc(size); + size = in->read(buffer, size); + delete in; + + s = base64_encode(buffer, size, true, maxlen); + safefree(buffer); + + out->write(s.c_str(), s.length()); + delete out; + + } else { + + while (intext->getline2(line)) { + strip(line); + s += line; + } + delete intext; + + buffer = (unsigned char *)safemalloc(s.length() / 4 * 3 + 100); + try { + size = base64_decode(s, buffer); + } catch(...) { + mxprint(stdout, "Error decoding data.\n"); + delete in; + delete out; + exit(1); + } + out->write(buffer, size); + + safefree(buffer); + delete out; + } + + mxprint(stdout, "Done.\n"); + + return 0; +} diff --git a/src/chapters.cpp b/src/chapters.cpp index 1b12e9615..4761d247b 100644 --- a/src/chapters.cpp +++ b/src/chapters.cpp @@ -339,3 +339,157 @@ void write_chapters_xml(KaxChapters *chapters, FILE *out) { } } } + +class chapter_entry_c { +public: + string name; + int64_t start, level; + + chapter_entry_c(string n, int64_t s, int64_t l); + + bool operator < (const chapter_entry_c &cmp) const; +}; + +chapter_entry_c::chapter_entry_c(string n, int64_t s, int64_t l) { + name = n; + start = s; + level = l; +} + +bool chapter_entry_c::operator <(const chapter_entry_c &cmp) const { + return start < cmp.start; +} + +static vector chapter_start_times, chapter_names, + chapter_entries; + +static void handle_name(int level, const char *name) { + int i, j; + vector::iterator itr; + + for (i = 0; i < chapter_start_times.size(); i++) { + chapter_entry_c &e = chapter_start_times[i]; + if (e.level == level) { + chapter_entries.push_back(chapter_entry_c(string(name), e.start, level)); + itr = chapter_start_times.begin(); + for (j = 0; j < i; j++) + itr++; + chapter_start_times.erase(itr); + return; + } + } + + chapter_names.push_back(chapter_entry_c(name, 0, level)); +} + +static void handle_start_time(int level, int64_t start_time) { + int i, j; + vector::iterator itr; + + for (i = 0; i < chapter_names.size(); i++) { + chapter_entry_c &e = chapter_names[i]; + if (e.level == level) { + chapter_entries.push_back(chapter_entry_c(e.name, start_time, level)); + itr = chapter_names.begin(); + for (j = 0; j < i; j++) + itr++; + chapter_names.erase(itr); + return; + } + } + + chapter_start_times.push_back(chapter_entry_c("", start_time, level)); +} + +static void write_chapter_atom_simple(KaxChapterAtom *atom, int level); + +static void write_chapter_display_simple(KaxChapterDisplay *display, + int level) { + int i; + EbmlElement *e; + char *s; + + for (i = 0; i < display->ListSize(); i++) { + e = (*display)[i]; + if (is_id(KaxChapterString)) { + s = UTFstring_to_cstrutf8(UTFstring(*static_cast + (e)).c_str()); + handle_name(level - 1, s); + safefree(s); + + } else if (is_id(KaxChapterAtom)) + write_chapter_atom_simple((KaxChapterAtom *)e, level + 1); + + } +} + +static void write_chapter_track_simple(KaxChapterTrack *track, int level) { + int i; + EbmlElement *e; + + for (i = 0; i < track->ListSize(); i++) { + e = (*track)[i]; + + if (is_id(KaxChapterAtom)) + write_chapter_atom_simple((KaxChapterAtom *)e, level + 1); + + } +} + +static void write_chapter_atom_simple(KaxChapterAtom *atom, int level) { + int i; + EbmlElement *e; + uint64_t v; + + for (i = 0; i < atom->ListSize(); i++) { + e = (*atom)[i]; + + if (is_id(KaxChapterTimeStart)) { + v = uint64(*static_cast(e)) / 1000000; + handle_start_time(level, level); + + } else if (is_id(KaxChapterTrack)) + write_chapter_track_simple((KaxChapterTrack *)e, level + 1); + + else if (is_id(KaxChapterDisplay)) + write_chapter_display_simple((KaxChapterDisplay *)e, level + 1); + + else if (is_id(KaxChapterAtom)) + write_chapter_atom_simple((KaxChapterAtom *)e, level + 1); + + } +} + +void write_chapters_simple(int &chapter_num, KaxChapters *chapters, + FILE *out) { + int i, j; + int64_t v; + KaxEditionEntry *edition; + + chapter_start_times.clear(); + chapter_names.clear(); + chapter_entries.clear(); + + for (i = 0; i < chapters->ListSize(); i++) { + if (is_id2((*chapters)[i], KaxEditionEntry)) { + edition = (KaxEditionEntry *)(*chapters)[i]; + for (j = 0; j < edition->ListSize(); j++) + if (is_id2((*edition)[j], KaxChapterAtom)) + write_chapter_atom_simple((KaxChapterAtom *)(*edition)[j], 2); + } + } + + for (i = 0; i < chapter_entries.size(); i++) { + v = chapter_entries[i].start; + mxprint(out, "CHAPTER%02d=%02lld:%02lld:%02lld.%03lld\n", chapter_num, + (v / 1000 / 60 / 60), (v / 1000 / 60) % 60, + (v / 1000) % 60, v % 1000); + mxprint(out, "CHAPTER%02dNAME=%s\n", chapter_num, + chapter_entries[i].name.c_str()); + chapter_num++; + } + + chapter_start_times.clear(); + chapter_names.clear(); + chapter_entries.clear(); +} diff --git a/src/chapters.h b/src/chapters.h index 70e621176..392acbacd 100644 --- a/src/chapters.h +++ b/src/chapters.h @@ -31,6 +31,7 @@ KaxChapters *parse_chapters(const char *file_name, int64_t min_tc = 0, int64_t max_tc = -1, int64_t offset = 0); void write_chapters_xml(KaxChapters *chapters, FILE *out); +void write_chapters_simple(int &chapter_num, KaxChapters *chapters, FILE *out); #endif // __CHAPTERS_H diff --git a/src/mkvextract.cpp b/src/mkvextract.cpp index cb466b4fe..61770f9ee 100644 --- a/src/mkvextract.cpp +++ b/src/mkvextract.cpp @@ -137,6 +137,7 @@ typedef struct { } kax_track_t; vector tracks; +bool chapter_format_simple = false; kax_track_t *find_track(int tid) { int i; @@ -176,9 +177,11 @@ void usage() { " Third mode extracts attachments from inname.\n" " AID:outname Write the attachment with the ID AID to 'outname'.\n" "\n" -" Second mode extracts the chapters and converts them to XML. The output is\n" +" Fourth mode extracts the chapters and converts them to XML. The output is\n" " written to the standard output. The output can be used as a source\n" " for mkvmerge.\n" +" -s, --simple Exports the chapter infomartion in the simple format\n" +" used in OGM tools (CHAPTER01=... CHAPTER01NAME=...).\n" "\n" " These options can be used instead of the mode keyword to obtain\n" " further information:\n" @@ -251,11 +254,19 @@ void parse_args(int argc, char **argv, char *&file_name, int &mode) { conv_handle = utf8_init(argv[i + 1]); i++; - } else if ((mode == MODE_TAGS) || (mode == MODE_CHAPTERS)) { + } else if (mode == MODE_TAGS) { mxprint(stderr, "Error: No further options allowed when extracting " "%s.\n", argv[1]); exit(1); + } else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--simple")) { + if (mode != MODE_CHAPTERS) { + mxprint(stderr, "Error: %s is only allowed for chapter extraction.\n", + argv[i]); + exit(1); + } + chapter_format_simple = true; + } else { copy = safestrdup(argv[i]); colon = strchr(copy, ':'); @@ -1580,6 +1591,7 @@ void extract_chapters(const char *file_name) { EbmlStream *es; mm_io_c *in; bool chapters_extracted = false; + int next_chapter = 1; // open input file try { @@ -1640,13 +1652,16 @@ void extract_chapters(const char *file_name) { chapters.Read(*es, KaxChapters::ClassInfos.Context, upper_lvl_el, l2, true); - if (!chapters_extracted) { + if (!chapters_extracted && !chapter_format_simple) { mxprint(stdout, "\n\n" "\n"); chapters_extracted = true; } - write_chapters_xml(&chapters, stdout); + if (chapter_format_simple) + write_chapters_simple(next_chapter, &chapters, stdout); + else + write_chapters_xml(&chapters, stdout); } else upper_lvl_el = 0; @@ -1683,7 +1698,7 @@ void extract_chapters(const char *file_name) { return; } - if (chapters_extracted) + if (chapters_extracted && !chapter_format_simple) mxprint(stdout, "\n"); }