]> git.sesse.net Git - vlc/blobdiff - plugins/dvd/dvd_ifo.c
* Ported Glide and MGA plugins to the new module API. MGA never worked,
[vlc] / plugins / dvd / dvd_ifo.c
index f11f0c4a53ef9278a9d313b158fd821343ff28d3..d3996f402eb544b306de7869ba31b0cc53000f1c 100644 (file)
@@ -2,10 +2,15 @@
  * dvd_ifo.c: Functions for ifo parsing
  *****************************************************************************
  * Copyright (C) 1999-2001 VideoLAN
- * $Id: dvd_ifo.c,v 1.3 2001/02/08 17:44:12 massiot Exp $
+ * $Id: dvd_ifo.c,v 1.13 2001/02/20 07:49:12 sam Exp $
  *
  * Author: Stéphane Borel <stef@via.ecp.fr>
  *
+ * based on:
+ *  - libifo by Thomas Mirlacher <dent@cosy.sbg.ac.at>
+ *  - IFO structure documentation by Thomas Mirlacher, Björn Englund,
+ *  Håkan Hjort
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
 
 #include "intf_msg.h"
 #include "dvd_ifo.h"
+#include "dvd_udf.h"
+#include "dvd_css.h"
 #include "input_dvd.h"
 
 /*
- * IFO Management.
+ * Local prototypes
  */
+static vmg_t ReadVMG    ( ifo_t* );
+void         CommandRead( ifo_command_t );
 
-/*****************************************************************************
- * IfoFindVMG : When reading directly on a device, finds the offset to the
- * beginning of video_ts.ifo.
- *****************************************************************************/
-static int IfoFindVMG( ifo_t* p_ifo )
-{
-    char    psz_ifo_start[12] = "DVDVIDEO-VMG";
-    char    psz_test[12];
-
-    read( p_ifo->i_fd, psz_test, 12 );
-
-    while( strncmp( psz_test, psz_ifo_start, 12 ) != 0 )
-    {
-        /* The start of ifo file is on a sector boundary */
-        p_ifo->i_pos = lseek( p_ifo->i_fd,
-                              p_ifo->i_pos + DVD_LB_SIZE,
-                              SEEK_SET );
-        read( p_ifo->i_fd, psz_test, 12 );
-    }
-    p_ifo->i_off = p_ifo->i_pos;
-
-//fprintf( stderr, "VMG Off : %lld\n", (long long)(p_ifo->i_off) );
-
-    return 0;
-}
-
-/*****************************************************************************
- * IfoFindVTS : beginning of vts_*.ifo.
- *****************************************************************************/
-static int IfoFindVTS( ifo_t* p_ifo )
-{
-    char    psz_ifo_start[12] = "DVDVIDEO-VTS";
-    char    psz_test[12];
-
-    read( p_ifo->i_fd, psz_test, 12 );
-
-    while( strncmp( psz_test, psz_ifo_start, 12 ) != 0 )
-    {
-        /* The start of ifo file is on a sector boundary */
-        p_ifo->i_pos = lseek( p_ifo->i_fd,
-                              p_ifo->i_pos + DVD_LB_SIZE,
-                              SEEK_SET );
-        read( p_ifo->i_fd, psz_test, 12 );
-    }
-    p_ifo->i_off = p_ifo->i_pos;
-
-//fprintf( stderr, "VTS Off : %lld\n", (long long)(p_ifo->i_off) );
-
-    return 0;
-}
+/*
+ * IFO Management.
+ */
 
 /*****************************************************************************
  * IfoInit : Creates an ifo structure and prepares for parsing directly
@@ -97,15 +59,20 @@ static int IfoFindVTS( ifo_t* p_ifo )
 ifo_t IfoInit( int i_fd )
 {
     ifo_t       ifo;
+    u32         i_lba;
     
     /* If we are here the dvd device has already been opened */
     ifo.i_fd = i_fd;
-    /* No data at the beginning of the disk
-     * 512000 bytes is just another value :) */
-    ifo.i_pos = lseek( ifo.i_fd, 250 *DVD_LB_SIZE, SEEK_SET );
-    /* FIXME : use udf filesystem to find the beginning of the file */
-    IfoFindVMG( &ifo );
-    
+
+    i_lba = UDFFindFile( i_fd, "/VIDEO_TS/VIDEO_TS.IFO");
+
+    ifo.i_off = (off_t)(i_lba) * DVD_LB_SIZE;
+    ifo.i_pos = lseek( ifo.i_fd, ifo.i_off, SEEK_SET );
+
+    /* Video Manager Initialization */
+    intf_WarnMsg( 2, "ifo: initializing VMG" );
+    ifo.vmg = ReadVMG( &ifo );
+
     return ifo;
 }
 
@@ -114,6 +81,7 @@ ifo_t IfoInit( int i_fd )
  *****************************************************************************/
 void IfoEnd( ifo_t* p_ifo )
 {
+#if 0
     int     i,j;
 
     /* Free structures from video title sets */
@@ -163,7 +131,7 @@ void IfoEnd( ifo_t* p_ifo )
     free( p_ifo->vmg.pgc.com_tab.p_cell_com );
     free( p_ifo->vmg.pgc.com_tab.p_post_com );
     free( p_ifo->vmg.pgc.com_tab.p_pre_com );
-
+#endif
     return;
 }
 
@@ -244,9 +212,10 @@ void IfoEnd( ifo_t* p_ifo )
                                 (int)((p_com)->i_dir_cmp),                  \
                                 (int)((p_com)->i_cmp),                      \
                                 (int)((p_com)->i_sub_cmd),                  \
-                                (int)((p_com)->i_v0),                       \
-                                (int)((p_com)->i_v2),                       \
-                                (int)((p_com)->i_v4) );           */          \
+                                (int)((p_com)->data.pi_16[0]),              \
+                                (int)((p_com)->data.pi_16[1]),              \
+                                (int)((p_com)->data.pi_16[2]));*/             \
+/*        CommandRead( *(p_com) );*/                                            \
         p_ifo->i_pos += 8;                                                  \
     }
 
@@ -261,6 +230,7 @@ static pgc_t ReadPGC( ifo_t* p_ifo )
     FLUSH(2);
     GETC( &pgc.i_prg_nb );
     GETC( &pgc.i_cell_nb );
+//fprintf( stderr, "PGC: Prg: %d Cell: %d\n", pgc.i_prg_nb, pgc.i_cell_nb );
     GETL( &pgc.i_play_time );
     GETL( &pgc.i_prohibited_user_op );
     for( i=0 ; i<8 ; i++ )
@@ -274,6 +244,7 @@ static pgc_t ReadPGC( ifo_t* p_ifo )
     GETS( &pgc.i_next_pgc_nb );
     GETS( &pgc.i_prev_pgc_nb );
     GETS( &pgc.i_goup_pgc_nb );
+//fprintf( stderr, "PGC: Prev: %d Next: %d Up: %d\n",pgc.i_prev_pgc_nb ,pgc.i_next_pgc_nb, pgc.i_goup_pgc_nb );
     GETC( &pgc.i_still_time );
     GETC( &pgc.i_play_mode );
     for( i=0 ; i<16 ; i++ )
@@ -386,7 +357,7 @@ static pgc_t ReadPGC( ifo_t* p_ifo )
         p_ifo->i_pos = lseek( p_ifo->i_fd, i_start
                             + pgc.i_cell_pos_inf_sbyte, SEEK_SET );
         pgc.p_cell_pos_inf = malloc( pgc.i_cell_nb *sizeof(cell_pos_inf_t) );
-        if( pgc.p_cell_play_inf == NULL )
+        if( pgc.p_cell_pos_inf == NULL )
         {
             intf_ErrMsg( "Out of memory" );
             p_ifo->b_error = 1;
@@ -436,6 +407,7 @@ static pgci_inf_t ReadUnit( ifo_t* p_ifo )
         p_ifo->i_pos = lseek( p_ifo->i_fd,
                          i_start + inf.p_srp[i].i_pgci_sbyte,
                          SEEK_SET );
+//fprintf( stderr, "Unit: PGC %d\n", i );
         inf.p_srp[i].pgc = ReadPGC( p_ifo );
     }
 
@@ -494,23 +466,24 @@ static pgci_ut_t ReadUnitTable( ifo_t* p_ifo )
 static c_adt_t ReadCellInf( ifo_t* p_ifo )
 {
     c_adt_t         c_adt;
-    int             i, i_max;
     off_t           i_start = p_ifo->i_pos;
+    int             i;
 
 //fprintf( stderr, "CELL ADD\n" );
 
     GETS( &c_adt.i_vob_nb );
     FLUSH( 2 );
     GETL( &c_adt.i_ebyte );
-    i_max = ( i_start + c_adt.i_ebyte + 1 - p_ifo->i_pos ) / sizeof(cell_inf_t);
-    c_adt.p_cell_inf = malloc( i_max *sizeof(cell_inf_t) );
+    c_adt.i_cell_nb =
+        ( i_start + c_adt.i_ebyte + 1 - p_ifo->i_pos ) / sizeof(cell_inf_t);
+    c_adt.p_cell_inf = malloc( c_adt.i_cell_nb *sizeof(cell_inf_t) );
     if( c_adt.p_cell_inf == NULL )
     {
         intf_ErrMsg( "Out of memory" );
         p_ifo->b_error = 1;
         return c_adt;
     }
-    for( i=0 ; i<i_max ; i++ )
+    for( i = 0 ; i < c_adt.i_cell_nb ; i++ )
     {
         GETS( &c_adt.p_cell_inf[i].i_vob_id );
         GETC( &c_adt.p_cell_inf[i].i_cell_id );
@@ -620,6 +593,7 @@ static vmg_ptt_srpt_t ReadVMGTitlePointer( ifo_t* p_ifo )
 //fprintf( stderr, "PTR\n" );
 
     GETS( &ptr.i_ttu_nb );
+//fprintf( stderr, "PTR: TTU nb %d\n", ptr.i_ttu_nb );
     FLUSH( 2 );
     GETL( &ptr.i_ebyte );
     /* Parsing of tts */
@@ -639,6 +613,7 @@ static vmg_ptt_srpt_t ReadVMGTitlePointer( ifo_t* p_ifo )
         GETC( &ptr.p_tts[i].i_tts_nb );
         GETC( &ptr.p_tts[i].i_vts_ttn );
         GETL( &ptr.p_tts[i].i_ssector );
+//fprintf( stderr, "PTR: %d %d %d\n", ptr.p_tts[i].i_ptt_nb, ptr.p_tts[i].i_tts_nb,ptr.p_tts[i].i_vts_ttn );
     }
 
     return ptr;
@@ -715,6 +690,7 @@ static vmg_vts_atrt_t ReadVTSAttr( ifo_t* p_ifo )
 //fprintf( stderr, "VTS ATTR\n" );
 
     GETS( &atrt.i_vts_nb );
+//fprintf( stderr, "VTS ATTR Nb: %d\n", atrt.i_vts_nb );
     FLUSH( 2 );
     GETL( &atrt.i_ebyte );
     atrt.pi_vts_atrt_sbyte = malloc( atrt.i_vts_nb *sizeof(u32) );
@@ -919,9 +895,10 @@ static vts_ptt_srpt_t ReadVTSTitlePointer( ifo_t* p_ifo )
     int             i;
     off_t           i_start = p_ifo->i_pos;
 
-//fprintf( stderr, "PTR\n" );
+//fprintf( stderr, "VTS PTR\n" );
 
     GETS( &ptr.i_ttu_nb );
+//fprintf( stderr, "VTS PTR nb: %d\n", ptr.i_ttu_nb );
     FLUSH( 2 );
     GETL( &ptr.i_ebyte );
     ptr.pi_ttu_sbyte = malloc( ptr.i_ttu_nb *sizeof(u32) );
@@ -949,6 +926,7 @@ static vts_ptt_srpt_t ReadVTSTitlePointer( ifo_t* p_ifo )
                         ptr.pi_ttu_sbyte[i], SEEK_SET );
         GETS( &ptr.p_ttu[i].i_pgc_nb );
         GETS( &ptr.p_ttu[i].i_prg_nb );
+//fprintf( stderr, "VTS %d PTR Pgc: %d Prg: %d\n", i,ptr.p_ttu[i].i_pgc_nb, ptr.p_ttu[i].i_prg_nb );
     }
 
     return ptr;
@@ -1010,79 +988,91 @@ static vts_tmap_ti_t ReadVTSTimeMap( ifo_t* p_ifo )
     
 
 /*****************************************************************************
- * ReadVTS : Parse vts*.ifo files to fill the Video Title Set structure.
+ * IfoReadVTS : Parse vts*.ifo files to fill the Video Title Set structure.
  *****************************************************************************/
-static vts_t ReadVTS( ifo_t* p_ifo )
+int IfoReadVTS( ifo_t* p_ifo )
 {
     vts_t       vts;
+    off_t       i_off;
+    int         i_title;
+
+    intf_WarnMsg( 2, "ifo: initializing VTS %d", p_ifo->i_title );
+
+    i_title = p_ifo->i_title;
+    i_off = (off_t)( p_ifo->vmg.ptt_srpt.p_tts[i_title].i_ssector ) *DVD_LB_SIZE
+                   + p_ifo->i_off;
+
+    p_ifo->i_pos = lseek( p_ifo->i_fd, i_off, SEEK_SET );
 
     vts.i_pos = p_ifo->i_pos;
 
     vts.mat = ReadVTSInfMat( p_ifo );
     if( vts.mat.i_ptt_srpt_ssector )
     {
-        p_ifo->i_pos = lseek( p_ifo->i_fd, p_ifo->i_off +
+        p_ifo->i_pos = lseek( p_ifo->i_fd, vts.i_pos +
                         vts.mat.i_ptt_srpt_ssector *DVD_LB_SIZE,
                         SEEK_SET );
         vts.ptt_srpt = ReadVTSTitlePointer( p_ifo );
     }
     if( vts.mat.i_m_pgci_ut_ssector )
     {
-        p_ifo->i_pos = lseek( p_ifo->i_fd, p_ifo->i_off +
+        p_ifo->i_pos = lseek( p_ifo->i_fd, vts.i_pos +
                         vts.mat.i_m_pgci_ut_ssector *DVD_LB_SIZE,
                         SEEK_SET );
         vts.pgci_ut = ReadUnitTable( p_ifo );
     }
     if( vts.mat.i_pgcit_ssector )
     {
-        p_ifo->i_pos = lseek( p_ifo->i_fd, p_ifo->i_off +
+        p_ifo->i_pos = lseek( p_ifo->i_fd, vts.i_pos +
                         vts.mat.i_pgcit_ssector *DVD_LB_SIZE,
                         SEEK_SET );
         vts.pgci_ti = ReadUnit( p_ifo );
     }
     if( vts.mat.i_tmap_ti_ssector )
     {
-        p_ifo->i_pos = lseek( p_ifo->i_fd, p_ifo->i_off +
+        p_ifo->i_pos = lseek( p_ifo->i_fd, vts.i_pos +
                         vts.mat.i_tmap_ti_ssector *DVD_LB_SIZE,
                         SEEK_SET );
         vts.tmap_ti = ReadVTSTimeMap( p_ifo );
     }
     if( vts.mat.i_m_c_adt_ssector )
     {
-        p_ifo->i_pos = lseek( p_ifo->i_fd, p_ifo->i_off +
+        p_ifo->i_pos = lseek( p_ifo->i_fd, vts.i_pos +
                         vts.mat.i_m_c_adt_ssector *DVD_LB_SIZE,
                         SEEK_SET );
         vts.m_c_adt = ReadCellInf( p_ifo );
     }
     if( vts.mat.i_m_vobu_admap_ssector )
     {
-        p_ifo->i_pos = lseek( p_ifo->i_fd, p_ifo->i_off +
+        p_ifo->i_pos = lseek( p_ifo->i_fd, vts.i_pos +
                         vts.mat.i_m_vobu_admap_ssector *DVD_LB_SIZE,
                         SEEK_SET );
         vts.m_vobu_admap = ReadMap( p_ifo );
     }
     if( vts.mat.i_c_adt_ssector )
     {
-        p_ifo->i_pos = lseek( p_ifo->i_fd, p_ifo->i_off +
+        p_ifo->i_pos = lseek( p_ifo->i_fd, vts.i_pos +
                         vts.mat.i_c_adt_ssector *DVD_LB_SIZE,
                         SEEK_SET );
         vts.c_adt = ReadCellInf( p_ifo );
     }
     if( vts.mat.i_vobu_admap_ssector )
     {
-        p_ifo->i_pos = lseek( p_ifo->i_fd, p_ifo->i_off +
+        p_ifo->i_pos = lseek( p_ifo->i_fd, vts.i_pos +
                         vts.mat.i_vobu_admap_ssector *DVD_LB_SIZE,
                         SEEK_SET );
         vts.vobu_admap = ReadMap( p_ifo );
     }
 
-    return vts;
+    p_ifo->vts = vts;
+
+    return 0;
 }
 
 /*
  * DVD Information Management
  */
-
+#if 0
 /*****************************************************************************
  * IfoRead : Function that fills structure and calls specified functions
  * to do it.
@@ -1092,7 +1082,7 @@ void IfoRead( ifo_t* p_ifo )
     int     i;
     off_t   i_off;
 
-    p_ifo->vmg = ReadVMG( p_ifo );
+    /* Video Title Sets initialization */
     p_ifo->p_vts = malloc( p_ifo->vmg.mat.i_tts_nb *sizeof(vts_t) );
     if( p_ifo->p_vts == NULL )
     {
@@ -1100,87 +1090,703 @@ void IfoRead( ifo_t* p_ifo )
         p_ifo->b_error = 1;
         return;
     }
-    for( i=0 ; i<1/*p_ifo->vmg.mat.i_tts_nb*/ ; i++ )
+
+    for( i=0 ; i<p_ifo->vmg.mat.i_tts_nb ; i++ )
     {
 
-        intf_WarnMsg( 3, "######### VTS %d #############\n", i+1 );
+        intf_WarnMsg( 2, "ifo: initializing VTS %d", i+1 );
+
+        i_off = (off_t)( p_ifo->vmg.ptt_srpt.p_tts[i].i_ssector ) *DVD_LB_SIZE
+                       + p_ifo->i_off;
 
-        i_off = p_ifo->vmg.ptt_srpt.p_tts[i].i_ssector *DVD_LB_SIZE;
         p_ifo->i_pos = lseek( p_ifo->i_fd, i_off, SEEK_SET );
-        /* FIXME : use udf filesystem to avoid this */
-        IfoFindVTS( p_ifo );
+
+        /* FIXME : I really don't know why udf find file
+         * does not give the exact beginning of file */
+
         p_ifo->p_vts[i] = ReadVTS( p_ifo );
+
     }
+
     return; 
 }
-
+#endif
 /*
- * IFO virtual machine : a set of commands that give the behaviour of the dvd
+ * IFO virtual machine : a set of commands that give the
+ * interactive behaviour of the dvd
  */
-#if 0
-/*****************************************************************************
- * CommandRead : translates the command strings in ifo into command
- * structures.
- *****************************************************************************/
-void CommandRead( ifo_command_t com )
+#if 1
+
+#define OP_VAL_16(i) (ntoh16( com.data.pi_16[i]))
+#define OP_VAL_8(i) ((com.data.pi_8[i]))
+
+static char ifo_reg[][80]=
 {
-    u8*     pi_code = (u8*)(&com);
+    "Menu_Language_Code",
+    "Audio_Stream_#",
+    "SubPicture_Stream_#",
+    "Angle_#",
+    "VTS_#",
+    "VTS_Title_#",
+    "PGC_#",
+    "PTT_#",
+    "Highlighted_Button_#",
+    "Nav_Timer",
+    "TimedPGC",
+    "Karaoke_audio_mixing_mode",
+    "Parental_mgmt_country_code",
+    "Parental_Level",
+    "Player_Video_Cfg",
+    "Player_Audio_Cfg",
+    "Audio_language_code_setting",
+    "Audio_language_extension_code",
+    "SPU_language_code_setting",
+    "SPU_language_extension_code",
+    "?Player_Regional_Code",
+    "Reserved_21",
+    "Reserved_22",
+    "Reserved_23"
+};
+
+static char * IfoMath( char val )
+{
+    static char math_op[][10] =
+    {
+        "none",
+        "=", 
+        "<->",    // swap
+        "+=",
+        "-=",
+        "*=",
+        "/=",
+        "%=",
+        "rnd",    // rnd
+        "&=",
+        "|=",
+        "^=",
+        "??",    // invalid
+        "??",    // invalid
+        "??",    // invalid
+        "??"    // invalid
+    };
+
+    return (char *) math_op[val & 0x0f];
+}
 
-    switch( com.i_type )
+
+char ifo_cmp[][10] =
+{
+    "none",
+    "&&",
+    "==",
+    "!=",
+    ">=",
+    ">",
+    "<",
+    "<="
+};
+
+char ifo_parental[][10] =
+{
+    "0",
+    "G",
+    "2",
+    "PG",
+    "PG-13",
+    "5",
+    "R",
+    "NC-17"
+};
+
+char ifo_menu_id[][80] =
+{
+    "-0-",
+    "-1-",
+    "Title (VTS menu)",
+    "Root",
+    "Sub-Picture",
+    "Audio",
+    "Angle",
+    "Part of Title",
+};
+
+char * IfoMenuName( char index )
+{
+    return ifo_menu_id[index&0x07];
+}
+
+static void IfoRegister( u16 i_data, u8 i_direct)
+{
+    if( i_direct )
     {
-        case 0:                                     /* Goto */
-            if( !pi_code[1] )
+        if( 0/*isalpha( i_data >> 8 & 0xff )*/ )
+        {
+            printf("'%c%c'", i_data>>8&0xff, i_data&0xff);
+        }
+        else
+        {
+            printf("0x%02x", i_data);
+        }
+    }
+    else
+    {
+        if( i_data & 0x80 )
+        {
+            i_data &= 0x1f;
+
+            if( i_data > 0x17 )
             {
-                fprintf( stderr, "NOP\n" );
+                printf("s[ILL]");
             }
-            else if( cmd.i_cmp )
+            else
             {
-                
+                printf("s[%s]", ifo_reg[i_data]);
             }
+        }
+        else
+        {
+            i_data &= 0x1f;
+
+            if( i_data > 0xf )
+            {
+                printf("r[ILL]");
+            }
+            else
+            {
+                printf("r[0x%02x]", i_data);
+            }
+        }
+    }
+}
+
+static void IfoAdvanced( u8 *pi_code )
+{
+    u8      i_cmd = pi_code[0];
+
+    printf(" { ");
+
+    if( pi_code[1]>>2 )
+    {
+        printf( " Highlight button %d; ", pi_code[1]>>2 );
+    }
+
+    if( i_cmd == 0xff )
+    {
+        printf( " Illegal " );
+    }
+
+    if( i_cmd == 0x00 )
+    {
+        printf( "ReSuME %d", pi_code[7] );
+    }
+    else if( ( i_cmd & 0x06) == 0x02 )
+    {    // XX01Y
+        printf ("Link to %s cell ", ( i_cmd & 0x01 ) ? "prev" : "next");
+    }
+    else
+    {
+        printf( "advanced (0x%02x) ", i_cmd );
+    }
+    printf(" } ");
+}
+
+static void IfoJmp( ifo_command_t com )
+{
+
+    printf ("jmp ");
+
+    switch( com.i_sub_cmd )
+    {
+    case 0x01:
+        printf( "Exit" );
+        break;
+    case 0x02:
+        printf( "VTS 0x%02x", OP_VAL_8(3) );
+        break;
+    case 0x03:
+        printf( "This VTS Title 0x%02x", OP_VAL_8(3) );
+        break;
+    case 0x05:
+        printf( "This VTS Title 0x%02x Part 0x%04x",
+                            OP_VAL_8(3),
+                            OP_VAL_8(0)<<8|OP_VAL_8(1));
+        break;
+    case 0x06:
+#if 0
+            printf ("in SystemSpace ");
+            switch (OP_VAL_8(3)>>4) {
+                case 0x00:
+                    printf ("to play first PGC");
+                    break;
+                case 0x01: {
+                    printf ("to menu \"%s\"", decode_menuname (OP_VAL_8(3)));
+                }
+                    break;
+                case 0x02:
+                    printf ("to VTS 0x%02x and TTN 0x%02x", OP_VAL_8(1), OP_VAL_8(2));
+                    break;
+                case 0x03:
+                    printf ("to VMGM PGC number 0x%02x", OP_VAL_8(0)<<8 | OP_VAL_8(1));
+                    break;
+                case 0x08:
+                    printf ("vts 0x%02x lu 0x%02x menu \"%s\"", OP_VAL_8(2), OP_VAL_8(1), decode_menuname (OP_VAL_8(3)));
+                    break;
+#else
+        switch( OP_VAL_8(3)>>6 )
+        {
+        case 0x00:
+            printf( "to play first PGC" );
+            break;                
+        case 0x01:
+            printf( "to VMG title menu (?)" );
             break;
-        case 1:                                     /* Lnk */
-            break;
-        case 2:                                     /* SetSystem */
-            break;
-        case 3:                                     /* Set */
-            break;
-        case 4:                                     /* */
+        case 0x02:
+            printf( "vts 0x%02x lu 0x%02x menu \"%s\"",
+                            OP_VAL_8(2),
+                            OP_VAL_8(1),
+                            IfoMenuName( OP_VAL_8(3)&0xF ) );
+            break;                
+        case 0x03:
+            printf( "vmg pgc 0x%04x (?)", ( OP_VAL_8(0)<<8) | OP_VAL_8(1) );
             break;
-        case 5:                                     /* */
+#endif
+        }
+        break;
+    case 0x08:
+#if 0
+            switch(OP_VAL_8(3)>>4) {
+                case 0x00:
+                    printf ("system first pgc");
+                    break;
+                case 0x01:
+                    printf ("system title menu");
+                    break;
+                case 0x02:
+                    printf ("system menu \"%s\"", decode_menuname (OP_VAL_8(3)));
+                    break;
+                case 0x03:
+                    printf ("system vmg pgc %02x ????", OP_VAL_8(0)<<8|OP_VAL_8(1));
+                    break;
+                case 0x08:
+                    printf ("system lu 0x%02x menu \"%s\"", OP_VAL_8(2), decode_menuname (OP_VAL_8(3)));
+                    break;
+                case 0x0c:
+                    printf ("system vmg pgc 0x%02x", OP_VAL_8(0)<<8|OP_VAL_8(1));
+                    break;
+            }
+#else
+        // OP_VAL_8(2) is number of cell
+        // it is processed BEFORE switch
+        // under some conditions, it is ignored
+        // I don't understand exactly what it means
+        printf( " ( spec cell 0x%02X ) ", OP_VAL_8(2) ); 
+
+        switch( OP_VAL_8(3)>>6 )
+        {
+        case 0:
+            printf( "to FP PGC" );
             break;
-        case 6:                                     /* */
+        case 1:
+            printf( "to VMG root menu (?)" );
             break;
-        default:
-            fprintf( stderr, "Unknown Command\n" );
+        case 2:
+            printf( "to VTS menu \"%s\" (?)",
+                    IfoMenuName(OP_VAL_8(3)&0xF) );
+            break; 
+        case 3:
+            printf( "vmg pgc 0x%02x (?)", (OP_VAL_8(0)<<8)|OP_VAL_8(1) );
             break;
+        }    
+#endif
+        break;
     }
+}
 
-    return;
+static void IfoLnk( ifo_command_t com )
+{
+    u16     i_button=OP_VAL_8(4)>>2;
+
+    printf ("lnk to ");
+
+    switch( com.i_sub_cmd )
+    {
+    case 0x01:
+        IfoAdvanced( &OP_VAL_8(4) );
+        break;
+
+    case 0x04:
+        printf( "PGC 0x%02x", OP_VAL_16(2) );
+        break; 
+
+    case 0x05:
+        printf( "PTT 0x%02x", OP_VAL_16(2) );
+        break; 
+
+    case 0x06:
+        printf( "Program 0x%02x this PGC", OP_VAL_8(5) );
+        break;
+
+    case 0x07:
+        printf( "Cell 0x%02x this PGC", OP_VAL_8(5) );
+        break;
+    default:
+        return;
+    }
+
+    if( i_button )
+    {
+        printf( ", Highlight 0x%02x", OP_VAL_8(4)>>2 );
+    }
+            
 }
 
-/*****************************************************************************
- * IfoGoto
- *****************************************************************************/
-static void IfoGoto( ifo_command_t cmd )
+void IfoSetSystem( ifo_command_t com )
 {
-    
+    switch( com.i_cmd )
+    {
+    case 1: {
+        int i;
 
-    return;
+        for( i=1; i<=3; i++ )
+        {
+            if( OP_VAL_8(i)&0x80 )
+            {
+                if( com.i_direct )
+                {
+                    printf ("s[%s] = 0x%02x;", ifo_reg[i], OP_VAL_8(i)&0xf);
+                }
+                else
+                {
+                    printf ("s[%s] = r[0x%02x];", ifo_reg[i], OP_VAL_8(i)&0xf);
+                }
+            }
+        }
+#if 0
+                if(op->direct) {
+                        if(OP_VAL_8(1]&0x80)
+                                printf ("s[%s] = 0x%02x;", reg_name[1], OP_VAL_8(1]&0xf);
+                        if(OP_VAL_8(2)&0x80)
+//DENT: lwhat about 0x7f here ???
+                                printf ("s[%s] = 0x%02x;", reg_name[2], OP_VAL_8(2)&0x7f);
+                        if(OP_VAL_8(3)&0x80)
+                                printf ("s[%s] = 0x%02x;", reg_name[3], OP_VAL_8(3)&0xf);
+                } else {
+                        if(OP_VAL_8(1)&0x80)
+                                printf ("s[%s] = r[0x%02x];", reg_name[1], OP_VAL_8(1)&0xf);
+                        if(OP_VAL_8(2)&0x80)
+                                printf ("s[%s] = r[0x%02x];", reg_name[2], OP_VAL_8(2)&0xf);
+                        if(OP_VAL_8(3)&0x80)
+                                printf ("s[%s] = r[0x%02x];", reg_name[3], OP_VAL_8(3)&0xf);
+                }
+#endif
+        }
+        break;
+    case 2:
+        if( com.i_direct )
+        {
+            printf( "s[%s] = 0x%02x", ifo_reg[9], OP_VAL_16(0) );
+        }
+        else
+        {
+            printf( "s[%s] = r[0x%02x]", ifo_reg[9], OP_VAL_8(1)&0x0f );
+        }
+
+        printf( "s[%s] = (s[%s]&0x7FFF)|0x%02x", 
+                        ifo_reg[10], ifo_reg[10], OP_VAL_16(1)&0x8000);
+        break;
+    case 3:
+        if( com.i_direct )
+        {
+            printf( "r[0x%02x] = 0x%02x", OP_VAL_8(3)&0x0f, OP_VAL_16(0) );
+        }
+        else
+        {
+            printf ("r[r[0x%02x]] = r[0x%02x]",
+                                    OP_VAL_8(3)&0x0f, OP_VAL_8(1)&0x0f);
+        }
+        break;
+    case 4:
+        //actually only bits 00011100 00011100 are set
+        if( com.i_direct )
+        {
+            printf ("s[%s] = 0x%02x", ifo_reg[11], OP_VAL_16(1));
+        }
+        else
+        {
+            printf ("s[%s] = r[0x%02x]", ifo_reg[11], OP_VAL_8(3)&0x0f );
+        }
+        break;
+    case 6:
+        //actually,
+        //s[%s]=(r[%s]&0x3FF) | (0x%02x << 0xA);
+        //but it is way too ugly
+        if( com.i_direct )
+        {
+            printf( "s[%s] = 0x%02x", ifo_reg[8], OP_VAL_8(2)>>2 );
+        }
+        else
+        {
+            printf( "s[%s] = r[0x%02x]", ifo_reg[8], OP_VAL_8(3)&0x0f );
+        }
+        break;
+    default:
+        printf ("unknown");
+    }
+}
+
+static void IfoSet( ifo_command_t com )
+{
+    IfoRegister( OP_VAL_16(0), 0 );
+    printf( " %s ", IfoMath( com.i_cmd ) );
+    IfoRegister( OP_VAL_16(1), com.i_direct );
 }
 
 /*****************************************************************************
- * IfoLnk
+ * CommandRead : translates the command strings in ifo into command
+ * structures.
  *****************************************************************************/
-static void IfoLnk( ifo_t* p_ifo )
+void CommandRead( ifo_command_t com )
 {
+    u8*     pi_code = (u8*)(&com);
+
+    switch( com.i_type )
+    {
+    /* Goto */
+    case 0:
+        /* Main command */
+        if( !pi_code[1] )
+        {
+            printf( "NOP\n" );
+        }
+        else
+        {
+            if( com.i_cmp )
+            {
+                printf ("if (r[0x%02x] %s ", OP_VAL_8(1)&0x0f,
+                                             ifo_cmp[com.i_cmp]);
+                IfoRegister (OP_VAL_16(1), com.i_dir_cmp);
+                printf (") ");
+            }
+        
+            /* Sub command */
+            switch( com.i_sub_cmd )
+            {
+            case 1:
+                printf( "goto Line 0x%02x", OP_VAL_16(2) );
+                break;
+        
+            case 2:
+                printf( "stop VM" );
+                break;
+        
+            case 3:
+                printf( "Set Parental Level To %s and goto Line 0x%02x",
+                                     ifo_parental[OP_VAL_8(4)&0x7],
+                                     OP_VAL_8(5) );
+                break;
+        
+            default:
+                printf( "Illegal" );
+                break;
+            }
+        }
+        break;
+
+    /* Lnk */
+    case 1:
+        /* Main command */
+        if( !pi_code[1] )
+        {
+            printf( "NOP\n" );
+        }
+        else
+        {
+            if( com.i_direct )
+            {
+                if( com.i_cmp )
+                {
+                    printf( "if (r[0x%02x] %s ", OP_VAL_8(4)&0x0f,
+                                                 ifo_cmp[com.i_cmp] );
+                    IfoRegister( OP_VAL_8(5), 0 );
+                    printf( ") " );
+                }
+
+                /* Sub command */
+                IfoJmp( com );
+            }
+            else
+            {    
+                if( com.i_cmp )
+                {
+                    printf( "if (r[0x%02x] %s ", OP_VAL_8(1)&0x0f,
+                                                 ifo_cmp[com.i_cmp] );
+                    IfoRegister( OP_VAL_16(1), com.i_dir_cmp );
+                    printf( ") " );
+                }
+
+                /* Sub command */
+                IfoLnk( com );
+            }
+        }
+        break;
+
+    /* SetSystem */
+    case 2:
+        if( !pi_code[1] )
+        {
+            IfoSetSystem( com );
+        }
+        else if( com.i_cmp && !com.i_sub_cmd )
+        {
+            printf ("if (r[0x%02x] %s ", OP_VAL_8(4)&0x0f, ifo_cmp[com.i_cmp]);
+            IfoRegister( OP_VAL_8(5), 0 );
+            printf (") ");
+            IfoSetSystem( com );
+        }
+        else if( !com.i_cmp && com.i_sub_cmd )
+        {
+            printf( "if (" );
+            IfoSetSystem( com );
+            printf( ") " );
+            IfoLnk( com );
+        }
+        else
+        {
+            printf("nop");
+        }
+        break;
+
+    /* Set */
+    case 3:
+          if( ! pi_code[1] )
+        {
+            IfoSet( com );
+        }
+        else if( com.i_cmp && !com.i_sub_cmd )
+        {
+            printf ("if (r[0x%02x] %s ", OP_VAL_8(0)&0x0f, ifo_cmp[com.i_cmp]);
+            IfoRegister( OP_VAL_16(2), com.i_dir_cmp );
+            printf (") ");
+            IfoSet( com );
+        }
+        else if( !com.i_cmp && com.i_sub_cmd )
+        {
+            printf ("if (");
+            IfoSet( com );
+            printf (") ");
+            IfoLnk( com );
+        }
+        else
+        {
+            printf( "nop" );
+        }
+        break;
+
+    /* 
+     * math command on r[opcode[1]] and
+     * direct?be2me_16(OP_VAL_8(0)):reg[OP_VAL_8(1)] is executed
+     * ( unless command is swap; then r[opcode[1]] and r[OP_VAL_8(1)]
+     * are swapped )
+     * boolean operation cmp on r[opcode[1]] and
+     * dir_cmp?be2me_16(OP_VAL_8(1)[1]):reg[OP_VAL_8(3)] is executed
+     * on true result, buttons(c[6], c[7]) is called
+     * problem is 'what is buttons()'
+     */
+    case 4:
+        printf( "r[0x%X] ", pi_code[1] );
+        printf( " %s ", IfoMath( com.i_cmd ) );
+        if( com.i_cmd == 2 )
+        {
+            printf( "r[0x%X] ", OP_VAL_8(1) );
+        }
+        else
+        {
+            IfoRegister( OP_VAL_16(0), com.i_direct );
+        }
+        printf("; ");
+
+        printf( "if ( r[%d] %s ", pi_code[1], ifo_cmp[com.i_cmp] );
+        IfoRegister( OP_VAL_8(1), com.i_dir_cmp );
+        printf( " )  then {" );
+        IfoAdvanced( &OP_VAL_8(4) );
+        printf( "}" );
+        break;
+
+    /*
+     * opposite to case 4: boolean, math and buttons.
+     */
+    case 5:
+    case 6:
+        printf("if (");
+
+        if( !com.i_direct && com.i_dir_cmp )
+        {
+            printf( "0x%X", OP_VAL_16(1) );
+        }
+        else
+        {
+            IfoRegister( OP_VAL_8(3), 0 );
+            if( OP_VAL_8(3)&0x80 )
+            {
+                printf( "s[%s]", ifo_reg[OP_VAL_8(3)&0x1F] );
+            }
+            else
+            {
+                printf( "r[0x%X]", OP_VAL_8(3)&0x1F);
+                    // 0x1F is either not a mistake,
+                    // or Microsoft programmer's mistake!!!
+            }
+        }
+
+        printf( " %s r[0x%X] ", ifo_cmp[com.i_cmp],
+                                com.i_direct ? OP_VAL_8(2) : OP_VAL_8(1) );
+           printf( " )  then {" );
+        printf( "r[0x%X] ", pi_code[1] & 0xF );
+        printf( " %s ", IfoMath( com.i_cmd ) );
+
+        if( com.i_cmd == 0x02 )    // swap
+        {
+            printf("r[0x%X] ", OP_VAL_8(0)&0x1F);
+        }
+        else
+        {
+            if( com.i_direct )
+            {
+                printf( "0x%X", OP_VAL_16(0) );
+            }
+            else
+            {
+                if( OP_VAL_8(0) & 0x80 )
+                {
+                    printf("s[%s]", ifo_reg[OP_VAL_8(0) & 0x1F] );
+                }
+                else
+                {
+                    printf("r[0x%X]", OP_VAL_8(0) & 0x1F );
+                }
+            }
+        }
+
+        printf("; ");
+        IfoAdvanced( &OP_VAL_8(4) );
+        printf("}");
+
+        break;
+
+    default:
+        printf( "Unknown Command\n" );
+        break;
+    }
+
     return;
 }
 
 /*****************************************************************************
- * IfoJmp
+ * CommandPrint : print in clear text (I hope so !) what a command does
  *****************************************************************************/
-static void IfoJmp( ifo_t* p_ifo )
+void CommandPrint( ifo_t ifo )
 {
     return;
 }
+
 #endif