rar.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:16k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * rar.c: uncompressed RAR stream filter (only the biggest file is extracted)
  3.  *****************************************************************************
  4.  * Copyright (C) 2008 Laurent Aimar
  5.  * $Id: f8280587ce4c89d54bff2fd933e85d77cb98b391 $
  6.  *
  7.  * Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
  8.  *
  9.  * This program is free software; you can redistribute it and/or modify
  10.  * it under the terms of the GNU General Public License as published by
  11.  * the Free Software Foundation; either version 2 of the License, or
  12.  * (at your option) any later version.
  13.  *
  14.  * This program is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  * GNU General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this program; if not, write to the Free Software
  21.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  22.  *****************************************************************************/
  23. /*****************************************************************************
  24.  * Preamble
  25.  *****************************************************************************/
  26. #ifdef HAVE_CONFIG_H
  27. # include "config.h"
  28. #endif
  29. #include <vlc_common.h>
  30. #include <vlc_plugin.h>
  31. #include <vlc_stream.h>
  32. #include <assert.h>
  33. #include <limits.h>
  34. /*****************************************************************************
  35.  * Module descriptor
  36.  *****************************************************************************/
  37. static int  Open ( vlc_object_t * );
  38. static void Close( vlc_object_t * );
  39. vlc_module_begin()
  40.     set_category( CAT_INPUT )
  41.     set_subcategory( SUBCAT_INPUT_STREAM_FILTER )
  42.     set_description( N_("Uncompressed RAR") )
  43.     set_capability( "stream_filter", 1 )
  44.     set_callbacks( Open, Close )
  45. vlc_module_end()
  46. /*****************************************************************************
  47.  *
  48.  *****************************************************************************/
  49. static const uint8_t p_rar_marker[] = {
  50.     0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00
  51. };
  52. static const int i_rar_marker = sizeof(p_rar_marker);
  53. typedef struct
  54. {
  55.     int64_t i_offset;
  56.     int64_t i_size;
  57.     int64_t i_cummulated_size;
  58. } rar_file_chunk_t;
  59. typedef struct
  60. {
  61.     char     *psz_name;
  62.     int64_t  i_size;
  63.     bool     b_complete;
  64.     int              i_chunk;
  65.     rar_file_chunk_t **pp_chunk;
  66.     int64_t          i_real_size;  /* Gathered size */
  67. } rar_file_t;
  68. static void RarFileDelete( rar_file_t * );
  69. struct stream_sys_t
  70. {
  71.     rar_file_t *p_file;
  72.     const rar_file_chunk_t *p_chunk;
  73.     int64_t i_position;
  74.     uint8_t *p_peek_alloc;
  75.     uint8_t *p_peek;
  76.     unsigned int i_peek;
  77. };
  78. /****************************************************************************
  79.  * Local prototypes
  80.  ****************************************************************************/
  81. static int  Read   ( stream_t *, void *p_read, unsigned int i_read );
  82. static int  Peek   ( stream_t *, const uint8_t **pp_peek, unsigned int i_peek );
  83. static int  Control( stream_t *, int i_query, va_list );
  84. static int  Parse  ( stream_t * );
  85. static int  Seek   ( stream_t *s, int64_t i_position );
  86. /****************************************************************************
  87.  * Open
  88.  ****************************************************************************/
  89. static int Open ( vlc_object_t *p_this )
  90. {
  91.     stream_t *s = (stream_t*)p_this;
  92.     stream_sys_t *p_sys;
  93.     /* */
  94.     const uint8_t *p_peek;
  95.     if( stream_Peek( s->p_source, &p_peek, i_rar_marker ) < i_rar_marker )
  96.         return VLC_EGENERIC;
  97.     if( memcmp( p_peek, p_rar_marker, i_rar_marker ) )
  98.         return VLC_EGENERIC;
  99.     /* */
  100.     s->pf_read = Read;
  101.     s->pf_peek = Peek;
  102.     s->pf_control = Control;
  103.     s->p_sys = p_sys = malloc( sizeof( *p_sys ) );
  104.     if( !p_sys )
  105.         return VLC_ENOMEM;
  106.     /* */
  107.     p_sys->p_file = NULL;
  108.     p_sys->i_position = 0;
  109.     p_sys->p_chunk = NULL;
  110.     p_sys->p_peek_alloc = NULL;
  111.     p_sys->p_peek = NULL;
  112.     p_sys->i_peek = 0;
  113.     /* */
  114.     if( Parse( s ) || !p_sys->p_file || p_sys->p_file->i_chunk <= 0 )
  115.     {
  116.         msg_Err( s, "Invalid or unsupported RAR archive" );
  117.         if( p_sys->p_file )
  118.             RarFileDelete( p_sys->p_file );
  119.         free( p_sys );
  120.         return VLC_EGENERIC;
  121.     }
  122.     /* */
  123.     Seek( s, 0 );
  124.     /* */
  125.     const rar_file_t *p_file = p_sys->p_file;
  126.     msg_Dbg( s, "Using RAR stream filter for '%s' %lld(expected %lld) bytes in %d chunks",
  127.              p_file->psz_name, p_file->i_real_size, p_file->i_size, p_file->i_chunk );
  128.     return VLC_SUCCESS;
  129. }
  130. /****************************************************************************
  131.  * Close
  132.  ****************************************************************************/
  133. static void Close( vlc_object_t *p_this )
  134. {
  135.     stream_t *s = (stream_t*)p_this;
  136.     stream_sys_t *p_sys = s->p_sys;
  137.     RarFileDelete( p_sys->p_file );
  138.     free( p_sys->p_peek_alloc );
  139.     free( p_sys );
  140. }
  141. /****************************************************************************
  142.  * Stream filters functions
  143.  ****************************************************************************/
  144. static int Read( stream_t *s, void *p_read, unsigned int i_read )
  145. {
  146.     stream_sys_t *p_sys = s->p_sys;
  147.     uint8_t *p_data = p_read;
  148.     unsigned int i_total = 0;
  149.     if( p_sys->i_peek > 0 && i_read > 0 )
  150.     {
  151.         const unsigned int i_copy = __MIN( i_read, p_sys->i_peek );
  152.         if( p_data )
  153.         {
  154.             memcpy( p_data, p_sys->p_peek, i_copy );
  155.             p_data += i_copy;
  156.         }
  157.         p_sys->i_peek -= i_copy;
  158.         p_sys->p_peek += i_copy;
  159.         i_total += i_copy;
  160.     }
  161.     while( i_total < i_read )
  162.     {
  163.         const int64_t i_chunk_end = p_sys->p_chunk->i_cummulated_size + p_sys->p_chunk->i_size;
  164.         int i_max = __MIN( i_read - i_total, i_chunk_end - p_sys->i_position );
  165.         if( i_max <= 0 )
  166.             break;
  167.         int i_real = stream_Read( s->p_source, p_data, i_max );
  168.         if( i_real <= 0 )
  169.             break;
  170.         i_total += i_real;
  171.         if( p_data )
  172.             p_data += i_real;
  173.         p_sys->i_position += i_real;
  174.         if( p_sys->i_position >= i_chunk_end )
  175.         {
  176.             if( Seek( s, p_sys->i_position ) )
  177.                 break;
  178.         }
  179.     }
  180.     return i_total;
  181. }
  182. static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned int i_peek )
  183. {
  184.     stream_sys_t *p_sys = s->p_sys;
  185.     if( i_peek <= p_sys->i_peek )
  186.     {
  187.         *pp_peek = p_sys->p_peek;
  188.         return i_peek;
  189.     }
  190.     /* */
  191.     uint8_t *p_peek = malloc( i_peek );
  192.     if( !p_peek )
  193.         return 0;
  194.     /* XXX yes stream_Read on ourself */
  195.     int i_read = stream_Read( s, p_peek, i_peek );
  196.     if( i_read <= 0 )
  197.     {
  198.         free( p_peek );
  199.         return i_read;
  200.     }
  201.     if( p_sys->p_peek_alloc )
  202.         free( p_sys->p_peek_alloc );
  203.     p_sys->p_peek_alloc =
  204.     p_sys->p_peek       = p_peek;
  205.     p_sys->i_peek       = i_read;
  206.     *pp_peek = p_sys->p_peek;
  207.     return p_sys->i_peek;
  208. }
  209. static int Control( stream_t *s, int i_query, va_list args )
  210. {
  211.     stream_sys_t *p_sys = s->p_sys;
  212.     switch( i_query )
  213.     {
  214.     /* */
  215.     case STREAM_SET_POSITION:
  216.     {
  217.         int64_t i_position = (int64_t)va_arg( args, int64_t );
  218.         return Seek( s, i_position );
  219.     }
  220.     case STREAM_GET_POSITION:
  221.     {
  222.         int64_t *pi_position = (int64_t*)va_arg( args, int64_t* );
  223.         *pi_position = p_sys->i_position - p_sys->i_peek;
  224.         return VLC_SUCCESS;
  225.     }
  226.     case STREAM_GET_SIZE:
  227.     {
  228.         int64_t *pi_size = (int64_t*)va_arg( args, int64_t* );
  229.         *pi_size = p_sys->p_file->i_real_size;
  230.         return VLC_SUCCESS;
  231.     }
  232.     /* */
  233.     case STREAM_GET_CONTENT_TYPE: /* arg1= char ** */
  234.         return VLC_EGENERIC;
  235.     case STREAM_UPDATE_SIZE: /* TODO maybe we should update i_real_size from file size and chunk offset ? */
  236.     case STREAM_CONTROL_ACCESS:
  237.     case STREAM_CAN_SEEK:
  238.     case STREAM_CAN_FASTSEEK:
  239.     case STREAM_SET_RECORD_STATE:
  240.         return stream_vaControl( s->p_source, i_query, args );
  241.     default:
  242.         return VLC_EGENERIC;
  243.     }
  244. }
  245. /****************************************************************************
  246.  * Helpers
  247.  ****************************************************************************/
  248. static int Seek( stream_t *s, int64_t i_position )
  249. {
  250.     stream_sys_t *p_sys = s->p_sys;
  251.     if( i_position < 0 )
  252.         i_position = 0;
  253.     else if( i_position > p_sys->p_file->i_real_size )
  254.         i_position = p_sys->p_file->i_real_size;
  255.     /* Search the chunk */
  256.     const rar_file_t *p_file = p_sys->p_file;
  257.     for( int i = 0; i < p_file->i_chunk; i++ )
  258.     {
  259.         p_sys->p_chunk = p_file->pp_chunk[i];
  260.         if( i_position < p_sys->p_chunk->i_cummulated_size + p_sys->p_chunk->i_size )
  261.             break;
  262.     }
  263.     p_sys->i_position = i_position;
  264.     p_sys->i_peek     = 0;
  265.     const int64_t i_seek = p_sys->p_chunk->i_offset +
  266.                            ( i_position - p_sys->p_chunk->i_cummulated_size );
  267.     return stream_Seek( s->p_source, i_seek );
  268. }
  269. static void RarFileDelete( rar_file_t *p_file )
  270. {
  271.     for( int i = 0; i < p_file->i_chunk; i++ )
  272.         free( p_file->pp_chunk[i] );
  273.     free( p_file->pp_chunk );
  274.     free( p_file->psz_name );
  275.     free( p_file );
  276. }
  277. typedef struct
  278. {
  279.     uint16_t i_crc;
  280.     uint8_t  i_type;
  281.     uint16_t i_flags;
  282.     uint16_t i_size;
  283.     uint32_t i_add_size;
  284. } rar_block_t;
  285. enum
  286. {
  287.     RAR_BLOCK_MARKER = 0x72,
  288.     RAR_BLOCK_ARCHIVE = 0x73,
  289.     RAR_BLOCK_FILE = 0x74,
  290.     RAR_BLOCK_END = 0x7b,
  291. };
  292. enum
  293. {
  294.     RAR_BLOCK_END_HAS_NEXT = 0x0001,
  295. };
  296. enum
  297. {
  298.     RAR_BLOCK_FILE_HAS_PREVIOUS = 0x0001,
  299.     RAR_BLOCK_FILE_HAS_NEXT     = 0x0002,
  300.     RAR_BLOCK_FILE_HAS_HIGH     = 0x0100,
  301. };
  302. static int PeekBlock( stream_t *s, rar_block_t *p_hdr )
  303. {
  304.     const uint8_t *p_peek;
  305.     int i_peek = stream_Peek( s->p_source, &p_peek, 11 );
  306.     if( i_peek < 7 )
  307.         return VLC_EGENERIC;
  308.     p_hdr->i_crc   = GetWLE( &p_peek[0] );
  309.     p_hdr->i_type  = p_peek[2];
  310.     p_hdr->i_flags = GetWLE( &p_peek[3] );
  311.     p_hdr->i_size  = GetWLE( &p_peek[5] );
  312.     p_hdr->i_add_size = 0;
  313.     if( p_hdr->i_flags & 0x8000 )
  314.     {
  315.         if( i_peek < 11 )
  316.             return VLC_EGENERIC;
  317.         p_hdr->i_add_size = GetDWLE( &p_peek[7] );
  318.     }
  319.     if( p_hdr->i_size < 7 )
  320.         return VLC_EGENERIC;
  321.     return VLC_SUCCESS;
  322. }
  323. static int SkipBlock( stream_t *s, const rar_block_t *p_hdr )
  324. {
  325.     int64_t i_size = (int64_t)p_hdr->i_size + p_hdr->i_add_size;
  326.     assert( i_size >= 0 );
  327.     while( i_size > 0 )
  328.     {
  329.         int i_skip = __MIN( i_size, INT_MAX );
  330.         if( stream_Read( s->p_source, NULL, i_skip ) < i_skip )
  331.             return VLC_EGENERIC;
  332.         i_size -= i_skip;
  333.     }
  334.     return VLC_SUCCESS;
  335. }
  336. static int IgnoreBlock( stream_t *s, int i_block )
  337. {
  338.     /* */
  339.     rar_block_t bk;
  340.     if( PeekBlock( s, &bk ) || bk.i_type != i_block )
  341.         return VLC_EGENERIC;
  342.     return SkipBlock( s, &bk );
  343. }
  344. static int SkipEnd( stream_t *s, const rar_block_t *p_hdr )
  345. {
  346.     if( !(p_hdr->i_flags & RAR_BLOCK_END_HAS_NEXT) )
  347.         return VLC_EGENERIC;
  348.     if( SkipBlock( s, p_hdr ) )
  349.         return VLC_EGENERIC;
  350.     /* Now, we need to look for a marker block,
  351.      * It seems that there is garbage at EOF */
  352.     for( ;; )
  353.     {
  354.         const uint8_t *p_peek;
  355.         if( stream_Peek( s->p_source, &p_peek, i_rar_marker ) < i_rar_marker )
  356.             return VLC_EGENERIC;
  357.         if( !memcmp( p_peek, p_rar_marker, i_rar_marker ) )
  358.             break;
  359.         if( stream_Read( s->p_source, NULL, 1 ) != 1 )
  360.             return VLC_EGENERIC;
  361.     }
  362.     /* Skip marker and archive blocks */
  363.     if( IgnoreBlock( s, RAR_BLOCK_MARKER ) )
  364.         return VLC_EGENERIC;
  365.     if( IgnoreBlock( s, RAR_BLOCK_ARCHIVE ) )
  366.         return VLC_EGENERIC;
  367.     return VLC_SUCCESS;
  368. }
  369. static int SkipFile( stream_t *s,const rar_block_t *p_hdr )
  370. {
  371.     stream_sys_t *p_sys = s->p_sys;
  372.     const uint8_t *p_peek;
  373.     int i_min_size = 7+21;
  374.     if( p_hdr->i_flags & RAR_BLOCK_FILE_HAS_HIGH )
  375.         i_min_size += 8;
  376.     if( p_hdr->i_size < i_min_size )
  377.         return VLC_EGENERIC;
  378.     if( stream_Peek( s->p_source, &p_peek, i_min_size ) < i_min_size )
  379.         return VLC_EGENERIC;
  380.     /* */
  381.     uint32_t i_file_size_low = GetDWLE( &p_peek[7+4] );
  382.     uint8_t  i_method = p_peek[7+18];
  383.     uint16_t i_name_size = GetWLE( &p_peek[7+19] );
  384.     uint32_t i_file_size_high = 0;
  385.     if( p_hdr->i_flags & RAR_BLOCK_FILE_HAS_HIGH )
  386.         i_file_size_high = GetDWLE( &p_peek[7+25] );
  387.     char *psz_name = calloc( 1, i_name_size + 1 );
  388.     if( !psz_name )
  389.         return VLC_EGENERIC;
  390.     const int i_name_offset = (p_hdr->i_flags & RAR_BLOCK_FILE_HAS_HIGH) ? (7+33) : (7+25);
  391.     if( i_name_offset + i_name_size <= p_hdr->i_size )
  392.     {
  393.         const int i_max_size = i_name_offset + i_name_size;
  394.         if( stream_Peek( s->p_source, &p_peek, i_max_size ) < i_max_size )
  395.         {
  396.             free( psz_name );
  397.             return VLC_EGENERIC;
  398.         }
  399.         memcpy( psz_name, &p_peek[i_name_offset], i_name_size );
  400.     }
  401.     if( i_method != 0x30 )
  402.     {
  403.         msg_Warn( s, "Ignoring compressed file %s (method=0x%2.2x)", psz_name, i_method );
  404.         goto exit;
  405.     }
  406.     /* Ignore smaller files */
  407.     const int64_t i_file_size = ((int64_t)i_file_size_high << 32) | i_file_size_low;
  408.     if( p_sys->p_file &&
  409.         p_sys->p_file->i_size < i_file_size )
  410.     {
  411.         RarFileDelete( p_sys->p_file );
  412.         p_sys->p_file = NULL;
  413.     }
  414.     /* */
  415.     rar_file_t *p_current = p_sys->p_file;
  416.     if( !p_current )
  417.     {
  418.         p_sys->p_file = p_current = malloc( sizeof( *p_sys->p_file ) );
  419.         if( !p_current )
  420.             goto exit;
  421.         /* */
  422.         p_current->psz_name = psz_name;
  423.         p_current->i_size = i_file_size;
  424.         p_current->b_complete = false;
  425.         p_current->i_real_size = 0;
  426.         TAB_INIT( p_current->i_chunk, p_current->pp_chunk );
  427.         psz_name = NULL;
  428.     }
  429.     /* Append chunks */
  430.     if( !p_current->b_complete )
  431.     {
  432.         bool b_append = false;
  433.         /* Append if first chunk */
  434.         if( p_current->i_chunk <= 0 )
  435.             b_append = true;
  436.         /* Append if it is really a continuous chunck */
  437.         if( p_current->i_size == i_file_size &&
  438.             ( !psz_name || !strcmp( p_current->psz_name, psz_name ) ) &&
  439.             ( p_hdr->i_flags & RAR_BLOCK_FILE_HAS_PREVIOUS ) )
  440.             b_append = true;
  441.         if( b_append )
  442.         {
  443.             rar_file_chunk_t *p_chunk = malloc( sizeof( *p_chunk ) );
  444.             if( p_chunk )
  445.             {
  446.                 p_chunk->i_offset = stream_Tell( s->p_source ) + p_hdr->i_size;
  447.                 p_chunk->i_size = p_hdr->i_add_size;
  448.                 p_chunk->i_cummulated_size = 0;
  449.                 if( p_current->i_chunk > 0 )
  450.                 {
  451.                     rar_file_chunk_t *p_previous = p_current->pp_chunk[p_current->i_chunk-1];
  452.                     p_chunk->i_cummulated_size += p_previous->i_cummulated_size +
  453.                                                   p_previous->i_size;
  454.                 }
  455.                 TAB_APPEND( p_current->i_chunk, p_current->pp_chunk, p_chunk );
  456.                 p_current->i_real_size += p_hdr->i_add_size;
  457.             }
  458.         }
  459.         if( !(p_hdr->i_flags & RAR_BLOCK_FILE_HAS_NEXT ) )
  460.             p_current->b_complete = true;
  461.     }
  462. exit:
  463.     /* */
  464.     free( psz_name );
  465.     /* We stop on the first non empty file if we cannot seek */
  466.     if( p_sys->p_file )
  467.     {
  468.         bool b_can_seek = false;
  469.         stream_Control( s->p_source, STREAM_CAN_SEEK, &b_can_seek );
  470.         if( !b_can_seek && p_current->i_size > 0 )
  471.             return VLC_EGENERIC;
  472.     }
  473.     if( SkipBlock( s, p_hdr ) )
  474.         return VLC_EGENERIC;
  475.     return VLC_SUCCESS;
  476. }
  477. static int Parse( stream_t *s )
  478. {
  479.     /* Skip marker */
  480.     if( IgnoreBlock( s, RAR_BLOCK_MARKER ) )
  481.         return VLC_EGENERIC;
  482.     /* Skip archive  */
  483.     if( IgnoreBlock( s, RAR_BLOCK_ARCHIVE ) )
  484.         return VLC_EGENERIC;
  485.     /* */
  486.     for( ;; )
  487.     {
  488.         rar_block_t bk;
  489.         int i_ret;
  490.         if( PeekBlock( s, &bk ) )
  491.             break;
  492.         switch( bk.i_type )
  493.         {
  494.         case RAR_BLOCK_END:
  495.             i_ret = SkipEnd( s, &bk );
  496.             break;
  497.         case RAR_BLOCK_FILE:
  498.             i_ret = SkipFile( s, &bk );
  499.             break;
  500.         default:
  501.             i_ret = SkipBlock( s, &bk );
  502.             break;
  503.         }
  504.         if( i_ret )
  505.             break;
  506.     }
  507.     return VLC_SUCCESS;
  508. }