Coverage Report

Created: 2026-07-12 08:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/demux/xiph_metadata.c
Line
Count
Source
1
/*****************************************************************************
2
 * xiph_metadata.h: Vorbis Comment parser
3
 *****************************************************************************
4
 * Copyright © 2008-2013 VLC authors and VideoLAN
5
 *
6
 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
7
 *          Jean-Baptiste Kempf <jb@videolan.org>
8
 *
9
 * This program is free software; you can redistribute it and/or modify it
10
 * under the terms of the GNU Lesser General Public License as published by
11
 * the Free Software Foundation; either version 2.1 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with this program; if not, write to the Free Software Foundation,
21
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
 *****************************************************************************/
23
24
#ifdef HAVE_CONFIG_H
25
# include "config.h"
26
#endif
27
28
#include <assert.h>
29
30
#include <vlc_common.h>
31
#include <vlc_arrays.h>
32
#include <vlc_charset.h>
33
#include <vlc_strings.h>
34
#include <vlc_arrays.h>
35
#include <vlc_input.h>
36
#include "xiph_metadata.h"
37
#include "../meta_engine/ID3Pictures.h"
38
39
input_attachment_t* ParseFlacPicture( const uint8_t *p_data, size_t size,
40
    int i_attachments, int *i_cover_score, int *i_cover_idx )
41
2.55k
{
42
2.55k
    uint32_t type, len;
43
44
2.55k
    if( size < 8 )
45
621
        return NULL;
46
1.93k
#define RM(x) \
47
12.0k
    do { \
48
12.0k
        assert(size >= (x)); \
49
12.0k
        size -= (x); \
50
12.0k
        p_data += (x); \
51
12.0k
    } while (0)
52
53
1.93k
    type = GetDWBE( p_data );
54
1.93k
    RM(4);
55
1.93k
    len = GetDWBE( p_data );
56
1.93k
    RM(4);
57
58
1.93k
    if( size < len )
59
17
        return NULL;
60
61
1.92k
    char *mime = strndup( (const char *)p_data, len );
62
1.92k
    if( unlikely(mime == NULL) )
63
0
        return NULL;
64
1.92k
    RM(len);
65
66
1.92k
    if( size < 4 )
67
0
    {
68
0
        free( mime );
69
0
        return NULL;
70
0
    }
71
72
1.92k
    len = GetDWBE( p_data );
73
1.92k
    RM(4);
74
75
1.92k
    if( size < len )
76
13
    {
77
13
        free( mime );
78
13
        return NULL;
79
13
    }
80
81
1.90k
    input_attachment_t *p_attachment = NULL;
82
1.90k
    char *description = strndup( (const char *)p_data, len );
83
1.90k
    if( unlikely(description == NULL) )
84
0
        goto error;
85
1.90k
    RM(len);
86
87
1.90k
    EnsureUTF8( description );
88
89
1.90k
    if( size < 20 )
90
706
        goto error;
91
92
1.20k
    RM(4 * 4); /* skip */
93
94
1.20k
    len = GetDWBE( p_data );
95
1.20k
    RM(4);
96
97
1.20k
    if( size < len )
98
137
        goto error;
99
100
    /* printf( "Picture type=%"PRIu32" mime=%s description='%s' "
101
               "file length=%zu\n", type, mime, description, len ); */
102
103
1.06k
    char name[7 + (sizeof (i_attachments) * 3) + 4 + 1];
104
105
1.06k
    snprintf( name, sizeof (name), "picture%u", i_attachments );
106
107
1.06k
    if( !strcasecmp( mime, "image/jpeg" ) )
108
128
        strcat( name, ".jpg" );
109
937
    else if( !strcasecmp( mime, "image/png" ) )
110
255
        strcat( name, ".png" );
111
112
1.06k
    p_attachment = vlc_input_attachment_New( name, mime, description, p_data,
113
1.06k
                                             size /* XXX: len instead? */ );
114
115
1.06k
    if( type < ARRAY_SIZE(ID3v2_cover_scores) &&
116
429
        *i_cover_score < ID3v2_cover_scores[type] )
117
2
    {
118
2
        *i_cover_idx = i_attachments;
119
2
        *i_cover_score = ID3v2_cover_scores[type];
120
2
    }
121
122
1.90k
error:
123
1.90k
    free( mime );
124
1.90k
    free( description );
125
1.90k
    return p_attachment;
126
1.06k
}
127
128
#undef RM
129
#define RM(x) \
130
58.1k
    do { \
131
58.1k
        i_data -= (x); \
132
58.1k
        p_data += (x); \
133
58.1k
    } while (0)
134
135
136
typedef struct chapters_array_t
137
{
138
    unsigned int i_size;
139
    seekpoint_t ** pp_chapters;
140
} chapters_array_t;
141
142
static seekpoint_t * getChapterEntry( unsigned int i_index, chapters_array_t *p_array )
143
2.12k
{
144
2.12k
    if ( i_index > 4096 ) return NULL;
145
1.73k
    if ( i_index >= p_array->i_size )
146
1.25k
    {
147
1.25k
        unsigned int i_newsize = p_array->i_size;
148
4.57k
        while( i_index >= i_newsize ) i_newsize += 50;
149
150
1.25k
        if ( !p_array->pp_chapters )
151
1.02k
        {
152
1.02k
            p_array->pp_chapters = calloc( i_newsize, sizeof( seekpoint_t * ) );
153
1.02k
            if ( !p_array->pp_chapters ) return NULL;
154
1.02k
            p_array->i_size = i_newsize;
155
1.02k
        } else {
156
232
            seekpoint_t **tmp = calloc( i_newsize, sizeof( seekpoint_t * ) );
157
232
            if ( !tmp ) return NULL;
158
232
            memcpy( tmp, p_array->pp_chapters, p_array->i_size * sizeof( seekpoint_t * ) );
159
232
            free( p_array->pp_chapters );
160
232
            p_array->pp_chapters = tmp;
161
232
            p_array->i_size = i_newsize;
162
232
        }
163
1.25k
    }
164
1.73k
    if ( !p_array->pp_chapters[i_index] )
165
1.70k
        p_array->pp_chapters[i_index] = vlc_seekpoint_New();
166
1.73k
    return p_array->pp_chapters[i_index];
167
1.73k
}
168
169
3.61k
#define XIPHMETA_Title        (1 << 0)
170
4.85k
#define XIPHMETA_Artist       (1 << 1)
171
3.52k
#define XIPHMETA_Genre        (1 << 2)
172
0
#define XIPHMETA_Copyright    (1 << 3)
173
417
#define XIPHMETA_Album        (1 << 4)
174
9.09k
#define XIPHMETA_TrackNum     (1 << 5)
175
1.12k
#define XIPHMETA_Description  (1 << 6)
176
1.60k
#define XIPHMETA_Rating       (1 << 7)
177
2.26k
#define XIPHMETA_Date         (1 << 8)
178
0
#define XIPHMETA_Language     (1 << 9)
179
257
#define XIPHMETA_Publisher    (1 << 10)
180
847
#define XIPHMETA_EncodedBy    (1 << 11)
181
641
#define XIPHMETA_TrackTotal   (1 << 12)
182
183
static char * xiph_ExtractCueSheetMeta( const char *psz_line,
184
                                        const char *psz_tag, int i_tag,
185
                                        bool b_quoted )
186
7.79k
{
187
7.79k
    if( !strncasecmp( psz_line, psz_tag, i_tag ) )
188
452
    {
189
452
        if( !b_quoted )
190
161
            return strdup( &psz_line[i_tag] );
191
192
        /* Unquote string value */
193
291
        char *psz_value = malloc( strlen( psz_line ) - i_tag + 1 );
194
291
        if( psz_value )
195
291
        {
196
291
            char *psz_out = psz_value;
197
291
            psz_line += i_tag;
198
291
            bool b_escaped = false;
199
2.35k
            while( *psz_line )
200
2.06k
            {
201
2.06k
                switch( *psz_line )
202
2.06k
                {
203
96
                    case '\\':
204
96
                        if( b_escaped )
205
0
                        {
206
0
                            b_escaped = false;
207
0
                            *(psz_out++) = *psz_line;
208
0
                        }
209
96
                        else
210
96
                        {
211
96
                            b_escaped = true;
212
96
                        }
213
96
                        break;
214
96
                    case '"':
215
96
                        if( b_escaped )
216
96
                        {
217
96
                            b_escaped = false;
218
96
                            *(psz_out++) = *psz_line;
219
96
                        }
220
96
                        break;
221
1.87k
                    default:
222
1.87k
                        *(psz_out++) = *psz_line;
223
1.87k
                        break;
224
2.06k
                }
225
2.06k
                psz_line++;
226
2.06k
            }
227
291
            *psz_out = 0;
228
291
            return psz_value;
229
291
        }
230
291
    }
231
7.33k
    return NULL;
232
7.79k
}
233
234
static void xiph_ParseCueSheetMeta( unsigned *pi_flags, vlc_meta_t *p_meta,
235
                                    const char *psz_line,
236
                                    int *pi_seekpoint, seekpoint_t ***ppp_seekpoint,
237
                                    seekpoint_t **pp_tmppoint, bool *pb_valid )
238
2.08k
{
239
2.08k
    VLC_UNUSED(pi_seekpoint);
240
2.08k
    VLC_UNUSED(ppp_seekpoint);
241
242
2.08k
    seekpoint_t *p_seekpoint = *pp_tmppoint;
243
2.08k
    char *psz_string;
244
245
2.08k
#define TRY_EXTRACT_CUEMETA(var, string, quoted) \
246
7.46k
    if( !(*pi_flags & XIPHMETA_##var) &&\
247
7.46k
         ( psz_string = xiph_ExtractCueSheetMeta( psz_line, string, sizeof(string) - 1, quoted ) ) )\
248
7.46k
    {\
249
404
        vlc_meta_Set( p_meta, vlc_meta_##var, psz_string );\
250
404
        free( psz_string );\
251
404
        *pi_flags |= XIPHMETA_##var;\
252
404
    }
253
254
2.08k
    TRY_EXTRACT_CUEMETA(Title, "TITLE \"", true)
255
1.84k
    else TRY_EXTRACT_CUEMETA(Genre, "REM GENRE ", false)
256
1.84k
    else TRY_EXTRACT_CUEMETA(Date, "REM DATE ", false)
257
1.68k
    else TRY_EXTRACT_CUEMETA(Artist, "PERFORMER \"", true)
258
1.68k
    else if( !strncasecmp( psz_line, "  TRACK ", 8 ) )
259
52
    {
260
52
        if( p_seekpoint )
261
1
        {
262
1
            if( *pb_valid )
263
1
                TAB_APPEND( *pi_seekpoint, *ppp_seekpoint, p_seekpoint );
264
1
            else
265
1
                vlc_seekpoint_Delete( p_seekpoint );
266
1
            *pb_valid = false;
267
1
        }
268
52
        *pp_tmppoint = p_seekpoint = vlc_seekpoint_New();
269
52
    }
270
1.63k
    else if( p_seekpoint && !strncasecmp( psz_line, "    INDEX 01 ", 13 ) )
271
0
    {
272
0
        unsigned m, s, f;
273
0
        if( sscanf( &psz_line[13], "%u:%u:%u", &m, &s, &f ) == 3 )
274
0
        {
275
0
            p_seekpoint->i_time_offset = vlc_tick_from_sec(m * 60 + s) + vlc_tick_from_samples(f, 75);
276
0
            *pb_valid = true;
277
0
        }
278
0
    }
279
1.63k
    else if( p_seekpoint && !p_seekpoint->psz_name )
280
982
    {
281
982
        p_seekpoint->psz_name = xiph_ExtractCueSheetMeta( psz_line, "    TITLE \"", 11, true );
282
982
    }
283
2.08k
}
284
285
static void xiph_ParseCueSheet( unsigned *pi_flags, vlc_meta_t *p_meta,
286
                                const char *p_data, int i_data,
287
                                int *pi_seekpoint, seekpoint_t ***ppp_seekpoint )
288
618
{
289
618
    seekpoint_t *p_seekpoint = NULL;
290
618
    bool b_valid = false;
291
292
618
    const char *p_head = p_data;
293
618
    const char *p_tail = p_head;
294
112k
    while( p_tail < p_data + i_data )
295
111k
    {
296
111k
        if( *p_tail == 0x0D )
297
2.08k
        {
298
2.08k
            char *psz = strndup( p_head, p_tail - p_head );
299
2.08k
            if( psz )
300
2.08k
            {
301
2.08k
                xiph_ParseCueSheetMeta( pi_flags, p_meta, psz,
302
2.08k
                                        pi_seekpoint, ppp_seekpoint,
303
2.08k
                                        &p_seekpoint, &b_valid );
304
2.08k
                free( psz );
305
2.08k
            }
306
2.08k
            if( *(++p_tail) == 0x0A )
307
582
                p_tail++;
308
2.08k
            p_head = p_tail;
309
2.08k
        }
310
109k
        else
311
109k
        {
312
109k
            p_tail++;
313
109k
        }
314
111k
    }
315
316
317
618
    if( p_seekpoint )
318
51
    {
319
51
        if( b_valid )
320
51
            TAB_APPEND( *pi_seekpoint, *ppp_seekpoint, p_seekpoint );
321
51
        else
322
51
            vlc_seekpoint_Delete( p_seekpoint );
323
51
    }
324
618
}
325
326
void vorbis_ParseComment( es_format_t *p_fmt, vlc_meta_t **pp_meta,
327
        const uint8_t *p_data, size_t i_data,
328
        int *i_attachments, input_attachment_t ***attachments,
329
        int *i_cover_score, int *i_cover_idx,
330
        int *i_seekpoint, seekpoint_t ***ppp_seekpoint )
331
9.97k
{
332
9.97k
    if( i_data < 8 )
333
36
        return;
334
335
9.93k
    uint32_t vendor_length = GetDWLE(p_data); RM(4);
336
337
9.93k
    if( vendor_length > i_data )
338
746
        return; /* invalid length */
339
340
9.19k
    RM(vendor_length); /* TODO: handle vendor payload */
341
342
9.19k
    if( i_data < 4 )
343
1
        return;
344
345
9.19k
    uint32_t i_comment = GetDWLE(p_data); RM(4);
346
347
9.19k
    if( i_comment > i_data || i_comment == 0 )
348
1.06k
        return; /* invalid length */
349
350
    /* */
351
8.12k
    vlc_meta_t *p_meta = *pp_meta;
352
8.12k
    if( !p_meta )
353
209
        *pp_meta = p_meta = vlc_meta_New();
354
355
8.12k
    if( unlikely( !p_meta ) )
356
0
        return;
357
358
    /* */
359
8.12k
    unsigned hasMetaFlags = 0;
360
361
8.12k
    chapters_array_t chapters_array = { 0, NULL };
362
363
21.1k
    for( ; i_comment > 0 && i_data >= 4; i_comment-- )
364
17.4k
    {
365
17.4k
        uint32_t comment_size = GetDWLE(p_data); RM(4);
366
367
17.4k
        if( comment_size > i_data )
368
4.36k
            break;
369
370
13.0k
        if( comment_size == 0 )
371
622
            continue;
372
373
12.4k
        char* psz_comment = malloc( comment_size + 1 );
374
375
12.4k
        if( unlikely( !psz_comment ) )
376
0
            goto next_comment;
377
378
12.4k
        memcpy( psz_comment, p_data, comment_size );
379
12.4k
        psz_comment[comment_size] = '\0';
380
381
12.4k
#define IF_EXTRACT(txt,var) \
382
106k
    if( !strncasecmp(psz_comment, txt, strlen(txt)) ) \
383
106k
    { \
384
5.46k
        size_t key_length = strlen(txt); \
385
5.46k
        EnsureUTF8( psz_comment + key_length ); \
386
5.46k
        const char *oldval = vlc_meta_Get( p_meta, vlc_meta_ ## var ); \
387
5.46k
        if( oldval && (hasMetaFlags & XIPHMETA_##var)) \
388
5.46k
        { \
389
1.97k
            char * newval; \
390
1.97k
            if( asprintf( &newval, "%s,%s", oldval, &psz_comment[key_length] ) == -1 ) \
391
1.97k
                newval = NULL; \
392
1.97k
            vlc_meta_Set( p_meta, vlc_meta_ ## var, newval ); \
393
1.97k
            free( newval ); \
394
1.97k
        } \
395
5.46k
        else \
396
5.46k
            vlc_meta_Set( p_meta, vlc_meta_ ## var, &psz_comment[key_length] ); \
397
5.46k
        hasMetaFlags |= XIPHMETA_##var; \
398
5.46k
    }
399
400
12.4k
#define IF_EXTRACT_ONCE_NUMBER(txt,var) \
401
17.4k
    if( !strncasecmp(psz_comment, txt, strlen(txt)) && !(hasMetaFlags & XIPHMETA_##var) ) \
402
17.4k
    { \
403
385
        bool isnum = true; \
404
385
        const char *num_str = &psz_comment[strlen(txt)], *c; \
405
1.90k
        for (c = num_str; isnum && *c != '\0'; c++) { \
406
1.51k
            isnum = *c >= '0' && *c <= '9'; \
407
1.51k
        } \
408
385
        if (isnum) \
409
385
        { \
410
256
            vlc_meta_Set( p_meta, vlc_meta_ ## var, num_str ); \
411
256
            hasMetaFlags |= XIPHMETA_##var; \
412
256
        } \
413
385
    }
414
415
12.4k
        IF_EXTRACT("TITLE=", Title )
416
11.7k
        else IF_EXTRACT("ARTIST=", Artist )
417
10.1k
        else IF_EXTRACT("GENRE=", Genre )
418
9.30k
        else IF_EXTRACT("COPYRIGHT=", Copyright )
419
9.30k
        else IF_EXTRACT("ALBUM=", Album )
420
9.09k
        else if( !(hasMetaFlags & XIPHMETA_TrackNum) && !strncasecmp(psz_comment, "TRACKNUMBER=", strlen("TRACKNUMBER=" ) ) )
421
252
        {
422
            /* Yeah yeah, such a clever idea, let's put xx/xx inside TRACKNUMBER
423
             * Oh, and let's not use TRACKTOTAL or TOTALTRACKS... */
424
252
            short unsigned u_track, u_total;
425
252
            int nb_values = sscanf( &psz_comment[strlen("TRACKNUMBER=")], "%hu/%hu", &u_track, &u_total );
426
252
            if( nb_values >= 1 )
427
1
            {
428
1
                char str[6];
429
1
                snprintf(str, 6, "%u", u_track);
430
1
                vlc_meta_Set( p_meta, vlc_meta_TrackNumber, str );
431
1
                hasMetaFlags |= XIPHMETA_TrackNum;
432
1
                if( nb_values >= 2 )
433
0
                {
434
0
                    snprintf(str, 6, "%u", u_total);
435
0
                    vlc_meta_Set( p_meta, vlc_meta_TrackTotal, str );
436
0
                    hasMetaFlags |= XIPHMETA_TrackTotal;
437
0
                }
438
1
            }
439
252
        }
440
8.84k
        else IF_EXTRACT_ONCE_NUMBER("TRACKTOTAL=", TrackTotal )
441
8.58k
        else IF_EXTRACT_ONCE_NUMBER("TOTALTRACKS=", TrackTotal )
442
8.45k
        else IF_EXTRACT("DESCRIPTION=", Description )
443
8.29k
        else IF_EXTRACT("COMMENT=", Description )
444
8.16k
        else IF_EXTRACT("COMMENTS=", Description )
445
7.89k
        else IF_EXTRACT("RATING=", Rating )
446
7.08k
        else IF_EXTRACT("DATE=", Date )
447
6.95k
        else if( !strncasecmp(psz_comment, "LANGUAGE=", strlen("LANGUAGE=") ) )
448
0
        {
449
0
            IF_EXTRACT("LANGUAGE=",Language)
450
0
            if( p_fmt )
451
0
            {
452
0
                free( p_fmt->psz_language );
453
0
                p_fmt->psz_language = strdup(&psz_comment[strlen("LANGUAGE=")]);
454
0
            }
455
0
        }
456
6.95k
        else IF_EXTRACT("ORGANIZATION=", Publisher )
457
6.82k
        else IF_EXTRACT("ENCODER=", EncodedBy )
458
6.33k
        else if( !strncasecmp( psz_comment, "METADATA_BLOCK_PICTURE=", strlen("METADATA_BLOCK_PICTURE=")))
459
373
        {
460
373
            if( attachments == NULL )
461
0
                goto next_comment;
462
463
373
            uint8_t *p_picture;
464
373
            size_t i_size = vlc_b64_decode_binary( &p_picture, &psz_comment[strlen("METADATA_BLOCK_PICTURE=")]);
465
373
            input_attachment_t *p_attachment = ParseFlacPicture( p_picture,
466
373
                i_size, *i_attachments, i_cover_score, i_cover_idx );
467
373
            free( p_picture );
468
373
            if( p_attachment )
469
0
            {
470
0
                TAB_APPEND_CAST( (input_attachment_t**),
471
0
                    *i_attachments, *attachments, p_attachment );
472
0
            }
473
373
        }
474
5.96k
        else if( !strncasecmp(psz_comment, "CHAPTER", 7) )
475
2.45k
        {
476
2.45k
            unsigned int i_chapt;
477
2.45k
            seekpoint_t *p_seekpoint = NULL;
478
479
40.9k
            for( int i = 0; psz_comment[i] && psz_comment[i] != '='; i++ )
480
38.4k
                if( psz_comment[i] >= 'a' && psz_comment[i] <= 'z' )
481
723
                    psz_comment[i] -= 'a' - 'A';
482
483
2.45k
            if( strstr( psz_comment, "NAME=" ) &&
484
2.12k
                    sscanf( psz_comment, "CHAPTER%uNAME=", &i_chapt ) == 1 )
485
2.12k
            {
486
2.12k
                char *p = strchr( psz_comment, '=' );
487
2.12k
                p_seekpoint = getChapterEntry( i_chapt, &chapters_array );
488
2.12k
                if ( !p || ! p_seekpoint ) goto next_comment;
489
1.73k
                EnsureUTF8( ++p );
490
1.73k
                if ( ! p_seekpoint->psz_name )
491
1.70k
                    p_seekpoint->psz_name = strdup( p );
492
1.73k
            }
493
336
            else if( sscanf( psz_comment, "CHAPTER%u=", &i_chapt ) == 1 )
494
329
            {
495
329
                unsigned int h, m, s, ms;
496
329
                char *p = strchr( psz_comment, '=' );
497
329
                if( p && sscanf( ++p, "%u:%u:%u.%u", &h, &m, &s, &ms ) == 4 )
498
0
                {
499
0
                    p_seekpoint = getChapterEntry( i_chapt, &chapters_array );
500
0
                    if ( ! p_seekpoint ) goto next_comment;
501
0
                    p_seekpoint->i_time_offset = vlc_tick_from_sec(h * 3600 + m * 60 + s) + VLC_TICK_FROM_MS(ms);
502
0
                }
503
329
            }
504
2.45k
        }
505
3.50k
        else if( !strncasecmp(psz_comment, "cuesheet=", 9) )
506
618
        {
507
618
            EnsureUTF8( &psz_comment[9] );
508
618
            xiph_ParseCueSheet( &hasMetaFlags, p_meta, &psz_comment[9], comment_size - 9,
509
618
                                i_seekpoint, ppp_seekpoint );
510
618
        }
511
2.89k
        else if( strchr( psz_comment, '=' ) )
512
1.59k
        {
513
            /* generic (PERFORMER/LICENSE/ORGANIZATION/LOCATION/CONTACT/ISRC,
514
             * undocumented tags and replay gain ) */
515
1.59k
            char *p = strchr( psz_comment, '=' );
516
1.59k
            *p++ = '\0';
517
1.59k
            EnsureUTF8( p );
518
519
14.9k
            for( int i = 0; psz_comment[i]; i++ )
520
13.3k
                if( psz_comment[i] >= 'a' && psz_comment[i] <= 'z' )
521
1.62k
                    psz_comment[i] -= 'a' - 'A';
522
523
1.59k
            vlc_meta_SetExtra( p_meta, psz_comment, p );
524
1.59k
        }
525
12.0k
#undef IF_EXTRACT
526
12.4k
next_comment:
527
12.4k
        free( psz_comment );
528
12.4k
        RM( comment_size );
529
12.4k
    }
530
8.12k
#undef RM
531
532
174k
    for ( unsigned int i=0; i<chapters_array.i_size; i++ )
533
166k
    {
534
166k
        if ( !chapters_array.pp_chapters[i] ) continue;
535
1.70k
        TAB_APPEND_CAST( (seekpoint_t**), *i_seekpoint, *ppp_seekpoint,
536
1.70k
                         chapters_array.pp_chapters[i] );
537
1.70k
    }
538
8.12k
    free( chapters_array.pp_chapters );
539
8.12k
}
540
541
const char *FindKateCategoryName( const char *psz_tag )
542
0
{
543
0
    for( size_t i = 0; i < sizeof(Katei18nCategories)/sizeof(Katei18nCategories[0]); i++ )
544
0
    {
545
0
        if( !strcmp( psz_tag, Katei18nCategories[i].psz_tag ) )
546
0
            return Katei18nCategories[i].psz_i18n;
547
0
    }
548
0
    return N_("Unknown category");
549
0
}