Coverage Report

Created: 2025-07-23 07:11

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