Coverage Report

Created: 2026-07-12 08:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/codec/atsc_a65.c
Line
Count
Source
1
/*****************************************************************************
2
 * atsc_a65.c : ATSC A65 decoding helpers
3
 *****************************************************************************
4
 * Copyright (C) 2016 - VideoLAN Authors
5
 *
6
 * This program is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published by
8
 * the Free Software Foundation; either version 2.1 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
 * GNU Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 *****************************************************************************/
19
20
#ifdef HAVE_CONFIG_H
21
# include "config.h"
22
#endif
23
24
#include <vlc_common.h>
25
#include <vlc_charset.h>
26
27
#include "atsc_a65.h"
28
29
enum
30
{
31
    ATSC_A65_COMPRESSION_NONE             = 0x00,
32
    ATSC_A65_COMPRESSION_HUFFMAN_C4C5     = 0x01,
33
    ATSC_A65_COMPRESSION_HUFFMAN_C6C7     = 0x02,
34
    ATSC_A65_COMPRESSION_RESERVED_FIRST   = 0x03,
35
    ATSC_A65_COMPRESSION_RESERVED_LAST    = 0xAF,
36
    ATSC_A65_COMPRESSION_OTHER_FIRST      = 0xB0,
37
    ATSC_A65_COMPRESSION_OTHER_LAST       = 0xFF,
38
};
39
40
enum
41
{
42
    ATSC_A65_MODE_UNICODE_RANGE_START     = 0x00, /* See reserved ranges */
43
    ATSC_A65_MODE_UNICODE_RANGE_END       = 0x33,
44
    ATSC_A65_MODE_SCSU                    = 0x3E,
45
    ATSC_A65_MODE_UNICODE_UTF16           = 0x3F,
46
    ATSC_A65_MODE_TAIWAN_FIRST            = 0x40,
47
    ATSC_A65_MODE_TAIWAN_LAST             = 0x41,
48
    ATSC_A65_MODE_SOUTH_KOREA             = 0x48,
49
    ATSC_A65_MODE_OTHER_FIRST             = 0xE0,
50
    ATSC_A65_MODE_OTHER_LAST              = 0xFE,
51
    ATSC_A65_MODE_NOT_APPLICABLE          = 0xFF,
52
};
53
54
const uint8_t ATSC_A65_MODE_RESERVED_RANGES[12] = {
55
    /* start, end */
56
    0x07, 0x08,
57
    0x11, 0x1F,
58
    0x28, 0x2F,
59
    0x34, 0x3D,
60
    0x42, 0x47,
61
    0x49, 0xDF,
62
};
63
64
struct atsc_a65_handle_t
65
{
66
    char *psz_lang;
67
    vlc_iconv_t iconv_u16be;
68
};
69
70
atsc_a65_handle_t *atsc_a65_handle_New( const char *psz_lang )
71
76
{
72
76
    atsc_a65_handle_t *p_handle = malloc( sizeof(*p_handle) );
73
76
    if( p_handle )
74
76
    {
75
76
        if( psz_lang && strnlen(psz_lang, 2+1) > 2 )
76
0
            p_handle->psz_lang = strdup( psz_lang );
77
76
        else
78
76
            p_handle->psz_lang = NULL;
79
80
76
        p_handle->iconv_u16be = NULL;
81
76
    }
82
76
    return p_handle;
83
76
}
84
85
void atsc_a65_handle_Release( atsc_a65_handle_t *p_handle )
86
76
{
87
76
    if( p_handle->iconv_u16be )
88
76
        vlc_iconv_close( p_handle->iconv_u16be );
89
76
    free( p_handle->psz_lang );
90
76
    free( p_handle );
91
76
}
92
93
static char *enlarge_to16( const uint8_t *p_src, size_t i_src, uint8_t i_prefix )
94
89
{
95
89
    if( i_src == 0 )
96
0
        return NULL;
97
98
89
    char *psz_new_allocated = malloc( i_src * 2 + 1 );
99
89
    char *psz_new = psz_new_allocated;
100
101
89
    if( psz_new )
102
89
    {
103
89
        memset( psz_new, i_prefix, i_src * 2 );
104
89
        psz_new[ i_src * 2 ] = 0;
105
1.51k
        while( i_src-- )
106
1.42k
        {
107
1.42k
            psz_new[1] = p_src[0];
108
1.42k
            p_src++;
109
1.42k
            psz_new += 2;
110
1.42k
        }
111
89
    }
112
89
    return psz_new_allocated;
113
89
}
114
115
static bool convert_encoding_set( atsc_a65_handle_t *p_handle,
116
                                  const uint8_t *p_src, size_t i_src,
117
                                  char **ppsz_merg, size_t *pi_mergmin1,
118
                                  uint8_t i_mode )
119
89
{
120
89
    char *psz_dest = *ppsz_merg;
121
89
    size_t i_mergmin1 = *pi_mergmin1;
122
89
    bool b_ret = true;
123
124
89
    if( i_src == 0 )
125
0
        return false;
126
127
    /* First exclude reserved ranges */
128
623
    for( unsigned i=0; i<12; i+=2 )
129
534
    {
130
534
        if( i_mode >= ATSC_A65_MODE_RESERVED_RANGES[i]   &&
131
0
            i_mode <= ATSC_A65_MODE_RESERVED_RANGES[i+1] )
132
0
            return false;
133
534
    }
134
135
89
    if( i_mode <= ATSC_A65_MODE_UNICODE_RANGE_END ) /* 8 range prefix + 8 */
136
89
    {
137
89
        if( !p_handle->iconv_u16be )
138
76
        {
139
76
            if ( !(p_handle->iconv_u16be = vlc_iconv_open("UTF-8", "UTF-16BE")) )
140
0
                return false;
141
76
        }
142
13
        else if ( VLC_ICONV_ERR == vlc_iconv( p_handle->iconv_u16be, NULL, NULL, NULL, NULL ) ) /* reset */
143
0
        {
144
0
            return false;
145
0
        }
146
147
89
        char *psz16 = enlarge_to16( p_src, i_src, i_mode ); /* Maybe we can skip and feed iconv 2 by 2 */
148
89
        if( psz16 )
149
89
        {
150
89
            char *psz_realloc = realloc( psz_dest, i_mergmin1 + (4 * i_src) + 1 );
151
89
            if( psz_realloc )
152
89
            {
153
89
                const char *p_inbuf = psz16;
154
89
                char *p_outbuf = &psz_realloc[i_mergmin1];
155
89
                const size_t i_outbuf_size = i_src * 4;
156
89
                size_t i_inbuf_remain = i_src * 2;
157
89
                size_t i_outbuf_remain = i_outbuf_size;
158
89
                b_ret = ( VLC_ICONV_ERR != vlc_iconv( p_handle->iconv_u16be, &p_inbuf, &i_inbuf_remain,
159
89
                                                                            &p_outbuf, &i_outbuf_remain ) );
160
89
                psz_dest = psz_realloc;
161
89
                i_mergmin1 += (i_outbuf_size - i_outbuf_remain);
162
89
                *p_outbuf = '\0';
163
89
            }
164
89
            free( psz16 );
165
89
        }
166
0
        else return false;
167
89
    }
168
0
    else
169
0
    {
170
        /* Unsupported encodings */
171
0
        return false;
172
0
    }
173
174
89
    *ppsz_merg = psz_dest;
175
89
    *pi_mergmin1 = i_mergmin1;
176
89
    return b_ret;
177
89
}
178
179
1.32k
#define BUF_ADVANCE(n) p_buffer += n; i_buffer -= n;
180
181
char * atsc_a65_Decode_multiple_string( atsc_a65_handle_t *p_handle, const uint8_t *p_buffer, size_t i_buffer )
182
235
{
183
235
    char *psz_res = NULL;
184
235
    size_t i_resmin1 = 0;
185
186
235
    if( i_buffer < 1 )
187
0
        return NULL;
188
189
235
    uint8_t i_nb = p_buffer[0];
190
235
    BUF_ADVANCE(1);
191
192
324
    for( ; i_nb > 0; i_nb-- )
193
235
    {
194
235
        if( i_buffer < 4 )
195
0
            goto error;
196
197
235
        bool b_skip = ( p_handle->psz_lang && memcmp(p_buffer, p_handle->psz_lang, 3) );
198
235
        BUF_ADVANCE(3);
199
200
235
        uint8_t i_seg = p_buffer[0];
201
235
        BUF_ADVANCE(1);
202
470
        for( ; i_seg > 0; i_seg-- )
203
381
        {
204
381
            if( i_buffer < 3 )
205
0
                goto error;
206
207
381
            const uint8_t i_compression = p_buffer[0];
208
381
            const uint8_t i_mode = p_buffer[1];
209
381
            const uint8_t i_bytes = p_buffer[2];
210
381
            BUF_ADVANCE(3);
211
212
381
            if( i_buffer < i_bytes )
213
146
                goto error;
214
215
235
            if( i_compression != ATSC_A65_COMPRESSION_NONE ) // TBD
216
146
            {
217
146
                b_skip = true;
218
146
            }
219
220
235
            if( !b_skip )
221
89
            {
222
89
                (void) convert_encoding_set( p_handle, p_buffer, i_bytes,
223
89
                                             &psz_res, &i_resmin1, i_mode );
224
89
            }
225
226
235
            BUF_ADVANCE(i_bytes);
227
235
        }
228
235
    }
229
230
89
    return psz_res;
231
232
146
error:
233
146
    free( psz_res );
234
146
    return NULL;
235
235
}
236
237
#undef BUF_ADVANCE
238
239
char * atsc_a65_Decode_simple_UTF16_string( atsc_a65_handle_t *p_handle, const uint8_t *p_buffer, size_t i_buffer )
240
89
{
241
89
    if( i_buffer < 1 )
242
0
        return NULL;
243
244
89
    if( !p_handle->iconv_u16be )
245
0
    {
246
0
        if ( !(p_handle->iconv_u16be = vlc_iconv_open("UTF-8", "UTF-16BE")) )
247
0
            return NULL;
248
0
    }
249
89
    else if ( VLC_ICONV_ERR == vlc_iconv( p_handle->iconv_u16be, NULL, NULL, NULL, NULL ) ) /* reset */
250
0
    {
251
0
        return NULL;
252
0
    }
253
254
89
    const size_t i_target_buffer = i_buffer * 3 / 2;
255
89
    size_t i_target_remaining = i_target_buffer;
256
89
    const char *psz_toconvert = (const char *) p_buffer;
257
89
    char *psz_converted_end;
258
89
    char *psz_converted = psz_converted_end = malloc( i_target_buffer );
259
260
89
    if( unlikely(!psz_converted) )
261
0
        return NULL;
262
263
89
    if( VLC_ICONV_ERR == vlc_iconv( p_handle->iconv_u16be, &psz_toconvert, &i_buffer,
264
89
                                                           &psz_converted_end, &i_target_remaining ) )
265
0
    {
266
0
        free( psz_converted );
267
0
        psz_converted = NULL;
268
0
    }
269
89
    else
270
89
        psz_converted[ i_target_buffer - i_target_remaining - 1 ] = 0;
271
272
89
    return psz_converted;
273
89
}