Created
March 10, 2023 18:46
-
-
Save jminor/410d0fcfe642074961d1b7626e616ae9 to your computer and use it in GitHub Desktop.
ImHex pattern file for QuickTime MOV files.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 you might try one of these as alternatives:
- https://gist.github.com/Zaggy1024/a030cf23069ed62d452161f579ed272f
- WerWolv/ImHex-Patterns#129 (mp4 is very similar to mov)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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