Skip to content

Instantly share code, notes, and snippets.

@jminor
Created March 10, 2023 18:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jminor/410d0fcfe642074961d1b7626e616ae9 to your computer and use it in GitHub Desktop.
Save jminor/410d0fcfe642074961d1b7626e616ae9 to your computer and use it in GitHub Desktop.
ImHex pattern file for QuickTime MOV files.
// MOV QuickTime File Format
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html#//apple_ref/doc/uid/TP40000939-CH203-BBCGDDDF
// See also Apple's "Atom Inspector" application available on the Apple Developer downloads portal.
// RIP Atomic Dumpster.
#include <std/mem.pat>
#include <std/sys.pat>
#pragma endian big
struct Fixed16 {
u8 real;
u8 fraction;
};
struct Fixed32 {
u16 real;
u16 fraction;
};
union AtomType {
char name[4];
u32 id [[hidden]];
} [[format("format_AtomType")]];
fn format_AtomType(AtomType x) {
return std::format("'{}' ({})", x.name, x.id);
};
struct Atom {
u128 start = $;
u32 size;
AtomType type;
bool done = false;
// known types
if (type.name == "wide") {
// 'wide' reserves space right before an atom which might want
// to expand to an extended size, so that the expanded header
// can overwrite the 'wide' atom.
std::assert_warn(size == 8, "'wide' atom with non-standard size");
}else if (type.name == "ftyp") {
// File type compatibility—identifies the file type and differentiates
// it from similar file types, such as MPEG-4 files and JPEG-2000
// files. If present, this atom must be the first atom in the file.
if ($ != 8) {
std::warning("ftyp atom should be first.");
}
AtomType Major_Brand;
u32 Minor_Version;
AtomType Compatible_Brands[while($ < start + size)];
done = true;
}else if (type.name == "moov") {
// Movie resource metadata about the movie (number and type of
// tracks, location of sample data, and so on). Describes where
// the movie data can be found and how to interpret it.
Atom children[while($ < start + size)];
done = true;
}else if (type.name == "mdat") {
// Movie sample data—media samples such as video frames and groups
// of audio samples. Usually this data can be interpreted only by
// using the movie resource.
}else if (type.name == "mvhd") {
u8 version;
u24 flags;
u32 creation_time;
u32 modification_time;
u32 time_scale;
u32 duration;
u32 preferred_rate;
u16 preferred_volume;
u8 reserved[10];
u8 matrix[36];
u32 preview_time;
u32 preview_duration;
u32 poster_time;
u32 selection_time;
u32 selection_duration;
u32 current_time;
u32 next_track_id;
done = true;
}else if (type.name == "tkhd") {
u8 version;
u24 flags;
u32 creation_time;
u32 modification_time;
u32 trackID;
u8 reserved[4];
u32 duration;
u8 reserved[8];
u16 layer;
u16 alternate_group;
Fixed16 volume;
u8 reserved[2];
u8 matrix[36];
Fixed32 track_width;
Fixed32 track_height;
done = true;
}else if (type.name == "hdlr") {
u8 version;
u24 flags;
u32 component_type;
u32 component_subtype;
u32 component_manufacturer;
u32 component_flags;
u32 component_flags_mask;
u8 name_length;
char name[name_length];
done = true;
}else if (type.name == "clip" ||
type.name == "udta" ||
type.name == "matt" ||
type.name == "edts" ||
type.name == "mdia" ||
type.name == "minf" ||
type.name == "dinf" ||
type.name == "stbl" ||
type.name == "trak" ) {
// These all contain nested atoms.
Atom children[while($ < start + size)];
done = true;
}
if (done) {
s128 delta = $ - (start + size);
if (delta != 0) {
std::warning(std::format("Atom '{}' length mismatch (delta {})", type.name, delta));
}
}else{
if (size == 0) {
// this is the last Atom, which extends to the end of the file.
u8 data[while($ < std::mem::size())];
}else if (size == 1) {
// Extended size
// TODO: Can any of the special case atoms
// handled above also use extended size?
u64 extendedSize;
u8 data[extendedSize-16];
}else{
u8 data[size-8];
}
}
} [[name(std::format("'{}' atom", type.name))]];
Atom topLevelAtoms[while($ < std::mem::size())] @ 0;
if ($ != std::mem::size()) {
std::warning("Mismatch between end of file and end of atoms.");
}
@cbenhagen
Copy link

Thanks for sharing this! Unfortunatlely ImHex runs out of memory when using ARRI sample files. You can get some here:
https://app.frame.io/reviews/3caaceb0-6b80-4aae-aac5-7c138166f248/16a0ad6e-1b46-40e3-be72-98a77d76ea90

@jminor
Copy link
Author

jminor commented Jun 25, 2023

@cbenhagen you might try one of these as alternatives:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment