aboutsummaryrefslogtreecommitdiff
path: root/Src/nsmkv
diff options
context:
space:
mode:
Diffstat (limited to 'Src/nsmkv')
-rw-r--r--Src/nsmkv/Attachments.cpp108
-rw-r--r--Src/nsmkv/Attachments.h47
-rw-r--r--Src/nsmkv/Chapters.cpp9
-rw-r--r--Src/nsmkv/Chapters.h22
-rw-r--r--Src/nsmkv/Cluster.cpp213
-rw-r--r--Src/nsmkv/Cluster.h106
-rw-r--r--Src/nsmkv/Cues.cpp205
-rw-r--r--Src/nsmkv/Cues.h76
-rw-r--r--Src/nsmkv/Lacing.cpp111
-rw-r--r--Src/nsmkv/Lacing.h19
-rw-r--r--Src/nsmkv/SeekTable.cpp230
-rw-r--r--Src/nsmkv/SeekTable.h54
-rw-r--r--Src/nsmkv/SegmentInfo.cpp235
-rw-r--r--Src/nsmkv/SegmentInfo.h107
-rw-r--r--Src/nsmkv/Tags.cpp202
-rw-r--r--Src/nsmkv/Tags.h32
-rw-r--r--Src/nsmkv/Tracks.cpp561
-rw-r--r--Src/nsmkv/Tracks.h292
-rw-r--r--Src/nsmkv/ebml_float.cpp52
-rw-r--r--Src/nsmkv/ebml_float.h6
-rw-r--r--Src/nsmkv/ebml_signed.cpp15
-rw-r--r--Src/nsmkv/ebml_signed.h5
-rw-r--r--Src/nsmkv/ebml_unsigned.cpp13
-rw-r--r--Src/nsmkv/ebml_unsigned.h6
-rw-r--r--Src/nsmkv/file_mkv_reader.cpp55
-rw-r--r--Src/nsmkv/file_mkv_reader.h22
-rw-r--r--Src/nsmkv/global_elements.cpp29
-rw-r--r--Src/nsmkv/global_elements.h15
-rw-r--r--Src/nsmkv/header.cpp138
-rw-r--r--Src/nsmkv/header.h79
-rw-r--r--Src/nsmkv/main.cpp226
-rw-r--r--Src/nsmkv/mkv_date.cpp8
-rw-r--r--Src/nsmkv/mkv_date.h5
-rw-r--r--Src/nsmkv/mkv_reader.h43
-rw-r--r--Src/nsmkv/nsmkv.h10
-rw-r--r--Src/nsmkv/nsmkv.sln19
-rw-r--r--Src/nsmkv/nsmkv.vcxproj254
-rw-r--r--Src/nsmkv/nsmkv.vcxproj.filters132
-rw-r--r--Src/nsmkv/read.cpp154
-rw-r--r--Src/nsmkv/read.h18
-rw-r--r--Src/nsmkv/segment.h14
-rw-r--r--Src/nsmkv/vint.cpp91
-rw-r--r--Src/nsmkv/vint.h14
43 files changed, 4052 insertions, 0 deletions
diff --git a/Src/nsmkv/Attachments.cpp b/Src/nsmkv/Attachments.cpp
new file mode 100644
index 00000000..5f1f8edd
--- /dev/null
+++ b/Src/nsmkv/Attachments.cpp
@@ -0,0 +1,108 @@
+#include "Attachments.h"
+#include "read.h"
+#include "global_elements.h"
+
+static uint64_t ReadAttachedFile(nsmkv::MKVReader *reader, uint64_t size, nsmkv::AttachedFile &attached_file)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_attachments_filename:
+ {
+ char *utf8 = 0;
+ if (read_utf8(reader, node.size, &utf8) == 0)
+ return 0;
+ if (utf8)
+ printf("Filename: %s\n", utf8);
+ attached_file.Own(attached_file.filename, utf8);
+ }
+ break;
+ case mkv_attachments_filemimetype:
+ {
+ char *utf8 = 0;
+ if (read_utf8(reader, node.size, &utf8) == 0)
+ return 0;
+ if (utf8)
+ printf("File MIME Type: %s\n", utf8);
+ attached_file.Own(attached_file.mime_type, utf8);
+ }
+ break;
+ case mkv_attachments_filedata:
+ {
+ printf("File Data: binary size %I64u\n", node.size);
+ reader->Skip(node.size);
+ }
+ break;
+ case mkv_attachments_fileuid:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+ printf("File UID: %I64x\n", val);
+ attached_file.file_uid = val;
+ }
+ break;
+ default:
+ nsmkv::ReadGlobal(reader, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
+uint64_t nsmkv::ReadAttachment(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Attachments &attachments)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_attachments_attachedfile:
+ {
+ printf("Attachmented File\\n");
+ nsmkv::AttachedFile attached_file;
+ ReadAttachedFile(reader, node.size, attached_file);
+ }
+ break;
+ default:
+ ReadGlobal(reader, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
diff --git a/Src/nsmkv/Attachments.h b/Src/nsmkv/Attachments.h
new file mode 100644
index 00000000..8fe7e42d
--- /dev/null
+++ b/Src/nsmkv/Attachments.h
@@ -0,0 +1,47 @@
+#pragma once
+#include <bfc/platform/types.h>
+#include "mkv_reader.h"
+
+// Attachments
+const uint32_t mkv_segment_attachments = 0x941a469;
+const uint32_t mkv_attachments_attachedfile = 0x21a7;
+const uint32_t mkv_attachments_filename = 0x66e;
+const uint32_t mkv_attachments_filemimetype = 0x660;
+const uint32_t mkv_attachments_filedata =0x65c;
+const uint32_t mkv_attachments_fileuid = 0x6ae;
+
+namespace nsmkv
+{
+ class AttachedFile
+ {
+ public:
+ AttachedFile()
+ {
+ file_uid=0;
+ filename=0;
+ mime_type=0;
+ }
+ ~AttachedFile()
+ {
+ free(filename);
+ free(mime_type);
+ }
+ void Own(char *&field, char *value)
+ {
+ if (field)
+ free(field);
+ field = value;
+ }
+
+ uint64_t file_uid;
+ char *filename;
+ char *mime_type;
+ };
+
+ class Attachments
+ {
+ public:
+ };
+
+ uint64_t ReadAttachment(MKVReader *reader, uint64_t size, nsmkv::Attachments &attachments);
+} \ No newline at end of file
diff --git a/Src/nsmkv/Chapters.cpp b/Src/nsmkv/Chapters.cpp
new file mode 100644
index 00000000..678f04ed
--- /dev/null
+++ b/Src/nsmkv/Chapters.cpp
@@ -0,0 +1,9 @@
+#include "Chapters.h"
+
+nsmkv::Chapters::Chapters(void)
+{
+}
+
+nsmkv::Chapters::~Chapters(void)
+{
+}
diff --git a/Src/nsmkv/Chapters.h b/Src/nsmkv/Chapters.h
new file mode 100644
index 00000000..589d2ea7
--- /dev/null
+++ b/Src/nsmkv/Chapters.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <bfc/platform/types.h>
+#include <stdio.h>
+
+// IDs
+// these are slightly different from the matroska spec because we specify
+// values after vint decoding and they specify before
+const uint32_t mkv_segment_chapters = 0x43a770;
+
+namespace nsmkv
+{
+ class Chapters
+ {
+ public:
+ Chapters(void);
+ ~Chapters(void);
+ };
+
+ // returns bytes read. 0 means EOF
+ uint64_t ReadChaptersInfo(FILE *f, uint64_t size, nsmkv::Chapters &chapters);
+};
diff --git a/Src/nsmkv/Cluster.cpp b/Src/nsmkv/Cluster.cpp
new file mode 100644
index 00000000..94949138
--- /dev/null
+++ b/Src/nsmkv/Cluster.cpp
@@ -0,0 +1,213 @@
+#include "Cluster.h"
+#include "read.h"
+#include "global_elements.h"
+#include <winsock.h>
+
+// returns bytes read. 0 means EOF
+uint64_t nsmkv::ReadBlockGroup(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Block &block, uint64_t *allowed_track_numbers, size_t num_allowed_track_numbers)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ uint64_t ebml_start_position = reader->Tell(); // need this for recording the block binary start position
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_blockgroup_referenceblock:
+ {
+ int64_t val;
+ if (read_signed(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Reference Block: %I64d\n", val);
+#endif
+ block.reference_block = val;
+ }
+ break;
+ case mkv_blockgroup_block:
+ {
+#ifdef WA_VALIDATE
+ printf(" Block: binary size %I64u\n", node.size);
+#endif
+ if (ReadBlockBinary(reader, node.size, block.binary, allowed_track_numbers, num_allowed_track_numbers) == 0)
+ return 0;
+ }
+ break;
+ case mkv_blockgroup_blockduration:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Block Duration: %I64u\n", val);
+#endif
+ block.block_duration = val;
+ }
+ break;
+ default:
+ nsmkv::ReadGlobal(reader, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
+// returns bytes read. 0 means EOF
+uint64_t nsmkv::ReadCluster(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Cluster &cluster)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_cluster_timecode:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Time Code: %I64u\n", val);
+ cluster.time_code_found = true;
+#endif
+ cluster.time_code = val;
+ }
+ break;
+ case mkv_cluster_blockgroup:
+ {
+#ifdef WA_VALIDATE
+ printf(" Block Group\n");
+#endif
+ Block block;
+ if (ReadBlockGroup(reader, node.size, block, 0, 0) == 0)
+ return 0;
+ }
+ break;
+ case mkv_cluster_simpleblock:
+ {
+#ifdef WA_VALIDATE
+ printf(" Simple Block, size: %I64u\n", node.size);
+#endif
+ BlockBinary bbinary;
+ if (ReadBlockBinary(reader, node.size, bbinary, 0, 0) == 0)
+ return 0;
+// fseek64(f, node.size, SEEK_CUR);
+ }
+ break;
+ default:
+ ReadGlobal(reader, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
+
+uint64_t nsmkv::ReadBlockBinary(nsmkv::MKVReader *reader, uint64_t size, nsmkv::BlockBinary &binary, uint64_t *allowed_track_numbers, size_t num_allowed_track_numbers)
+{
+ uint64_t orig_size = size;
+ size_t bytes_read = (size_t)read_vint(reader, &binary.track_number);
+ if (!bytes_read)
+ return 0;
+ size-=bytes_read;
+ bool allowed_track=false;
+ if (num_allowed_track_numbers == (size_t)-1)
+ {
+ allowed_track=true;
+ }
+ else
+ {
+ for (size_t i=0;i!=num_allowed_track_numbers;i++)
+ {
+ if (binary.track_number == allowed_track_numbers[i])
+ {
+ allowed_track=true;
+ break;
+ }
+ }
+ }
+#ifdef WA_VALIDATE
+ // if we are validating the file, force all tracks to go thru
+ allowed_track = true;
+#endif
+ if (allowed_track && size >= 3)
+ {
+ uint16_t time_code_endian;
+ size_t bytes_read;
+ reader->Read(&time_code_endian, sizeof(uint16_t), &bytes_read);
+ if (bytes_read != sizeof(uint16_t))
+ return 0;
+ binary.time_code = htons(time_code_endian);
+ size-=sizeof(uint16_t);
+
+ uint8_t flags;
+ reader->Read(&flags, 1, &bytes_read);
+ if (bytes_read != 1)
+ return 0;
+
+ binary.flags = flags;
+ size -= sizeof(uint8_t);
+
+ binary.data_size = (size_t)size;
+#ifndef WA_VALIDATE
+ binary.data = malloc((size_t)size);
+ if (!binary.data)
+ return 0;
+ reader->Read(binary.data, (size_t)size, &bytes_read);
+ if (bytes_read != size)
+ {
+ free(binary.data);
+ return 0;
+ }
+#else
+ printf(" track number = %I64u\n",binary.track_number);
+ printf(" timecode = %u\n",binary.time_code);
+ printf(" flags = %u\n",binary.flags);
+ printf(" data_size = %I64u\n",size);
+ // if we are validating the nmk file we don't need to
+ // actually read the data, just skip past it
+ //fseek(reader, size, SEEK_CUR);
+#endif
+ return orig_size;
+ }
+ else
+ {
+ reader->Skip(size);
+ return orig_size;
+ }
+ return 0;
+}
+
diff --git a/Src/nsmkv/Cluster.h b/Src/nsmkv/Cluster.h
new file mode 100644
index 00000000..4458c776
--- /dev/null
+++ b/Src/nsmkv/Cluster.h
@@ -0,0 +1,106 @@
+#pragma once
+#include "mkv_reader.h"
+#include <bfc/platform/types.h>
+
+const uint32_t mkv_segment_cluster = 0xf43b675;
+const uint32_t mkv_cluster_timecode = 0x67;
+const uint32_t mkv_cluster_blockgroup = 0x20;
+const uint32_t mkv_cluster_simpleblock = 0x23;
+const uint32_t mkv_blockgroup_referenceblock = 0x7b;
+const uint32_t mkv_blockgroup_block = 0x21;
+const uint32_t mkv_blockgroup_blockduration = 0x1b;
+
+/* TODO: benski>
+need to think this whole thing through.
+Ideally, we would be able to enumerate through the clusters/blocks,
+but the output plugin might have a different audio buffer size
+than the size that the encoder assumed.
+so the first attempt is going to be random access
+and we'll see if there is a better way after implementation
+*/
+namespace nsmkv
+{
+
+ class BlockBinary
+ {
+ public:
+ BlockBinary()
+ {
+ data=0;
+ data_size=0;
+ track_number=0;
+ flags=0;
+ time_code=0;
+ }
+ ~BlockBinary()
+ {
+ free(data);
+ }
+ uint64_t track_number;
+ int16_t time_code;
+ uint8_t flags;
+ size_t data_size;
+ void *data; // maybe optionally allow the user to pass in the buffer to reduce mallocs
+
+ enum
+ {
+ LACE_MASK = 0x6,
+ XIPH_LACING= 0x2,
+ FIXED_LACING = 0x4,
+ EBML_LACING = 0x6,
+ NO_LACING = 0x0,
+ };
+ };
+
+ class Block
+ {
+ public:
+ Block()
+#ifdef WA_VALIDATE
+ :
+ reference_block_found(false),
+ block_duration_found(false)
+#endif
+ {
+ reference_block=0;
+ block_duration=0;
+ }
+ BlockBinary binary;
+ uint64_t reference_block;
+ uint64_t block_duration;
+
+#ifdef WA_VALIDATE
+ bool reference_block_found;
+ bool block_duration_found;
+#endif
+ };
+
+ class Cluster
+ {
+ public:
+ Cluster()
+#ifdef WA_VALIDATE
+ :
+ time_code_found(false)
+#endif
+ {
+ time_code = 0;
+ position = 0;
+ previous_size = 0;
+ }
+ uint64_t time_code;
+ uint64_t position;
+ uint64_t previous_size;
+#ifdef WA_VALIDATE
+ bool time_code_found;
+#endif
+ };
+
+ class Clusters
+ {
+
+ };
+ uint64_t ReadCluster(MKVReader *reader, uint64_t size, nsmkv::Cluster &cluster);
+ uint64_t ReadBlockBinary(MKVReader *reader, uint64_t size, nsmkv::BlockBinary &binary, uint64_t *allowed_track_numbers, size_t num_allowed_track_numbers);
+ uint64_t ReadBlockGroup(MKVReader *reader, uint64_t size, nsmkv::Block &block, uint64_t *allowed_track_numbers, size_t num_allowed_track_numbers);
+} \ No newline at end of file
diff --git a/Src/nsmkv/Cues.cpp b/Src/nsmkv/Cues.cpp
new file mode 100644
index 00000000..95b3b538
--- /dev/null
+++ b/Src/nsmkv/Cues.cpp
@@ -0,0 +1,205 @@
+#include "Cues.h"
+#include "read.h"
+#include "global_elements.h"
+
+nsmkv::CuePoint *nsmkv::Cues::GetCuePoint(uint64_t when, uint64_t current_time, int seek_direction)
+{
+ CuePoint *last=0;
+ CuePoints::iterator itr;
+ for (itr=cue_points.begin(); itr!=cue_points.end();itr++)
+ {
+ CuePoint *cue_point = itr->second;
+ if (cue_point->cue_time == when)
+ {
+ if (seek_direction != SEEK_FORWARD || current_time < cue_point->cue_time)
+ return cue_point;
+ }
+ else if (cue_point->cue_time > when)
+ {
+ if (last)
+ {
+ if (seek_direction != SEEK_FORWARD || current_time < last->cue_time)
+ return last;
+ }
+ else
+ return cue_point;
+ }
+
+ last = cue_point;
+ }
+ return last; // will be 0 if we don't have any cue points, which is what we want.
+}
+
+nsmkv::CueTrackPosition *nsmkv::CuePoint::GetPosition(uint64_t track)
+{
+ CueTrackPositions::iterator found = cue_track_positions.find(track);
+ if (found != cue_track_positions.end())
+ return found->second;
+ else
+ return 0;
+}
+
+// returns bytes read. 0 means EOF
+static uint64_t ReadCueTrackPositions(nsmkv::MKVReader *reader, uint64_t size, nsmkv::CueTrackPosition &track_position)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_cuetrackpositions_cuetrack:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+ track_position.track = val;
+#ifdef WA_VALIDATE
+ printf(" Cue Track: %I64u\n", val);
+ track_position.track_found = true;
+#endif
+ }
+ break;
+ case mkv_cuetrackpositions_cueclusterposition:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+ track_position.cluster_position = val;
+#ifdef WA_VALIDATE
+ printf(" Cue Cluster Position: %I64u\n", val);
+ track_position.cluster_position_found = true;
+#endif
+ }
+ break;
+ default:
+ nsmkv::ReadGlobal(reader, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
+// returns bytes read. 0 means EOF
+static uint64_t ReadCuePoint(nsmkv::MKVReader *reader, uint64_t size, nsmkv::CuePoint &cue_point)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_cuepoint_cuetime:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+ cue_point.cue_time = val;
+
+#ifdef WA_VALIDATE
+ printf(" Cue Time: %I64u\n", val);
+ cue_point.cue_time_found = true;
+#endif
+ }
+ break;
+ case mkv_cuepoint_cuetrackpositions:
+ {
+#ifdef WA_VALIDATE
+ printf(" Cue Track Positions\n");
+#endif
+ nsmkv::CueTrackPosition *track_position = new nsmkv::CueTrackPosition;
+ if (ReadCueTrackPositions(reader, node.size, *track_position) == 0)
+ {
+ delete track_position;
+ return 0;
+ }
+ cue_point.cue_track_positions[track_position->track] = track_position;
+ }
+ break;
+ default:
+ nsmkv::ReadGlobal(reader, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
+// returns bytes read. 0 means EOF
+uint64_t nsmkv::ReadCues(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Cues &cues)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_cues_cuepoint:
+ {
+#ifdef WA_VALIDATE
+ printf(" Cue Point\n");
+#endif
+ CuePoint *cue_point = new CuePoint;
+ if (ReadCuePoint(reader, node.size, *cue_point) == 0)
+ {
+ delete cue_point;
+ return 0;
+ }
+ cues.cue_points[cue_point->cue_time] = cue_point;
+ }
+ break;
+ default:
+ ReadGlobal(reader, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
diff --git a/Src/nsmkv/Cues.h b/Src/nsmkv/Cues.h
new file mode 100644
index 00000000..31a26982
--- /dev/null
+++ b/Src/nsmkv/Cues.h
@@ -0,0 +1,76 @@
+#pragma once
+#include <bfc/platform/types.h>
+#include "mkv_reader.h"
+#include <map>
+
+const uint32_t mkv_segment_cues = 0xc53bb6b;
+const uint32_t mkv_cues_cuepoint = 0x3b;
+const uint32_t mkv_cuepoint_cuetime=0x33;
+const uint32_t mkv_cuepoint_cuetrackpositions = 0x37;
+const uint32_t mkv_cuetrackpositions_cuetrack= 0x77;
+const uint32_t mkv_cuetrackpositions_cueclusterposition=0x71;
+
+namespace nsmkv
+{
+
+ class CueTrackPosition
+ {
+ public:
+ CueTrackPosition()
+#ifdef WA_VALIDATE
+ :
+ track_found(false),
+ cluster_position_found(false)
+// block_number_found(false)
+#endif
+ {
+ track=0;
+ cluster_position=0;
+ block_number=0;
+ }
+ uint64_t track;
+ uint64_t cluster_position;
+ uint64_t block_number;
+
+#ifdef WA_VALIDATE
+ bool track_found;
+ bool cluster_position_found;
+// bool block_number_found;
+#endif
+ };
+
+ class CuePoint
+ {
+ public:
+ CuePoint()
+#ifdef WA_VALIDATE
+ :
+ cue_time_found(false)
+#endif
+ {
+ cue_time = 0;
+ }
+ CueTrackPosition *GetPosition(uint64_t track);
+ uint64_t cue_time;
+ typedef std::map<uint64_t, CueTrackPosition*> CueTrackPositions; // keyed on track number
+ CueTrackPositions cue_track_positions;
+#ifdef WA_VALIDATE
+ bool cue_time_found;
+#endif
+ };
+
+ class Cues
+ {
+ public:
+ enum
+ {
+ SEEK_WHATEVER=0,
+ SEEK_FORWARD=1,
+ SEEK_BACKWARD=2,
+ };
+ CuePoint *GetCuePoint(uint64_t when, uint64_t current_time=0, int seek_direction=SEEK_WHATEVER);
+ typedef std::map<uint64_t, CuePoint*> CuePoints; // use Map on cue_time to ensure order
+ CuePoints cue_points;
+ };
+ uint64_t ReadCues(MKVReader *reader, uint64_t size, nsmkv::Cues &cues);
+}
diff --git a/Src/nsmkv/Lacing.cpp b/Src/nsmkv/Lacing.cpp
new file mode 100644
index 00000000..3341a522
--- /dev/null
+++ b/Src/nsmkv/Lacing.cpp
@@ -0,0 +1,111 @@
+#include "Lacing.h"
+#include "Cluster.h"
+#include "vint.h"
+
+
+
+bool nsmkv::Lacing::GetState(uint8_t flags, const uint8_t *data, size_t data_len, nsmkv::LacingState *state)
+{
+ // TODO: error check by making sure data_len doesn't go below 0
+ switch(flags & BlockBinary::LACE_MASK)
+ {
+ case BlockBinary::NO_LACING:
+ state->laces = 1;
+ state->positions[0] = 0;
+ state->sizes[0] = data_len;
+#ifdef WA_VALIDATE
+ printf(" Lacing = NO_LACING\n");
+#endif
+ return true;
+ case BlockBinary::EBML_LACING:
+ {
+ uint16_t number_of_frames = state->laces = *data++ + 1;
+ uint64_t delta = vint_get_number_bytes(data[0]);
+ state->sizes[0] = vint_read_ptr_len((uint8_t)delta, data);
+ delta++;
+ state->positions[0] = 1;
+ state->positions[1] = 1+state->sizes[0];
+ for (uint16_t i=1;i<number_of_frames-1;i++)
+ {
+ uint8_t this_len = vint_get_number_bytes(data[delta]);
+ state->sizes[i] = vsint_read_ptr_len(this_len, data+delta);
+ state->sizes[i] += state->sizes[i-1];
+ state->positions[i+1] = state->positions[i] + state->sizes[i];
+ delta+=this_len + 1;
+ }
+ state->sizes[number_of_frames-1] = data_len - state->positions[number_of_frames-1] - delta;
+
+ for (uint16_t i=0;i<number_of_frames;i++)
+ {
+ state->positions[i] += delta;
+ }
+#ifdef WA_VALIDATE
+ printf(" Lacing = EBML_LACING\n");
+#endif
+ }
+ return true;
+ case BlockBinary::XIPH_LACING:
+ {
+ const uint8_t *orig_data = data;
+ uint16_t number_of_frames=state->laces = *data++ + 1;
+ for (uint16_t i=0;i!=number_of_frames-1;i++)
+ {
+ size_t frame_len = 0;
+ do
+ {
+ frame_len += *data;
+ } while (data && *data++ == 255);
+ state->sizes[i] = frame_len;
+ }
+ uint64_t delta = data - orig_data;
+ uint64_t last_position = delta;
+ uint64_t last_size = 0;
+ for (uint16_t i=0;i!=number_of_frames;i++)
+ {
+ state->positions[i] = last_position + last_size;
+ last_position = state->positions[i];
+ last_size = state->sizes[i];
+ }
+ state->sizes[number_of_frames-1] = data_len - state->positions[number_of_frames-1];
+#ifdef WA_VALIDATE
+ printf(" Lacing = XIPH LACING\n");
+#endif
+ }
+ return true;
+ case BlockBinary::FIXED_LACING:
+ {
+ uint16_t number_of_frames=state->laces=data[0]+1;
+ uint32_t size_per_frame = (uint32_t)(data_len-1) / number_of_frames;
+ for (uint16_t i=0;i<number_of_frames;i++)
+ {
+ state->positions[i] = (uint64_t)(1 + size_per_frame*i);
+ state->sizes[i] = size_per_frame;
+ }
+#ifdef WA_VALIDATE
+ printf(" Lacing = FIXED LACING\n");
+#endif
+ }
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool nsmkv::Lacing::GetFrame(uint16_t frame_number, const uint8_t *data, size_t data_len, const uint8_t **frame, size_t *frame_len, const LacingState *state)
+{
+ if (frame_number >= state->laces)
+ return false;
+
+ const uint8_t *lace = data + state->positions[frame_number];
+ size_t lace_len = (size_t)state->sizes[frame_number];
+ if (lace < data // if the lace starts before our data
+ || (state->positions[frame_number] + lace_len) > data_len) // or extends out past our data
+ {
+ return false;
+ }
+
+ *frame = lace;
+ *frame_len = lace_len;
+ return true;
+}
diff --git a/Src/nsmkv/Lacing.h b/Src/nsmkv/Lacing.h
new file mode 100644
index 00000000..9a7f3d6a
--- /dev/null
+++ b/Src/nsmkv/Lacing.h
@@ -0,0 +1,19 @@
+#pragma once
+#include <bfc/platform/types.h>
+
+namespace nsmkv
+{
+
+ struct LacingState
+ {
+ uint16_t laces;
+ int64_t sizes[256]; // max 256 laces (stored as 8bit number and 1 added)
+ uint64_t positions[256];
+ };
+
+ namespace Lacing
+ {
+ bool GetState(uint8_t flags, const uint8_t *data, size_t data_len, LacingState *state);
+ bool GetFrame(uint16_t frame_number, const uint8_t *data, size_t data_len, const uint8_t **frame, size_t *frame_len, const LacingState *state);
+ }
+}
diff --git a/Src/nsmkv/SeekTable.cpp b/Src/nsmkv/SeekTable.cpp
new file mode 100644
index 00000000..78bf118c
--- /dev/null
+++ b/Src/nsmkv/SeekTable.cpp
@@ -0,0 +1,230 @@
+#include "SeekTable.h"
+#include "read.h"
+#include "global_elements.h"
+#include "vint.h"
+#include <stdio.h>
+#include <assert.h>
+
+#ifdef WA_VALIDATE
+extern uint32_t num_seekhead_elements_found;
+extern uint32_t num_seek_elements_found;
+#endif
+
+bool nsmkv::SeekTable::GetEntry(uint64_t id, uint64_t *position)
+{
+ return EnumEntry(0, id, position);
+}
+
+bool nsmkv::SeekTable::EnumEntry(size_t i, uint64_t id, uint64_t *position)
+{
+ SeekMap::iterator found = seekMap.find(id);
+ if (found != seekMap.end())
+ {
+ SeekEntries *entries = found->second;
+ if (entries && entries->size() > i)
+ {
+ *position = entries->at(i).position;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool nsmkv::SeekTable::EnumEntry(size_t i, uint64_t id, SeekEntry **seek_entry)
+{
+ SeekMap::iterator found = seekMap.find(id);
+ if (found != seekMap.end())
+ {
+ SeekEntries *entries = found->second;
+ if (entries && entries->size() > i)
+ {
+ *seek_entry = &entries->at(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+void nsmkv::SeekTable::AddEntry(nsmkv::SeekEntry &entry, int flags)
+{
+ // check for duplicates
+ size_t i=0;
+ SeekEntry *seek_entry;
+ while (EnumEntry(i++, entry.id, &seek_entry))
+ {
+ if (flags & ADDENTRY_SINGLE)
+ {
+ if (flags & ADDENTRY_FOUND)
+ seek_entry->position = entry.position;
+ return;
+ }
+
+ if (entry.position == seek_entry->position)
+ {
+ return;
+ }
+ }
+
+ SeekEntries *&entries = seekMap[entry.id];
+ if (!entries)
+ {
+ entries = new SeekEntries;
+ }
+ entries->push_back(entry);
+#ifdef WA_VALIDATE
+ num_seek_elements_found++;
+#endif
+}
+
+void nsmkv::SeekTable::Dump()
+{
+ SeekMap::iterator itr;
+ for (itr=seekMap.begin();itr!=seekMap.end();itr++)
+ {
+ SeekEntries *entries = itr->second;
+ if (entries)
+ {
+ SeekEntries::iterator seekItr;
+ for (seekItr=entries->begin();seekItr!=entries->end();seekItr++)
+ {
+ SeekEntry &entry = *seekItr;
+ printf("Seek Entry -> id=%I64x, position=%I64u\n", entry.id, entry.position);
+ }
+ }
+ }
+}
+
+// returns bytes read. 0 means EOF
+static uint64_t ReadSeek(nsmkv::MKVReader *reader, uint64_t size, nsmkv::SeekTable &seekTable)
+{
+ uint64_t total_bytes_read=0;
+ nsmkv::SeekEntry entry;
+
+ enum
+ {
+ ID_FIELD_PARSED = 0x1,
+ POSITION_FIELD_PARSED = 0x2,
+ };
+ int fields_parsed=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_metaseek_seekid:
+ {
+ uint8_t binary[9] = {0};
+ if (!node.size || node.size > 9)
+ {
+#ifdef WA_VALIDATE
+ printf(" SeekID size invalid, size=%d\n",node.size);
+#endif
+ assert(node.size<9);
+ return 0;
+ }
+
+ size_t bytes_read;
+ reader->Read(binary, (size_t)node.size, &bytes_read);
+ if (bytes_read != (size_t)node.size)
+ return 0;
+#ifdef WA_VALIDATE
+ printf(" SeekID: %I64x\n", vint_read_ptr_len(node.size-1, binary));
+#endif
+ entry.id = vint_read_ptr_len((uint8_t)node.size-1, binary);
+ fields_parsed |= ID_FIELD_PARSED;
+ }
+ break;
+ case mkv_metaseek_seekposition:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+#ifdef WA_VALIDATE
+ printf(" SeekPosition: %I64u\n", val);
+#endif
+ entry.position = val;
+ fields_parsed |= POSITION_FIELD_PARSED;
+ }
+ break;
+ default:
+ nsmkv::ReadGlobal(reader, node.id, node.size);
+ }
+ }
+ if (fields_parsed == 0x3)
+ {
+ //entry.state = nsmkv::NOT_READ;
+ seekTable.AddEntry(entry);
+ }
+#ifdef WA_VALIDATE
+ else if (fields_parsed == 0x2)
+ {
+ printf(" Seek only contains SeekPosition\n");
+ } else if (fields_parsed == 0x01)
+ {
+ printf(" Seek element only contains SeekID\n");
+ }
+#endif
+ return total_bytes_read;
+}
+
+
+// returns bytes read. 0 means EOF
+uint64_t nsmkv::ReadSeekHead(nsmkv::MKVReader *reader, uint64_t size, nsmkv::SeekTable &seekTable)
+{
+ uint64_t total_bytes_read=0;
+#ifdef WA_VALIDATE
+ num_seekhead_elements_found++;
+#endif
+
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_metaseek_seek:
+ {
+#ifdef WA_VALIDATE
+ printf(" Seek\n");
+#endif
+ ReadSeek(reader, node.size, seekTable);
+ }
+ break;
+ default:
+ ReadGlobal(reader, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
diff --git a/Src/nsmkv/SeekTable.h b/Src/nsmkv/SeekTable.h
new file mode 100644
index 00000000..774ddcf9
--- /dev/null
+++ b/Src/nsmkv/SeekTable.h
@@ -0,0 +1,54 @@
+#pragma once
+#include <map>
+#include <vector>
+#include "mkv_reader.h"
+
+const uint32_t mkv_metaseek_seekhead = 0x14d9b74;
+const uint32_t mkv_metaseek_seek=0xdbb;
+const uint32_t mkv_metaseek_seekid = 0x13ab;
+const uint32_t mkv_metaseek_seekposition=0x13ac;
+
+/* this represents a seek table of other nodes in EBML
+ be careful, CuePoints (CuePoints.h) is for seeking to a certain time in the song
+the SeekTable (SeekTable.h) is for fast indexing of the mkv file structure
+*/
+namespace nsmkv
+{
+ class SeekEntry
+ {
+ public:
+ SeekEntry()
+ {
+ id=0;
+ position=0;
+ }
+ SeekEntry(uint64_t id, uint64_t position) : id(id), position(position)
+ {
+ }
+ uint64_t id; // ID of the EBML node
+ uint64_t position;
+ };
+
+ class SeekTable
+ {
+ public:
+ void AddEntry(SeekEntry &entry, int flags=0);
+ void Dump();
+ bool GetEntry(uint64_t id, uint64_t *position);
+ bool EnumEntry(size_t i, uint64_t id, uint64_t *position);
+
+ enum // flags for AddEntry
+ {
+ ADDENTRY_SINGLE = 0x1, // if there can only be one
+ ADDENTRY_FOUND = 0x2, // pass this is you physically found the entry in the file - this takes priority over the SeekHead
+ };
+ private:
+ bool EnumEntry(size_t i, uint64_t id, SeekEntry **seek_entry);
+ typedef std::vector<SeekEntry> SeekEntries;
+ typedef std::map<uint64_t, SeekEntries*> SeekMap;
+ SeekMap seekMap;
+ };
+
+ // returns bytes read. 0 means EOF
+ uint64_t ReadSeekHead(nsmkv::MKVReader *reader, uint64_t size, nsmkv::SeekTable &seekTable);
+} \ No newline at end of file
diff --git a/Src/nsmkv/SegmentInfo.cpp b/Src/nsmkv/SegmentInfo.cpp
new file mode 100644
index 00000000..a1877a19
--- /dev/null
+++ b/Src/nsmkv/SegmentInfo.cpp
@@ -0,0 +1,235 @@
+#include "SegmentInfo.h"
+#include "read.h"
+#include "global_elements.h"
+#include <time.h>
+#include <TCHAR.h>
+#include <sys/stat.h>
+
+int FileExists(const char * filename);
+
+// returns bytes read. 0 means EOF
+uint64_t nsmkv::ReadSegmentInfo(nsmkv::MKVReader *reader, uint64_t size, nsmkv::SegmentInfo &segment_info)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_segmentinfo_timecodescale:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Time Code Scale: %I64u\n", val);
+ segment_info.time_code_scale_found = true;
+#endif
+ segment_info.time_code_scale = val;
+ }
+ break;
+ case mkv_segmentinfo_muxingapp:
+ {
+ char *utf8 = 0;
+ if (node.size && read_utf8(reader, node.size, &utf8) == 0)
+ return 0;
+ if (utf8)
+#ifdef WA_VALIDATE
+ printf(" Muxing App: %s\n", utf8);
+ segment_info.muxing_app_found = true;
+#endif
+ segment_info.Own(segment_info.muxing_app, utf8);
+ }
+ break;
+ case mkv_segmentinfo_writingapp:
+ {
+ char *utf8 = 0;
+ if (node.size && read_utf8(reader, node.size, &utf8) == 0)
+ return 0;
+ if (utf8)
+#ifdef WA_VALIDATE
+ printf(" Writing App: %s\n", utf8);
+ segment_info.writing_app_found = true;
+#endif
+ segment_info.Own(segment_info.writing_app, utf8);
+ }
+ break;
+ case mkv_segmentinfo_duration:
+ {
+ double val;
+ if (read_float(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Duration: %g\n", val);
+ segment_info.duration_found = true;
+#endif
+ segment_info.duration = val;
+ }
+ break;
+ case mkv_segmentinfo_dateutc:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+ segment_info.production_date = val;
+ // value is in nanoseconds, relative to jan 01, 2001. ugh
+
+ __time64_t val_time = mkv_date_as_time_t(val);
+#ifdef WA_VALIDATE
+ printf(" Date UTC: %s", _ctime64(&val_time));
+ segment_info.production_date_found = true;
+#endif
+ }
+ break;
+ case mkv_segmentinfo_segmentuid:
+ {
+#ifdef WA_VALIDATE
+ printf(" Segment UID: binary size %I64u\n", node.size);
+ segment_info.segment_uid_found = true;
+#endif
+ if (node.size == 16)
+ {
+ size_t bytes_read;
+ reader->Read(&segment_info.segment_uid, (size_t)node.size, &bytes_read);
+ if (bytes_read != node.size)
+ return 0;
+ }
+ else // bad size, let's just skip it
+ {
+ reader->Skip(node.size);
+ }
+ }
+ break;
+ case mkv_segmentinfo_prevuid:
+ {
+#ifdef WA_VALIDATE
+ printf(" Previous UID: binary size %I64u\n", node.size);
+ segment_info.prev_uid_found = true;
+#endif
+ if (node.size == 16)
+ {
+ size_t bytes_read;
+ reader->Read(&segment_info.prev_uid, (size_t)node.size, &bytes_read);
+ if (bytes_read != node.size)
+ return 0;
+ }
+ else // bad size, let's just skip it
+ {
+ reader->Skip(node.size);
+ }
+ }
+ break;
+ case mkv_segmentinfo_nextuid:
+ {
+#ifdef WA_VALIDATE
+ printf(" Next Segment UID: binary size %I64u\n", node.size);
+ segment_info.next_uid_found = true;
+#endif
+ if (node.size == 16)
+ {
+ size_t bytes_read;
+ reader->Read(&segment_info.next_uid, (size_t)node.size, &bytes_read);
+ if (bytes_read != node.size)
+ return 0;
+ }
+ else // bad size, let's just skip it
+ {
+ reader->Skip(node.size);
+ }
+ }
+ break;
+ case mkv_segmentinfo_title:
+ {
+ char *utf8 = 0;
+ if (read_utf8(reader, node.size, &utf8) == 0)
+ return 0;
+ if (utf8)
+#ifdef WA_VALIDATE
+ printf(" Title: %s\n", utf8);
+ segment_info.title_found = true;
+#endif
+ segment_info.Own(segment_info.title, utf8);
+ }
+ break;
+ case mkv_segmentinfo_prevfilename:
+ {
+ char *utf8 = 0;
+ if (read_utf8(reader, node.size, &utf8) == 0)
+ return 0;
+ if (utf8)
+#ifdef WA_VALIDATE
+ printf(" Previous Filename: %s\n", utf8);
+ segment_info.prev_filename_found = true;
+ if (FileExists(segment_info.prev_filename) != 0)
+ {
+ printf("****Specified previous filename not found");
+ }
+#endif
+ segment_info.Own(segment_info.prev_filename,utf8);
+ }
+ break;
+ case mkv_segmentinfo_nextfilename:
+ {
+ char *utf8 = 0;
+ if (read_utf8(reader, node.size, &utf8) == 0)
+ return 0;
+ if (utf8)
+#ifdef WA_VALIDATE
+ printf(" Next Filename: %s\n", utf8);
+ segment_info.next_filename_found = true;
+ if (FileExists(segment_info.next_filename) != 0)
+ {
+ printf("****Specified next filename not found");
+ }
+#endif
+ segment_info.Own(segment_info.next_filename, utf8);
+ }
+ break;
+
+ default:
+ nsmkv::ReadGlobal(reader, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
+int nsmkv::SegmentInfo::GetDurationMilliseconds() const
+{
+ double nanoseconds = (double)time_code_scale * duration;
+ double microseconds = nanoseconds / 1000.0;
+ double milliseconds = microseconds / 1000.0;
+ return (int)milliseconds;
+}
+
+uint64_t nsmkv::SegmentInfo::ConvertMillisecondsToTime(int milliseconds) const
+{
+ double time_code = (double)milliseconds * 1000000.0 / (double)time_code_scale;
+ return (uint64_t)time_code;
+}
+
+int FileExists(const char * filename) {
+ int iStat;
+ struct _stat64 fileInfo;
+
+ // get the file attributes
+ return (iStat = _stat64(filename,&fileInfo));
+}
diff --git a/Src/nsmkv/SegmentInfo.h b/Src/nsmkv/SegmentInfo.h
new file mode 100644
index 00000000..39646599
--- /dev/null
+++ b/Src/nsmkv/SegmentInfo.h
@@ -0,0 +1,107 @@
+#pragma once
+#include "mkv_date.h"
+#include "mkv_reader.h"
+#include <bfc/platform/guid.h>
+/*
+Time Scale: 986946
+Muxing App: libebml v0.7.7 + libmatroska v0.8.1
+Writing App: mkvmerge v2.0.2 ('You're My Flame') built on Sep 20 2007 09:35:09
+Duration: 60257
+Date UTC: Sun Nov 18 20:23:18 2007
+Segment UID: binary size 16
+*/
+
+const uint32_t mkv_segment_segmentinfo = 0x549a966;
+const uint32_t mkv_segmentinfo_timecodescale = 0xad7b1;
+const uint32_t mkv_segmentinfo_muxingapp=0xd80;
+const uint32_t mkv_segmentinfo_writingapp=0x1741;
+const uint32_t mkv_segmentinfo_duration=0x489;
+const uint32_t mkv_segmentinfo_dateutc=0x461;
+const uint32_t mkv_segmentinfo_segmentuid=0x33a4;
+const uint32_t mkv_segmentinfo_nextuid=0x1eb923;
+const uint32_t mkv_segmentinfo_prevuid=0x1cb923;
+const uint32_t mkv_segmentinfo_nextfilename=0x1e83bb;
+const uint32_t mkv_segmentinfo_prevfilename=0x1c83ab;
+const uint32_t mkv_segmentinfo_title=0x3ba9;
+
+namespace nsmkv
+{
+ class SegmentInfo
+ {
+ public:
+ SegmentInfo() :
+ time_code_scale(1000000),
+ muxing_app(0),
+ writing_app(0),
+ duration(0),
+ production_date(0),
+ segment_uid(INVALID_GUID),
+ next_uid(INVALID_GUID),
+ prev_uid(INVALID_GUID),
+ next_filename(0),
+ prev_filename(0),
+ title(0)
+#ifdef WA_VALIDATE
+ ,
+ time_code_scale_found(false),
+ muxing_app_found(false),
+ writing_app_found(false),
+ duration_found(false),
+ production_date_found(false),
+ segment_uid_found(false),
+ next_uid_found(false),
+ prev_uid_found(false),
+ next_filename_found(false),
+ prev_filename_found(false),
+ title_found(false)
+#endif
+
+ {
+ }
+ ~SegmentInfo()
+ {
+ free(muxing_app);
+ free(writing_app);
+ free(title);
+ }
+ void Own(char *&field, char *value)
+ {
+ if (field)
+ free(field);
+ field = value;
+ }
+
+ int GetDurationMilliseconds() const;
+ uint64_t ConvertMillisecondsToTime(int milliseconds) const;
+ uint64_t time_code_scale;
+ char *muxing_app;
+ char *writing_app;
+ char *title;
+ double duration;
+ mkv_date_t production_date;
+ GUID segment_uid;
+ GUID prev_uid;
+ GUID next_uid;
+ char *prev_filename;
+ char *next_filename;
+
+#ifdef WA_VALIDATE
+ bool segment_uid_found;
+ bool prev_uid_found;
+ bool next_uid_found;
+ bool prev_filename_found;
+ bool next_filename_found;
+ bool time_code_scale_found;
+ bool duration_found;
+ bool muxing_app_found;
+ bool writing_app_found;
+ bool production_date_found;
+ bool title_found;
+
+
+#endif
+
+ };
+
+ uint64_t ReadSegmentInfo(nsmkv::MKVReader *reader, uint64_t size, nsmkv::SegmentInfo &segment_info);
+} \ No newline at end of file
diff --git a/Src/nsmkv/Tags.cpp b/Src/nsmkv/Tags.cpp
new file mode 100644
index 00000000..2d036327
--- /dev/null
+++ b/Src/nsmkv/Tags.cpp
@@ -0,0 +1,202 @@
+#include "Tags.h"
+#include "global_elements.h"
+#include "read.h"
+
+nsmkv::Tags::Tags(void)
+{
+}
+
+nsmkv::Tags::~Tags(void)
+{
+}
+
+static uint64_t ReadSimpleTag(FILE *f, uint64_t size)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node((nsmkv::MKVReader*)f, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_tags_tag_simpletag_tagname:
+ {
+ char *utf8 = 0;
+ if (node.size && read_utf8((nsmkv::MKVReader*)f, node.size, &utf8) == 0)
+ return 0;
+ if (utf8)
+ printf(" Tag Name: %s\n", utf8);
+ free(utf8);
+ }
+ break;
+ case mkv_tags_tag_simpletag_tagstring:
+ {
+ char *utf8 = 0;
+ if (node.size && read_utf8((nsmkv::MKVReader*)f, node.size, &utf8) == 0)
+ return 0;
+ if (utf8)
+ printf(" Tag String: %s\n", utf8);
+ free(utf8);
+ }
+ break;
+ case mkv_tags_tag_simpletag_taglanguage:
+ {
+ char *utf8 = 0;
+ if (node.size && read_utf8((nsmkv::MKVReader*)f, node.size, &utf8) == 0)
+ return 0;
+ if (utf8)
+ printf(" Tag Language: %s\n", utf8);
+ free(utf8);
+ }
+ break;
+ case mkv_tags_tag_simpletag_tagdefault:
+ {
+ uint64_t val;
+ if (read_unsigned((nsmkv::MKVReader*)f, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Tag Default: %I64u\n", val);
+#endif
+ }
+ break;
+ default:
+ nsmkv::ReadGlobal((nsmkv::MKVReader*)f, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
+static uint64_t ReadTarget(FILE *f, uint64_t size)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node((nsmkv::MKVReader*)f, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_tags_tag_target_targettypevalue:
+ {
+ uint64_t val;
+ if (read_unsigned((nsmkv::MKVReader*)f, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Target Type Value: %I64u\n", val);
+#endif
+ }
+ break;
+ default:
+ nsmkv::ReadGlobal((nsmkv::MKVReader*)f, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
+static uint64_t ReadTag(FILE *f, uint64_t size)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node((nsmkv::MKVReader*)f, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_tags_tag_target:
+ {
+ ReadTarget(f, node.size);
+ }
+ break;
+ case mkv_tags_tag_simpletag:
+ {
+ ReadSimpleTag(f, node.size);
+ }
+ break;
+ default:
+ nsmkv::ReadGlobal((nsmkv::MKVReader*)f, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
+uint64_t nsmkv::ReadTags(FILE *f, uint64_t size, nsmkv::Tags &tags)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node((nsmkv::MKVReader*)f, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_tags_tag:
+ {
+ ReadTag(f, node.size);
+ }
+ break;
+ default:
+ nsmkv::ReadGlobal((nsmkv::MKVReader*)f, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+} \ No newline at end of file
diff --git a/Src/nsmkv/Tags.h b/Src/nsmkv/Tags.h
new file mode 100644
index 00000000..d5c35e35
--- /dev/null
+++ b/Src/nsmkv/Tags.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <bfc/platform/types.h>
+#include <stdio.h>
+
+// IDs
+// these are slightly different from the matroska spec because we specify
+// values after vint decoding and they specify before
+const uint32_t mkv_segment_tags = 0x254c367;
+const uint32_t mkv_tags_tag = 0x3373;
+const uint32_t mkv_tags_tag_target = 0x23c0;
+const uint32_t mkv_tags_tag_target_targettypevalue = 0x28ca;
+const uint32_t mkv_tags_tag_simpletag = 0x27c8;
+const uint32_t mkv_tags_tag_simpletag_tagname = 0x5a3;
+const uint32_t mkv_tags_tag_simpletag_tagstring = 0x487;
+const uint32_t mkv_tags_tag_simpletag_taglanguage = 0x47a;
+const uint32_t mkv_tags_tag_simpletag_tagdefault = 0x484;
+
+namespace nsmkv
+{
+
+ class Tags
+ {
+ public:
+ Tags(void);
+ ~Tags(void);
+ };
+
+ // returns bytes read. 0 means EOF
+ uint64_t ReadTags(FILE *f, uint64_t size, nsmkv::Tags &tags);
+
+};
diff --git a/Src/nsmkv/Tracks.cpp b/Src/nsmkv/Tracks.cpp
new file mode 100644
index 00000000..1cb9d69a
--- /dev/null
+++ b/Src/nsmkv/Tracks.cpp
@@ -0,0 +1,561 @@
+#include "Tracks.h"
+#include "read.h"
+#include "global_elements.h"
+
+// returns bytes read. 0 means EOF
+static uint64_t ReadTracksVideo(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Video &video)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_video_pixelwidth:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Pixel Width: %I64u\n", val);
+ video.pixel_width_found=true;
+#endif
+ video.pixel_width=val;
+ }
+ break;
+ case mkv_video_pixelheight:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Pixel Height: %I64u\n", val);
+ video.pixel_height_found=true;
+#endif
+ video.pixel_height=val;
+ }
+ break;
+ case mkv_video_flaginterlaced:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Flag Interlaced: 0x%I64x\n", val);
+ video.flag_interlaced_found=true;
+#endif
+ video.flag_interlaced = !!val;
+ }
+ break;
+ case mkv_video_displaywidth:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Display Width: %I64u\n", val);
+ video.display_width_found = true;
+#endif
+ video.display_width = val;
+ }
+ break;
+ case mkv_video_displayheight:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Display Height: %I64u\n", val);
+ video.display_height_found = true;
+#endif
+ video.display_height = val;
+ }
+ break;
+ case mkv_video_pixelcropleft:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Pixel Crop Left: %I64u\n", val);
+ video.pixel_crop_left_found = true;
+#endif
+ video.pixel_crop_left = val;
+ }
+ break;
+ case mkv_video_pixelcroptop:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Pixel Crop Top: %I64u\n", val);
+ video.pixel_crop_top_found = true;
+#endif
+ video.pixel_crop_top = val;
+ }
+ break;
+ case mkv_video_pixelcropbottom:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Pixel Crop Bottom: %I64u\n", val);
+ video.pixel_crop_bottom_found = true;
+#endif
+ video.pixel_crop_bottom = val;
+ }
+ break;
+ case mkv_video_pixelcropright:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Pixel Crop Right: %I64u\n", val);
+ video.pixel_crop_right_found = true;
+#endif
+ video.pixel_crop_right = val;
+ }
+ break;
+ default:
+ nsmkv::ReadGlobal(reader, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
+// returns bytes read. 0 means EOF
+static uint64_t ReadTracksAudio(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Audio &audio)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_audio_samplingfrequency:
+ {
+ double val;
+ if (read_float(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Sampling Frequency: %g\n", val);
+ audio.sampling_frequency_found = true;
+#endif
+ audio.sampling_frequency = (uint64_t)val;
+ }
+ break;
+ case mkv_audio_channels:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Channels: %I64u\n", val);
+ audio.channels_found = true;
+#endif
+ audio.channels = val;
+ }
+ break;
+ case mkv_audio_output_samplingfrequency:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Output Sampling Frequency: %I64u\n", val);
+ audio.output_sampling_frequency_found = true;
+#endif
+ audio.output_sampling_frequency = val;
+ }
+ break;
+ default:
+ nsmkv::ReadGlobal(reader, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
+
+static uint64_t ReadTrackEntry(nsmkv::MKVReader *reader, uint64_t size, nsmkv::TrackEntry &track_entry)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_tracks_tracknumber:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+#ifdef WA_VALIDATE
+ printf(" Track Number: %I64u\n", val);
+ track_entry.track_number_found = true;
+#endif
+ track_entry.track_number = val;
+ }
+ break;
+ case mkv_tracks_trackuid:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Track UID: %I64u\n", val);
+ track_entry.track_uid_found = true;
+#endif
+ track_entry.track_uid = val;
+ }
+ break;
+ case mkv_tracks_tracktype:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Track Type: 0x%I64x\n", val);
+ track_entry.track_type_found = true;
+#endif
+ track_entry.track_type = val;
+ }
+ break;
+ case mkv_tracks_flagenabled:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Flag Enabled: 0x%I64x\n", val);
+ track_entry.flag_enabled_found = true;
+#endif
+ track_entry.flag_enabled = !!val;
+ }
+ break;
+ case mkv_tracks_flagdefault:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Flag Default: 0x%I64x\n", val);
+ track_entry.flag_default_found = true;
+#endif
+ track_entry.flag_default = !!val;
+ }
+ break;
+ case mkv_tracks_flagforced:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Flag Forced: 0x%I64x\n", val);
+ track_entry.flag_forced_found = true;
+#endif
+ track_entry.flag_forced = !!val;
+ }
+ break;
+ case mkv_tracks_flaglacing:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Flag Lacing: 0x%I64x\n", val);
+ track_entry.flag_lacing_found = true;
+#endif
+ track_entry.flag_lacing = !!val;
+ }
+ break;
+ case mkv_tracks_mincache:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Min Cache: %I64u\n", val);
+ track_entry.min_cache_found = true;
+#endif
+ track_entry.min_cache = !!val;
+ }
+ break;
+ case mkv_tracks_tracktimecodescale:
+ {
+ double val;
+ if (read_float(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Track Time Code Scale: %g\n", val);
+ track_entry.track_timecode_scale_found = true;
+#endif
+ track_entry.track_timecode_scale = val;
+ }
+ break;
+ case mkv_tracks_maxblockadditionid:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Max Block Addition ID: %I64u\n", val);
+ track_entry.max_block_additional_id_found=true;
+#endif
+ track_entry.max_block_additional_id=val;
+ }
+ break;
+ case mkv_tracks_codecid:
+ {
+ char *utf8=0;
+ if (read_utf8(reader, node.size, &utf8) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ if (utf8)
+ printf(" Codec ID: %s\n", utf8);
+ track_entry.codec_id_found = true;
+#endif
+ track_entry.Own(track_entry.codec_id, utf8);
+ }
+ break;
+ case mkv_tracks_codecdecodeall:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Codec Decode All: %I64u\n", val);
+ track_entry.decode_all_found = true;
+#endif
+ track_entry.decode_all = !!val;
+ }
+ break;
+ case mkv_tracks_defaultduration:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Default Duration: %I64u\n", val);
+ track_entry.default_duration_found = true;
+#endif
+ track_entry.default_duration = val;
+ }
+ break;
+ case mkv_tracks_codecprivate:
+ {
+#ifdef WA_VALIDATE
+ printf(" Codec Private: binary size %I64u\n", node.size);
+#endif
+
+ void *codec_private = malloc((size_t)node.size);
+ if (!codec_private)
+ return 0;
+ size_t bytes_read;
+ reader->Read(codec_private, (size_t)node.size, &bytes_read);
+ if (bytes_read != node.size)
+ {
+ free(codec_private);
+ return 0;
+ }
+ track_entry.OwnCodecPrivate(codec_private, (size_t)node.size);
+#ifdef WA_VALIDATE
+ track_entry.codec_private_found = true;
+#endif
+ }
+ break;
+ case mkv_tracks_language:
+ {
+ char *utf8=0;
+ if (read_utf8(reader, node.size, &utf8) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ if (utf8)
+ printf(" Codec Language: %s\n", utf8);
+ track_entry.language_found = true;
+#endif
+ track_entry.Own(track_entry.language, utf8);
+ }
+ break;
+ case mkv_tracks_video:
+ {
+#ifdef WA_VALIDATE
+ printf(" Video Settings\n");
+#endif
+ if (ReadTracksVideo(reader, node.size, track_entry.video) == 0)
+ return 0;
+ }
+ break;
+ case mkv_tracks_audio:
+ {
+#ifdef WA_VALIDATE
+ printf(" Audio Settings\n");
+#endif
+ if (ReadTracksAudio(reader, node.size, track_entry.audio) == 0)
+ return 0;
+ }
+ break;
+ case mkv_tracks_name:
+ {
+ char *utf8=0;
+ if (read_utf8(reader, node.size, &utf8) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ if (utf8)
+ printf(" Track Name: %s\n", utf8);
+ track_entry.name_found = true;
+#endif
+ track_entry.Own(track_entry.name, utf8);
+ }
+ break;
+ case mkv_tracks_maxcache:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+#ifdef WA_VALIDATE
+ printf(" Max Cache: %I64u\n", val);
+ track_entry.max_cache_found = true;
+#endif
+ track_entry.max_cache = val;
+ }
+ break;
+ default:
+ nsmkv::ReadGlobal(reader, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
+
+// returns bytes read. 0 means EOF
+uint64_t nsmkv::ReadTracks(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Tracks &tracks)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_tracks_trackentry:
+ {
+#ifdef WA_VALIDATE
+ printf(" Track Entry\n");
+#endif
+ TrackEntry *track_entry = new TrackEntry;
+ if (ReadTrackEntry(reader, node.size, *track_entry) == 0)
+ {
+ delete track_entry;
+ return 0;
+ }
+ tracks.tracks.push_back(track_entry);
+ }
+ break;
+ default:
+ ReadGlobal(reader, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
+const nsmkv::TrackEntry *nsmkv::Tracks::EnumTrack(size_t i) const
+{
+ if (tracks.size() > i)
+ {
+ return tracks[i];
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/Src/nsmkv/Tracks.h b/Src/nsmkv/Tracks.h
new file mode 100644
index 00000000..4d34ef7e
--- /dev/null
+++ b/Src/nsmkv/Tracks.h
@@ -0,0 +1,292 @@
+#pragma once
+#include <bfc/platform/types.h>
+#include "mkv_reader.h"
+#include <vector>
+
+const uint32_t mkv_segment_tracks = 0x654ae6b;
+const uint32_t mkv_tracks_trackentry = 0x2e;
+const uint32_t mkv_tracks_tracknumber=0x57;
+const uint32_t mkv_tracks_trackuid=0x33c5;
+const uint32_t mkv_tracks_tracktype=0x3;
+const uint32_t mkv_tracks_flagenabled=0x39;
+const uint32_t mkv_tracks_flagdefault=0x8;
+const uint32_t mkv_tracks_flagforced=0x15aa;
+const uint32_t mkv_tracks_flaglacing=0x1c;
+const uint32_t mkv_tracks_mincache=0x2de7;
+const uint32_t mkv_tracks_tracktimecodescale=0x3314f;
+const uint32_t mkv_tracks_maxblockadditionid=0x15ee;
+const uint32_t mkv_tracks_codecid=0x6;
+const uint32_t mkv_tracks_codecdecodeall=0x2A;
+const uint32_t mkv_tracks_defaultduration=0x3e383;
+const uint32_t mkv_tracks_codecprivate=0x23a2;
+const uint32_t mkv_tracks_language=0x2b59c;
+const uint32_t mkv_tracks_name=0x136e;
+const uint32_t mkv_tracks_maxcache=0x2df8;
+
+// Track - video settings
+const uint32_t mkv_tracks_video=0x60;
+const uint32_t mkv_video_pixelwidth = 0x30;
+const uint32_t mkv_video_pixelheight = 0x3a;
+const uint32_t mkv_video_flaginterlaced = 0x1a;
+const uint32_t mkv_video_displaywidth = 0x14b0;
+const uint32_t mkv_video_displayheight = 0x14ba;
+const uint32_t mkv_video_pixelcropbottom = 0x14aa;
+const uint32_t mkv_video_pixelcroptop = 0x14bb;
+const uint32_t mkv_video_pixelcropleft = 0x14cc;
+const uint32_t mkv_video_pixelcropright = 0x14dd;
+
+
+// Track - audio settings;
+const uint32_t mkv_tracks_audio=0x61;
+const uint32_t mkv_audio_samplingfrequency=0x35;
+const uint32_t mkv_audio_channels = 0x1f;
+const uint32_t mkv_audio_output_samplingfrequency=0x38b5;
+
+// Track Types
+enum
+{
+ mkv_track_type_video = 0x01,
+ mkv_track_type_audio = 0x02,
+ mkv_track_type_complex = 0x03, // i.e., combined video and audio
+ mkv_track_type_logo = 0x10,
+ mkv_track_type_subtitle = 0x11,
+ mkv_track_type_buttons = 0x12,
+ mkv_track_type_control = 0x20,
+};
+/* TODO: benski>
+
+*/
+namespace nsmkv
+{
+#pragma pack(push, 8)
+ struct VideoData
+ {
+ size_t struct_size;
+ uint64_t pixel_width;
+ uint64_t pixel_height;
+ uint64_t pixel_crop_bottom;
+ uint64_t pixel_crop_top;
+ uint64_t pixel_crop_left;
+ uint64_t pixel_crop_right;
+ uint64_t display_width;
+ uint64_t display_height;
+ uint64_t display_unit;
+ bool flag_interlaced;
+ };
+#pragma pack(pop)
+ class Video : public VideoData
+ {
+ public:
+ Video()
+#ifdef WA_VALIDATE
+ :
+ pixel_width_found(false),
+ pixel_height_found(false),
+ pixel_crop_bottom_found(false),
+ pixel_crop_top_found(false),
+ pixel_crop_left_found(false),
+ pixel_crop_right_found(false),
+ display_width_found(false),
+ display_height_found(false),
+// display_unit_found(false),
+ flag_interlaced_found(false)
+#endif
+ {
+ struct_size = sizeof(VideoData);
+ pixel_width=0;
+ pixel_height=0;
+ pixel_crop_bottom=0;
+ pixel_crop_top=0;
+ pixel_crop_left=0;
+ pixel_crop_right=0;
+ display_width=0;
+ display_height=0;
+ display_unit=0;
+ flag_interlaced=false;
+ }
+#ifdef WA_VALIDATE
+ bool pixel_width_found;
+ bool pixel_height_found;
+ bool pixel_crop_bottom_found;
+ bool pixel_crop_top_found;
+ bool pixel_crop_left_found;
+ bool pixel_crop_right_found;
+ bool display_width_found;
+ bool display_height_found;
+// bool display_unit_found;
+ bool flag_interlaced_found;
+#endif
+ };
+#pragma pack(push, 8)
+ struct AudioData
+ {
+ size_t struct_size;
+ uint64_t sampling_frequency;
+ uint64_t channels;
+ uint64_t bit_depth;
+ uint64_t output_sampling_frequency;
+ };
+#pragma pack(pop)
+ class Audio : public AudioData
+ {
+ public:
+ Audio()
+#ifdef WA_VALIDATE
+ :
+ sampling_frequency_found(false),
+ channels_found(false),
+// bit_depth_found(false),
+ output_sampling_frequency_found(false)
+#endif
+ {
+ struct_size = sizeof(AudioData);
+ sampling_frequency=8000;
+ channels=1;
+ bit_depth=0;
+ output_sampling_frequency=0;
+ }
+#ifdef WA_VALIDATE
+ bool sampling_frequency_found;
+ bool channels_found;
+// bool bit_depth_found;
+ bool output_sampling_frequency_found;
+#endif
+ };
+#pragma pack(push, 8)
+ struct TrackEntryData
+ {
+ size_t struct_size;
+ uint64_t track_number;
+ uint64_t track_uid;
+ uint64_t track_type;
+ bool flag_enabled;
+ bool flag_default;
+ bool flag_forced;
+ bool flag_lacing;
+ bool decode_all;
+ uint64_t min_cache;
+ uint64_t max_cache;
+ uint64_t default_duration;
+ double track_timecode_scale;
+ uint64_t max_block_additional_id;
+ char *name;
+ char *language;
+ char *codec_id;
+ void *codec_private;
+ size_t codec_private_len;
+ char *codec_name;
+ uint64_t attachment_link;
+ };
+#pragma pack(pop)
+ class TrackEntry : public TrackEntryData
+ {
+ public:
+ TrackEntry()
+#ifdef WA_VALIDATE
+ :
+ track_number_found(false),
+ track_uid_found(false),
+ track_type_found(false),
+ flag_enabled_found(false),
+ flag_default_found(false),
+ flag_forced_found(false),
+ flag_lacing_found(false),
+ min_cache_found(false),
+ max_cache_found(false),
+ default_duration_found(false),
+ track_timecode_scale_found(false),
+ max_block_additional_id_found(false),
+ decode_all_found(false),
+ name_found(false),
+ language_found(false),
+ codec_id_found(false),
+ codec_private_found(false),
+ codec_name_found(false)
+// attachment_link_found(false)
+#endif
+ {
+ struct_size = sizeof(TrackEntryData);
+ track_number = 0;
+ track_uid = 0;
+ track_type = 0;
+ flag_enabled = true;
+ flag_default = true;
+ flag_forced = false;
+ flag_lacing = false;
+ min_cache = 0;
+ max_cache = 0;
+ default_duration = 0;
+ track_timecode_scale = 0;
+ max_block_additional_id = 0;
+ decode_all = true;
+ name = 0;
+ language = 0;
+ codec_id = 0;
+ codec_private = 0;
+ codec_private_len = 0;
+ codec_name = 0;
+ attachment_link = 0;
+ }
+ ~TrackEntry()
+ {
+ free(name);
+ free(language);
+ free(codec_id);
+ free(codec_private);
+ free(codec_name);
+ }
+ void Own(char *&field, char *value)
+ {
+ if (field)
+ free(field);
+ field = value;
+ }
+ void OwnCodecPrivate(void *_codec_private, size_t _codec_private_len)
+ {
+ free(codec_private);
+ codec_private=_codec_private;
+ codec_private_len = _codec_private_len;
+ }
+
+ Video video;
+ Audio audio;
+#ifdef WA_VALIDATE
+ bool track_number_found;
+ bool track_uid_found;
+ bool track_type_found;
+ bool flag_enabled_found;
+ bool flag_default_found;
+ bool flag_forced_found;
+ bool flag_lacing_found;
+ bool min_cache_found;
+ bool max_cache_found;
+ bool default_duration_found;
+ bool track_timecode_scale_found;
+ bool max_block_additional_id_found;
+ bool decode_all_found;
+ bool name_found;
+ bool language_found;
+ bool codec_id_found;
+ bool codec_private_found;
+ bool codec_name_found;
+// bool attachment_link_found;
+#endif
+ };
+ class Tracks
+ {
+ public:
+ ~Tracks()
+ {
+ //tracks.deleteAll();
+ for (auto obj : tracks)
+ {
+ delete obj;
+ }
+ tracks.clear();
+ }
+ const nsmkv::TrackEntry *EnumTrack(size_t i) const;
+ typedef std::vector<nsmkv::TrackEntry*> TrackEntryList;
+ TrackEntryList tracks;
+ };
+ uint64_t ReadTracks(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Tracks &tracks);
+} \ No newline at end of file
diff --git a/Src/nsmkv/ebml_float.cpp b/Src/nsmkv/ebml_float.cpp
new file mode 100644
index 00000000..505b549d
--- /dev/null
+++ b/Src/nsmkv/ebml_float.cpp
@@ -0,0 +1,52 @@
+#include "ebml_float.h"
+
+double float_read_ptr_len(uint64_t len, const uint8_t *ptr)
+{
+
+// TODO: big endian support
+ if (len == 4)
+ {
+ float val;
+ uint8_t *dest = (uint8_t *)&val;
+ dest[3]=ptr[0];
+ dest[2]=ptr[1];
+ dest[1]=ptr[2];
+ dest[0]=ptr[3];
+ return val;
+ }
+ else if (len == 8)
+ {
+ double val;
+ uint8_t *dest = (uint8_t *)&val;
+ dest[7]=ptr[0];
+ dest[6]=ptr[1];
+ dest[5]=ptr[2];
+ dest[4]=ptr[3];
+ dest[3]=ptr[4];
+ dest[2]=ptr[5];
+ dest[1]=ptr[6];
+ dest[0]=ptr[7];
+ return val;
+ }
+ else if (len == 10)
+ {
+ long double val;
+ memset(&val, 0, sizeof(val));
+ uint8_t *dest = (uint8_t *)&val;
+ dest[9]=ptr[0];
+ dest[8]=ptr[1];
+ dest[7]=ptr[2];
+ dest[6]=ptr[3];
+ dest[5]=ptr[4];
+ dest[4]=ptr[5];
+ dest[3]=ptr[6];
+ dest[2]=ptr[7];
+ dest[1]=ptr[8];
+ dest[0]=ptr[9];
+ return val;
+ }
+ else
+ return 0;
+
+
+} \ No newline at end of file
diff --git a/Src/nsmkv/ebml_float.h b/Src/nsmkv/ebml_float.h
new file mode 100644
index 00000000..a695f95e
--- /dev/null
+++ b/Src/nsmkv/ebml_float.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <bfc/platform/types.h>
+
+/* len is passed as uint64_t, but better be 8 or less! */
+double float_read_ptr_len(uint64_t len, const uint8_t *ptr); \ No newline at end of file
diff --git a/Src/nsmkv/ebml_signed.cpp b/Src/nsmkv/ebml_signed.cpp
new file mode 100644
index 00000000..c78f0f46
--- /dev/null
+++ b/Src/nsmkv/ebml_signed.cpp
@@ -0,0 +1,15 @@
+#include "ebml_signed.h"
+#include "ebml_unsigned.h"
+
+int64_t signed_read_ptr_len(uint64_t len, const uint8_t *ptr)
+{
+ int64_t val = -1;
+ uint8_t *dest = (uint8_t *)&val;
+ for (int64_t i=0;i!=len;i++)
+ {
+ dest[len-i-1]=ptr[i];
+ }
+ return val;
+
+}
+
diff --git a/Src/nsmkv/ebml_signed.h b/Src/nsmkv/ebml_signed.h
new file mode 100644
index 00000000..f0e33a29
--- /dev/null
+++ b/Src/nsmkv/ebml_signed.h
@@ -0,0 +1,5 @@
+#pragma once
+#include <bfc/platform/types.h>
+
+/* len is passed as uint64_t, but better be 8 or less! */
+int64_t signed_read_ptr_len(uint64_t len, const uint8_t *ptr); \ No newline at end of file
diff --git a/Src/nsmkv/ebml_unsigned.cpp b/Src/nsmkv/ebml_unsigned.cpp
new file mode 100644
index 00000000..510f9cea
--- /dev/null
+++ b/Src/nsmkv/ebml_unsigned.cpp
@@ -0,0 +1,13 @@
+#include "ebml_unsigned.h"
+
+uint64_t unsigned_read_ptr_len(uint64_t len, const uint8_t *ptr)
+{
+ uint64_t val=*ptr++;
+ while (--len)
+ {
+ val <<= 8;
+ val |= *ptr++;
+ }
+ return val;
+}
+
diff --git a/Src/nsmkv/ebml_unsigned.h b/Src/nsmkv/ebml_unsigned.h
new file mode 100644
index 00000000..5f15840d
--- /dev/null
+++ b/Src/nsmkv/ebml_unsigned.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <bfc/platform/types.h>
+
+/* len is passed as uint64_t, but better be 8 or less! */
+uint64_t unsigned_read_ptr_len(uint64_t len, const uint8_t *ptr); \ No newline at end of file
diff --git a/Src/nsmkv/file_mkv_reader.cpp b/Src/nsmkv/file_mkv_reader.cpp
new file mode 100644
index 00000000..d2f3db17
--- /dev/null
+++ b/Src/nsmkv/file_mkv_reader.cpp
@@ -0,0 +1,55 @@
+#include "file_mkv_reader.h"
+
+MKVReaderFILE::MKVReaderFILE(FILE *f) : f(f)
+{
+}
+MKVReaderFILE::MKVReaderFILE(const wchar_t *filename)
+{
+ f = _wfopen(filename, L"rb");
+}
+
+int MKVReaderFILE::Read(void *buffer, size_t read_length, size_t *bytes_read)
+{
+ *bytes_read = fread(buffer, 1, read_length, f);
+ return nsmkv::READ_OK;
+}
+
+int MKVReaderFILE::Peek(void *buffer, size_t read_length, size_t *bytes_read)
+{
+ *bytes_read = fread(buffer, 1, read_length, f);
+ fseek(f, (long)(-read_length), SEEK_CUR);
+ return nsmkv::READ_OK;
+}
+
+int MKVReaderFILE::Seek(uint64_t position)
+{
+ fsetpos(f, (const fpos_t *)&position);
+ return nsmkv::READ_OK;
+}
+
+uint64_t MKVReaderFILE::Tell()
+{
+ uint64_t pos;
+ fgetpos(f, (fpos_t *)&pos);
+ return pos;
+}
+
+int MKVReaderFILE::Skip(uint64_t skip_bytes)
+{
+ _fseeki64(f, skip_bytes, SEEK_CUR);
+ return nsmkv::READ_OK;
+}
+
+MKVReaderFILE::~MKVReaderFILE()
+{
+ fclose(f);
+}
+
+uint64_t MKVReaderFILE::GetContentLength()
+{
+ uint64_t old = Tell();
+ Seek(0);
+ uint64_t content_length = Tell();
+ Seek(old);
+ return content_length;
+} \ No newline at end of file
diff --git a/Src/nsmkv/file_mkv_reader.h b/Src/nsmkv/file_mkv_reader.h
new file mode 100644
index 00000000..8b5ff62c
--- /dev/null
+++ b/Src/nsmkv/file_mkv_reader.h
@@ -0,0 +1,22 @@
+#pragma once
+#include "mkv_reader.h"
+#include <stdio.h>
+
+class MKVReaderFILE : public nsmkv::MKVReader
+{
+public:
+ MKVReaderFILE(FILE *f);
+ MKVReaderFILE(const wchar_t *filename);
+ ~MKVReaderFILE();
+
+ /* avi_reader implementation */
+ int Read(void *buffer, size_t read_length, size_t *bytes_read);
+ int Peek(void *buffer, size_t read_length, size_t *bytes_read);
+ int Seek(uint64_t position);
+ uint64_t Tell();
+ int Skip(uint64_t skip_bytes);
+ void GetFilename(wchar_t *fn, size_t fn_len) {}
+ uint64_t GetContentLength();
+private:
+ FILE *f;
+}; \ No newline at end of file
diff --git a/Src/nsmkv/global_elements.cpp b/Src/nsmkv/global_elements.cpp
new file mode 100644
index 00000000..08236fef
--- /dev/null
+++ b/Src/nsmkv/global_elements.cpp
@@ -0,0 +1,29 @@
+#include "global_elements.h"
+#include "read.h"
+
+uint64_t nsmkv::ReadGlobal(nsmkv::MKVReader *reader, uint64_t id, uint64_t size)
+{
+ switch(id)
+ {
+ case mkv_void:
+
+#ifdef WA_VALIDATE
+ printf("void (empty), size:%I64u\n", size);
+#endif
+ reader->Skip(size);
+ return size;
+ default:
+
+#ifdef WA_VALIDATE
+ printf("*** UNKNOWN ID *** ID:%I64x size:%I64u\n", id, size);
+#endif
+ reader->Skip(size);
+ return size;
+ }
+}
+
+uint64_t nsmkv::SkipNode(nsmkv::MKVReader *reader, uint64_t id, uint64_t size)
+{
+ reader->Skip(size);
+ return size;
+} \ No newline at end of file
diff --git a/Src/nsmkv/global_elements.h b/Src/nsmkv/global_elements.h
new file mode 100644
index 00000000..24cb74f4
--- /dev/null
+++ b/Src/nsmkv/global_elements.h
@@ -0,0 +1,15 @@
+#pragma once
+#include "mkv_reader.h"
+#include <bfc/platform/types.h>
+// IDs
+// these are slightly different from the matroska spec because we specify
+// values after vint decoding and they specify before
+const uint32_t mkv_void=0x6C;
+const uint32_t mkv_crc=0x3F;
+
+namespace nsmkv
+{
+ // doesn't really do anything but fseek, but will output unknown values in debug mode
+ uint64_t ReadGlobal(nsmkv::MKVReader *reader, uint64_t id, uint64_t size);
+ uint64_t SkipNode(nsmkv::MKVReader *reader, uint64_t id, uint64_t size); // same thing as ReadGlobal but doesn't display unknown nodes
+} \ No newline at end of file
diff --git a/Src/nsmkv/header.cpp b/Src/nsmkv/header.cpp
new file mode 100644
index 00000000..e67297a7
--- /dev/null
+++ b/Src/nsmkv/header.cpp
@@ -0,0 +1,138 @@
+#include "header.h"
+#include "read.h"
+#include "global_elements.h"
+
+#ifdef WA_VALIDATE
+extern uint64_t max_id_length;
+extern uint64_t max_size_length;
+#endif
+
+// returns bytes read. 0 means EOF
+uint64_t nsmkv::ReadHeader(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Header &header)
+{
+ uint64_t total_bytes_read=0;
+ while (size)
+ {
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node(reader, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_header_doctype:
+ {
+ char *utf8=0;
+ if (read_utf8(reader, node.size, &utf8) == 0)
+ return 0;
+
+ header.OwnDocType(utf8);
+#ifdef WA_VALIDATE
+ header.doctype_found = true;
+ printf(" DocType: %s\n", header.doctype);
+#endif
+ }
+ break;
+ case mkv_header_doctype_version:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+ header.doctype_version = val;
+#ifdef WA_VALIDATE
+ header.doctype_version_found = true;
+ printf(" DocType Version: %I64u\n", header.doctype_version);
+#endif
+ }
+ break;
+ case mkv_header_doctype_read_version:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+ header.doctype_read_version = val;
+#ifdef WA_VALIDATE
+ header.doctype_read_version_found = true;
+ printf(" DocType Read Version: %I64u\n", header.doctype_read_version);
+#endif
+ }
+ break;
+ case mkv_header_ebml_version:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+ header.ebml_version = val;
+#ifdef WA_VALIDATE
+ header.ebml_version_found = true;
+ printf(" EBML Version: %I64u\n", header.ebml_version);
+#endif
+ }
+ break;
+ case mkv_header_ebml_read_version:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+ header.ebml_read_version = val;
+#ifdef WA_VALIDATE
+ header.ebml_read_version_found = true;
+ printf(" EBML Read Version: %I64u\n", header.ebml_read_version);
+#endif
+ }
+ break;
+ case mkv_header_ebml_max_id_length:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+ header.ebml_max_id_length = val;
+#ifdef WA_VALIDATE
+ max_id_length = val;
+ header.ebml_max_id_length_found = true;
+ printf(" EBML Max ID Length: %I64u\n", header.ebml_max_id_length);
+#endif
+ }
+ break;
+ case mkv_header_ebml_max_size_length:
+ {
+ uint64_t val;
+ if (read_unsigned(reader, node.size, &val) == 0)
+ return 0;
+
+ header.ebml_max_size_length = val;
+#ifdef WA_VALIDATE
+ max_size_length = val;
+ header.ebml_max_size_length_found = true;
+ printf(" EBML Max Size Length: %I64u\n", header.ebml_max_size_length);
+#endif
+
+ }
+ break;
+ default:
+ {
+ if (ReadGlobal(reader, node.id, node.size) == 0)
+ return 0;
+ }
+ }
+ }
+
+ return total_bytes_read;
+}
diff --git a/Src/nsmkv/header.h b/Src/nsmkv/header.h
new file mode 100644
index 00000000..162a7c1c
--- /dev/null
+++ b/Src/nsmkv/header.h
@@ -0,0 +1,79 @@
+#pragma once
+#include <bfc/platform/types.h>
+#include "mkv_reader.h"
+
+// IDs
+// these are slightly different from the matroska spec because we specify
+// values after vint decoding and they specify before
+const uint32_t mkv_header=0xa45dfa3;
+const uint32_t mkv_header_ebml_version=0x286;
+const uint32_t mkv_header_ebml_read_version=0x2f7;
+const uint32_t mkv_header_ebml_max_id_length=0x2f2;
+const uint32_t mkv_header_ebml_max_size_length=0x2f3;
+const uint32_t mkv_header_doctype=0x282;
+const uint32_t mkv_header_doctype_read_version=0x285;
+const uint32_t mkv_header_doctype_version=0x287;
+
+namespace nsmkv
+{
+ class Header
+ {
+ public:
+ // defaults provided as per spec for matroska
+ // *_found variables indicate whether the field was found in the file
+ Header() :
+ ebml_version(1),
+ ebml_read_version(1),
+ ebml_max_id_length(4),
+ ebml_max_size_length(8),
+ doctype(0),
+ doctype_version(1),
+ doctype_read_version(1),
+ ebml_header_found(false)
+#ifdef WA_VALIDATE
+ ,
+ ebml_version_found(false),
+ ebml_read_version_found(false),
+ ebml_max_id_length_found(false),
+ ebml_max_size_length_found(false),
+ doctype_version_found(false),
+ doctype_found(false),
+ doctype_read_version_found(false)
+#endif
+ {
+ }
+ ~Header()
+ {
+ if (doctype)
+ free(doctype);
+ }
+ void OwnDocType(char *_doctype)
+ {
+ if (doctype)
+ free(doctype);
+ doctype = _doctype;
+ }
+
+ uint64_t ebml_version;
+ uint64_t ebml_read_version;
+ uint64_t ebml_max_id_length;
+ uint64_t ebml_max_size_length;
+ char *doctype;
+ uint64_t doctype_version;
+ uint64_t doctype_read_version;
+ bool ebml_header_found;
+
+#ifdef WA_VALIDATE
+ bool ebml_version_found;
+ bool ebml_read_version_found;
+ bool ebml_max_id_length_found;
+ bool doctype_found;
+ bool ebml_max_size_length_found;
+ bool doctype_version_found;
+ bool doctype_read_version_found;
+#endif
+ };
+
+ // returns bytes read. 0 means EOF
+ uint64_t ReadHeader(nsmkv::MKVReader *reader, uint64_t size, nsmkv::Header &header);
+};
diff --git a/Src/nsmkv/main.cpp b/Src/nsmkv/main.cpp
new file mode 100644
index 00000000..85497a44
--- /dev/null
+++ b/Src/nsmkv/main.cpp
@@ -0,0 +1,226 @@
+#include "vint.h"
+#include "header.h"
+#include "ebml_float.h"
+#include "segment.h"
+#include "ebml_unsigned.h"
+#include "ebml_signed.h"
+#include <stdio.h>
+#include <assert.h>
+#include "SeekTable.h"
+#include <time.h>
+#include "parser.h"
+
+#include "SegmentInfo.h"
+#include "global_elements.h"
+#include "Tracks.h"
+#include "Cluster.h"
+#include "Cues.h"
+#include "Chapters.h"
+#include "Tags.h"
+#include "read.h"
+#include "Attachments.h"
+
+using namespace nsmkv;
+static nsmkv::SeekTable seekTable;
+static nsmkv::Header header;
+static SegmentInfo segment_info;
+static Tracks tracks;
+static Cues cues;
+static Attachments attachments;
+static Tags tags;
+
+uint64_t max_id_length = header.ebml_max_id_length;
+uint64_t max_size_length = header.ebml_max_size_length;
+
+uint32_t num_seekhead_elements_found = 0;
+uint32_t num_seek_elements_found = 0;
+
+bool ebml_segment_found = false;
+
+uint64_t segment_data_offset = 0;
+
+// returns bytes read. 0 means EOF
+uint64_t read_vsint(FILE *f, int64_t *val)
+{
+ uint8_t data[9];
+ size_t bytes_read = fread(data, 1, 1, f);
+ if (bytes_read != 1)
+ return 0;
+ uint8_t length = vint_get_number_bytes(data[0]);
+ bytes_read = fread(data+1, 1, length, f);
+ if (bytes_read != length)
+ return 0;
+
+ *val = vsint_read_ptr_len(length+1, data);
+ return bytes_read+1;
+}
+
+
+
+
+char *read_utf8(FILE *f, size_t size)
+{
+ char *doctype = (char *)malloc(size + 1);
+ if (doctype)
+ {
+ doctype[size]=0;
+ if (fread(doctype, 1, size, f) == size)
+ return doctype;
+
+ }
+ free(doctype);
+ return 0;
+}
+
+// returns bytes read. 0 means EOF
+uint64_t ReadSegment(FILE *f, uint64_t size)
+{
+ uint64_t total_bytes_read=0;
+
+ // store the segment element data offset for later use
+ segment_data_offset = ftell(f);
+#ifdef WA_VALIDATE
+ printf("[%I64u] Segment element data offset\n", segment_data_offset);
+#endif
+
+ while (size)
+ {
+ uint64_t this_position = ftell(f);
+#ifdef WA_VALIDATE
+ printf("[%I64u] ", this_position);
+#endif
+ ebml_node node;
+ uint64_t bytes_read = read_ebml_node((nsmkv::MKVReader*)f, &node);
+
+ if (bytes_read == 0)
+ return 0;
+
+ // benski> checking bytes_read and node.size separately prevents possible integer overflow attack
+ if (bytes_read > size)
+ return 0;
+ total_bytes_read+=bytes_read;
+ size-=bytes_read;
+
+ if (node.size > size)
+ return 0;
+ total_bytes_read+=node.size;
+ size-=node.size;
+
+ switch(node.id)
+ {
+ case mkv_segment_attachments:
+ {
+ printf(" Attachments\n");
+ ReadAttachment((nsmkv::MKVReader*)f, node.size, attachments);
+ }
+ break;
+ case mkv_metaseek_seekhead:
+ {
+ printf(" SeekHead\n");
+ ReadSeekHead((nsmkv::MKVReader*)f, node.size, seekTable);
+ }
+ break;
+ case mkv_segment_segmentinfo:
+ {
+ printf(" SegmentInfo\n");
+ ReadSegmentInfo((nsmkv::MKVReader*)f, node.size, segment_info);
+ }
+ break;
+ case mkv_segment_tracks:
+ {
+ printf(" Tracks\n");
+ ReadTracks((nsmkv::MKVReader*)f, node.size, tracks);
+ }
+ break;
+ case mkv_segment_cues:
+ {
+ printf(" Cues\n");
+ ReadCues((nsmkv::MKVReader*)f, node.size, cues);
+ }
+ break;
+ case mkv_segment_cluster:
+ {
+ printf(" Clusters\n");
+ nsmkv::Cluster cluster;
+ ReadCluster((nsmkv::MKVReader*)f, node.size, cluster);
+ }
+ break;
+ case mkv_segment_chapters:
+ {
+ printf(" Chapters\n");
+ }
+ break;
+ case mkv_segment_tags:
+ {
+ printf(" Tags\n");
+ nsmkv::ReadTags(f, node.size, tags);
+ }
+ break;
+ default:
+ ReadGlobal((nsmkv::MKVReader*)f, node.id, node.size);
+ }
+ }
+ return total_bytes_read;
+}
+
+int main()
+{
+// char *file_in = "\\\\o2d2\\ftp\\usr\\nullsoft\\test media\\mkv\\Ratatouille.2007.nHD.720.x264.NhaNc3.mkv";
+ //char *file_in = "\\\\o2d2\\ftp\\usr\\nullsoft\\test media\\mkv\\cham_mp4v_aac.mkv";
+ char *file_in = "c:/users/benski/desktop/metadata.mkv";
+
+ FILE *f = fopen(file_in, "rb");
+ if (f == NULL)
+ {
+ printf("****Error attempting to open file: %s\n",file_in);
+ return -1;
+ }
+ else
+ {
+ printf("Starting Processing of File: %s\n",file_in);
+ }
+
+ ebml_node node;
+
+ while (read_ebml_node((nsmkv::MKVReader*)f, &node))
+ {
+ switch(node.id)
+ {
+ case mkv_header:
+ if (header.ebml_header_found == false)
+ {
+#ifdef WA_VALIDATE
+ printf("MKV header found, processing...\n");
+#endif
+ header.ebml_header_found = true;
+ nsmkv::ReadHeader((nsmkv::MKVReader*)f, node.size, header);
+ }
+ else
+ {
+#ifdef WA_VALIDATE
+ printf("Extra MKV header found, ignoring...\n");
+#endif
+ nsmkv::Header extraHeader;
+ nsmkv::ReadHeader((nsmkv::MKVReader*)f, node.size, extraHeader);
+ }
+ break;
+ case mkv_segment:
+ printf("MKV Segment element found, processing\n");
+#ifdef WA_VALIDATE
+ ebml_segment_found = true;
+#endif
+ ReadSegment(f, node.size);
+ break;
+ default:
+ ReadGlobal((nsmkv::MKVReader*)f, node.id, node.size);
+ }
+ }
+
+// seekTable.Dump();
+
+ fclose(f);
+
+ printf("Number of SeekHead elements found: %I32u\n",num_seekhead_elements_found);
+ printf("Number of Seek elements found: %I32u\n",num_seek_elements_found);
+
+}
diff --git a/Src/nsmkv/mkv_date.cpp b/Src/nsmkv/mkv_date.cpp
new file mode 100644
index 00000000..f8ba8dc4
--- /dev/null
+++ b/Src/nsmkv/mkv_date.cpp
@@ -0,0 +1,8 @@
+#include "mkv_date.h"
+
+__time64_t mkv_date_as_time_t(mkv_date_t val)
+{
+ __time64_t val_time = (__time64_t)val / 1000ULL /*nano->micro*/ / 1000ULL /*micro->milli*/ / 1000ULL /*milli->second*/;
+ val_time+=978325200ULL;
+ return val_time;
+} \ No newline at end of file
diff --git a/Src/nsmkv/mkv_date.h b/Src/nsmkv/mkv_date.h
new file mode 100644
index 00000000..8b296437
--- /dev/null
+++ b/Src/nsmkv/mkv_date.h
@@ -0,0 +1,5 @@
+#pragma once
+#include <bfc/platform/types.h>
+typedef uint64_t mkv_date_t;
+
+__time64_t mkv_date_as_time_t(mkv_date_t d); \ No newline at end of file
diff --git a/Src/nsmkv/mkv_reader.h b/Src/nsmkv/mkv_reader.h
new file mode 100644
index 00000000..8c7ceb90
--- /dev/null
+++ b/Src/nsmkv/mkv_reader.h
@@ -0,0 +1,43 @@
+#pragma once
+#include <bfc/platform/types.h>
+#include <stdio.h>
+
+namespace nsmkv
+{
+ // return codes from mkv_reader functions
+
+ enum
+ {
+ READ_OK = 0,
+ READ_EOF = 1,
+ READ_FAILED = 2,
+ READ_INVALID_DATA = 3, // read was successful but data didn't make any sense
+ READ_INVALID_CALL = 4, // wrong time to call this function
+ READ_NOT_FOUND = 5, // requested item doesn't exist in the file
+ READ_OUT_OF_MEMORY = 6, // some malloc failed and so we're aborting
+ READ_DISCONNECT = 7,
+ };
+
+class MKVReader
+{
+public:
+ virtual int Read(void *buffer, size_t read_length, size_t *bytes_read)=0;
+
+ // TODO: need to put an upper bound on Peek buffer sizes
+ virtual int Peek(void *buffer, size_t read_length, size_t *bytes_read)=0;
+
+ // in_mkv will call this before descending into certain chunks that will be read entirely (e.g. avih)
+ // you aren't required to do anything in response
+ virtual void OverlappedHint(uint32_t read_length){}
+
+ virtual int Seek(uint64_t position)=0;
+
+ virtual uint64_t Tell()=0;
+
+ // skip ahead a certain number of bytes. equivalent to fseek(..., SEEK_CUR)
+ virtual int Skip(uint64_t skip_bytes)=0;
+ virtual uint64_t GetContentLength()=0;
+ virtual void GetFilename(wchar_t *fn, size_t len)=0;
+};
+
+} \ No newline at end of file
diff --git a/Src/nsmkv/nsmkv.h b/Src/nsmkv/nsmkv.h
new file mode 100644
index 00000000..0081a915
--- /dev/null
+++ b/Src/nsmkv/nsmkv.h
@@ -0,0 +1,10 @@
+#pragma once
+#include "header.h"
+#include "read.h"
+#include "global_elements.h"
+#include "segment.h"
+#include "SeekTable.h"
+#include "SegmentInfo.h"
+#include "Tracks.h"
+#include "Cluster.h"
+#include "Lacing.h" \ No newline at end of file
diff --git a/Src/nsmkv/nsmkv.sln b/Src/nsmkv/nsmkv.sln
new file mode 100644
index 00000000..06273a7a
--- /dev/null
+++ b/Src/nsmkv/nsmkv.sln
@@ -0,0 +1,19 @@
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsmkv", "nsmkv.vcproj", "{83C58A3A-1DD7-4E11-9EC0-DDB85494FB65}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {83C58A3A-1DD7-4E11-9EC0-DDB85494FB65}.Debug|Win32.ActiveCfg = Debug|Win32
+ {83C58A3A-1DD7-4E11-9EC0-DDB85494FB65}.Debug|Win32.Build.0 = Debug|Win32
+ {83C58A3A-1DD7-4E11-9EC0-DDB85494FB65}.Release|Win32.ActiveCfg = Release|Win32
+ {83C58A3A-1DD7-4E11-9EC0-DDB85494FB65}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Src/nsmkv/nsmkv.vcxproj b/Src/nsmkv/nsmkv.vcxproj
new file mode 100644
index 00000000..080bcd8c
--- /dev/null
+++ b/Src/nsmkv/nsmkv.vcxproj
@@ -0,0 +1,254 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>17.0</VCProjectVersion>
+ <ProjectGuid>{83C58A3A-1DD7-4E11-9EC0-DDB85494FB65}</ProjectGuid>
+ <RootNamespace>nsmkv</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>17.0.32505.173</_ProjectFileVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Vcpkg">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Vcpkg">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;WA_VALIDATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention />
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../h264dec/ldecod/inc;../h264dec/lcommon/inc;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;WA_VALIDATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>../h264dec/ldecod/inc;../h264dec/lcommon/inc;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>../h264dec/ldecod/inc;../h264dec/lcommon/inc;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="Attachments.cpp" />
+ <ClCompile Include="Chapters.cpp" />
+ <ClCompile Include="Cluster.cpp" />
+ <ClCompile Include="Cues.cpp" />
+ <ClCompile Include="ebml_float.cpp" />
+ <ClCompile Include="ebml_signed.cpp" />
+ <ClCompile Include="ebml_unsigned.cpp" />
+ <ClCompile Include="global_elements.cpp" />
+ <ClCompile Include="header.cpp" />
+ <ClCompile Include="Lacing.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="mkv_date.cpp" />
+ <ClCompile Include="read.cpp" />
+ <ClCompile Include="SeekTable.cpp" />
+ <ClCompile Include="SegmentInfo.cpp" />
+ <ClCompile Include="Tags.cpp" />
+ <ClCompile Include="Tracks.cpp" />
+ <ClCompile Include="vint.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="Attachments.h" />
+ <ClInclude Include="Chapters.h" />
+ <ClInclude Include="Cluster.h" />
+ <ClInclude Include="Cues.h" />
+ <ClInclude Include="ebml_float.h" />
+ <ClInclude Include="ebml_signed.h" />
+ <ClInclude Include="ebml_unsigned.h" />
+ <ClInclude Include="global_elements.h" />
+ <ClInclude Include="header.h" />
+ <ClInclude Include="Lacing.h" />
+ <ClInclude Include="mkv_date.h" />
+ <ClInclude Include="read.h" />
+ <ClInclude Include="SeekTable.h" />
+ <ClInclude Include="segment.h" />
+ <ClInclude Include="SegmentInfo.h" />
+ <ClInclude Include="Tags.h" />
+ <ClInclude Include="Tracks.h" />
+ <ClInclude Include="vint.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/nsmkv/nsmkv.vcxproj.filters b/Src/nsmkv/nsmkv.vcxproj.filters
new file mode 100644
index 00000000..39724ac0
--- /dev/null
+++ b/Src/nsmkv/nsmkv.vcxproj.filters
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx</Extensions>
+ </Filter>
+ <Filter Include="ebml">
+ <UniqueIdentifier>{812f40e9-f7c9-40ae-9973-aa073d98f2ce}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="Attachments.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Chapters.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Cluster.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Cues.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="global_elements.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="header.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Lacing.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="mkv_date.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="read.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SeekTable.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SegmentInfo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Tags.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Tracks.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vint.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ebml_float.cpp">
+ <Filter>ebml</Filter>
+ </ClCompile>
+ <ClCompile Include="ebml_signed.cpp">
+ <Filter>ebml</Filter>
+ </ClCompile>
+ <ClCompile Include="ebml_unsigned.cpp">
+ <Filter>ebml</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="Attachments.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Chapters.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Cluster.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Cues.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="global_elements.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="header.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="mkv_date.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="read.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SeekTable.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="segment.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SegmentInfo.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Tags.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Tracks.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="vint.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ebml_float.h">
+ <Filter>ebml</Filter>
+ </ClInclude>
+ <ClInclude Include="ebml_signed.h">
+ <Filter>ebml</Filter>
+ </ClInclude>
+ <ClInclude Include="ebml_unsigned.h">
+ <Filter>ebml</Filter>
+ </ClInclude>
+ <ClInclude Include="Lacing.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/nsmkv/read.cpp b/Src/nsmkv/read.cpp
new file mode 100644
index 00000000..ea58e6f2
--- /dev/null
+++ b/Src/nsmkv/read.cpp
@@ -0,0 +1,154 @@
+#include "read.h"
+#include "vint.h"
+#include "ebml_float.h"
+#include "ebml_unsigned.h"
+#include "ebml_signed.h"
+#include <limits.h>
+#ifdef WA_VALIDATE
+extern uint64_t max_id_length;
+extern uint64_t max_size_length;
+#endif
+
+// returns bytes read. 0 means EOF
+uint64_t read_vint(nsmkv::MKVReader *reader, uint64_t *val)
+{
+ uint8_t data[9] = {0};
+ size_t bytes_read = 0;
+ reader->Read(data, 1, &bytes_read);
+
+ if (bytes_read != 1)
+ return 0;
+ uint8_t length = vint_get_number_bytes(data[0]);
+ reader->Read(data+1, length, &bytes_read);
+ if (bytes_read != length)
+ return 0;
+
+ *val = vint_read_ptr(data);
+ return bytes_read+1;
+}
+
+
+// returns bytes read. 0 means EOF
+uint64_t read_ebml_node(nsmkv::MKVReader *reader, ebml_node *node)
+{
+ uint64_t bytes_read = read_vint(reader, &node->id);
+ if (!bytes_read)
+ return 0;
+
+ bytes_read += read_vint(reader, &node->size);
+
+ return bytes_read;
+}
+
+uint64_t read_utf8(nsmkv::MKVReader *reader, uint64_t size, char **utf8)
+{
+ if (utf8)
+ {
+ if (size == SIZE_MAX) // prevent integer overflow
+ return 0;
+
+ char *&val = *utf8;
+ val = (char *)calloc((size_t)size + 1, sizeof(char));
+ if (val)
+ {
+ val[size]=0;
+ size_t bytes_read;
+ reader->Read(val, (size_t)size, &bytes_read);
+ if (bytes_read != (size_t)size)
+ {
+ free(val);
+ return 0;
+ }
+
+ return size;
+ }
+ return 0; // actually, out of memory and not EOF, but still we should abort ASAP
+ }
+ else
+ {
+ reader->Skip(size);
+ return size;
+ }
+}
+
+#if 0
+int fseek64(nsmkv::MKVReader *reader, int64_t pos, int whence)
+{
+ switch(whence)
+ {
+ case SEEK_SET:
+ return fsetpos(f, &pos);
+ case SEEK_CUR:
+ {
+ fpos_t curpos=0;
+ int ret = fgetpos(f, &curpos);
+ if (ret != 0)
+ return ret;
+ pos+=curpos;
+ return fsetpos(f, &pos);
+ }
+ case SEEK_END:
+ {
+ return _fseeki64(f, pos, SEEK_END);
+ }
+ }
+ return 1;
+}
+
+int64_t ftell64(nsmkv::MKVReader *reader)
+{
+ fpos_t pos;
+ if (fgetpos(f, &pos) == 0)
+ return pos;
+ else
+ return -1L;
+}
+#endif
+uint64_t read_unsigned(nsmkv::MKVReader *reader, uint64_t size, uint64_t *val)
+{
+ uint8_t data[8] = {0};
+ if (size == 0 || size > 8)
+ return 0;
+
+ size_t bytes_read = 0;
+ reader->Read(data, (size_t)size, &bytes_read);
+ if (bytes_read != size)
+ {
+ return 0;
+ }
+ *val = unsigned_read_ptr_len(size, data);
+ return size;
+}
+
+uint64_t read_float(nsmkv::MKVReader *reader, uint64_t size, double *val)
+{
+ uint8_t data[10] = {0};
+ if (size == 0 || size > 10)
+ return 0;
+
+ size_t bytes_read = 0;
+ reader->Read(data, (size_t)size, &bytes_read);
+ if (bytes_read != size)
+ {
+ return 0;
+ }
+ *val = float_read_ptr_len(size, data);
+ return size;
+}
+
+uint64_t read_signed(nsmkv::MKVReader *reader, uint64_t size, int64_t *val)
+{
+ uint8_t data[8] = {0};
+ if (size == 0 || size > 8)
+ return 0;
+
+ size_t bytes_read = 0;
+ reader->Read(data, (size_t)size, &bytes_read);
+ if (bytes_read != size)
+ {
+ return 0;
+ }
+ *val = signed_read_ptr_len(size, data);
+ return size;
+}
+
diff --git a/Src/nsmkv/read.h b/Src/nsmkv/read.h
new file mode 100644
index 00000000..5277ecc6
--- /dev/null
+++ b/Src/nsmkv/read.h
@@ -0,0 +1,18 @@
+#pragma once
+#include <windows.h>
+#include <bfc/platform/types.h>
+#include "mkv_reader.h"
+
+struct ebml_node
+{
+ uint64_t id;
+ uint64_t size;
+};
+
+// returns bytes read. 0 means EOF
+uint64_t read_ebml_node(nsmkv::MKVReader *reader, ebml_node *node);
+uint64_t read_vint(nsmkv::MKVReader *reader, uint64_t *val);
+uint64_t read_utf8(nsmkv::MKVReader *reader, uint64_t size, char **utf8);
+uint64_t read_unsigned(nsmkv::MKVReader *reader, uint64_t size, uint64_t *val);
+uint64_t read_float(nsmkv::MKVReader *reader, uint64_t size, double *val);
+uint64_t read_signed(nsmkv::MKVReader *reader, uint64_t size, int64_t *val); \ No newline at end of file
diff --git a/Src/nsmkv/segment.h b/Src/nsmkv/segment.h
new file mode 100644
index 00000000..3a87d14a
--- /dev/null
+++ b/Src/nsmkv/segment.h
@@ -0,0 +1,14 @@
+#pragma once
+#include <bfc/platform/types.h>
+// IDs
+// these are slightly different from the matroska spec because we specify
+// values after vint decoding and they specify before
+
+// Header
+const uint32_t mkv_segment = 0x8538067;
+
+// Cluster
+
+
+// Cueing Data
+
diff --git a/Src/nsmkv/vint.cpp b/Src/nsmkv/vint.cpp
new file mode 100644
index 00000000..4124f084
--- /dev/null
+++ b/Src/nsmkv/vint.cpp
@@ -0,0 +1,91 @@
+#include <bfc/platform/types.h>
+
+#ifdef _MSC_VER
+#include <intrin.h>
+static uint32_t __inline clz(uint32_t value)
+{
+ DWORD leading_zero = 0;
+ if (_BitScanReverse(&leading_zero, value))
+ {
+ return 31 - leading_zero;
+ }
+ else
+ {
+ return 32;
+ }
+}
+#endif
+
+uint8_t vint_get_number_bytes(uint8_t first_byte)
+{
+ return (uint8_t)clz((uint32_t)first_byte) - 24;
+}
+
+static uint8_t masks[] =
+{
+ 0x7F, // 0111 1111
+ 0x3F, // 0011 1111
+ 0x1F, // 0001 1111
+ 0x0F, // 0000 1111
+ 0x07, // 0000 0111
+ 0x03, // 0000 0011
+ 0x01, // 0000 0001
+ 0x00, // 0000 0000
+};
+
+/* call if you already know the len (e.g. from vint_get_number_bytes earlier */
+uint64_t vint_read_ptr_len(uint8_t len, const uint8_t *ptr)
+{
+ uint64_t ret = masks[len] & ptr[0];
+
+ while (len--)
+ {
+ ret <<= 8;
+ ret |= *++ptr;
+ }
+ return ret;
+}
+
+uint64_t vint_read_ptr(const uint8_t *ptr)
+{
+ uint8_t len = vint_get_number_bytes(ptr[0]);
+ return vint_read_ptr_len(len, ptr);
+}
+
+bool vint_unknown_length(uint8_t len, const uint8_t *ptr)
+{
+ if (masks[len] != (masks[len] & ptr[0]))
+ return false;
+
+ while (len--)
+ {
+ if (*++ptr == 0xFF)
+ return false;
+ }
+ return true;
+}
+
+static int64_t vsint_substr[] =
+{
+ 0x3F,
+ 0x1FFF,
+ 0x0FFFFF,
+ 0x07FFFFFF,
+ 0x03FFFFFFFF,
+ 0x01FFFFFFFFFF,
+ 0x00FFFFFFFFFFFF,
+ 0x007FFFFFFFFFFFFF,
+};
+
+int64_t vsint_read_ptr_len(uint8_t len, const uint8_t *ptr)
+{
+ uint64_t val = vint_read_ptr_len(len, ptr);
+ return val - vsint_substr[len];
+}
+
+int64_t vsint_read_ptr(const uint8_t *ptr)
+{
+ uint8_t len = vint_get_number_bytes(ptr[0]);
+ uint64_t val = vint_read_ptr(ptr);
+ return val - vsint_substr[len];
+} \ No newline at end of file
diff --git a/Src/nsmkv/vint.h b/Src/nsmkv/vint.h
new file mode 100644
index 00000000..03c7c284
--- /dev/null
+++ b/Src/nsmkv/vint.h
@@ -0,0 +1,14 @@
+#pragma once
+#include <bfc/platform/types.h>
+
+uint8_t vint_get_number_bytes(uint8_t first_byte);
+/* call if you already know the len (e.g. from vint_get_number_bytes earlier */
+uint64_t vint_read_ptr_len(uint8_t len, const uint8_t *ptr);
+int64_t vsint_read_ptr_len(uint8_t len, const uint8_t *ptr);
+
+/* don't call this unless you're sure that you have enough room in the buffer! */
+uint64_t vint_read_ptr(const uint8_t *ptr);
+int64_t vsint_read_ptr(const uint8_t *ptr);
+
+/* values encoded as all 1's are supposed to indicate 'unknown value' */
+bool vint_unknown_length(uint8_t len, const uint8_t *ptr); \ No newline at end of file