summaryrefslogtreecommitdiff
path: root/src/file_type.zig
blob: 18dd7a4a50f39152d9a36830ace85d486cfe7f11 (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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
const std = @import("std");

const type_details: []const struct {
    typ: FileType,
    sigs: []const []const ?u8,
    exts: []const []const u8,
} = &.{
    .{
        .typ = .jpeg,
        .sigs = &.{
            &.{ 0xFF, 0xD8, 0xFF, 0xDB },
            &.{ 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01 },
            &.{ 0xFF, 0xD8, 0xFF, 0xEE },
            &.{ 0xFF, 0xD8, 0xFF, 0xE1, null, null, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 },
            &.{ 0xFF, 0xD8, 0xFF, 0xE0 },
        },
        .exts = &.{ ".jpg", ".jpeg", ".jfif" },
    },
    .{
        .typ = .png,
        .sigs = &.{&.{ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }},
        .exts = &.{".png"},
    },
    .{
        .typ = .gif,
        .sigs = &.{
            &.{ 'G', 'I', 'F', '8', '7', 'a' },
            &.{ 'G', 'I', 'F', '8', '9', 'a' },
        },
        .exts = &.{".gif"},
    },
    .{
        .typ = .bmp,
        .sigs = &.{&.{ 'B', 'M' }},
        .exts = &.{".bmp"},
    },
    .{
        .typ = .qoi,
        .sigs = &.{&.{ 'q', 'o', 'i', 'f' }},
        .exts = &.{".qoi"},
    },
    .{
        .typ = .webp,
        .sigs = &.{
            &.{ 0x52, 0x49, 0x46, 0x46, null, null, null, null, 0x57, 0x45, 0x42, 0x50 },
        },
        .exts = &.{".webp"},
    },
};

/// This is a helper for detecting file types based on magic bytes.
///
/// Ref: https://en.wikipedia.org/wiki/List_of_file_signatures
pub const FileType = enum {
    /// JPEG image file.
    jpeg,

    /// PNG image file.
    png,

    /// GIF image file.
    gif,

    /// BMP image file.
    bmp,

    /// QOI image file.
    qoi,

    /// WebP image file.
    webp,

    /// Unknown file format.
    unknown,

    /// Detect file type based on the magic bytes
    /// at the start of the provided file contents.
    pub fn detect(contents: []const u8) FileType {
        inline for (type_details) |typ| {
            inline for (typ.sigs) |signature| {
                if (contents.len >= signature.len) {
                    for (contents[0..signature.len], signature) |f, sig| {
                        if (sig) |s| if (f != s) break;
                    } else {
                        return typ.typ;
                    }
                }
            }
        }
        return .unknown;
    }

    /// Guess file type from its extension.
    pub fn guessFromExtension(extension: []const u8) FileType {
        inline for (type_details) |typ| {
            inline for (typ.exts) |ext| {
                if (std.ascii.eqlIgnoreCase(extension, ext)) return typ.typ;
            }
        }
        return .unknown;
    }
};