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};