-
-
Notifications
You must be signed in to change notification settings - Fork 255
Open
Description
Hey all, I had a go with making a pattern for APEv2 but didn't have any luck, but it's really beyond me.
The funny this is that the APE tag can go at the end for just about any file.
This is the spec:
https://wiki.hydrogenaud.io/index.php?title=APEv2_specification
and there is one for fq:
https://github.com/wader/fq/blob/master/format/ape/apev2.go
#pragma description "APEv2 Tags"
#pragma MIME "audio/mpeg"
namespace APEv2 {
struct TagHeader {
char preamble[8]; // "APETAGEX"
u32 version; // Usually 2000 (0x000007D0)
u32 tag_size; // Total size including header/footer
u32 item_count; // Number of items
u32 flags; // Tag flags
u8 reserved[8]; // Always zero
};
bitfield ItemFlags {
unused0 : 6; // Bits 0-5
binary : 1; // Bit 6: Binary item flag
unused1 : 25; // Bits 7-31
};
// Function to find length of null-terminated key
fn get_key_length(u32 start) {
u32 pos = start;
while (true) {
u8 current_byte = std::read(pos);
if (current_byte == 0) {
break;
}
pos += 1;
}
return pos - start;
};
struct TagItem {
u32 item_size; // Size of value data
ItemFlags flags; // Item flags (includes binary flag)
u32 key_start = $; // Capture starting position
u32 key_length = get_key_length(key_start); // Precompute key length
char key[key_length]; // Null-terminated key
u8 key_terminator; // Null byte after key
if (flags.binary) {
u32 filename_start = $;
u32 filename_length = get_key_length(filename_start); // Null-terminated filename
char filename[filename_length];
u8 filename_terminator; // Null byte after filename
u32 remaining_size = item_size - (key_length + 1 + filename_length + 1);
u8 value[remaining_size]; // Remaining bytes as raw value
} else {
char value[item_size]; // UTF-8 value
}
};
struct TagFooter : TagHeader {};
// Function to check for next item
fn has_next_item(u32 tag_start, u32 tag_size) {
bool has_next = $ < (tag_start + tag_size - sizeof(TagFooter));
if (has_next) {
has_next = std::read($) != 0; // Check for padding/end
}
return has_next;
};
struct Tag {
// Start with footer at end of file
TagFooter footer @ std::end - sizeof(TagFooter);
if (footer.preamble == "APETAGEX" && footer.version == 2000) {
u32 tag_start = std::end - footer.tag_size;
TagHeader header @ tag_start;
// Note: No check for item_count > 1000 as imHex doesn't support error throwing
$ = tag_start + sizeof(TagHeader);
TagItem items[while(has_next_item(tag_start, footer.tag_size))];
}
};
}
APEv2::Tag apev2_tag @ 0;
Cheers,
Steve
Metadata
Metadata
Assignees
Labels
No labels