Skip to content

Commit

Permalink
patterns: Added pattern for ID3 tags (WerWolv#48)
Browse files Browse the repository at this point in the history
* Add naive first implementation of ID3 patterns

* Refine string handling a bit

* Optimize structs using the static keyword

* Add MIME type pragma and update README
  • Loading branch information
flimberger authored Oct 9, 2022
1 parent 665c50b commit 6cb208d
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Hex patterns, include patterns and magic files for the use with the ImHex Hex Ed
| BSON | `application/bson` | [`patterns/bson.hexpat`](patterns/bson.hexpat) | BSON (Binary JSON) format |
| msgpack | `application/x-msgpack` | [`patterns/msgpack.hexpat`](patterns/msgpack.hexpat) | MessagePack binary serialization format |
| MiniDump | `application/x-dmp` | [`patterns/minidump.hexpat`](patterns/minidump.hexpat) | Windows MiniDump files |
| ID3 | `audio/mpeg` | [`patterns/id3tag.hexpat`](patterns/id3tag.hexpat) | ID3 tags in MP3 files |

### Scripts

Expand Down
156 changes: 156 additions & 0 deletions patterns/id3.hexpat
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#pragma MIME audio/mpeg

#include <std/mem.pat>

namespace v1 {

struct Tag {
char header[3];
char title[30];
char artist[30];
char album[30];
char year[4];
char comment[30];
u8 zero;
u8 track;
u8 genre;
} [[static]];

}

namespace v2 {

struct SyncSafeInt {
u8 bytes[4];
} [[sealed, static, format("v2::ssi_value"), transform("v2::ssi_value")]];

fn ssi_value(ref SyncSafeInt n) {
u32 result = 0;
for (u8 i = 0, i < 4, i = i + 1) {
u8 byteval = n.bytes[i] & 0x7F;
u8 shift = 7 * (4 - i - 1);
result = result | (byteval << shift);
}
return result;
};

struct TagVersion {
u8 major;
u8 revision;
};

bitfield TagHeaderFlags {
unsynchronized : 1;
extended : 1;
experimental : 1;
footer : 1;
padding : 4;
};

struct TagHeader {
char identifier[3];
TagVersion version;
TagHeaderFlags flags;
SyncSafeInt size;
} [[static]];

bitfield ExtendedFlag {
padding : 1;
update : 1;
crcpresent : 1;
restrictions : 1;
padding : 4;
};

struct TagExtendedHeader {
SyncSafeInt size;
u8 nflagbytes;
ExtendedFlag flags[nflagbytes];
u8 data[size];
};

struct TagFooter {
char identifier[3];
TagVersion version;
TagHeaderFlags flags;
SyncSafeInt size;
} [[static]];

bitfield FrameFlags {
padding : 1;
tagalterpreservation : 1;
filealterpreservation : 1;
readonly : 1;
padding : 5;
groupid : 1;
padding : 2;
compressed : 1;
encrypted : 1;
unzynchronized : 1;
datalengthindicator : 1;
};

enum TextEncoding : u8 {
ISO88591 = 0x00,
UTF16BOM = 0x01,
UTF16BE = 0x02,
UTF8 = 0x03,
};

struct StringData {
TextEncoding encoding;
if (encoding == TextEncoding::UTF16BOM) {
u16 bom;
char16 data[(parent.size - 3) / 2];
} else if (encoding == TextEncoding::UTF16BE)
be char16 data[(parent.size - 1) / 2];
else
char data[parent.size - 1];
};

// Hack to work around the fact, that chars may not be compared
union FrameId {
char cdata[4];
u8 ndata[4];
} [[sealed, static, format("v2::impl::format_frame_id")]];

namespace impl {

fn format_frame_id(ref FrameId id) {
return id.cdata;
};

}

struct Frame {
FrameId id;
SyncSafeInt size;
FrameFlags flags;
if (id.ndata[0] == 'T')
StringData data;
else
u8 data[size];
};

fn has_next_frame(u32 size) {
bool hasnext = $ < size;
if (hasnext) {
hasnext = std::mem::read_unsigned($, 1) != 0;
$ = $ - 1;
}
return hasnext;
};

struct Tag {
TagHeader header;
if (header.flags.extended)
TagExtendedHeader extendedheader;
Frame frames[while(v2::has_next_frame(header.size))];
if (header.flags.footer)
TagFooter footer;
};

}

v2::Tag tag @ 0;
v1::Tag oldtag @ std::mem::size() - 128;

0 comments on commit 6cb208d

Please sign in to comment.