/* csstest.c - test program for libdvdcss
 *
 * Sam Hocevar <sam@zoy.org> - June 2001
 *   Updated on Nov 13th 2001 for libdvdcss version 1.0.0
 *   Additional error checks on Aug 9th 2002
 *   Aligned data reads on Jan 28th 2003
 *
 * This piece of code is public domain */

#include <stdio.h>
#include <stdlib.h>

#include <dvdcss/dvdcss.h>

#define DVDCSS_DSI_PES 0x400

static int  isscrambled( unsigned char * );
static void dumpsector ( unsigned char * );

int main( int i_argc, char *ppsz_argv[] )
{
    dvdcss_t       dvdcss;
    unsigned char  p_data[ DVDCSS_BLOCK_SIZE * 2 ];
    unsigned char *p_buffer;
    unsigned int   i_sector;
    int            i_ret;

    /* Check for 2 arguments */
    if( i_argc != 3 )
    {
        printf( "usage: %s <target> <sector>\n", ppsz_argv[0] );
        printf( "examples:\n" );
        printf( "  %s /dev/hdc 1024\n", ppsz_argv[0] );
        printf( "  %s D: 1024\n", ppsz_argv[0] );
        printf( "  %s scrambledfile.vob 1024\n", ppsz_argv[0] );
        return -1;
    }

    /* Save the requested sector */
    i_sector = atoi( ppsz_argv[2] );

    /* Initialize libdvdcss */
    dvdcss = dvdcss_open( ppsz_argv[1] );
    if( dvdcss == NULL )
    {
        printf( "argh ! couldn't open DVD (%s)\n", ppsz_argv[1] );
        return -1;
    }

    /* Align our read buffer */
    p_buffer = p_data + DVDCSS_BLOCK_SIZE
                      - ((long int)p_data & (DVDCSS_BLOCK_SIZE-1));

    /* Set the file descriptor at sector i_sector and read one sector */
    i_ret = dvdcss_seek( dvdcss, i_sector, DVDCSS_NOFLAGS );
    if( i_ret != (int)i_sector )
    {
        printf( "seek failed (%s)\n", dvdcss_error( dvdcss ) );
        dvdcss_close( dvdcss );
        return i_ret;
    }

    i_ret = dvdcss_read( dvdcss, p_buffer, 1, DVDCSS_NOFLAGS );
    if( i_ret != 1 )
    {
        printf( "read failed (%s)\n", dvdcss_error( dvdcss ) );
        dvdcss_close( dvdcss );
        return i_ret;
    }

    /* Print the sector */
    printf( "requested sector:\t" );
    dumpsector( p_buffer );

    /* Check if sector was encrypted */
    if( isscrambled( p_buffer ) )
    {
        /* Set the file descriptor position to the previous location */
        /* ... and get the appropriate key for this sector */
        i_ret = dvdcss_seek( dvdcss, i_sector, DVDCSS_SEEK_KEY );
        if( i_ret != (int)i_sector )
        {
            printf( "seek failed (%s)\n", dvdcss_error( dvdcss ) );
            dvdcss_close( dvdcss );
            return i_ret;
        }

        /* Read sector again, and decrypt it on the fly */
        i_ret = dvdcss_read( dvdcss, p_buffer, 1, DVDCSS_READ_DECRYPT );
        if( i_ret != 1 )
        {
            printf( "read failed (%s)\n", dvdcss_error( dvdcss ) );
            dvdcss_close( dvdcss );
            return i_ret;
        }

        /* Print the decrypted sector */
        printf( "unscrambled sector:\t" );
        dumpsector( p_buffer );
    }
    else
    {
        printf( "sector is not scrambled\n" );
    }

    /* Close the device */
    i_ret = dvdcss_close( dvdcss );

    return i_ret;
}

/* Check if a sector is scrambled */
static int isscrambled( unsigned char *p_buffer )
{
    return p_buffer[ 0x14 ] & 0x30;
}

static void dumpmem( unsigned char *p_buffer, int sz )
{
    for( ; sz ; sz--, p_buffer++ )
        printf( "%.2x", *p_buffer );
}

/* Print parts of a 2048 bytes buffer */
static void dumpsector( unsigned char *p_buffer )
{
    /* print pack header and first PES bytes up to flags */
    dumpmem( &p_buffer[0], 21 );
    printf( "..." );
    /* print DSI up to block number */
    dumpmem( &p_buffer[DVDCSS_DSI_PES], 17 );
    printf( "..." );
    dumpmem( &p_buffer[DVDCSS_BLOCK_SIZE - 8], 8 );
    printf( "\n" );
}
