From bb19cb43ee68646bde44b7f0c0dc8465a4fea2e2 Mon Sep 17 00:00:00 2001 From: blondecake433 <40399900+blondecake433@users.noreply.github.com> Date: Sat, 17 Jun 2023 16:07:08 +0300 Subject: [PATCH] patterns:/mp4: Added mp4 pattern (#129) --- README.md | 1 + patterns/mp4.hexpat | 286 ++++++++++++++++++++++++ tests/patterns/test_data/mp4.hexpat.mp4 | Bin 0 -> 1983 bytes 3 files changed, 287 insertions(+) create mode 100644 patterns/mp4.hexpat create mode 100644 tests/patterns/test_data/mp4.hexpat.mp4 diff --git a/README.md b/README.md index a11cf52..5f7a7c3 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | Mach-O | `application/x-mach-binary` | [`patterns/macho.hexpat`](patterns/macho.hexpat) | Mach-O executable | | MIDI | `audio/midi` | [`patterns/midi.hexpat`](patterns/midi.hexpat) | MIDI header, event fields provided | | MiniDump | `application/x-dmp` | [`patterns/minidump.hexpat`](patterns/minidump.hexpat) | Windows MiniDump files | +| mp4 | `video/mp4` | [`patterns/mp4.hexpat`](patterns/mp4.hexpat) | MPEG-4 Part 14 digital multimedia container format | | msgpack | `application/x-msgpack` | [`patterns/msgpack.hexpat`](patterns/msgpack.hexpat) | MessagePack binary serialization format | | NACP | | [`patterns/nacp.hexpat`](patterns/nacp.hexpat) | Nintendo Switch NACP files | | NBT | | [`patterns/nbt.hexpat`](patterns/nbt.hexpat) | Minecraft NBT format | diff --git a/patterns/mp4.hexpat b/patterns/mp4.hexpat new file mode 100644 index 0000000..435f8eb --- /dev/null +++ b/patterns/mp4.hexpat @@ -0,0 +1,286 @@ +#pragma endian big + +#pragma MIME audio/mp4 +#pragma MIME video/mp4 +#pragma MIME application/mp4 + +#include +#include +#include + +fn to_string(auto var) { + return str(var); +}; + +fn format_string(auto string) { + return string.value; +}; + +struct string { + char value[std::mem::find_sequence_in_range(0, $, std::mem::size(), 0x00) - $]; +} [[sealed, format("format_string")]]; + +struct BaseBox { + u64 boxSize = 0; + u64 startOffset = $; + u64 endOffset = 0; + + u32 size; + char type[4]; + + // Calculate the size of the current box + // 1. If the size is equal to 1 -> the box size is equal to 'largeSize' attribute. + // 2. If the size is equal to 0 -> The box extends to the end of the file. + // 3. Otherwise, size is equaly to the 'size' attribute. + if (this.size == 1) { + u64 largeSize; + boxSize = largeSize; + endOffset = startOffset + boxSize; + } else if (this.size == 0) { + boxSize = std::mem::size() - startOffset; + endOffset = std::mem::size(); + } else { + boxSize = size; + endOffset = startOffset + boxSize; + } + + if (this.type == "uuid") { + char usertype[16]; + } +}; + +struct FullBox : BaseBox { + u8 version; + u24 flags; +}; + +struct UnknownBox : BaseBox { + u8 unk[while($ != endOffset)]; +}; + +using brand = u32 [[format("to_string")]]; +struct FileTypeBox : BaseBox { + brand major_brand; + u32 minor_version; + brand compatible_brands[(endOffset - $) / sizeof(brand)]; +}; + +struct MovieHeaderBox : FullBox { + if (this.version == 1) { + u64 creation_time; + u64 modification_time; + u32 timescale; + u64 duration; + } else { // version == 0 + u32 creation_time; + u32 modification_time; + u32 timescale; + u32 duration; + } + u32 rate [[comment("Fixed point number 16.16")]]; + u16 volume [[comment("Fixed point number 8.8")]]; + u8 reserved[10] [[sealed]]; + u32 matrix[9]; + u32 preview_time; + u32 preview_duration; + u32 poster_time; + u32 selection_time; + u32 selection_duration; + u32 current_time; + u32 next_track_id; +}; + +struct TrackHeaderBox : FullBox { + if (this.version == 1) { + u64 creation_time; + u64 modification_time; + u32 track_id; + u32 reserved; + u64 duration; + } else { // version == 0 + u32 creation_time; + u32 modification_time; + u32 track_id; + u32 reserved; + u32 duration; + } + u32 reserved_2[2] [[sealed]]; + s16 layer; + s16 alternate_group; + s16 volume; + u16 reserved_3; + s32 matrix[9]; + u32 width; + u32 height; +}; + +struct DataEntryBox : FullBox { + if (std::string::contains(this.type, "url")) { + string location; + } else if (std::string::contains(this.type, "urn")) { + string name; + string location; + } else { + std::error("Invalid DataEntryBox"); + } +}; + +struct DataReferenceBox : FullBox { + u32 entry_count; + DataEntryBox data_entries[this.entry_count]; +}; + +struct SubDataInformationBox { + u32 type = std::mem::read_unsigned($ + 4, 4, std::mem::Endian::Big); + + match (str(type)) { + ("dref"): DataReferenceBox box [[inline]]; + (_): UnknownBox box [[inline]]; + } +} [[name(std::format("DataInformationBox({})", box.type))]]; + +struct DataInformationBox : BaseBox { + SubDataInformationBox box[while($ < endOffset)] [[inline]]; +}; + +struct HandlerBox : FullBox { + u32 component_type; + u32 handler_type; + u32 reserved[3]; + char name[endOffset - $]; +}; + +struct VideoMediaHeaderBox : FullBox { + u16 graphicsmode; + u16 opcolor[3]; +}; + +struct SubMediaInformationBox { + u32 type = std::mem::read_unsigned($ + 4, 4, std::mem::Endian::Big); + + match (str(type)) { + ("vmhd"): VideoMediaHeaderBox box [[inline]]; + ("hdlr"): HandlerBox box [[inline]]; + ("dinf"): DataInformationBox box [[inline]]; + (_): UnknownBox box [[inline]]; + // TODO: Add stbl + } +} [[name(std::format("MediaInformationBox({})", box.type))]]; + +struct MediaInformationBox : BaseBox { + SubMediaInformationBox box[while($ < endOffset)] [[inline]]; +}; + +struct MediaHeaderBox : FullBox { + if (this.version == 1) { + u64 creation_time; + u64 modification_time; + u32 timescale; + u64 duration; + } else { // version==0 + u32 creation_time; + u32 modification_time; + u32 timescale; + u32 duration; + } + u16 language [[comment("ISO-639-2/T language code")]]; + u16 quality; +}; + +struct SubMediaBox { + u32 type = std::mem::read_unsigned($ + 4, 4, std::mem::Endian::Big); + + match (str(type)) { + ("mdhd"): MediaHeaderBox box [[inline]]; + ("hdlr"): HandlerBox box [[inline]]; + ("minf"): MediaInformationBox box [[inline]]; + (_): UnknownBox box [[inline]]; + } +} [[name(std::format("MediaBox({})", box.type))]]; + +struct MediaBox : BaseBox { + SubMediaBox box[while($ < endOffset)] [[inline]]; +}; + +struct EditListEntry64 { + u64 segment_duration; + s64 media_time; + s16 media_rate_integer; + s16 media_rate_fraction; +}; + +struct EditListEntry32 { + u32 segment_duration; + s32 media_time; + s16 media_rate_integer; + s16 media_rate_fraction; +}; + +struct EditListBox : FullBox { + u32 entry_count; + if (this.version == 1) { + EditListEntry64 entry_list[this.entry_count]; + } else { // version 0 + EditListEntry32 entry_list[this.entry_count]; + } +}; + +struct SubEditBox { + u32 type = std::mem::read_unsigned($ + 4, 4, std::mem::Endian::Big); + + match (str(type)) { + ("elst"): EditListBox box [[inline]]; + (_): UnknownBox box [[inline]]; + } +} [[name(std::format("EditBox({})", box.type))]]; + +struct EditBox : BaseBox { + SubEditBox box[while($ < endOffset)] [[inline]]; +}; + +struct SubTrackBox { + u32 type = std::mem::read_unsigned($ + 4, 4, std::mem::Endian::Big); + + match (str(type)) { + ("mdia"): MediaBox box [[inline]]; + ("edts"): EditBox box [[inline]]; + ("tkhd"): TrackHeaderBox box [[inline]]; + (_): UnknownBox box [[inline]]; + } +} [[name(std::format("TrackBox({})", box.type))]]; + +struct TrackBox : BaseBox { + SubTrackBox box[while($ < endOffset)] [[inline]]; +}; + +struct SubMovieBox { + u32 type = std::mem::read_unsigned($ + 4, 4, std::mem::Endian::Big); + + match (str(type)) { + ("mvhd"): MovieHeaderBox box [[inline]]; + ("trak"): TrackBox box [[inline]]; + (_): UnknownBox box [[inline]]; + // TODO: Add "iods" box + } +} [[name(std::format("MovieBox({})", box.type))]]; + +struct MovieBox : BaseBox { + SubMovieBox box[while($ < endOffset)] [[inline]]; +}; + +struct MediaDataBox : BaseBox { + u8 data[while($ < endOffset)] [[sealed]]; +}; + +struct Box { + u32 type = std::mem::read_unsigned($ + 4, 4, std::mem::Endian::Big); + + match (str(type)) { + ("ftyp"): FileTypeBox box [[inline]]; + ("moov"): MovieBox box [[inline]]; + ("mdat"): MediaDataBox box [[inline]]; + (_): UnknownBox box [[inline]]; + } +} [[name(std::format("Box({})", box.type))]]; + +Box mp4[while(!std::mem::eof())] @ 0x0; diff --git a/tests/patterns/test_data/mp4.hexpat.mp4 b/tests/patterns/test_data/mp4.hexpat.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..871cd283682acf5f23c624c5324fb10785c031c9 GIT binary patch literal 1983 zcmZWqeM}Tb6rYpBP&Gm$MMYy*#jgs-?j2A#x)x$VQnkfu6^VMC+nwVUc5j#61@360 z;3rD8F%3kcXbpc^# zsJ%J$Jab|Ez59N9RtA&C2&zsP4xsK}b21q>CM1Jpfhe+pLHg|Cf*ENUO#b}oU?~y- zi0P^pkcr1+>@4d@v$J+PD7~hsIn&c?YHDoNl1Nk;`)sOSmQGKxc}+zI8&xwU)n_;v z0lTrlbBsjwK(207D&98;_0^VnD6j zE-e695_vmdEMko&;dZ$tY|uj_orKb=AyS#wbOWbq#OEqgHDJR^4O}Zo0Alm@O!g^s zDUuJij3E#o5vm|bmR?iGw4ypOyx^`2u4m~|UKe;l_b`eZ%1YCM4R3eYSf-LD!?U(5 zfEo=PlGx8@<$%I4i3XLEH0Tk0570u0g)5=`w3(<5Axjr&ScxVCJEva%Vp+d1Y(|Us zvD%v4`i`e_-rdzFcHSCu_EJa2S#Pm;dvQVi{KG>@?#GAPTBnu%a`G}hkTvge!e-~l z^2wd+7qwp}3!LevQS!v2`4?ZE(e~irckL;E4!7oZ_I9^UYdSUdYLB^Z4jDimHCF!` zAG0Ir=aMpM$&&09vrjKQa};g5DRup@=UnN~mHW$`4P(y7_jz`&i*4$kd1b?uyV;X7 zR(#VG`|g)-$1i^J;EE*Z(yoG@%guWhbZ=kY?e8$r-oG{!d#xYM&AwJXb83RIx_4Sv zV{rV^gpD0z`zAJ|9^1ZW_mD%|l6O8FI{3`-OSKDTPjQ=P@2vUs#Al}uTHmwXuvSi7 zwW&9MUD?SrR9^cqY5%c$ztEeV(vmsnaIW`*LCv3JTFso?wZ|L>mVLeTmCfJB47xXJ zdDpIWvvNw#^`U;xjhC6Gk|U3Y{dA+TaL}asKdd)f#ln%z%0J7G+jfmw*zwwfJN18; zHU^j{bFUuh9ADSIF#ge--A)H7>}&b)e&Jqwef1EoBFz%} zqejdv*o;FK=ysI$`3(@ykqG5Q+x{O9fX6N$KH3VP7MT^HGh?F6BGJZ0>B2C=M2Hvm zc~oEbbRv{OMALxXXd)Z%z=j6(2*(ZiTE10834@MN#20ga1Jnc1`&}WwG_NS@Ap{G3 zIwP3M`Sd9*!ah+Z)W#yhugL=-v04cQjlO1z80JQc@ROy^FepEyN|%{1wxk%QTLyf@ zFpWqR8ig9iNR