| // Copyright 2021 The Wuffs Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // ---------------- Magic Numbers |
| |
| WUFFS_BASE__MAYBE_STATIC int32_t // |
| wuffs_base__magic_number_guess_fourcc(wuffs_base__slice_u8 prefix) { |
| // table holds the 'magic numbers' (which are actually variable length |
| // strings). The strings may contain NUL bytes, so the "const char* magic" |
| // value starts with the length-minus-1 of the 'magic number'. |
| // |
| // Keep it sorted by magic[1], then magic[0] descending and finally by |
| // magic[2:]. When multiple entries match, the longest one wins. |
| // |
| // The fourcc field might be negated, in which case there's further |
| // specialization (see § below). |
| static struct { |
| int32_t fourcc; |
| const char* magic; |
| } table[] = { |
| {-0x30302020, "\x01\x00\x00"}, // '00 'be |
| {+0x424D5020, "\x01\x42\x4D"}, // BMP |
| {+0x47494620, "\x03\x47\x49\x46\x38"}, // GIF |
| {+0x54494646, "\x03\x49\x49\x2A\x00"}, // TIFF (little-endian) |
| {+0x54494646, "\x03\x4D\x4D\x00\x2A"}, // TIFF (big-endian) |
| {-0x52494646, "\x03\x52\x49\x46\x46"}, // RIFF |
| {+0x4E494520, "\x02\x6E\xC3\xAF"}, // NIE |
| {+0x504E4720, "\x03\x89\x50\x4E\x47"}, // PNG |
| {+0x4A504547, "\x01\xFF\xD8"}, // JPEG |
| }; |
| static const size_t table_len = sizeof(table) / sizeof(table[0]); |
| |
| if (prefix.len == 0) { |
| return -1; |
| } |
| uint8_t pre_first_byte = prefix.ptr[0]; |
| |
| int32_t fourcc = 0; |
| size_t i; |
| for (i = 0; i < table_len; i++) { |
| uint8_t mag_first_byte = ((uint8_t)(table[i].magic[1])); |
| if (pre_first_byte < mag_first_byte) { |
| break; |
| } else if (pre_first_byte > mag_first_byte) { |
| continue; |
| } |
| fourcc = table[i].fourcc; |
| |
| uint8_t mag_remaining_len = ((uint8_t)(table[i].magic[0])); |
| if (mag_remaining_len == 0) { |
| goto match; |
| } |
| |
| const char* mag_remaining_ptr = table[i].magic + 2; |
| uint8_t* pre_remaining_ptr = prefix.ptr + 1; |
| size_t pre_remaining_len = prefix.len - 1; |
| if (pre_remaining_len < mag_remaining_len) { |
| if (!memcmp(pre_remaining_ptr, mag_remaining_ptr, pre_remaining_len)) { |
| return -1; |
| } |
| } else { |
| if (!memcmp(pre_remaining_ptr, mag_remaining_ptr, mag_remaining_len)) { |
| goto match; |
| } |
| } |
| } |
| return 0; |
| |
| match: |
| // Negative FourCC values (see § above) are further specialized. |
| if (fourcc < 0) { |
| fourcc = -fourcc; |
| |
| if (fourcc == 0x52494646) { // 'RIFF'be |
| if (prefix.len < 12) { |
| return -1; |
| } |
| uint32_t x = wuffs_base__peek_u32be__no_bounds_check(prefix.ptr + 8); |
| if (x == 0x57454250) { // 'WEBP'be |
| return 0x57454250; // 'WEBP'be |
| } |
| |
| } else if (fourcc == 0x30302020) { // '00 'be |
| // Binary data starting with multiple 0x00 NUL bytes is quite common. |
| if (prefix.len < 4) { |
| return -1; |
| } else if ((prefix.ptr[2] != 0x00) && |
| ((prefix.ptr[2] >= 0x80) || (prefix.ptr[3] != 0x00))) { |
| // Roughly speaking, this could be a non-degenerate (non-0-width and |
| // non-0-height) WBMP image. |
| return 0x57424D50; // 'WBMP'be |
| } else if (((prefix.ptr[2] == 0x01) || (prefix.ptr[2] == 0x02)) && |
| (prefix.ptr[3] == 0x00)) { |
| return 0x49434F20; // 'ICO 'be |
| } |
| return 0; |
| } |
| } |
| return fourcc; |
| } |