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