1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * FITS implementation of common functions
3cabdff1aSopenharmony_ci * Copyright (c) 2017 Paras Chadha
4cabdff1aSopenharmony_ci *
5cabdff1aSopenharmony_ci * This file is part of FFmpeg.
6cabdff1aSopenharmony_ci *
7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
11cabdff1aSopenharmony_ci *
12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15cabdff1aSopenharmony_ci * Lesser General Public License for more details.
16cabdff1aSopenharmony_ci *
17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20cabdff1aSopenharmony_ci */
21cabdff1aSopenharmony_ci
22cabdff1aSopenharmony_ci#include <inttypes.h>
23cabdff1aSopenharmony_ci#include <limits.h>
24cabdff1aSopenharmony_ci#include <stdio.h>
25cabdff1aSopenharmony_ci#include <string.h>
26cabdff1aSopenharmony_ci#include "libavutil/dict.h"
27cabdff1aSopenharmony_ci#include "libavutil/error.h"
28cabdff1aSopenharmony_ci#include "libavutil/log.h"
29cabdff1aSopenharmony_ci#include "fits.h"
30cabdff1aSopenharmony_ci
31cabdff1aSopenharmony_ciint avpriv_fits_header_init(FITSHeader *header, FITSHeaderState state)
32cabdff1aSopenharmony_ci{
33cabdff1aSopenharmony_ci    header->state = state;
34cabdff1aSopenharmony_ci    header->naxis_index = 0;
35cabdff1aSopenharmony_ci    header->blank_found = 0;
36cabdff1aSopenharmony_ci    header->pcount = 0;
37cabdff1aSopenharmony_ci    header->gcount = 1;
38cabdff1aSopenharmony_ci    header->groups = 0;
39cabdff1aSopenharmony_ci    header->rgb = 0;
40cabdff1aSopenharmony_ci    header->image_extension = 0;
41cabdff1aSopenharmony_ci    header->bscale = 1.0;
42cabdff1aSopenharmony_ci    header->bzero = 0;
43cabdff1aSopenharmony_ci    header->data_min_found = 0;
44cabdff1aSopenharmony_ci    header->data_max_found = 0;
45cabdff1aSopenharmony_ci    return 0;
46cabdff1aSopenharmony_ci}
47cabdff1aSopenharmony_ci
48cabdff1aSopenharmony_cistatic int dict_set_if_not_null(AVDictionary ***metadata, char *keyword, char *value)
49cabdff1aSopenharmony_ci{
50cabdff1aSopenharmony_ci    if (metadata)
51cabdff1aSopenharmony_ci        av_dict_set(*metadata, keyword, value, 0);
52cabdff1aSopenharmony_ci    return 0;
53cabdff1aSopenharmony_ci}
54cabdff1aSopenharmony_ci
55cabdff1aSopenharmony_ci/**
56cabdff1aSopenharmony_ci * Extract keyword and value from a header line (80 bytes) and store them in keyword and value strings respectively
57cabdff1aSopenharmony_ci * @param ptr8 pointer to the data
58cabdff1aSopenharmony_ci * @param keyword pointer to the char array in which keyword is to be stored
59cabdff1aSopenharmony_ci * @param value pointer to the char array in which value is to be stored
60cabdff1aSopenharmony_ci * @return 0 if calculated successfully otherwise AVERROR_INVALIDDATA
61cabdff1aSopenharmony_ci */
62cabdff1aSopenharmony_cistatic int read_keyword_value(const uint8_t *ptr8, char *keyword, char *value)
63cabdff1aSopenharmony_ci{
64cabdff1aSopenharmony_ci    int i;
65cabdff1aSopenharmony_ci
66cabdff1aSopenharmony_ci    for (i = 0; i < 8 && ptr8[i] != ' '; i++) {
67cabdff1aSopenharmony_ci        keyword[i] = ptr8[i];
68cabdff1aSopenharmony_ci    }
69cabdff1aSopenharmony_ci    keyword[i] = '\0';
70cabdff1aSopenharmony_ci
71cabdff1aSopenharmony_ci    if (ptr8[8] == '=') {
72cabdff1aSopenharmony_ci        i = 10;
73cabdff1aSopenharmony_ci        while (i < 80 && ptr8[i] == ' ') {
74cabdff1aSopenharmony_ci            i++;
75cabdff1aSopenharmony_ci        }
76cabdff1aSopenharmony_ci
77cabdff1aSopenharmony_ci        if (i < 80) {
78cabdff1aSopenharmony_ci            *value++ = ptr8[i];
79cabdff1aSopenharmony_ci            i++;
80cabdff1aSopenharmony_ci            if (ptr8[i-1] == '\'') {
81cabdff1aSopenharmony_ci                for (; i < 80 && ptr8[i] != '\''; i++) {
82cabdff1aSopenharmony_ci                    *value++ = ptr8[i];
83cabdff1aSopenharmony_ci                }
84cabdff1aSopenharmony_ci                *value++ = '\'';
85cabdff1aSopenharmony_ci            } else if (ptr8[i-1] == '(') {
86cabdff1aSopenharmony_ci                for (; i < 80 && ptr8[i] != ')'; i++) {
87cabdff1aSopenharmony_ci                    *value++ = ptr8[i];
88cabdff1aSopenharmony_ci                }
89cabdff1aSopenharmony_ci                *value++ = ')';
90cabdff1aSopenharmony_ci            } else {
91cabdff1aSopenharmony_ci                for (; i < 80 && ptr8[i] != ' ' && ptr8[i] != '/'; i++) {
92cabdff1aSopenharmony_ci                    *value++ = ptr8[i];
93cabdff1aSopenharmony_ci                }
94cabdff1aSopenharmony_ci            }
95cabdff1aSopenharmony_ci        }
96cabdff1aSopenharmony_ci    }
97cabdff1aSopenharmony_ci    *value = '\0';
98cabdff1aSopenharmony_ci    return 0;
99cabdff1aSopenharmony_ci}
100cabdff1aSopenharmony_ci
101cabdff1aSopenharmony_ci#define CHECK_KEYWORD(key) \
102cabdff1aSopenharmony_ci    if (strcmp(keyword, key)) { \
103cabdff1aSopenharmony_ci        av_log(avcl, AV_LOG_ERROR, "expected %s keyword, found %s = %s\n", key, keyword, value); \
104cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA; \
105cabdff1aSopenharmony_ci    }
106cabdff1aSopenharmony_ci
107cabdff1aSopenharmony_ci#define CHECK_VALUE(key, val) \
108cabdff1aSopenharmony_ci    if (sscanf(value, "%d", &header->val) != 1) { \
109cabdff1aSopenharmony_ci        av_log(avcl, AV_LOG_ERROR, "invalid value of %s keyword, %s = %s\n", key, keyword, value); \
110cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA; \
111cabdff1aSopenharmony_ci    }
112cabdff1aSopenharmony_ci
113cabdff1aSopenharmony_ciint avpriv_fits_header_parse_line(void *avcl, FITSHeader *header, const uint8_t line[80], AVDictionary ***metadata)
114cabdff1aSopenharmony_ci{
115cabdff1aSopenharmony_ci    int dim_no, ret;
116cabdff1aSopenharmony_ci    int64_t t;
117cabdff1aSopenharmony_ci    double d;
118cabdff1aSopenharmony_ci    char keyword[10], value[72], c;
119cabdff1aSopenharmony_ci
120cabdff1aSopenharmony_ci    read_keyword_value(line, keyword, value);
121cabdff1aSopenharmony_ci    switch (header->state) {
122cabdff1aSopenharmony_ci    case STATE_SIMPLE:
123cabdff1aSopenharmony_ci        CHECK_KEYWORD("SIMPLE");
124cabdff1aSopenharmony_ci
125cabdff1aSopenharmony_ci        if (value[0] == 'F') {
126cabdff1aSopenharmony_ci            av_log(avcl, AV_LOG_WARNING, "not a standard FITS file\n");
127cabdff1aSopenharmony_ci        } else if (value[0] != 'T') {
128cabdff1aSopenharmony_ci            av_log(avcl, AV_LOG_ERROR, "invalid value of SIMPLE keyword, SIMPLE = %c\n", value[0]);
129cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
130cabdff1aSopenharmony_ci        }
131cabdff1aSopenharmony_ci
132cabdff1aSopenharmony_ci        header->state = STATE_BITPIX;
133cabdff1aSopenharmony_ci        break;
134cabdff1aSopenharmony_ci    case STATE_XTENSION:
135cabdff1aSopenharmony_ci        CHECK_KEYWORD("XTENSION");
136cabdff1aSopenharmony_ci
137cabdff1aSopenharmony_ci        if (!strcmp(value, "'IMAGE   '")) {
138cabdff1aSopenharmony_ci            header->image_extension = 1;
139cabdff1aSopenharmony_ci        }
140cabdff1aSopenharmony_ci
141cabdff1aSopenharmony_ci        header->state = STATE_BITPIX;
142cabdff1aSopenharmony_ci        break;
143cabdff1aSopenharmony_ci    case STATE_BITPIX:
144cabdff1aSopenharmony_ci        CHECK_KEYWORD("BITPIX");
145cabdff1aSopenharmony_ci        CHECK_VALUE("BITPIX", bitpix);
146cabdff1aSopenharmony_ci
147cabdff1aSopenharmony_ci        switch(header->bitpix) {
148cabdff1aSopenharmony_ci        case   8:
149cabdff1aSopenharmony_ci        case  16:
150cabdff1aSopenharmony_ci        case  32: case -32:
151cabdff1aSopenharmony_ci        case  64: case -64: break;
152cabdff1aSopenharmony_ci        default:
153cabdff1aSopenharmony_ci            av_log(avcl, AV_LOG_ERROR, "invalid value of BITPIX %d\n", header->bitpix); \
154cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
155cabdff1aSopenharmony_ci        }
156cabdff1aSopenharmony_ci
157cabdff1aSopenharmony_ci        dict_set_if_not_null(metadata, keyword, value);
158cabdff1aSopenharmony_ci
159cabdff1aSopenharmony_ci        header->state = STATE_NAXIS;
160cabdff1aSopenharmony_ci        break;
161cabdff1aSopenharmony_ci    case STATE_NAXIS:
162cabdff1aSopenharmony_ci        CHECK_KEYWORD("NAXIS");
163cabdff1aSopenharmony_ci        CHECK_VALUE("NAXIS", naxis);
164cabdff1aSopenharmony_ci        dict_set_if_not_null(metadata, keyword, value);
165cabdff1aSopenharmony_ci
166cabdff1aSopenharmony_ci        if (header->naxis) {
167cabdff1aSopenharmony_ci            header->state = STATE_NAXIS_N;
168cabdff1aSopenharmony_ci        } else {
169cabdff1aSopenharmony_ci            header->state = STATE_REST;
170cabdff1aSopenharmony_ci        }
171cabdff1aSopenharmony_ci        break;
172cabdff1aSopenharmony_ci    case STATE_NAXIS_N:
173cabdff1aSopenharmony_ci        ret = sscanf(keyword, "NAXIS%d", &dim_no);
174cabdff1aSopenharmony_ci        if (ret != 1 || dim_no != header->naxis_index + 1) {
175cabdff1aSopenharmony_ci            av_log(avcl, AV_LOG_ERROR, "expected NAXIS%d keyword, found %s = %s\n", header->naxis_index + 1, keyword, value);
176cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
177cabdff1aSopenharmony_ci        }
178cabdff1aSopenharmony_ci
179cabdff1aSopenharmony_ci        if (sscanf(value, "%d", &header->naxisn[header->naxis_index]) != 1) {
180cabdff1aSopenharmony_ci            av_log(avcl, AV_LOG_ERROR, "invalid value of NAXIS%d keyword, %s = %s\n", header->naxis_index + 1, keyword, value);
181cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
182cabdff1aSopenharmony_ci        }
183cabdff1aSopenharmony_ci
184cabdff1aSopenharmony_ci        dict_set_if_not_null(metadata, keyword, value);
185cabdff1aSopenharmony_ci        header->naxis_index++;
186cabdff1aSopenharmony_ci        if (header->naxis_index == header->naxis) {
187cabdff1aSopenharmony_ci            header->state = STATE_REST;
188cabdff1aSopenharmony_ci        }
189cabdff1aSopenharmony_ci        break;
190cabdff1aSopenharmony_ci    case STATE_REST:
191cabdff1aSopenharmony_ci        if (!strcmp(keyword, "BLANK") && sscanf(value, "%"SCNd64"", &t) == 1) {
192cabdff1aSopenharmony_ci            header->blank = t;
193cabdff1aSopenharmony_ci            header->blank_found = 1;
194cabdff1aSopenharmony_ci        } else if (!strcmp(keyword, "BSCALE") && sscanf(value, "%lf", &d) == 1) {
195cabdff1aSopenharmony_ci            if (d <= 0)
196cabdff1aSopenharmony_ci                return AVERROR_INVALIDDATA;
197cabdff1aSopenharmony_ci            header->bscale = d;
198cabdff1aSopenharmony_ci        } else if (!strcmp(keyword, "BZERO") && sscanf(value, "%lf", &d) == 1) {
199cabdff1aSopenharmony_ci            header->bzero = d;
200cabdff1aSopenharmony_ci        } else if (!strcmp(keyword, "CTYPE3") && !strncmp(value, "'RGB", 4)) {
201cabdff1aSopenharmony_ci            header->rgb = 1;
202cabdff1aSopenharmony_ci        } else if (!strcmp(keyword, "DATAMAX") && sscanf(value, "%lf", &d) == 1) {
203cabdff1aSopenharmony_ci            header->data_max_found = 1;
204cabdff1aSopenharmony_ci            header->data_max = d;
205cabdff1aSopenharmony_ci        } else if (!strcmp(keyword, "DATAMIN") && sscanf(value, "%lf", &d) == 1) {
206cabdff1aSopenharmony_ci            header->data_min_found = 1;
207cabdff1aSopenharmony_ci            header->data_min = d;
208cabdff1aSopenharmony_ci        } else if (!strcmp(keyword, "END")) {
209cabdff1aSopenharmony_ci            return 1;
210cabdff1aSopenharmony_ci        } else if (!strcmp(keyword, "GROUPS") && sscanf(value, "%c", &c) == 1) {
211cabdff1aSopenharmony_ci            header->groups = (c == 'T');
212cabdff1aSopenharmony_ci        } else if (!strcmp(keyword, "GCOUNT") && sscanf(value, "%"SCNd64"", &t) == 1) {
213cabdff1aSopenharmony_ci            if (t < 0 || t > INT_MAX)
214cabdff1aSopenharmony_ci                return AVERROR_INVALIDDATA;
215cabdff1aSopenharmony_ci            header->gcount = t;
216cabdff1aSopenharmony_ci        } else if (!strcmp(keyword, "PCOUNT") && sscanf(value, "%"SCNd64"", &t) == 1) {
217cabdff1aSopenharmony_ci            if (t < 0 || t > INT_MAX)
218cabdff1aSopenharmony_ci                return AVERROR_INVALIDDATA;
219cabdff1aSopenharmony_ci            header->pcount = t;
220cabdff1aSopenharmony_ci        }
221cabdff1aSopenharmony_ci        dict_set_if_not_null(metadata, keyword, value);
222cabdff1aSopenharmony_ci        break;
223cabdff1aSopenharmony_ci    }
224cabdff1aSopenharmony_ci    return 0;
225cabdff1aSopenharmony_ci}
226