master
 1//! https://en.wikipedia.org/wiki/Resource_Interchange_File_Format
 2//! https://www.moon-soft.com/program/format/windows/ani.htm
 3//! https://www.gdgsoft.com/anituner/help/aniformat.htm
 4//! https://www.lomont.org/software/aniexploit/ExploitANI.pdf
 5//!
 6//! RIFF( 'ACON'
 7//!   [LIST( 'INFO' <info_data> )]
 8//!   [<DISP_ck>]
 9//!   anih( <ani_header> )
10//!   [rate( <rate_info> )]
11//!   ['seq '( <sequence_info> )]
12//!   LIST( 'fram' icon( <icon_file> ) ... )
13//! )
14
15const std = @import("std");
16
17const AF_ICON: u32 = 1;
18
19pub fn isAnimatedIcon(reader: *std.Io.Reader) bool {
20    const flags = getAniheaderFlags(reader) catch return false;
21    return flags & AF_ICON == AF_ICON;
22}
23
24fn getAniheaderFlags(reader: *std.Io.Reader) !u32 {
25    const riff_header = try reader.takeArray(4);
26    if (!std.mem.eql(u8, riff_header, "RIFF")) return error.InvalidFormat;
27
28    _ = try reader.takeInt(u32, .little); // size of RIFF chunk
29
30    const form_type = try reader.takeArray(4);
31    if (!std.mem.eql(u8, form_type, "ACON")) return error.InvalidFormat;
32
33    while (true) {
34        const chunk_id = try reader.takeArray(4);
35        const chunk_len = try reader.takeInt(u32, .little);
36        if (!std.mem.eql(u8, chunk_id, "anih")) {
37            // TODO: Move file cursor instead of skipBytes
38            try reader.discardAll(chunk_len);
39            continue;
40        }
41
42        const aniheader = try reader.takeStruct(ANIHEADER, .little);
43        return aniheader.flags;
44    }
45}
46
47/// From Microsoft Multimedia Data Standards Update April 15, 1994
48const ANIHEADER = extern struct {
49    cbSizeof: u32,
50    cFrames: u32,
51    cSteps: u32,
52    cx: u32,
53    cy: u32,
54    cBitCount: u32,
55    cPlanes: u32,
56    jifRate: u32,
57    flags: u32,
58};