1 | // Aseprite Document IO Library |
2 | // Copyright (c) 2021 Igara Studio S.A. |
3 | // Copyright (c) 2016-2018 David Capello |
4 | // |
5 | // This file is released under the terms of the MIT license. |
6 | // Read LICENSE.txt for more information. |
7 | |
8 | #include "dio/detect_format.h" |
9 | |
10 | #include "base/file_handle.h" |
11 | #include "base/fs.h" |
12 | #include "base/string.h" |
13 | #include "flic/flic_details.h" |
14 | |
15 | #include <cstring> |
16 | |
17 | #define ASE_MAGIC_NUMBER 0xA5E0 |
18 | #define BMP_MAGIC_NUMBER 0x4D42 // "BM" |
19 | #define JPG_MAGIC_NUMBER 0xD8FF |
20 | #define GIF_87_STAMP "GIF87a" |
21 | #define GIF_89_STAMP "GIF89a" |
22 | #define PNG_MAGIC_DWORD1 0x474E5089 |
23 | #define PNG_MAGIC_DWORD2 0x0A1A0A0D |
24 | #define WEBP_STAMP_1 "RIFF" // "RIFFnnnnWEBP" |
25 | #define WEBP_STAMP_2 "WEBP" |
26 | #define PSD_STAMP "8BPS" |
27 | |
28 | namespace dio { |
29 | |
30 | FileFormat detect_format(const std::string& filename) |
31 | { |
32 | FileFormat ff = detect_format_by_file_content(filename); |
33 | if (ff == FileFormat::UNKNOWN) |
34 | ff = detect_format_by_file_extension(filename); |
35 | return ff; |
36 | } |
37 | |
38 | FileFormat detect_format_by_file_content_bytes(const uint8_t* buf, |
39 | const int n) |
40 | { |
41 | #define IS_MAGIC_WORD(offset, word) \ |
42 | ((buf[offset+0] == (word & 0xff)) && \ |
43 | (buf[offset+1] == ((word & 0xff00) >> 8))) |
44 | |
45 | #define IS_MAGIC_DWORD(offset, dword) \ |
46 | ((buf[offset+0] == (dword & 0xff)) && \ |
47 | (buf[offset+1] == ((dword & 0xff00) >> 8)) && \ |
48 | (buf[offset+2] == ((dword & 0xff0000) >> 16)) && \ |
49 | (buf[offset+3] == ((dword & 0xff000000) >> 24))) |
50 | |
51 | if (n >= 2) { |
52 | if (n >= 6) { |
53 | if (n >= 8) { |
54 | if (n >= 12) { |
55 | if (std::strncmp((const char*)buf, WEBP_STAMP_1, 4) == 0 || |
56 | std::strncmp((const char*)buf+8, WEBP_STAMP_2, 4) == 0) |
57 | return FileFormat::WEBP_ANIMATION; |
58 | } |
59 | |
60 | if (IS_MAGIC_DWORD(0, PNG_MAGIC_DWORD1) && |
61 | IS_MAGIC_DWORD(4, PNG_MAGIC_DWORD2)) |
62 | return FileFormat::PNG_IMAGE; |
63 | } |
64 | |
65 | if (std::strncmp((const char*)buf, GIF_87_STAMP, 6) == 0 || |
66 | std::strncmp((const char*)buf, GIF_89_STAMP, 6) == 0) |
67 | return FileFormat::GIF_ANIMATION; |
68 | |
69 | if (std::strncmp((const char*)buf, PSD_STAMP, 4) == 0) |
70 | return FileFormat::PSD_IMAGE; |
71 | |
72 | if (IS_MAGIC_WORD(4, ASE_MAGIC_NUMBER)) |
73 | return FileFormat::ASE_ANIMATION; |
74 | |
75 | if (IS_MAGIC_WORD(4, FLI_MAGIC_NUMBER) || |
76 | IS_MAGIC_WORD(4, FLC_MAGIC_NUMBER)) |
77 | return FileFormat::FLIC_ANIMATION; |
78 | } |
79 | |
80 | if (IS_MAGIC_WORD(0, BMP_MAGIC_NUMBER)) |
81 | return FileFormat::BMP_IMAGE; |
82 | |
83 | if (IS_MAGIC_WORD(0, JPG_MAGIC_NUMBER)) |
84 | return FileFormat::JPEG_IMAGE; |
85 | } |
86 | |
87 | return FileFormat::UNKNOWN; |
88 | } |
89 | |
90 | FileFormat detect_format_by_file_content(const std::string& filename) |
91 | { |
92 | base::FileHandle handle(base::open_file(filename.c_str(), "rb" )); |
93 | if (!handle) |
94 | return FileFormat::ERROR; |
95 | |
96 | FILE* f = handle.get(); |
97 | uint8_t buf[12]; |
98 | int n = (int)fread(buf, 1, 12, f); |
99 | |
100 | return detect_format_by_file_content_bytes(buf, n); |
101 | } |
102 | |
103 | FileFormat detect_format_by_file_extension(const std::string& filename) |
104 | { |
105 | // By extension |
106 | const std::string ext = base::string_to_lower(base::get_file_extension(filename)); |
107 | |
108 | if (ext == "ase" || |
109 | ext == "aseprite" ) |
110 | return FileFormat::ASE_ANIMATION; |
111 | |
112 | if (ext == "act" ) |
113 | return FileFormat::ACT_PALETTE; |
114 | |
115 | if (ext == "bmp" ) |
116 | return FileFormat::BMP_IMAGE; |
117 | |
118 | if (ext == "col" ) |
119 | return FileFormat::COL_PALETTE; |
120 | |
121 | if (ext == "flc" || |
122 | ext == "fli" ) |
123 | return FileFormat::FLIC_ANIMATION; |
124 | |
125 | if (ext == "gif" ) |
126 | return FileFormat::GIF_ANIMATION; |
127 | |
128 | if (ext == "gpl" ) |
129 | return FileFormat::GPL_PALETTE; |
130 | |
131 | if (ext == "hex" ) |
132 | return FileFormat::HEX_PALETTE; |
133 | |
134 | if (ext == "ico" ) |
135 | return FileFormat::ICO_IMAGES; |
136 | |
137 | if (ext == "jpg" || |
138 | ext == "jpeg" ) |
139 | return FileFormat::JPEG_IMAGE; |
140 | |
141 | if (ext == "pal" ) |
142 | return FileFormat::PAL_PALETTE; |
143 | |
144 | if (ext == "pcx" || |
145 | ext == "pcc" ) |
146 | return FileFormat::PCX_IMAGE; |
147 | |
148 | if (ext == "png" ) |
149 | return FileFormat::PNG_IMAGE; |
150 | |
151 | if (ext == "svg" ) |
152 | return FileFormat::SVG_IMAGE; |
153 | |
154 | if (ext == "tga" ) |
155 | return FileFormat::TARGA_IMAGE; |
156 | |
157 | if (ext == "css" ) |
158 | return FileFormat::CSS_STYLE; |
159 | |
160 | if (ext == "webp" ) |
161 | return FileFormat::WEBP_ANIMATION; |
162 | |
163 | if (ext == "psd" || |
164 | ext == "psb" ) |
165 | return FileFormat::PSD_IMAGE; |
166 | |
167 | return FileFormat::UNKNOWN; |
168 | } |
169 | |
170 | } // namespace dio |
171 | |