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
28namespace dio {
29
30FileFormat 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
38FileFormat 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
90FileFormat 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
103FileFormat 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