diff options
Diffstat (limited to 'Src/nsmkv')
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 |