9a8749b8795a6871eb9a438e350d8d87b235faa4
[vlc.git] / modules / demux / mkv / chapter_command.cpp
1 /*****************************************************************************
2  * chapter_command.cpp : matroska demuxer
3  *****************************************************************************
4  * Copyright (C) 2003-2004 VLC authors and VideoLAN
5  *
6  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7  *          Steve Lhomme <steve.lhomme@free.fr>
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 #include "chapter_command.hpp"
25 #include "demux.hpp"
26 #include <algorithm>
27
28 namespace mkv {
29
30 void chapter_codec_cmds_c::AddCommand( const KaxChapterProcessCommand & command )
31 {
32     uint32 codec_time = uint32(-1);
33     for( size_t i = 0; i < command.ListSize(); i++ )
34     {
35         if( MKV_CHECKED_PTR_DECL( p_cpt, KaxChapterProcessTime const, command[i] ) )
36         {
37             codec_time = static_cast<uint32>( *p_cpt );
38             break;
39         }
40     }
41
42     for( size_t i = 0; i < command.ListSize(); i++ )
43     {
44         if( MKV_CHECKED_PTR_DECL( p_cpd, KaxChapterProcessData const, command[i] ) )
45         {
46             std::vector<KaxChapterProcessData*> *containers[] = {
47                 &during_cmds, /* codec_time = 0 */
48                 &enter_cmds,  /* codec_time = 1 */
49                 &leave_cmds   /* codec_time = 2 */
50             };
51
52             if( codec_time < 3 )
53                 containers[codec_time]->push_back( new KaxChapterProcessData( *p_cpd ) );
54         }
55     }
56 }
57
58 int16 dvd_chapter_codec_c::GetTitleNumber()
59 {
60     if ( p_private_data->GetSize() >= 3)
61     {
62         const binary* p_data = p_private_data->GetBuffer();
63         if ( p_data[0] == MATROSKA_DVD_LEVEL_SS )
64         {
65             return int16( (p_data[2] << 8) + p_data[3] );
66         }
67     }
68     return -1;
69 }
70
71 bool dvd_chapter_codec_c::Enter()
72 {
73     return EnterLeaveHelper( "Matroska DVD enter command", &enter_cmds );
74 }
75
76 bool dvd_chapter_codec_c::Leave()
77 {
78     return EnterLeaveHelper( "Matroska DVD leave command", &leave_cmds );
79 }
80
81 bool dvd_chapter_codec_c::EnterLeaveHelper( char const * str_diag, std::vector<KaxChapterProcessData*> * p_container )
82 {
83     bool f_result = false;
84     std::vector<KaxChapterProcessData*>::iterator it = p_container->begin ();
85     while( it != p_container->end() )
86     {
87         if( (*it)->GetSize() )
88         {
89             binary *p_data = (*it)->GetBuffer();
90             size_t i_size  = std::min<size_t>( *p_data++, ( (*it)->GetSize() - 1 ) >> 3 ); // avoid reading too much
91             for( ; i_size > 0; i_size -=1, p_data += 8 )
92             {
93                 msg_Dbg( &sys.demuxer, "%s", str_diag);
94                 f_result |= sys.dvd_interpretor.Interpret( p_data );
95             }
96         }
97         ++it;
98     }
99     return f_result;
100 }
101
102
103 std::string dvd_chapter_codec_c::GetCodecName( bool f_for_title ) const
104 {
105     std::string result;
106     if ( p_private_data->GetSize() >= 3)
107     {
108         const binary* p_data = p_private_data->GetBuffer();
109 /*        if ( p_data[0] == MATROSKA_DVD_LEVEL_TT )
110         {
111             uint16_t i_title = (p_data[1] << 8) + p_data[2];
112             char psz_str[11];
113             sprintf( psz_str, " %d  ---", i_title );
114             result = "---  DVD Title";
115             result += psz_str;
116         }
117         else */ if ( p_data[0] == MATROSKA_DVD_LEVEL_LU )
118         {
119             char psz_str[11];
120             sprintf( psz_str, " (%c%c)  ---", p_data[1], p_data[2] );
121             result = "---  DVD Menu";
122             result += psz_str;
123         }
124         else if ( p_data[0] == MATROSKA_DVD_LEVEL_SS && f_for_title )
125         {
126             if ( p_data[1] == 0x00 )
127                 result = "First Played";
128             else if ( p_data[1] == 0xC0 )
129                 result = "Video Manager";
130             else if ( p_data[1] == 0x80 )
131             {
132                 uint16_t i_title = (p_data[2] << 8) + p_data[3];
133                 char psz_str[20];
134                 sprintf( psz_str, " %d -----", i_title );
135                 result = "----- Title";
136                 result += psz_str;
137             }
138         }
139     }
140
141     return result;
142 }
143 class virtual_segment_c;
144 class chapter_item_c;
145
146 // see http://www.dvd-replica.com/DVD/vmcmdset.php for a description of DVD commands
147 bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_size )
148 {
149     if ( i_size != 8 )
150         return false;
151
152     virtual_segment_c *p_vsegment = NULL;
153     virtual_chapter_c *p_vchapter = NULL;
154     bool f_result = false;
155     uint16 i_command = ( p_command[0] << 8 ) + p_command[1];
156
157     // handle register tests if there are some
158     if ( (i_command & 0xF0) != 0 )
159     {
160         bool b_test_positive = true;//(i_command & CMD_DVD_IF_NOT) == 0;
161         bool b_test_value    = (i_command & CMD_DVD_TEST_VALUE) != 0;
162         uint8 i_test = i_command & 0x70;
163         uint16 i_value;
164
165         // see http://dvd.sourceforge.net/dvdinfo/vmi.html
166         uint8  i_cr1;
167         uint16 i_cr2;
168         switch ( i_command >> 12 )
169         {
170         default:
171             i_cr1 = p_command[3];
172             i_cr2 = (p_command[4] << 8) + p_command[5];
173             break;
174         case 3:
175         case 4:
176         case 5:
177             i_cr1 = p_command[6];
178             i_cr2 = p_command[7];
179             b_test_value = false;
180             break;
181         case 6:
182         case 7:
183             if ( ((p_command[1] >> 4) & 0x7) == 0)
184             {
185                 i_cr1 = p_command[4];
186                 i_cr2 = (p_command[6] << 8) + p_command[7];
187             }
188             else
189             {
190                 i_cr1 = p_command[5];
191                 i_cr2 = (p_command[6] << 8) + p_command[7];
192             }
193             break;
194         }
195
196         if ( b_test_value )
197             i_value = i_cr2;
198         else
199             i_value = GetPRM( i_cr2 );
200
201         switch ( i_test )
202         {
203         case CMD_DVD_IF_GPREG_EQUAL:
204             // if equals
205             msg_Dbg( &sys.demuxer, "IF %s EQUALS %s", GetRegTypeName( false, i_cr1 ).c_str(), GetRegTypeName( b_test_value, i_value ).c_str() );
206             if (!( GetPRM( i_cr1 ) == i_value ))
207             {
208                 b_test_positive = false;
209             }
210             break;
211         case CMD_DVD_IF_GPREG_NOT_EQUAL:
212             // if not equals
213             msg_Dbg( &sys.demuxer, "IF %s NOT EQUALS %s", GetRegTypeName( false, i_cr1 ).c_str(), GetRegTypeName( b_test_value, i_value ).c_str() );
214             if (!( GetPRM( i_cr1 ) != i_value ))
215             {
216                 b_test_positive = false;
217             }
218             break;
219         case CMD_DVD_IF_GPREG_INF:
220             // if inferior
221             msg_Dbg( &sys.demuxer, "IF %s < %s", GetRegTypeName( false, p_command[3] ).c_str(), GetRegTypeName( b_test_value, i_value ).c_str() );
222             if (!( GetPRM( i_cr1 ) < i_value ))
223             {
224                 b_test_positive = false;
225             }
226             break;
227         case CMD_DVD_IF_GPREG_INF_EQUAL:
228             // if inferior or equal
229             msg_Dbg( &sys.demuxer, "IF %s < %s", GetRegTypeName( false, p_command[3] ).c_str(), GetRegTypeName( b_test_value, i_value ).c_str() );
230             if (!( GetPRM( i_cr1 ) <= i_value ))
231             {
232                 b_test_positive = false;
233             }
234             break;
235         case CMD_DVD_IF_GPREG_AND:
236             // if logical and
237             msg_Dbg( &sys.demuxer, "IF %s & %s", GetRegTypeName( false, p_command[3] ).c_str(), GetRegTypeName( b_test_value, i_value ).c_str() );
238             if (!( GetPRM( i_cr1 ) & i_value ))
239             {
240                 b_test_positive = false;
241             }
242             break;
243         case CMD_DVD_IF_GPREG_SUP:
244             // if superior
245             msg_Dbg( &sys.demuxer, "IF %s >= %s", GetRegTypeName( false, p_command[3] ).c_str(), GetRegTypeName( b_test_value, i_value ).c_str() );
246             if (!( GetPRM( i_cr1 ) > i_value ))
247             {
248                 b_test_positive = false;
249             }
250             break;
251         case CMD_DVD_IF_GPREG_SUP_EQUAL:
252             // if superior or equal
253             msg_Dbg( &sys.demuxer, "IF %s >= %s", GetRegTypeName( false, p_command[3] ).c_str(), GetRegTypeName( b_test_value, i_value ).c_str() );
254             if (!( GetPRM( i_cr1 ) >= i_value ))
255             {
256                 b_test_positive = false;
257             }
258             break;
259         }
260
261         if ( !b_test_positive )
262             return false;
263     }
264
265     // strip the test command
266     i_command &= 0xFF0F;
267
268     switch ( i_command )
269     {
270     case CMD_DVD_NOP:
271     case CMD_DVD_NOP2:
272         {
273             msg_Dbg( &sys.demuxer, "NOP" );
274             break;
275         }
276     case CMD_DVD_BREAK:
277         {
278             msg_Dbg( &sys.demuxer, "Break" );
279             // TODO
280             break;
281         }
282     case CMD_DVD_JUMP_TT:
283         {
284             uint8 i_title = p_command[5];
285             msg_Dbg( &sys.demuxer, "JumpTT %d", i_title );
286
287             // find in the ChapProcessPrivate matching this Title level
288             p_vchapter = sys.BrowseCodecPrivate( 1, MatchTitleNumber, &i_title, sizeof(i_title), p_vsegment );
289             if ( p_vsegment != NULL && p_vchapter != NULL )
290             {
291                 /* enter via the First Cell */
292                 uint8 i_cell = 1;
293                 p_vchapter = p_vchapter->BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD, MatchCellNumber, &i_cell, sizeof(i_cell) );
294                 if ( p_vchapter != NULL )
295                 {
296                     sys.JumpTo( *p_vsegment, *p_vchapter );
297                     f_result = true;
298                 }
299             }
300
301             break;
302         }
303     case CMD_DVD_CALLSS_VTSM1:
304         {
305             msg_Dbg( &sys.demuxer, "CallSS" );
306             binary p_type;
307             switch( (p_command[6] & 0xC0) >> 6 ) {
308                 case 0:
309                     p_type = p_command[5] & 0x0F;
310                     switch ( p_type )
311                     {
312                     case 0x00:
313                         msg_Dbg( &sys.demuxer, "CallSS PGC (rsm_cell %x)", p_command[4]);
314                         break;
315                     case 0x02:
316                         msg_Dbg( &sys.demuxer, "CallSS Title Entry (rsm_cell %x)", p_command[4]);
317                         break;
318                     case 0x03:
319                         msg_Dbg( &sys.demuxer, "CallSS Root Menu (rsm_cell %x)", p_command[4]);
320                         break;
321                     case 0x04:
322                         msg_Dbg( &sys.demuxer, "CallSS Subpicture Menu (rsm_cell %x)", p_command[4]);
323                         break;
324                     case 0x05:
325                         msg_Dbg( &sys.demuxer, "CallSS Audio Menu (rsm_cell %x)", p_command[4]);
326                         break;
327                     case 0x06:
328                         msg_Dbg( &sys.demuxer, "CallSS Angle Menu (rsm_cell %x)", p_command[4]);
329                         break;
330                     case 0x07:
331                         msg_Dbg( &sys.demuxer, "CallSS Chapter Menu (rsm_cell %x)", p_command[4]);
332                         break;
333                     default:
334                         msg_Dbg( &sys.demuxer, "CallSS <unknown> (rsm_cell %x)", p_command[4]);
335                         break;
336                     }
337                     p_vchapter = sys.BrowseCodecPrivate( 1, MatchPgcType, &p_type, 1, p_vsegment );
338                     if ( p_vsegment != NULL && p_vchapter != NULL )
339                     {
340                         /* enter via the first Cell */
341                         uint8 i_cell = 1;
342                         p_vchapter = p_vchapter->BrowseCodecPrivate( MATROSKA_CHAPTER_CODEC_DVD, MatchCellNumber, &i_cell, sizeof(i_cell) );
343                         if ( p_vchapter != NULL )
344                         {
345                             sys.JumpTo( *p_vsegment, *p_vchapter );
346                             f_result = true;
347                         }
348                     }
349                 break;
350                 case 1:
351                     msg_Dbg( &sys.demuxer, "CallSS VMGM (menu %d, rsm_cell %x)", p_command[5] & 0x0F, p_command[4]);
352                 break;
353                 case 2:
354                     msg_Dbg( &sys.demuxer, "CallSS VTSM (menu %d, rsm_cell %x)", p_command[5] & 0x0F, p_command[4]);
355                 break;
356                 case 3:
357                     msg_Dbg( &sys.demuxer, "CallSS VMGM (pgc %d, rsm_cell %x)", (p_command[2] << 8) + p_command[3], p_command[4]);
358                 break;
359             }
360             break;
361         }
362     case CMD_DVD_JUMP_SS:
363         {
364             msg_Dbg( &sys.demuxer, "JumpSS");
365             binary p_type;
366             switch( (p_command[5] & 0xC0) >> 6 ) {
367                 case 0:
368                     msg_Dbg( &sys.demuxer, "JumpSS FP");
369                 break;
370                 case 1:
371                     p_type = p_command[5] & 0x0F;
372                     switch ( p_type )
373                     {
374                     case 0x02:
375                         msg_Dbg( &sys.demuxer, "JumpSS VMGM Title Entry");
376                         break;
377                     case 0x03:
378                         msg_Dbg( &sys.demuxer, "JumpSS VMGM Root Menu");
379                         break;
380                     case 0x04:
381                         msg_Dbg( &sys.demuxer, "JumpSS VMGM Subpicture Menu");
382                         break;
383                     case 0x05:
384                         msg_Dbg( &sys.demuxer, "JumpSS VMGM Audio Menu");
385                         break;
386                     case 0x06:
387                         msg_Dbg( &sys.demuxer, "JumpSS VMGM Angle Menu");
388                         break;
389                     case 0x07:
390                         msg_Dbg( &sys.demuxer, "JumpSS VMGM Chapter Menu");
391                         break;
392                     default:
393                         msg_Dbg( &sys.demuxer, "JumpSS <unknown>");
394                         break;
395                     }
396                     // find the VMG
397                     p_vchapter = sys.BrowseCodecPrivate( 1, MatchIsVMG, NULL, 0, p_vsegment );
398                     if ( p_vsegment != NULL )
399                     {
400                         p_vchapter = p_vsegment->BrowseCodecPrivate( 1, MatchPgcType, &p_type, 1 );
401                         if ( p_vchapter != NULL )
402                         {
403                             sys.JumpTo( *p_vsegment, *p_vchapter );
404                             f_result = true;
405                         }
406                     }
407                 break;
408                 case 2:
409                     p_type = p_command[5] & 0x0F;
410                     switch ( p_type )
411                     {
412                     case 0x02:
413                         msg_Dbg( &sys.demuxer, "JumpSS VTSM (vts %d, ttn %d) Title Entry", p_command[4], p_command[3]);
414                         break;
415                     case 0x03:
416                         msg_Dbg( &sys.demuxer, "JumpSS VTSM (vts %d, ttn %d) Root Menu", p_command[4], p_command[3]);
417                         break;
418                     case 0x04:
419                         msg_Dbg( &sys.demuxer, "JumpSS VTSM (vts %d, ttn %d) Subpicture Menu", p_command[4], p_command[3]);
420                         break;
421                     case 0x05:
422                         msg_Dbg( &sys.demuxer, "JumpSS VTSM (vts %d, ttn %d) Audio Menu", p_command[4], p_command[3]);
423                         break;
424                     case 0x06:
425                         msg_Dbg( &sys.demuxer, "JumpSS VTSM (vts %d, ttn %d) Angle Menu", p_command[4], p_command[3]);
426                         break;
427                     case 0x07:
428                         msg_Dbg( &sys.demuxer, "JumpSS VTSM (vts %d, ttn %d) Chapter Menu", p_command[4], p_command[3]);
429                         break;
430                     default:
431                         msg_Dbg( &sys.demuxer, "JumpSS VTSM (vts %d, ttn %d) <unknown>", p_command[4], p_command[3]);
432                         break;
433                     }
434
435                     p_vchapter = sys.BrowseCodecPrivate( 1, MatchVTSMNumber, &p_command[4], 1, p_vsegment );
436
437                     if ( p_vsegment != NULL && p_vchapter != NULL )
438                     {
439                         // find the title in the VTS
440                         p_vchapter = p_vchapter->BrowseCodecPrivate( 1, MatchTitleNumber, &p_command[3], 1 );
441                         if ( p_vchapter != NULL )
442                         {
443                             // find the specified menu in the VTSM
444                             p_vchapter = p_vsegment->BrowseCodecPrivate( 1, MatchPgcType, &p_type, 1 );
445                             if ( p_vchapter != NULL )
446                             {
447                                 sys.JumpTo( *p_vsegment, *p_vchapter );
448                                 f_result = true;
449                             }
450                         }
451                         else
452                             msg_Dbg( &sys.demuxer, "Title (%d) does not exist in this VTS", p_command[3] );
453                     }
454                     else
455                         msg_Dbg( &sys.demuxer, "DVD Domain VTS (%d) not found", p_command[4] );
456                 break;
457                 case 3:
458                     msg_Dbg( &sys.demuxer, "JumpSS VMGM (pgc %d)", (p_command[2] << 8) + p_command[3]);
459                 break;
460             }
461             break;
462         }
463     case CMD_DVD_JUMPVTS_PTT:
464         {
465             uint8 i_title = p_command[5];
466             uint8 i_ptt = p_command[3];
467
468             msg_Dbg( &sys.demuxer, "JumpVTS Title (%d) PTT (%d)", i_title, i_ptt);
469
470             // find the current VTS content segment
471             p_vchapter = sys.p_current_vsegment->BrowseCodecPrivate( 1, MatchIsDomain, NULL, 0 );
472             if ( p_vchapter != NULL )
473             {
474                 int16 i_curr_title = ( p_vchapter->p_chapter )? p_vchapter->p_chapter->GetTitleNumber() : 0;
475                 if ( i_curr_title > 0 )
476                 {
477                     p_vchapter = sys.BrowseCodecPrivate( 1, MatchVTSNumber, &i_curr_title, sizeof(i_curr_title), p_vsegment );
478
479                     if ( p_vsegment != NULL && p_vchapter != NULL )
480                     {
481                         // find the title in the VTS
482                         p_vchapter = p_vchapter->BrowseCodecPrivate( 1, MatchTitleNumber, &i_title, sizeof(i_title) );
483                         if ( p_vchapter != NULL )
484                         {
485                             // find the chapter in the title
486                             p_vchapter = p_vchapter->BrowseCodecPrivate( 1, MatchChapterNumber, &i_ptt, sizeof(i_ptt) );
487                             if ( p_vchapter != NULL )
488                             {
489                                 sys.JumpTo( *p_vsegment, *p_vchapter );
490                                 f_result = true;
491                             }
492                         }
493                     else
494                         msg_Dbg( &sys.demuxer, "Title (%d) does not exist in this VTS", i_title );
495                     }
496                     else
497                         msg_Dbg( &sys.demuxer, "DVD Domain VTS (%d) not found", i_curr_title );
498                 }
499                 else
500                     msg_Dbg( &sys.demuxer, "JumpVTS_PTT command found but not in a VTS(M)");
501             }
502             else
503                 msg_Dbg( &sys.demuxer, "JumpVTS_PTT command but the DVD domain wasn't found");
504             break;
505         }
506     case CMD_DVD_SET_GPRMMD:
507         {
508             msg_Dbg( &sys.demuxer, "Set GPRMMD [%d]=%d", (p_command[4] << 8) + p_command[5], (p_command[2] << 8) + p_command[3]);
509
510             if ( !SetGPRM( (p_command[4] << 8) + p_command[5], (p_command[2] << 8) + p_command[3] ) )
511                 msg_Dbg( &sys.demuxer, "Set GPRMMD failed" );
512             break;
513         }
514     case CMD_DVD_LINKPGCN:
515         {
516             uint16 i_pgcn = (p_command[6] << 8) + p_command[7];
517
518             msg_Dbg( &sys.demuxer, "Link PGCN(%d)", i_pgcn );
519             p_vchapter = sys.p_current_vsegment->BrowseCodecPrivate( 1, MatchPgcNumber, &i_pgcn, 2 );
520             if ( p_vchapter != NULL )
521             {
522                 sys.JumpTo( *sys.p_current_vsegment, *p_vchapter );
523                 f_result = true;
524             }
525             break;
526         }
527     case CMD_DVD_LINKCN:
528         {
529             uint8 i_cn = p_command[7];
530
531             p_vchapter = sys.p_current_vsegment->CurrentChapter();
532
533             msg_Dbg( &sys.demuxer, "LinkCN (cell %d)", i_cn );
534             p_vchapter = p_vchapter->BrowseCodecPrivate( 1, MatchCellNumber, &i_cn, 1 );
535             if ( p_vchapter != NULL )
536             {
537                 sys.JumpTo( *sys.p_current_vsegment, *p_vchapter );
538                 f_result = true;
539             }
540             break;
541         }
542     case CMD_DVD_GOTO_LINE:
543         {
544             msg_Dbg( &sys.demuxer, "GotoLine (%d)", (p_command[6] << 8) + p_command[7] );
545             // TODO
546             break;
547         }
548     case CMD_DVD_SET_HL_BTNN1:
549         {
550             msg_Dbg( &sys.demuxer, "SetHL_BTN (%d)", p_command[4] );
551             SetSPRM( 0x88, p_command[4] );
552             break;
553         }
554     default:
555         {
556             msg_Dbg( &sys.demuxer, "unsupported command : %02X %02X %02X %02X %02X %02X %02X %02X"
557                      ,p_command[0]
558                      ,p_command[1]
559                      ,p_command[2]
560                      ,p_command[3]
561                      ,p_command[4]
562                      ,p_command[5]
563                      ,p_command[6]
564                      ,p_command[7]);
565             break;
566         }
567     }
568
569     return f_result;
570 }
571
572
573
574 bool dvd_command_interpretor_c::MatchIsDomain( const chapter_codec_cmds_c &data, const void *, size_t )
575 {
576     return ( data.p_private_data != NULL && data.p_private_data->GetBuffer()[0] == MATROSKA_DVD_LEVEL_SS );
577 }
578
579 bool dvd_command_interpretor_c::MatchIsVMG( const chapter_codec_cmds_c &data, const void *, size_t )
580 {
581     if ( data.p_private_data == NULL || data.p_private_data->GetSize() < 2 )
582         return false;
583
584     return ( data.p_private_data->GetBuffer()[0] == MATROSKA_DVD_LEVEL_SS && data.p_private_data->GetBuffer()[1] == 0xC0);
585 }
586
587 bool dvd_command_interpretor_c::MatchVTSNumber( const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size )
588 {
589     if ( i_cookie_size != 2 || data.p_private_data == NULL || data.p_private_data->GetSize() < 4 )
590         return false;
591
592     if ( data.p_private_data->GetBuffer()[0] != MATROSKA_DVD_LEVEL_SS || data.p_private_data->GetBuffer()[1] != 0x80 )
593         return false;
594
595     uint16 i_gtitle = (data.p_private_data->GetBuffer()[2] << 8 ) + data.p_private_data->GetBuffer()[3];
596     uint16 i_title = *static_cast<uint16 const*>( p_cookie );
597
598     return (i_gtitle == i_title);
599 }
600
601 bool dvd_command_interpretor_c::MatchVTSMNumber( const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size )
602 {
603     if ( i_cookie_size != 1 || data.p_private_data == NULL || data.p_private_data->GetSize() < 4 )
604         return false;
605
606     if ( data.p_private_data->GetBuffer()[0] != MATROSKA_DVD_LEVEL_SS || data.p_private_data->GetBuffer()[1] != 0x40 )
607         return false;
608
609     uint8 i_gtitle = data.p_private_data->GetBuffer()[3];
610     uint8 i_title = *static_cast<uint8 const*>( p_cookie );
611
612     return (i_gtitle == i_title);
613 }
614
615 bool dvd_command_interpretor_c::MatchTitleNumber( const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size )
616 {
617     if ( i_cookie_size != 1 || data.p_private_data == NULL || data.p_private_data->GetSize() < 4 )
618         return false;
619
620     if ( data.p_private_data->GetBuffer()[0] != MATROSKA_DVD_LEVEL_TT )
621         return false;
622
623     uint16 i_gtitle = (data.p_private_data->GetBuffer()[1] << 8 ) + data.p_private_data->GetBuffer()[2];
624     uint8 i_title = *static_cast<uint8 const*>( p_cookie );
625
626     return (i_gtitle == i_title);
627 }
628
629 bool dvd_command_interpretor_c::MatchPgcType( const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size )
630 {
631     if ( i_cookie_size != 1 || data.p_private_data == NULL || data.p_private_data->GetSize() < 8 )
632         return false;
633
634     if ( data.p_private_data->GetBuffer()[0] != MATROSKA_DVD_LEVEL_PGC )
635         return false;
636
637     uint8 i_pgc_type = data.p_private_data->GetBuffer()[3] & 0x0F;
638     uint8 i_pgc = *static_cast<uint8 const*>( p_cookie );
639
640     return (i_pgc_type == i_pgc);
641 }
642
643 bool dvd_command_interpretor_c::MatchPgcNumber( const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size )
644 {
645     if ( i_cookie_size != 2 || data.p_private_data == NULL || data.p_private_data->GetSize() < 8 )
646         return false;
647
648     if ( data.p_private_data->GetBuffer()[0] != MATROSKA_DVD_LEVEL_PGC )
649         return false;
650
651     uint16 const* i_pgc_n = static_cast<uint16 const*>( p_cookie );
652     uint16 i_pgc_num = (data.p_private_data->GetBuffer()[1] << 8) + data.p_private_data->GetBuffer()[2];
653
654     return (i_pgc_num == *i_pgc_n);
655 }
656
657 bool dvd_command_interpretor_c::MatchChapterNumber( const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size )
658 {
659     if ( i_cookie_size != 1 || data.p_private_data == NULL || data.p_private_data->GetSize() < 2 )
660         return false;
661
662     if ( data.p_private_data->GetBuffer()[0] != MATROSKA_DVD_LEVEL_PTT )
663         return false;
664
665     uint8 i_chapter = data.p_private_data->GetBuffer()[1];
666     uint8 i_ptt = *static_cast<uint8 const*>( p_cookie );
667
668     return (i_chapter == i_ptt);
669 }
670
671 bool dvd_command_interpretor_c::MatchCellNumber( const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size )
672 {
673     if ( i_cookie_size != 1 || data.p_private_data == NULL || data.p_private_data->GetSize() < 5 )
674         return false;
675
676     if ( data.p_private_data->GetBuffer()[0] != MATROSKA_DVD_LEVEL_CN )
677         return false;
678
679     uint8 const* i_cell_n = static_cast<uint8 const*>( p_cookie );
680     uint8 i_cell_num = data.p_private_data->GetBuffer()[3];
681
682     return (i_cell_num == *i_cell_n);
683 }
684
685 const std::string matroska_script_interpretor_c::CMD_MS_GOTO_AND_PLAY = "GotoAndPlay";
686
687 // see http://www.matroska.org/technical/specs/chapters/index.html#mscript
688 //  for a description of existing commands
689 bool matroska_script_interpretor_c::Interpret( const binary * p_command, size_t i_size )
690 {
691     bool b_result = false;
692
693     std::string sz_command( reinterpret_cast<const char*> (p_command), i_size );
694
695     msg_Dbg( &sys.demuxer, "command : %s", sz_command.c_str() );
696
697     if ( sz_command.compare( 0, CMD_MS_GOTO_AND_PLAY.size(), CMD_MS_GOTO_AND_PLAY ) == 0 )
698     {
699         size_t i,j;
700
701         // find the (
702         for ( i=CMD_MS_GOTO_AND_PLAY.size(); i<sz_command.size(); i++)
703         {
704             if ( sz_command[i] == '(' )
705             {
706                 i++;
707                 break;
708             }
709         }
710         // find the )
711         for ( j=i; j<sz_command.size(); j++)
712         {
713             if ( sz_command[j] == ')' )
714             {
715                 i--;
716                 break;
717             }
718         }
719
720         std::string st = sz_command.substr( i+1, j-i-1 );
721         int64_t i_chapter_uid = atoll( st.c_str() );
722
723         virtual_segment_c *p_vsegment;
724         virtual_chapter_c *p_vchapter = sys.FindChapter( i_chapter_uid, p_vsegment );
725
726         if ( p_vchapter == NULL )
727             msg_Dbg( &sys.demuxer, "Chapter %" PRId64 " not found", i_chapter_uid);
728         else
729         {
730             if ( !p_vchapter->EnterAndLeave( sys.p_current_vsegment->CurrentChapter() ) )
731                 p_vsegment->Seek( sys.demuxer, p_vchapter->i_mk_virtual_start_time, p_vchapter );
732             b_result = true;
733         }
734     }
735
736     return b_result;
737 }
738
739 bool matroska_script_codec_c::Enter()
740 {
741     bool f_result = false;
742     std::vector<KaxChapterProcessData*>::iterator index = enter_cmds.begin();
743     while ( index != enter_cmds.end() )
744     {
745         if ( (*index)->GetSize() )
746         {
747             msg_Dbg( &sys.demuxer, "Matroska Script enter command" );
748             f_result |= interpretor.Interpret( (*index)->GetBuffer(), (*index)->GetSize() );
749         }
750         ++index;
751     }
752     return f_result;
753 }
754
755 bool matroska_script_codec_c::Leave()
756 {
757     bool f_result = false;
758     std::vector<KaxChapterProcessData*>::iterator index = leave_cmds.begin();
759     while ( index != leave_cmds.end() )
760     {
761         if ( (*index)->GetSize() )
762         {
763             msg_Dbg( &sys.demuxer, "Matroska Script leave command" );
764             f_result |= interpretor.Interpret( (*index)->GetBuffer(), (*index)->GetSize() );
765         }
766         ++index;
767     }
768     return f_result;
769 }
770
771 } // namespace