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