Coverage Report

Created: 2026-09-28 07:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/demux/mkv/chapter_command_dvd.cpp
Line
Count
Source
1
// Copyright (C) 2003-2024 VLC authors and VideoLAN
2
// SPDX-License-Identifier: LGPL-2.1-or-later
3
//
4
// chapter_command_dvd.cpp : DVD codec for Matroska Chapter Codecs
5
// Authors: Laurent Aimar <fenrir@via.ecp.fr>
6
//          Steve Lhomme <steve.lhomme@free.fr>
7
8
9
#include "chapter_command_dvd.hpp"
10
#include "virtual_segment.hpp"
11
12
#include <vlc_subpicture.h> // vlc_spu_highlight_t
13
#include <vlc_bits.h>
14
15
namespace mkv {
16
17
constexpr binary MATROSKA_DVD_LEVEL_SS   = 0x30;
18
constexpr binary MATROSKA_DVD_LEVEL_LU   = 0x2A;
19
constexpr binary MATROSKA_DVD_LEVEL_TT   = 0x28;
20
constexpr binary MATROSKA_DVD_LEVEL_PGC  = 0x20;
21
// constexpr binary MATROSKA_DVD_LEVEL_PG   = 0x18;
22
constexpr binary MATROSKA_DVD_LEVEL_PTT  = 0x10;
23
constexpr binary MATROSKA_DVD_LEVEL_CN   = 0x08;
24
25
int16_t dvd_chapter_codec_c::GetTitleNumber() const
26
0
{
27
0
    if ( p_private_data != nullptr && p_private_data->GetSize() > 3)
28
0
    {
29
0
        const binary* p_data = p_private_data->GetBuffer();
30
0
        if ( p_data[0] == MATROSKA_DVD_LEVEL_SS )
31
0
        {
32
0
            return int16_t( (p_data[2] << 8) + p_data[3] );
33
0
        }
34
0
    }
35
0
    return -1;
36
0
}
37
38
bool dvd_chapter_codec_c::Enter()
39
0
{
40
0
    return EnterLeaveHelper( "Matroska DVD enter command", enter_cmds, MATROSKA_CHAPPROCESSTIME_BEFORE );
41
0
}
42
43
bool dvd_chapter_codec_c::Leave()
44
0
{
45
0
    return EnterLeaveHelper( "Matroska DVD leave command", leave_cmds, MATROSKA_CHAPPROCESSTIME_AFTER );
46
0
}
47
48
bool dvd_chapter_codec_c::EnterLeaveHelper( char const * str_diag, ChapterProcess & p_container, MatroskaChapterProcessTime process_time )
49
0
{
50
0
    bool f_result = false;
51
0
    ChapterProcess::iterator it = p_container.begin ();
52
0
    while( it != p_container.end() )
53
0
    {
54
0
        if( (*it).GetSize() )
55
0
        {
56
0
            binary *p_data = (*it).GetBuffer();
57
0
            size_t i_size  = std::min<size_t>( *p_data++, ( (*it).GetSize() - 1 ) >> 3 ); // avoid reading too much
58
0
            for( ; i_size > 0; i_size -=1, p_data += 8 )
59
0
            {
60
0
                vlc_debug( l, "%s", str_diag);
61
0
                f_result |= intepretor.Interpret( process_time, p_data, 8 );
62
0
            }
63
0
        }
64
0
        ++it;
65
0
    }
66
0
    return f_result;
67
0
}
68
69
70
std::string dvd_chapter_codec_c::GetCodecName( bool f_for_title ) const
71
0
{
72
0
    std::string result;
73
0
    if ( p_private_data && p_private_data->GetSize() >= 3)
74
0
    {
75
0
        const binary* p_data = p_private_data->GetBuffer();
76
/*        if ( p_data[0] == MATROSKA_DVD_LEVEL_TT )
77
        {
78
            uint16_t i_title = (p_data[1] << 8) + p_data[2];
79
            char psz_str[11];
80
            sprintf( psz_str, " %d  ---", i_title );
81
            result = "---  DVD Title";
82
            result += psz_str;
83
        }
84
0
        else */ if ( p_data[0] == MATROSKA_DVD_LEVEL_LU )
85
0
        {
86
0
            char psz_str[11];
87
0
            snprintf( psz_str, ARRAY_SIZE(psz_str), " (%c%c)  ---", p_data[1], p_data[2] );
88
0
            result = "---  DVD Menu";
89
0
            result += psz_str;
90
0
        }
91
0
        else if ( p_data[0] == MATROSKA_DVD_LEVEL_SS && f_for_title )
92
0
        {
93
0
            if ( p_data[1] == 0x00 )
94
0
                result = "First Played";
95
0
            else if ( p_data[1] == 0xC0 )
96
0
                result = "Video Manager";
97
0
            else if ( p_data[1] == 0x80 && p_private_data->GetSize() > 3)
98
0
            {
99
0
                uint16_t i_title = (p_data[2] << 8) + p_data[3];
100
0
                char psz_str[20];
101
0
                snprintf( psz_str, ARRAY_SIZE(psz_str), " %d -----", i_title );
102
0
                result = "----- Title";
103
0
                result += psz_str;
104
0
            }
105
0
        }
106
0
    }
107
108
0
    return result;
109
0
}
110
111
// see http://www.dvd-replica.com/DVD/vmcmdset.php for a description of DVD commands
112
bool dvd_command_interpretor_c::Interpret( MatroskaChapterProcessTime, const binary * p_command, size_t i_size )
113
0
{
114
0
    if ( i_size != 8 )
115
0
        return false;
116
117
0
    virtual_segment_c *p_vsegment = NULL;
118
0
    virtual_chapter_c *p_vchapter = NULL;
119
0
    bool f_result = false;
120
0
    uint16_t i_command = ( p_command[0] << 8 ) + p_command[1];
121
122
    // handle register tests if there are some
123
0
    if ( (i_command & 0xF0) != 0 )
124
0
    {
125
0
        bool b_test_positive = true;//(i_command & CMD_DVD_IF_NOT) == 0;
126
0
        bool b_test_value    = (i_command & CMD_DVD_TEST_VALUE) != 0;
127
0
        uint8_t i_test = i_command & 0x70;
128
0
        uint16_t i_value;
129
130
        // see http://dvd.sourceforge.net/dvdinfo/vmi.html
131
0
        uint8_t  i_cr1;
132
0
        uint16_t i_cr2;
133
0
        switch ( i_command >> 12 )
134
0
        {
135
0
        default:
136
0
            i_cr1 = p_command[3];
137
0
            i_cr2 = (p_command[4] << 8) + p_command[5];
138
0
            break;
139
0
        case 3:
140
0
        case 4:
141
0
        case 5:
142
0
            i_cr1 = p_command[6];
143
0
            i_cr2 = p_command[7];
144
0
            b_test_value = false;
145
0
            break;
146
0
        case 6:
147
0
        case 7:
148
0
            if ( ((p_command[1] >> 4) & 0x7) == 0)
149
0
            {
150
0
                i_cr1 = p_command[4];
151
0
                i_cr2 = (p_command[6] << 8) + p_command[7];
152
0
            }
153
0
            else
154
0
            {
155
0
                i_cr1 = p_command[5];
156
0
                i_cr2 = (p_command[6] << 8) + p_command[7];
157
0
            }
158
0
            break;
159
0
        }
160
161
0
        if ( b_test_value )
162
0
            i_value = i_cr2;
163
0
        else
164
0
            i_value = GetPRM( i_cr2 );
165
166
0
        switch ( i_test )
167
0
        {
168
0
        case CMD_DVD_IF_GPREG_EQUAL:
169
            // if equals
170
0
            vlc_debug( l, "IF %s EQUALS %s", GetRegTypeName( false, i_cr1 ).c_str(), GetRegTypeName( b_test_value, i_value ).c_str() );
171
0
            if (!( GetPRM( i_cr1 ) == i_value ))
172
0
            {
173
0
                b_test_positive = false;
174
0
            }
175
0
            break;
176
0
        case CMD_DVD_IF_GPREG_NOT_EQUAL:
177
            // if not equals
178
0
            vlc_debug( l, "IF %s NOT EQUALS %s", GetRegTypeName( false, i_cr1 ).c_str(), GetRegTypeName( b_test_value, i_value ).c_str() );
179
0
            if (!( GetPRM( i_cr1 ) != i_value ))
180
0
            {
181
0
                b_test_positive = false;
182
0
            }
183
0
            break;
184
0
        case CMD_DVD_IF_GPREG_INF:
185
            // if inferior
186
0
            vlc_debug( l, "IF %s < %s", GetRegTypeName( false, p_command[3] ).c_str(), GetRegTypeName( b_test_value, i_value ).c_str() );
187
0
            if (!( GetPRM( i_cr1 ) < i_value ))
188
0
            {
189
0
                b_test_positive = false;
190
0
            }
191
0
            break;
192
0
        case CMD_DVD_IF_GPREG_INF_EQUAL:
193
            // if inferior or equal
194
0
            vlc_debug( l, "IF %s < %s", GetRegTypeName( false, p_command[3] ).c_str(), GetRegTypeName( b_test_value, i_value ).c_str() );
195
0
            if (!( GetPRM( i_cr1 ) <= i_value ))
196
0
            {
197
0
                b_test_positive = false;
198
0
            }
199
0
            break;
200
0
        case CMD_DVD_IF_GPREG_AND:
201
            // if logical and
202
0
            vlc_debug( l, "IF %s & %s", GetRegTypeName( false, p_command[3] ).c_str(), GetRegTypeName( b_test_value, i_value ).c_str() );
203
0
            if (!( GetPRM( i_cr1 ) & i_value ))
204
0
            {
205
0
                b_test_positive = false;
206
0
            }
207
0
            break;
208
0
        case CMD_DVD_IF_GPREG_SUP:
209
            // if superior
210
0
            vlc_debug( l, "IF %s >= %s", GetRegTypeName( false, p_command[3] ).c_str(), GetRegTypeName( b_test_value, i_value ).c_str() );
211
0
            if (!( GetPRM( i_cr1 ) > i_value ))
212
0
            {
213
0
                b_test_positive = false;
214
0
            }
215
0
            break;
216
0
        case CMD_DVD_IF_GPREG_SUP_EQUAL:
217
            // if superior or equal
218
0
            vlc_debug( l, "IF %s >= %s", GetRegTypeName( false, p_command[3] ).c_str(), GetRegTypeName( b_test_value, i_value ).c_str() );
219
0
            if (!( GetPRM( i_cr1 ) >= i_value ))
220
0
            {
221
0
                b_test_positive = false;
222
0
            }
223
0
            break;
224
0
        }
225
226
0
        if ( !b_test_positive )
227
0
            return false;
228
0
    }
229
230
    // strip the test command
231
0
    i_command &= 0xFF0F;
232
233
0
    switch ( i_command )
234
0
    {
235
0
    case CMD_DVD_NOP:
236
0
    case CMD_DVD_NOP2:
237
0
        {
238
0
            vlc_debug( l, "NOP" );
239
0
            break;
240
0
        }
241
0
    case CMD_DVD_BREAK:
242
0
        {
243
0
            vlc_debug( l, "Break" );
244
            // TODO
245
0
            break;
246
0
        }
247
0
    case CMD_DVD_JUMP_TT:
248
0
        {
249
0
            uint8_t i_title = p_command[5];
250
0
            vlc_debug( l, "JumpTT %d", i_title );
251
252
            // find in the ChapProcessPrivate matching this Title level
253
0
            p_vchapter = vm.BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
254
0
                [i_title](const chapter_codec_cmds_c &data) {
255
0
                    return MatchTitleNumber(data, i_title);
256
0
                }, p_vsegment );
257
0
            if ( p_vsegment != NULL && p_vchapter != NULL )
258
0
            {
259
                /* enter via the First Cell */
260
0
                uint8_t i_cell = 1;
261
0
                p_vchapter = p_vchapter->BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
262
0
                    [i_cell](const chapter_codec_cmds_c &data) {
263
0
                        return MatchCellNumber( data, i_cell );
264
0
                    });
265
0
                if ( p_vchapter != NULL )
266
0
                {
267
0
                    vm.JumpTo( *p_vsegment, *p_vchapter );
268
0
                    f_result = true;
269
0
                }
270
0
            }
271
272
0
            break;
273
0
        }
274
0
    case CMD_DVD_CALLSS_VTSM1:
275
0
        {
276
0
            vlc_debug( l, "CallSS" );
277
0
            binary p_type;
278
0
            switch( (p_command[6] & 0xC0) >> 6 ) {
279
0
                case 0:
280
0
                    p_type = p_command[5] & 0x0F;
281
0
                    switch ( p_type )
282
0
                    {
283
0
                    case 0x00:
284
0
                        vlc_debug( l, "CallSS PGC (rsm_cell %x)", p_command[4]);
285
0
                        break;
286
0
                    case 0x02:
287
0
                        vlc_debug( l, "CallSS Title Entry (rsm_cell %x)", p_command[4]);
288
0
                        break;
289
0
                    case 0x03:
290
0
                        vlc_debug( l, "CallSS Root Menu (rsm_cell %x)", p_command[4]);
291
0
                        break;
292
0
                    case 0x04:
293
0
                        vlc_debug( l, "CallSS Subpicture Menu (rsm_cell %x)", p_command[4]);
294
0
                        break;
295
0
                    case 0x05:
296
0
                        vlc_debug( l, "CallSS Audio Menu (rsm_cell %x)", p_command[4]);
297
0
                        break;
298
0
                    case 0x06:
299
0
                        vlc_debug( l, "CallSS Angle Menu (rsm_cell %x)", p_command[4]);
300
0
                        break;
301
0
                    case 0x07:
302
0
                        vlc_debug( l, "CallSS Chapter Menu (rsm_cell %x)", p_command[4]);
303
0
                        break;
304
0
                    default:
305
0
                        vlc_debug( l, "CallSS <unknown> (rsm_cell %x)", p_command[4]);
306
0
                        break;
307
0
                    }
308
0
                    p_vchapter = vm.BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
309
0
                        [p_type](const chapter_codec_cmds_c &data) {
310
0
                            return MatchPgcType( data, p_type );
311
0
                        }, p_vsegment );
312
0
                    if ( p_vsegment != NULL && p_vchapter != NULL )
313
0
                    {
314
                        /* enter via the first Cell */
315
0
                        uint8_t i_cell = 1;
316
0
                        p_vchapter = p_vchapter->BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
317
0
                            [i_cell](const chapter_codec_cmds_c &data) {
318
0
                                return MatchCellNumber( data, i_cell ); } );
319
0
                        if ( p_vchapter != NULL )
320
0
                        {
321
0
                            vm.JumpTo( *p_vsegment, *p_vchapter );
322
0
                            f_result = true;
323
0
                        }
324
0
                    }
325
0
                break;
326
0
                case 1:
327
0
                    vlc_debug( l, "CallSS VMGM (menu %d, rsm_cell %x)", p_command[5] & 0x0F, p_command[4]);
328
0
                break;
329
0
                case 2:
330
0
                    vlc_debug( l, "CallSS VTSM (menu %d, rsm_cell %x)", p_command[5] & 0x0F, p_command[4]);
331
0
                break;
332
0
                case 3:
333
0
                    vlc_debug( l, "CallSS VMGM (pgc %d, rsm_cell %x)", (p_command[2] << 8) + p_command[3], p_command[4]);
334
0
                break;
335
0
            }
336
0
            break;
337
0
        }
338
0
    case CMD_DVD_JUMP_SS:
339
0
        {
340
0
            vlc_debug( l, "JumpSS");
341
0
            binary p_type;
342
0
            switch( (p_command[5] & 0xC0) >> 6 ) {
343
0
                case 0:
344
0
                    vlc_debug( l, "JumpSS FP");
345
0
                break;
346
0
                case 1:
347
0
                    p_type = p_command[5] & 0x0F;
348
0
                    switch ( p_type )
349
0
                    {
350
0
                    case 0x02:
351
0
                        vlc_debug( l, "JumpSS VMGM Title Entry");
352
0
                        break;
353
0
                    case 0x03:
354
0
                        vlc_debug( l, "JumpSS VMGM Root Menu");
355
0
                        break;
356
0
                    case 0x04:
357
0
                        vlc_debug( l, "JumpSS VMGM Subpicture Menu");
358
0
                        break;
359
0
                    case 0x05:
360
0
                        vlc_debug( l, "JumpSS VMGM Audio Menu");
361
0
                        break;
362
0
                    case 0x06:
363
0
                        vlc_debug( l, "JumpSS VMGM Angle Menu");
364
0
                        break;
365
0
                    case 0x07:
366
0
                        vlc_debug( l, "JumpSS VMGM Chapter Menu");
367
0
                        break;
368
0
                    default:
369
0
                        vlc_debug( l, "JumpSS <unknown>");
370
0
                        break;
371
0
                    }
372
                    // find the VMG
373
0
                    p_vchapter = vm.BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
374
0
                        [](const chapter_codec_cmds_c &data) {
375
0
                            return MatchIsVMG( data); }, p_vsegment );
376
0
                    if ( p_vsegment != NULL )
377
0
                    {
378
0
                        p_vchapter = p_vsegment->BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
379
0
                            [p_type](const chapter_codec_cmds_c &data) {
380
0
                                return MatchPgcType( data, p_type ); } );
381
0
                        if ( p_vchapter != NULL )
382
0
                        {
383
0
                            vm.JumpTo( *p_vsegment, *p_vchapter );
384
0
                            f_result = true;
385
0
                        }
386
0
                    }
387
0
                break;
388
0
                case 2:
389
0
                    p_type = p_command[5] & 0x0F;
390
0
                    switch ( p_type )
391
0
                    {
392
0
                    case 0x02:
393
0
                        vlc_debug( l, "JumpSS VTSM (vts %d, ttn %d) Title Entry", p_command[4], p_command[3]);
394
0
                        break;
395
0
                    case 0x03:
396
0
                        vlc_debug( l, "JumpSS VTSM (vts %d, ttn %d) Root Menu", p_command[4], p_command[3]);
397
0
                        break;
398
0
                    case 0x04:
399
0
                        vlc_debug( l, "JumpSS VTSM (vts %d, ttn %d) Subpicture Menu", p_command[4], p_command[3]);
400
0
                        break;
401
0
                    case 0x05:
402
0
                        vlc_debug( l, "JumpSS VTSM (vts %d, ttn %d) Audio Menu", p_command[4], p_command[3]);
403
0
                        break;
404
0
                    case 0x06:
405
0
                        vlc_debug( l, "JumpSS VTSM (vts %d, ttn %d) Angle Menu", p_command[4], p_command[3]);
406
0
                        break;
407
0
                    case 0x07:
408
0
                        vlc_debug( l, "JumpSS VTSM (vts %d, ttn %d) Chapter Menu", p_command[4], p_command[3]);
409
0
                        break;
410
0
                    default:
411
0
                        vlc_debug( l, "JumpSS VTSM (vts %d, ttn %d) <unknown>", p_command[4], p_command[3]);
412
0
                        break;
413
0
                    }
414
415
0
                    {
416
0
                    uint8_t i_vts = p_command[4];
417
0
                    p_vchapter = vm.BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
418
0
                        [i_vts](const chapter_codec_cmds_c &data) {
419
0
                            return MatchVTSMNumber( data,  i_vts ); }, p_vsegment );
420
421
0
                    if ( p_vsegment != NULL && p_vchapter != NULL )
422
0
                    {
423
                        // find the title in the VTS
424
0
                        uint8_t i_title = p_command[3];
425
0
                        p_vchapter = p_vchapter->BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
426
0
                            [i_title](const chapter_codec_cmds_c &data) {
427
0
                                return MatchTitleNumber( data, i_title ); } );
428
0
                        if ( p_vchapter != NULL )
429
0
                        {
430
                            // find the specified menu in the VTSM
431
0
                            p_vchapter = p_vsegment->BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
432
0
                                [p_type](const chapter_codec_cmds_c &data) {
433
0
                                    return MatchPgcType( data, p_type ); } );
434
0
                            if ( p_vchapter != NULL )
435
0
                            {
436
0
                                vm.JumpTo( *p_vsegment, *p_vchapter );
437
0
                                f_result = true;
438
0
                            }
439
0
                        }
440
0
                        else
441
0
                            vlc_debug( l, "Title (%d) does not exist in this VTS", i_title );
442
0
                    }
443
0
                    else
444
0
                        vlc_debug( l, "DVD Domain VTS (%d) not found", i_vts );
445
0
                    }
446
0
                break;
447
0
                case 3:
448
0
                    vlc_debug( l, "JumpSS VMGM (pgc %d)", (p_command[2] << 8) + p_command[3]);
449
0
                break;
450
0
            }
451
0
            break;
452
0
        }
453
0
    case CMD_DVD_JUMPVTS_PTT:
454
0
        {
455
0
            uint8_t i_title = p_command[5];
456
0
            uint8_t i_ptt = p_command[3];
457
458
0
            vlc_debug( l, "JumpVTS Title (%d) PTT (%d)", i_title, i_ptt);
459
460
            // find the current VTS content segment
461
0
            p_vchapter = vm.GetCurrentVSegment()->BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
462
0
                [](const chapter_codec_cmds_c &data) {
463
0
                    return MatchIsDomain( data); } );
464
0
            if ( p_vchapter != NULL )
465
0
            {
466
0
                int16_t i_curr_title = ( p_vchapter->p_chapter )? p_vchapter->p_chapter->GetTitleNumber() : 0;
467
0
                if ( i_curr_title > 0 )
468
0
                {
469
0
                    p_vchapter = vm.BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
470
0
                        [i_curr_title](const chapter_codec_cmds_c &data) {
471
0
                            return MatchVTSNumber( data, i_curr_title ); }, p_vsegment );
472
473
0
                    if ( p_vsegment != NULL && p_vchapter != NULL )
474
0
                    {
475
                        // find the title in the VTS
476
0
                        p_vchapter = p_vchapter->BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
477
0
                            [i_title](const chapter_codec_cmds_c &data) {
478
0
                                return MatchTitleNumber( data, i_title ); } );
479
0
                        if ( p_vchapter != NULL )
480
0
                        {
481
                            // find the chapter in the title
482
0
                            p_vchapter = p_vchapter->BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
483
0
                                [i_ptt](const chapter_codec_cmds_c &data) {
484
0
                                    return MatchChapterNumber( data, i_ptt ); } );
485
0
                            if ( p_vchapter != NULL )
486
0
                            {
487
0
                                vm.JumpTo( *p_vsegment, *p_vchapter );
488
0
                                f_result = true;
489
0
                            }
490
0
                        }
491
0
                    else
492
0
                        vlc_debug( l, "Title (%d) does not exist in this VTS", i_title );
493
0
                    }
494
0
                    else
495
0
                        vlc_debug( l, "DVD Domain VTS (%d) not found", i_curr_title );
496
0
                }
497
0
                else
498
0
                    vlc_debug( l, "JumpVTS_PTT command found but not in a VTS(M)");
499
0
            }
500
0
            else
501
0
                vlc_debug( l, "JumpVTS_PTT command but the DVD domain wasn't found");
502
0
            break;
503
0
        }
504
0
    case CMD_DVD_SET_GPRMMD:
505
0
        {
506
0
            vlc_debug( l, "Set GPRMMD [%d]=%d", (p_command[4] << 8) + p_command[5], (p_command[2] << 8) + p_command[3]);
507
508
0
            if ( !SetGPRM( (p_command[4] << 8) + p_command[5], (p_command[2] << 8) + p_command[3] ) )
509
0
                vlc_debug( l, "Set GPRMMD failed" );
510
0
            break;
511
0
        }
512
0
    case CMD_DVD_LINKPGCN:
513
0
        {
514
0
            uint16_t i_pgcn = (p_command[6] << 8) + p_command[7];
515
516
0
            vlc_debug( l, "Link PGCN(%d)", i_pgcn );
517
0
            p_vchapter = vm.GetCurrentVSegment()->BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
518
0
                [i_pgcn](const chapter_codec_cmds_c &data) {
519
0
                    return MatchPgcNumber( data, i_pgcn ); } );
520
0
            if ( p_vchapter != NULL )
521
0
            {
522
0
                vm.JumpTo( *vm.GetCurrentVSegment(), *p_vchapter );
523
0
                f_result = true;
524
0
            }
525
0
            break;
526
0
        }
527
0
    case CMD_DVD_LINKCN:
528
0
        {
529
0
            uint8_t i_cn = p_command[7];
530
531
0
            p_vchapter = vm.GetCurrentVSegment()->CurrentChapter();
532
533
0
            vlc_debug( l, "LinkCN (cell %d)", i_cn );
534
0
            p_vchapter = p_vchapter->BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD,
535
0
                [i_cn](const chapter_codec_cmds_c &data) {
536
0
                    return MatchCellNumber( data, i_cn ); } );
537
0
            if ( p_vchapter != NULL )
538
0
            {
539
0
                vm.JumpTo( *vm.GetCurrentVSegment(), *p_vchapter );
540
0
                f_result = true;
541
0
            }
542
0
            break;
543
0
        }
544
0
    case CMD_DVD_GOTO_LINE:
545
0
        {
546
0
            vlc_debug( l, "GotoLine (%d)", (p_command[6] << 8) + p_command[7] );
547
            // TODO
548
0
            break;
549
0
        }
550
0
    case CMD_DVD_SET_HL_BTNN1:
551
0
        {
552
0
            vlc_debug( l, "SetHL_BTN (%d)", p_command[4] );
553
0
            SetSPRM( 0x88, p_command[4] );
554
0
            break;
555
0
        }
556
0
    default:
557
0
        {
558
0
            vlc_debug( l, "unsupported command : %02X %02X %02X %02X %02X %02X %02X %02X"
559
0
                     ,p_command[0]
560
0
                     ,p_command[1]
561
0
                     ,p_command[2]
562
0
                     ,p_command[3]
563
0
                     ,p_command[4]
564
0
                     ,p_command[5]
565
0
                     ,p_command[6]
566
0
                     ,p_command[7]);
567
0
            break;
568
0
        }
569
0
    }
570
571
0
    return f_result;
572
0
}
573
574
bool dvd_command_interpretor_c::ProcessNavAction( uint16_t button )
575
0
{
576
0
    const pci_t & pci = pci_packet;
577
578
0
    if( button == 0 || button > pci.hli.hl_gi.btn_ns || button > ARRAY_SIZE(pci.hli.btnit) )
579
0
        return false;
580
581
0
    SetSPRM( 0x88, button );
582
0
    const btni_t & button_ptr = pci.hli.btnit[button-1];
583
0
    if ( button_ptr.auto_action_mode )
584
0
    {
585
        // process the button action
586
0
        return Interpret( MATROSKA_CHAPPROCESSTIME_DURING, button_ptr.cmd.bytes, 8 );
587
0
    }
588
0
    return false;
589
0
}
590
591
bool dvd_command_interpretor_c::HandleKeyEvent( NavivationKey key )
592
0
{
593
0
    const pci_t & pci = pci_packet;
594
0
    uint16_t i_curr_button = GetSPRM( 0x88 );
595
596
0
    if( i_curr_button == 0 || i_curr_button > pci.hli.hl_gi.btn_ns || i_curr_button > ARRAY_SIZE(pci.hli.btnit) )
597
0
        return false;
598
599
0
    const btni_t & button_ptr = pci.hli.btnit[i_curr_button-1];
600
601
0
    switch( key )
602
0
    {
603
0
    case LEFT:  return ProcessNavAction( button_ptr.left );
604
0
    case RIGHT: return ProcessNavAction( button_ptr.right );
605
0
    case UP:    return ProcessNavAction( button_ptr.up );
606
0
    case DOWN:  return ProcessNavAction( button_ptr.down );
607
0
    case OK:
608
        // process the button action
609
0
        return Interpret( MATROSKA_CHAPPROCESSTIME_DURING, button_ptr.cmd.bytes, 8 );
610
0
    case MENU:
611
0
    case POPUP:
612
0
        return false;
613
0
    }
614
0
    vlc_assert_unreachable();
615
0
}
616
617
void dvd_command_interpretor_c::HandleMousePressed( unsigned x, unsigned y )
618
0
{
619
0
    const pci_t & pci = pci_packet;
620
621
0
    uint16_t best;
622
0
    uint32_t dist,d;
623
0
    int32_t mx,my,dx,dy;
624
625
    // get current button
626
0
    best = 0;
627
0
    dist = std::numeric_limits<uint32_t>::max(); /* >> than  (720*720)+(567*567); */
628
0
    for(uint16_t button = 1; button <= std::min<uint16_t>(pci.hli.hl_gi.btn_ns, ARRAY_SIZE(pci.hli.btnit)); button++)
629
0
    {
630
0
        const btni_t & button_ptr = pci.hli.btnit[button-1];
631
632
0
        if((x >= button_ptr.x_start)
633
0
            && (x <= button_ptr.x_end)
634
0
            && (y >= button_ptr.y_start)
635
0
            && (y <= button_ptr.y_end))
636
0
        {
637
0
            mx = (button_ptr.x_start + button_ptr.x_end)/2;
638
0
            my = (button_ptr.y_start + button_ptr.y_end)/2;
639
0
            dx = mx - x;
640
0
            dy = my - y;
641
0
            d = (dx*dx) + (dy*dy);
642
            /* If the mouse is within the button and the mouse is closer
643
            * to the center of this button then it is the best choice. */
644
0
            if(d < dist) {
645
0
                dist = d;
646
0
                best = button;
647
0
            }
648
0
        }
649
0
    }
650
651
0
    if ( best == 0)
652
0
        return;
653
654
0
    const btni_t & button_ptr = pci.hli.btnit[best-1];
655
0
    uint16_t i_curr_button = GetSPRM( 0x88 );
656
657
0
    vlc_debug( l, "Clicked button %" PRIu16, best );
658
659
    // process the button action
660
0
    SetSPRM( 0x88, best );
661
0
    Interpret( MATROSKA_CHAPPROCESSTIME_DURING, button_ptr.cmd.bytes, 8 );
662
663
0
    vlc_debug( l, "Processed button %" PRIu16, best );
664
665
    // select new button
666
0
    if ( best != i_curr_button )
667
0
    {
668
        // TODO: make sure we do not overflow in the conversion
669
0
        vlc_spu_highlight_t spu_hl = vlc_spu_highlight_t();
670
671
0
        spu_hl.x_start = (int)button_ptr.x_start;
672
0
        spu_hl.y_start = (int)button_ptr.y_start;
673
674
0
        spu_hl.x_end = (int)button_ptr.x_end;
675
0
        spu_hl.y_end = (int)button_ptr.y_end;
676
677
0
        uint32_t i_palette;
678
679
0
        if(button_ptr.btn_coln != 0) {
680
0
            i_palette = pci.hli.btn_colit.btn_coli[button_ptr.btn_coln-1][1];
681
0
        } else {
682
0
            i_palette = 0;
683
0
        }
684
685
0
        for( int i = 0; i < 4; i++ )
686
0
        {
687
0
            uint32_t i_yuv = 0xFF;//p_sys->clut[(hl.palette>>(16+i*4))&0x0f];
688
0
            uint8_t i_alpha = (i_palette>>(i*4))&0x0f;
689
0
            i_alpha = i_alpha == 0xf ? 0xff : i_alpha << 4;
690
691
0
            spu_hl.palette.palette[i][0] = (i_yuv >> 16) & 0xff;
692
0
            spu_hl.palette.palette[i][1] = (i_yuv >> 0) & 0xff;
693
0
            spu_hl.palette.palette[i][2] = (i_yuv >> 8) & 0xff;
694
0
            spu_hl.palette.palette[i][3] = i_alpha;
695
0
        }
696
697
0
        vm.SetHighlight( spu_hl );
698
0
    }
699
0
}
700
701
bool dvd_command_interpretor_c::MatchIsDomain( const chapter_codec_cmds_c &data )
702
0
{
703
0
    return ( data.p_private_data != NULL && data.p_private_data->GetBuffer()[0] == MATROSKA_DVD_LEVEL_SS );
704
0
}
705
706
bool dvd_command_interpretor_c::MatchIsVMG( const chapter_codec_cmds_c &data )
707
0
{
708
0
    if ( data.p_private_data == NULL || data.p_private_data->GetSize() < 2 )
709
0
        return false;
710
711
0
    return ( data.p_private_data->GetBuffer()[0] == MATROSKA_DVD_LEVEL_SS && data.p_private_data->GetBuffer()[1] == 0xC0);
712
0
}
713
714
bool dvd_command_interpretor_c::MatchVTSNumber( const chapter_codec_cmds_c &data, uint16_t i_title )
715
0
{
716
0
    if ( data.p_private_data == NULL || data.p_private_data->GetSize() < 4 )
717
0
        return false;
718
719
0
    if ( data.p_private_data->GetBuffer()[0] != MATROSKA_DVD_LEVEL_SS || data.p_private_data->GetBuffer()[1] != 0x80 )
720
0
        return false;
721
722
0
    uint16_t i_gtitle = (data.p_private_data->GetBuffer()[2] << 8 ) + data.p_private_data->GetBuffer()[3];
723
724
0
    return (i_gtitle == i_title);
725
0
}
726
727
bool dvd_command_interpretor_c::MatchVTSMNumber( const chapter_codec_cmds_c &data, uint8_t i_title )
728
0
{
729
0
    if ( data.p_private_data == NULL || data.p_private_data->GetSize() < 4 )
730
0
        return false;
731
732
0
    if ( data.p_private_data->GetBuffer()[0] != MATROSKA_DVD_LEVEL_SS || data.p_private_data->GetBuffer()[1] != 0x40 )
733
0
        return false;
734
735
0
    uint8_t i_gtitle = data.p_private_data->GetBuffer()[3];
736
737
0
    return (i_gtitle == i_title);
738
0
}
739
740
bool dvd_command_interpretor_c::MatchTitleNumber( const chapter_codec_cmds_c &data, uint8_t i_title )
741
0
{
742
0
    if ( data.p_private_data == NULL || data.p_private_data->GetSize() < 4 )
743
0
        return false;
744
745
0
    if ( data.p_private_data->GetBuffer()[0] != MATROSKA_DVD_LEVEL_TT )
746
0
        return false;
747
748
0
    uint16_t i_gtitle = (data.p_private_data->GetBuffer()[1] << 8 ) + data.p_private_data->GetBuffer()[2];
749
750
0
    return (i_gtitle == i_title);
751
0
}
752
753
bool dvd_command_interpretor_c::MatchPgcType( const chapter_codec_cmds_c &data, uint8_t i_pgc )
754
0
{
755
0
    if ( data.p_private_data == NULL || data.p_private_data->GetSize() < 8 )
756
0
        return false;
757
758
0
    if ( data.p_private_data->GetBuffer()[0] != MATROSKA_DVD_LEVEL_PGC )
759
0
        return false;
760
761
0
    uint8_t i_pgc_type = data.p_private_data->GetBuffer()[3] & 0x0F;
762
763
0
    return (i_pgc_type == i_pgc);
764
0
}
765
766
bool dvd_command_interpretor_c::MatchPgcNumber( const chapter_codec_cmds_c &data, uint16_t i_pgc_n )
767
0
{
768
0
    if ( data.p_private_data == NULL || data.p_private_data->GetSize() < 8 )
769
0
        return false;
770
771
0
    if ( data.p_private_data->GetBuffer()[0] != MATROSKA_DVD_LEVEL_PGC )
772
0
        return false;
773
774
0
    uint16_t i_pgc_num = (data.p_private_data->GetBuffer()[1] << 8) + data.p_private_data->GetBuffer()[2];
775
776
0
    return (i_pgc_num == i_pgc_n);
777
0
}
778
779
bool dvd_command_interpretor_c::MatchChapterNumber( const chapter_codec_cmds_c &data, uint8_t i_ptt )
780
0
{
781
0
    if ( data.p_private_data == NULL || data.p_private_data->GetSize() < 2 )
782
0
        return false;
783
784
0
    if ( data.p_private_data->GetBuffer()[0] != MATROSKA_DVD_LEVEL_PTT )
785
0
        return false;
786
787
0
    uint8_t i_chapter = data.p_private_data->GetBuffer()[1];
788
789
0
    return (i_chapter == i_ptt);
790
0
}
791
792
bool dvd_command_interpretor_c::MatchCellNumber( const chapter_codec_cmds_c &data, uint8_t i_cell_n )
793
0
{
794
0
    if ( data.p_private_data == NULL || data.p_private_data->GetSize() < 5 )
795
0
        return false;
796
797
0
    if ( data.p_private_data->GetBuffer()[0] != MATROSKA_DVD_LEVEL_CN )
798
0
        return false;
799
800
0
    uint8_t i_cell_num = data.p_private_data->GetBuffer()[3];
801
802
0
    return (i_cell_num == i_cell_n);
803
0
}
804
805
void dvd_command_interpretor_c::SetPci(const uint8_t *data, unsigned size)
806
0
{
807
0
    if (size < PCI_RAW_SIZE)
808
0
        return;
809
810
0
    bs_t bs;
811
0
    bs_init(&bs, data, size);
812
813
0
    pci_packet.pci_gi.nv_pck_lbn = bs_read(&bs, 32);
814
0
    pci_packet.pci_gi.vobu_cat   = bs_read(&bs, 16);
815
0
    bs_skip(&bs, 16);
816
817
0
    bs_skip(&bs, 7);
818
0
    pci_packet.pci_gi.vobu_uop_ctl.video_pres_mode_change = bs_read1(&bs);
819
820
0
    pci_packet.pci_gi.vobu_uop_ctl.karaoke_audio_pres_mode_change = bs_read1(&bs);
821
0
    pci_packet.pci_gi.vobu_uop_ctl.angle_change           = bs_read1(&bs);
822
0
    pci_packet.pci_gi.vobu_uop_ctl.subpic_stream_change   = bs_read1(&bs);
823
0
    pci_packet.pci_gi.vobu_uop_ctl.audio_stream_change    = bs_read1(&bs);
824
0
    pci_packet.pci_gi.vobu_uop_ctl.pause_on               = bs_read1(&bs);
825
0
    pci_packet.pci_gi.vobu_uop_ctl.still_off              = bs_read1(&bs);
826
0
    pci_packet.pci_gi.vobu_uop_ctl.button_select_or_activate = bs_read1(&bs);
827
0
    pci_packet.pci_gi.vobu_uop_ctl.resume                 = bs_read1(&bs);
828
829
0
    pci_packet.pci_gi.vobu_uop_ctl.chapter_menu_call      = bs_read1(&bs);
830
0
    pci_packet.pci_gi.vobu_uop_ctl.angle_menu_call        = bs_read1(&bs);
831
0
    pci_packet.pci_gi.vobu_uop_ctl.audio_menu_call        = bs_read1(&bs);
832
0
    pci_packet.pci_gi.vobu_uop_ctl.subpic_menu_call       = bs_read1(&bs);
833
0
    pci_packet.pci_gi.vobu_uop_ctl.root_menu_call         = bs_read1(&bs);
834
0
    pci_packet.pci_gi.vobu_uop_ctl.title_menu_call        = bs_read1(&bs);
835
0
    pci_packet.pci_gi.vobu_uop_ctl.backward_scan          = bs_read1(&bs);
836
0
    pci_packet.pci_gi.vobu_uop_ctl.forward_scan           = bs_read1(&bs);
837
838
0
    pci_packet.pci_gi.vobu_uop_ctl.next_pg_search         = bs_read1(&bs);
839
0
    pci_packet.pci_gi.vobu_uop_ctl.prev_or_top_pg_search  = bs_read1(&bs);
840
0
    pci_packet.pci_gi.vobu_uop_ctl.time_or_chapter_search = bs_read1(&bs);
841
0
    pci_packet.pci_gi.vobu_uop_ctl.go_up                  = bs_read1(&bs);
842
0
    pci_packet.pci_gi.vobu_uop_ctl.stop                   = bs_read1(&bs);
843
0
    pci_packet.pci_gi.vobu_uop_ctl.title_play             = bs_read1(&bs);
844
0
    pci_packet.pci_gi.vobu_uop_ctl.chapter_search_or_play = bs_read1(&bs);
845
0
    pci_packet.pci_gi.vobu_uop_ctl.title_or_time_play     = bs_read1(&bs);
846
847
0
    pci_packet.pci_gi.vobu_s_ptm    = bs_read(&bs, 32);
848
0
    pci_packet.pci_gi.vobu_e_ptm    = bs_read(&bs, 32);
849
0
    pci_packet.pci_gi.vobu_se_e_ptm = bs_read(&bs, 32);
850
851
0
    pci_packet.pci_gi.e_eltm.hour    = bs_read(&bs, 8);
852
0
    pci_packet.pci_gi.e_eltm.minute  = bs_read(&bs, 8);
853
0
    pci_packet.pci_gi.e_eltm.second  = bs_read(&bs, 8);
854
0
    pci_packet.pci_gi.e_eltm.frame_u = bs_read(&bs, 8);
855
856
    // FIXME read buffer in one pass using bs_t
857
0
    for (size_t i = 0; i < ARRAY_SIZE(pci_packet.pci_gi.vobu_isrc); i++)
858
0
        pci_packet.pci_gi.vobu_isrc[i] = bs_read(&bs, 8);
859
860
0
    for (size_t i = 0; i < ARRAY_SIZE(pci_packet.nsml_agli.nsml_agl_dsta); i++)
861
0
        pci_packet.nsml_agli.nsml_agl_dsta[i] = bs_read(&bs, 32);
862
863
0
    pci_packet.hli.hl_gi.hli_ss       = bs_read(&bs, 16);
864
0
    pci_packet.hli.hl_gi.hli_s_ptm    = bs_read(&bs, 32);
865
0
    pci_packet.hli.hl_gi.hli_e_ptm    = bs_read(&bs, 32);
866
0
    pci_packet.hli.hl_gi.btn_se_e_ptm = bs_read(&bs, 32);
867
0
    bs_skip(&bs, 2);
868
0
    pci_packet.hli.hl_gi.btngr_ns      = bs_read(&bs, 2);
869
0
    bs_skip(&bs, 1);
870
0
    pci_packet.hli.hl_gi.btngr1_dsp_ty = bs_read(&bs, 3);
871
0
    bs_skip(&bs, 1);
872
0
    pci_packet.hli.hl_gi.btngr2_dsp_ty = bs_read(&bs, 3);
873
0
    bs_skip(&bs, 1);
874
0
    pci_packet.hli.hl_gi.btngr3_dsp_ty = bs_read(&bs, 3);
875
0
    pci_packet.hli.hl_gi.btn_ofn       = bs_read(&bs, 8);
876
0
    pci_packet.hli.hl_gi.btn_ns        = bs_read(&bs, 8);
877
0
    pci_packet.hli.hl_gi.nsl_btn_ns    = bs_read(&bs, 8);
878
0
    bs_skip(&bs, 8);
879
0
    pci_packet.hli.hl_gi.fosl_btnn     = bs_read(&bs, 8);
880
0
    pci_packet.hli.hl_gi.foac_btnn     = bs_read(&bs, 8);
881
882
0
    for (size_t i = 0; i < 3; i++)
883
0
        for (size_t j = 0; j < 2; j++)
884
0
            pci_packet.hli.btn_colit.btn_coli[i][j] = bs_read(&bs, 32 );
885
886
0
    for (size_t i = 0; i < ARRAY_SIZE(pci_packet.hli.btnit); i++)
887
0
    {
888
0
        auto & btn = pci_packet.hli.btnit[i];
889
0
        btn.btn_coln = bs_read(&bs, 2);
890
0
        btn.x_start  = bs_read(&bs, 10);
891
0
        bs_skip(&bs, 2);
892
0
        btn.x_end    = bs_read(&bs, 10);
893
894
0
        btn.auto_action_mode = bs_read(&bs, 2);
895
0
        btn.y_start = bs_read(&bs, 10);
896
0
        bs_skip(&bs, 2);
897
0
        btn.y_end   = bs_read(&bs, 10);
898
899
0
        bs_skip(&bs, 2);
900
0
        btn.up = bs_read(&bs, 6);
901
902
0
        bs_skip(&bs, 2);
903
0
        btn.down    = bs_read(&bs, 6);
904
0
        bs_skip(&bs, 2);
905
0
        btn.left    = bs_read(&bs, 6);
906
0
        bs_skip(&bs, 2);
907
0
        btn.right   = bs_read(&bs, 6);
908
909
        // FIXME read buffer in one pass using bs_t
910
0
        for (size_t i = 0; i < ARRAY_SIZE(btn.cmd.bytes); i++)
911
0
            btn.cmd.bytes[i] = bs_read(&bs, 8);
912
0
    }
913
0
    bs_skip(&bs, 8*189);
914
    assert(!bs_error(&bs));
915
0
}
916
917
} // namespace