aboutsummaryrefslogtreecommitdiff
path: root/lib/compiler/resinator/ani.zig
blob: 88064d2219e4eb0c36085a0e7a230331939e36a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//! https://en.wikipedia.org/wiki/Resource_Interchange_File_Format
//! https://www.moon-soft.com/program/format/windows/ani.htm
//! https://www.gdgsoft.com/anituner/help/aniformat.htm
//! https://www.lomont.org/software/aniexploit/ExploitANI.pdf
//!
//! RIFF( 'ACON'
//!   [LIST( 'INFO' <info_data> )]
//!   [<DISP_ck>]
//!   anih( <ani_header> )
//!   [rate( <rate_info> )]
//!   ['seq '( <sequence_info> )]
//!   LIST( 'fram' icon( <icon_file> ) ... )
//! )

const std = @import("std");

const AF_ICON: u32 = 1;

pub fn isAnimatedIcon(reader: *std.Io.Reader) bool {
    const flags = getAniheaderFlags(reader) catch return false;
    return flags & AF_ICON == AF_ICON;
}

fn getAniheaderFlags(reader: *std.Io.Reader) !u32 {
    const riff_header = try reader.takeArray(4);
    if (!std.mem.eql(u8, riff_header, "RIFF")) return error.InvalidFormat;

    _ = try reader.takeInt(u32, .little); // size of RIFF chunk

    const form_type = try reader.takeArray(4);
    if (!std.mem.eql(u8, form_type, "ACON")) return error.InvalidFormat;

    while (true) {
        const chunk_id = try reader.takeArray(4);
        const chunk_len = try reader.takeInt(u32, .little);
        if (!std.mem.eql(u8, chunk_id, "anih")) {
            // TODO: Move file cursor instead of skipBytes
            try reader.discardAll(chunk_len);
            continue;
        }

        const aniheader = try reader.takeStruct(ANIHEADER, .little);
        return aniheader.flags;
    }
}

/// From Microsoft Multimedia Data Standards Update April 15, 1994
const ANIHEADER = extern struct {
    cbSizeof: u32,
    cFrames: u32,
    cSteps: u32,
    cx: u32,
    cy: u32,
    cBitCount: u32,
    cPlanes: u32,
    jifRate: u32,
    flags: u32,
};