Skip to content

Instantly share code, notes, and snippets.

@summivox
Created November 25, 2022 07:37
Show Gist options
  • Save summivox/b511db55f36f39f2dd424866f1273b7b to your computer and use it in GitHub Desktop.
Save summivox/b511db55f36f39f2dd424866f1273b7b to your computer and use it in GitHub Desktop.
MP4 & GPMF ImHex Pattern definitions (partial)
#pragma eval_depth 50
#pragma endian big
#include <std/io.pat>
enum GpmfElemType : char {
CONTAINER = '\x00',
COMPLEX = '?',
I8 = 'b',
U8 = 'B',
I16 = 's',
U16 = 'S',
I32 = 'l',
U32 = 'L',
F32 = 'f',
I64 = 'j',
U64 = 'J',
F64 = 'd',
CHAR = 'c',
FOURCC = 'F',
GUID = 'G',
Q15_16 = 'q',
Q31_32 = 'Q',
DATETIME = 'U',
};
fn GpmfPayloadSize(u8 elem_size, u16 num_elems) {
return (le u32(elem_size) * le u32(num_elems));
};
using GpmfBox;
struct GpmfContainer<auto size> {
GpmfBox children[while($ < addressof(parent) + size + 8)];
};
struct Array<T, auto n> {
T elem[n];
};
struct GpmfBox {
char fourcc[4];
GpmfElemType data_type;
u8 elem_size;
u16 num_elems;
if (data_type == GpmfElemType::CONTAINER) {
GpmfContainer<GpmfPayloadSize(elem_size, num_elems)> children;
} else if (data_type == GpmfElemType::I8) {
if (elem_size == 1) {
s8 a[num_elems];
} else {
Array<s8, elem_size/1> a[num_elems];
}
} else if (data_type == GpmfElemType::U8) {
if (elem_size == 1) {
u8 a[num_elems];
} else {
Array<u8, elem_size/1> a[num_elems];
}
} else if (data_type == GpmfElemType::I16) {
if (elem_size == 2) {
s16 a[num_elems];
} else {
Array<s16, elem_size/2> a[num_elems];
}
} else if (data_type == GpmfElemType::U16) {
if (elem_size == 2) {
u16 a[num_elems];
} else {
Array<u16, elem_size/2> a[num_elems];
}
} else if (data_type == GpmfElemType::I32 || data_type == GpmfElemType::Q15_16) {
if (elem_size == 4) {
s32 a[num_elems];
} else {
Array<s32, elem_size/4> a[num_elems];
}
} else if (data_type == GpmfElemType::U32) {
if (elem_size == 4) {
u32 a[num_elems];
} else {
Array<u32, elem_size/4> a[num_elems];
}
} else if (data_type == GpmfElemType::I64 || data_type == GpmfElemType::Q31_32) {
if (elem_size == 8) {
s64 a[num_elems];
} else {
Array<s64, elem_size/8> a[num_elems];
}
} else if (data_type == GpmfElemType::U64) {
if (elem_size == 8) {
u64 a[num_elems];
} else {
Array<u64, elem_size/8> a[num_elems];
}
} else if (data_type == GpmfElemType::F32) {
if (elem_size == 4) {
float a[num_elems];
} else {
Array<float, elem_size/4> a[num_elems];
}
} else if (data_type == GpmfElemType::F64) {
if (elem_size == 8) {
double a[num_elems];
} else {
Array<double, elem_size/8> a[num_elems];
}
} else if (data_type == GpmfElemType::CHAR && elem_size == 1) {
char a[num_elems];
} else if (data_type == GpmfElemType::FOURCC && elem_size == 4) {
char a[num_elems * 4];
} else {
u8 payload[GpmfPayloadSize(elem_size, num_elems)];
}
$ = ($ - addressof(fourcc) + 3) / 4 * 4 + addressof(fourcc);
};
// GpmfBox root @ 0;
#pragma endian big
#pragma eval_depth 100
#include <std/ctype.pat>
#include <std/core.pat>
#include <std/mem.pat>
#include <std/io.pat>
#include <std/string.pat>
#include <std/ptr.pat>
struct Mp4BoxHeader {
u32 len;
char fourcc[4];
if (len == 1) {
u64 len64;
}
};
fn Probe(auto base) {
u32 x = std::mem::read_unsigned(base, 4, 0x00000000);
// assuming we are on little endian system --- builtin endianness conversion did not work somehow...
u32 len =
(((x >> 0) & 0xFF) << 24) |
(((x >> 8) & 0xFF) << 16) |
(((x >> 16) & 0xFF) << 8) |
(((x >> 24) & 0xFF) << 0);
str fourcc = std::mem::read_string(base + 4, 4);
// std::print("probe @ {:08x} => len={}, fourcc={}", base, len, fourcc);
if (len < 8) {
return false;
}
for (u8 i = 0, i < 4, i = i + 1) {
if (!std::ctype::isalpha(std::string::at(fourcc, i))) {
return false;
}
}
return true;
};
fn ProbeLimit(auto base, auto pbase, auto len) {
std::print("ProbeLimit(base={:08x}, pbase={:08x}, len={:08x})", base, pbase, len);
if (base >= pbase + len) {
return false;
}
return Probe(base);
};
bitfield TkhdFlags {
enabled : 1;
in_movie : 1;
in_preview : 1;
in_poster: 1;
padding : 20;
};
struct Tkhd {
u8 version;
TkhdFlags flags;
u32 creation_time;
u32 modification_time;
u32 track_id;
padding[4];
u32 duration;
padding[8];
u16 layer;
u16 alternate_group;
u16 volume;
padding[2];
u32 matrix[9];
u32 track_width;
u32 track_height;
};
struct Mdhd {
u8 version;
u24 flags;
u32 creation_time;
u32 modification_time;
u32 time_scale;
u32 duration;
u16 language;
u16 quality;
};
struct SttsEntry {
u32 sample_count;
u32 sample_duration;
};
struct SttsTable {
u8 version;
u24 flags;
u32 num_entries;
SttsEntry entries[num_entries];
};
struct StscEntry {
u32 first_chunk;
u32 samples_per_chunk;
u32 sample_description_id;
};
struct StscTable {
u8 version;
u24 flags;
u32 num_entries;
StscEntry entries[num_entries];
};
struct StszTable {
u8 version;
u24 flags;
u32 size;
u32 num_samples;
if (size == 0) {
u32 sizes[num_samples];
}
};
struct StcoTable {
u8 version;
u24 flags;
u32 num_chunks;
u32 chunk_offsets[num_chunks];
};
using Mp4Box;
struct Mp4Children<auto L> {
Mp4Box children[while(ProbeLimit($, addressof(parent), L))];
};
struct Mp4Box : Mp4BoxHeader {
if (Probe($)) {
Mp4Children<len> children;
} else if (fourcc == "tkhd") {
Tkhd tkhd;
} else if (fourcc == "mdhd") {
Mdhd mdhd;
} else if (fourcc == "stts") {
SttsTable stts;
} else if (fourcc == "stsc") {
StscTable stsc;
} else if (fourcc == "stsz") {
StszTable stsz;
} else if (fourcc == "stco") {
StcoTable stco;
} else {
// std::print("{:x}", $);
if (len == 0) {
} else {
u8 payload[len - 8];
}
}
$ = addressof(len) + len;
};
struct Mp4 {
Mp4Box root[while(Probe($))];
};
Mp4 mp4 @ 0;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment