mirror of
https://code.videolan.org/videolan/vlc
synced 2024-10-07 03:56:28 +02:00
Added support for multiple files in RAR archives.
It is not yet perfect as it should probably create a XSPF playlist by reusing some code from the zip modules.
This commit is contained in:
parent
5d3e4a5dba
commit
fdf2f0fcec
@ -53,6 +53,9 @@ SOURCES_access_imem = imem.c
|
||||
SOURCES_access_avio = avio.c avio.h
|
||||
SOURCES_access_attachment = attachment.c
|
||||
|
||||
libstream_filter_rar_plugin_la_SOURCES = rar/rar.c rar/rar.h rar/stream.c
|
||||
libaccess_rar_plugin_la_SOURCES = rar/rar.c rar/rar.h rar/access.c
|
||||
|
||||
libaccess_rtmp_plugin_la_SOURCES = \
|
||||
rtmp/access.c \
|
||||
rtmp/rtmp_amf_flv.c \
|
||||
@ -71,6 +74,8 @@ libvlc_LTLIBRARIES += \
|
||||
libaccess_ftp_plugin.la \
|
||||
libaccess_imem_plugin.la \
|
||||
libaccess_attachment_plugin.la \
|
||||
libaccess_rar_plugin.la \
|
||||
libstream_filter_rar_plugin.la \
|
||||
$(NULL)
|
||||
|
||||
libxcb_screen_plugin_la_SOURCES = screen/xcb.c
|
||||
|
214
modules/access/rar/access.c
Normal file
214
modules/access/rar/access.c
Normal file
@ -0,0 +1,214 @@
|
||||
/*****************************************************************************
|
||||
* rar.c: uncompressed RAR access
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2008-2010 Laurent Aimar
|
||||
* $Id$
|
||||
*
|
||||
* Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
|
||||
*
|
||||
* 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
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
* Preamble
|
||||
*****************************************************************************/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <vlc_common.h>
|
||||
#include <vlc_plugin.h>
|
||||
#include <vlc_access.h>
|
||||
#include <vlc_stream.h>
|
||||
#include <vlc_url.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "rar.h"
|
||||
|
||||
/*****************************************************************************
|
||||
* Module descriptor
|
||||
*****************************************************************************/
|
||||
static int Open (vlc_object_t *);
|
||||
static void Close(vlc_object_t *);
|
||||
|
||||
vlc_module_begin()
|
||||
set_category(CAT_INPUT)
|
||||
set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
|
||||
set_description(N_("Uncompressed RAR"))
|
||||
set_capability("access", 0)
|
||||
set_callbacks(Open, Close)
|
||||
add_shortcut("rar")
|
||||
vlc_module_end()
|
||||
|
||||
/*****************************************************************************
|
||||
* Local definitions/prototypes
|
||||
*****************************************************************************/
|
||||
struct access_sys_t {
|
||||
stream_t *s;
|
||||
rar_file_t *file;
|
||||
const rar_file_chunk_t *chunk;
|
||||
};
|
||||
|
||||
static int Seek(access_t *access, uint64_t position)
|
||||
{
|
||||
access_sys_t *sys = access->p_sys;
|
||||
const rar_file_t *file = sys->file;
|
||||
|
||||
if (position > file->real_size)
|
||||
position = file->real_size;
|
||||
|
||||
/* Search the chunk */
|
||||
for (int i = 0; i < file->chunk_count; i++) {
|
||||
sys->chunk = file->chunk[i];
|
||||
if (position < sys->chunk->cummulated_size + sys->chunk->size)
|
||||
break;
|
||||
}
|
||||
access->info.i_pos = position;
|
||||
access->info.b_eof = false;
|
||||
|
||||
const uint64_t offset = sys->chunk->offset +
|
||||
(position - sys->chunk->cummulated_size);
|
||||
return stream_Seek(sys->s, offset);
|
||||
}
|
||||
|
||||
static ssize_t Read(access_t *access, uint8_t *data, size_t size)
|
||||
{
|
||||
access_sys_t *sys = access->p_sys;
|
||||
|
||||
size_t total = 0;
|
||||
while (total < size) {
|
||||
const uint64_t chunk_end = sys->chunk->cummulated_size + sys->chunk->size;
|
||||
int max = __MIN(__MIN((int64_t)(size - total), (int64_t)(chunk_end - access->info.i_pos)), INT_MAX);
|
||||
if (max <= 0)
|
||||
break;
|
||||
|
||||
int r = stream_Read(sys->s, data, max);
|
||||
if (r <= 0)
|
||||
break;
|
||||
|
||||
total += r;
|
||||
if( data )
|
||||
data += r;
|
||||
access->info.i_pos += r;
|
||||
if (access->info.i_pos >= chunk_end &&
|
||||
Seek(access, access->info.i_pos))
|
||||
break;
|
||||
}
|
||||
if (size > 0 && total <= 0)
|
||||
access->info.b_eof = true;
|
||||
return total;
|
||||
|
||||
}
|
||||
|
||||
static int Control(access_t *access, int query, va_list args)
|
||||
{
|
||||
stream_t *s = access->p_sys->s;
|
||||
switch (query) {
|
||||
case ACCESS_CAN_SEEK: {
|
||||
bool *b = va_arg(args, bool *);
|
||||
return stream_Control(s, STREAM_CAN_SEEK, b);
|
||||
}
|
||||
case ACCESS_CAN_FASTSEEK: {
|
||||
bool *b = va_arg(args, bool *);
|
||||
return stream_Control(s, STREAM_CAN_FASTSEEK, b);
|
||||
}
|
||||
/* FIXME the following request should ask the underlying access object */
|
||||
case ACCESS_CAN_PAUSE:
|
||||
case ACCESS_CAN_CONTROL_PACE: {
|
||||
bool *b = va_arg(args, bool *);
|
||||
*b = true;
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
case ACCESS_GET_PTS_DELAY: {
|
||||
int64_t *delay = va_arg(args, int64_t *);
|
||||
*delay = DEFAULT_PTS_DELAY;
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
case ACCESS_SET_PAUSE_STATE:
|
||||
return VLC_SUCCESS;
|
||||
|
||||
default:
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
}
|
||||
|
||||
static int Open(vlc_object_t *object)
|
||||
{
|
||||
access_t *access = (access_t*)object;
|
||||
|
||||
if (!strchr(access->psz_location, '|'))
|
||||
return VLC_EGENERIC;
|
||||
|
||||
char *base = strdup(access->psz_location);
|
||||
if (!base)
|
||||
return VLC_EGENERIC;
|
||||
char *name = strchr(base, '|');
|
||||
*name++ = '\0';
|
||||
decode_URI(base);
|
||||
|
||||
stream_t *s = stream_UrlNew(access, base);
|
||||
if (!s)
|
||||
goto error;
|
||||
int count;
|
||||
rar_file_t **files;
|
||||
if (RarProbe(s) || RarParse(s, &count, &files) || count <= 0)
|
||||
goto error;
|
||||
rar_file_t *file = NULL;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!file && !strcmp(files[i]->name, name))
|
||||
file = files[i];
|
||||
else
|
||||
RarFileDelete(files[i]);
|
||||
}
|
||||
free(files);
|
||||
if (!file)
|
||||
goto error;
|
||||
|
||||
access_sys_t *sys = access->p_sys = malloc(sizeof(*sys));
|
||||
sys->s = s;
|
||||
sys->file = file;
|
||||
|
||||
access->pf_read = Read;
|
||||
access->pf_block = NULL;
|
||||
access->pf_control = Control;
|
||||
access->pf_seek = Seek;
|
||||
|
||||
access_InitFields(access);
|
||||
access->info.i_size = file->size;
|
||||
|
||||
Seek(access, 0);
|
||||
|
||||
free(base);
|
||||
return VLC_SUCCESS;
|
||||
|
||||
error:
|
||||
if (s)
|
||||
stream_Delete(s);
|
||||
free(base);
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
|
||||
static void Close(vlc_object_t *object)
|
||||
{
|
||||
access_t *access = (access_t*)object;
|
||||
access_sys_t *sys = access->p_sys;
|
||||
|
||||
stream_Delete(sys->s);
|
||||
RarFileDelete(sys->file);
|
||||
free(sys);
|
||||
}
|
||||
|
305
modules/access/rar/rar.c
Normal file
305
modules/access/rar/rar.c
Normal file
@ -0,0 +1,305 @@
|
||||
/*****************************************************************************
|
||||
* rar.h: uncompressed RAR parser
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2008-2010 Laurent Aimar
|
||||
* $Id$
|
||||
*
|
||||
* Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
|
||||
*
|
||||
* 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
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
* Preamble
|
||||
*****************************************************************************/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <vlc_common.h>
|
||||
#include <vlc_plugin.h>
|
||||
#include <vlc_stream.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "rar.h"
|
||||
|
||||
static const uint8_t rar_marker[] = {
|
||||
0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00
|
||||
};
|
||||
static const int rar_marker_size = sizeof(rar_marker);
|
||||
|
||||
void RarFileDelete(rar_file_t *file)
|
||||
{
|
||||
for (int i = 0; i < file->chunk_count; i++)
|
||||
free(file->chunk[i]);
|
||||
free(file->chunk);
|
||||
free(file->name);
|
||||
free(file);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint16_t crc;
|
||||
uint8_t type;
|
||||
uint16_t flags;
|
||||
uint16_t size;
|
||||
uint32_t add_size;
|
||||
} rar_block_t;
|
||||
|
||||
enum {
|
||||
RAR_BLOCK_MARKER = 0x72,
|
||||
RAR_BLOCK_ARCHIVE = 0x73,
|
||||
RAR_BLOCK_FILE = 0x74,
|
||||
RAR_BLOCK_END = 0x7b,
|
||||
};
|
||||
enum {
|
||||
RAR_BLOCK_END_HAS_NEXT = 0x0001,
|
||||
};
|
||||
enum {
|
||||
RAR_BLOCK_FILE_HAS_PREVIOUS = 0x0001,
|
||||
RAR_BLOCK_FILE_HAS_NEXT = 0x0002,
|
||||
RAR_BLOCK_FILE_HAS_HIGH = 0x0100,
|
||||
};
|
||||
|
||||
static int PeekBlock(stream_t *s, rar_block_t *hdr)
|
||||
{
|
||||
const uint8_t *peek;
|
||||
int peek_size = stream_Peek(s, &peek, 11);
|
||||
|
||||
if (peek_size < 7)
|
||||
return VLC_EGENERIC;
|
||||
|
||||
hdr->crc = GetWLE(&peek[0]);
|
||||
hdr->type = peek[2];
|
||||
hdr->flags = GetWLE(&peek[3]);
|
||||
hdr->size = GetWLE(&peek[5]);
|
||||
hdr->add_size = 0;
|
||||
if (hdr->flags & 0x8000) {
|
||||
if (peek_size < 11)
|
||||
return VLC_EGENERIC;
|
||||
hdr->add_size = GetDWLE(&peek[7]);
|
||||
}
|
||||
|
||||
if (hdr->size < 7)
|
||||
return VLC_EGENERIC;
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
static int SkipBlock(stream_t *s, const rar_block_t *hdr)
|
||||
{
|
||||
uint64_t size = (uint64_t)hdr->size + hdr->add_size;
|
||||
|
||||
while (size > 0) {
|
||||
int skip = __MIN(size, INT_MAX);
|
||||
if (stream_Read(s, NULL, skip) < skip)
|
||||
return VLC_EGENERIC;
|
||||
|
||||
size -= skip;
|
||||
}
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
static int IgnoreBlock(stream_t *s, int block)
|
||||
{
|
||||
/* */
|
||||
rar_block_t bk;
|
||||
if (PeekBlock(s, &bk) || bk.type != block)
|
||||
return VLC_EGENERIC;
|
||||
return SkipBlock(s, &bk);
|
||||
}
|
||||
|
||||
static int SkipEnd(stream_t *s, const rar_block_t *hdr)
|
||||
{
|
||||
if (!(hdr->flags & RAR_BLOCK_END_HAS_NEXT))
|
||||
return VLC_EGENERIC;
|
||||
|
||||
if (SkipBlock(s, hdr))
|
||||
return VLC_EGENERIC;
|
||||
|
||||
/* Now, we need to look for a marker block,
|
||||
* It seems that there is garbage at EOF */
|
||||
for (;;) {
|
||||
const uint8_t *peek;
|
||||
|
||||
if (stream_Peek(s, &peek, rar_marker_size) < rar_marker_size)
|
||||
return VLC_EGENERIC;
|
||||
|
||||
if (!memcmp(peek, rar_marker, rar_marker_size))
|
||||
break;
|
||||
|
||||
if (stream_Read(s, NULL, 1) != 1)
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
|
||||
/* Skip marker and archive blocks */
|
||||
if (IgnoreBlock(s, RAR_BLOCK_MARKER))
|
||||
return VLC_EGENERIC;
|
||||
if (IgnoreBlock(s, RAR_BLOCK_ARCHIVE))
|
||||
return VLC_EGENERIC;
|
||||
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
static int SkipFile(stream_t *s, int *count, rar_file_t ***file, const rar_block_t *hdr)
|
||||
{
|
||||
const uint8_t *peek;
|
||||
|
||||
int min_size = 7+21;
|
||||
if (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH)
|
||||
min_size += 8;
|
||||
if (hdr->size < (unsigned)min_size)
|
||||
return VLC_EGENERIC;
|
||||
|
||||
if (stream_Peek(s, &peek, min_size) < min_size)
|
||||
return VLC_EGENERIC;
|
||||
|
||||
/* */
|
||||
uint32_t file_size_low = GetDWLE(&peek[7+4]);
|
||||
uint8_t method = peek[7+18];
|
||||
uint16_t name_size = GetWLE(&peek[7+19]);
|
||||
uint32_t file_size_high = 0;
|
||||
if (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH)
|
||||
file_size_high = GetDWLE(&peek[7+25]);
|
||||
const uint64_t file_size = ((uint64_t)file_size_high << 32) | file_size_low;
|
||||
|
||||
char *name = calloc(1, name_size + 1);
|
||||
if (!name)
|
||||
return VLC_EGENERIC;
|
||||
|
||||
const int name_offset = (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH) ? (7+33) : (7+25);
|
||||
if (name_offset + name_size <= hdr->size) {
|
||||
const int max_size = name_offset + name_size;
|
||||
if (stream_Peek(s, &peek, max_size) < max_size) {
|
||||
free(name);
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
memcpy(name, &peek[name_offset], name_size);
|
||||
}
|
||||
|
||||
if (method != 0x30) {
|
||||
msg_Warn(s, "Ignoring compressed file %s (method=0x%2.2x)", name, method);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* */
|
||||
rar_file_t *current = *count > 0 ? (*file)[*count - 1] : NULL;
|
||||
if (current &&
|
||||
(current->is_complete ||
|
||||
current->size != file_size ||
|
||||
strcmp(current->name, name) ||
|
||||
(hdr->flags & RAR_BLOCK_FILE_HAS_PREVIOUS) == 0))
|
||||
current = NULL;
|
||||
|
||||
if (!current) {
|
||||
current = malloc(sizeof(*current));
|
||||
if (!current)
|
||||
goto exit;
|
||||
TAB_APPEND(*count, *file, current);
|
||||
|
||||
current->name = name;
|
||||
current->size = file_size;
|
||||
current->is_complete = false;
|
||||
current->real_size = 0;
|
||||
TAB_INIT(current->chunk_count, current->chunk);
|
||||
|
||||
name = NULL;
|
||||
}
|
||||
|
||||
/* Append chunks */
|
||||
rar_file_chunk_t *chunk = malloc(sizeof(*chunk));
|
||||
if (chunk) {
|
||||
chunk->offset = stream_Tell(s) + hdr->size;
|
||||
chunk->size = hdr->add_size;
|
||||
chunk->cummulated_size = 0;
|
||||
if (current->chunk_count > 0) {
|
||||
rar_file_chunk_t *previous = current->chunk[current->chunk_count-1];
|
||||
|
||||
chunk->cummulated_size += previous->cummulated_size +
|
||||
previous->size;
|
||||
}
|
||||
|
||||
TAB_APPEND(current->chunk_count, current->chunk, chunk);
|
||||
|
||||
current->real_size += hdr->add_size;
|
||||
}
|
||||
if ((hdr->flags & RAR_BLOCK_FILE_HAS_NEXT) == 0)
|
||||
current->is_complete = true;
|
||||
|
||||
exit:
|
||||
/* */
|
||||
free(name);
|
||||
|
||||
/* We stop on the first non empty file if we cannot seek */
|
||||
if (current) {
|
||||
bool can_seek = false;
|
||||
stream_Control(s, STREAM_CAN_SEEK, &can_seek);
|
||||
if (!can_seek && current->size > 0)
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
|
||||
if (SkipBlock(s, hdr))
|
||||
return VLC_EGENERIC;
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
int RarProbe(stream_t *s)
|
||||
{
|
||||
const uint8_t *peek;
|
||||
if (stream_Peek(s, &peek, rar_marker_size) < rar_marker_size)
|
||||
return VLC_EGENERIC;
|
||||
if (memcmp(peek, rar_marker, rar_marker_size))
|
||||
return VLC_EGENERIC;
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
int RarParse(stream_t *s, int *count, rar_file_t ***file)
|
||||
{
|
||||
*count = 0;
|
||||
*file = NULL;
|
||||
|
||||
/* Skip marker */
|
||||
if (IgnoreBlock(s, RAR_BLOCK_MARKER))
|
||||
return VLC_EGENERIC;
|
||||
|
||||
/* Skip archive */
|
||||
if (IgnoreBlock(s, RAR_BLOCK_ARCHIVE))
|
||||
return VLC_EGENERIC;
|
||||
|
||||
/* */
|
||||
for (;;) {
|
||||
rar_block_t bk;
|
||||
int ret;
|
||||
|
||||
if (PeekBlock(s, &bk))
|
||||
break;
|
||||
|
||||
switch(bk.type) {
|
||||
case RAR_BLOCK_END:
|
||||
ret = SkipEnd(s, &bk);
|
||||
break;
|
||||
case RAR_BLOCK_FILE:
|
||||
ret = SkipFile(s, count, file, &bk);
|
||||
break;
|
||||
default:
|
||||
ret = SkipBlock(s, &bk);
|
||||
break;
|
||||
}
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
43
modules/access/rar/rar.h
Normal file
43
modules/access/rar/rar.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*****************************************************************************
|
||||
* rar.h: uncompressed RAR parser
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2008-2010 Laurent Aimar
|
||||
* $Id$
|
||||
*
|
||||
* Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
|
||||
*
|
||||
* 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
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||
*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
uint64_t offset;
|
||||
uint64_t size;
|
||||
uint64_t cummulated_size;
|
||||
} rar_file_chunk_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
uint64_t size;
|
||||
bool is_complete;
|
||||
|
||||
int chunk_count;
|
||||
rar_file_chunk_t **chunk;
|
||||
uint64_t real_size; /* Gathered size */
|
||||
} rar_file_t;
|
||||
|
||||
int RarProbe(stream_t *);
|
||||
void RarFileDelete(rar_file_t *);
|
||||
int RarParse(stream_t *, int *, rar_file_t ***);
|
||||
|
164
modules/access/rar/stream.c
Normal file
164
modules/access/rar/stream.c
Normal file
@ -0,0 +1,164 @@
|
||||
/*****************************************************************************
|
||||
* rar.c: uncompressed RAR stream filter
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2008-2010 Laurent Aimar
|
||||
* $Id$
|
||||
*
|
||||
* Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
|
||||
*
|
||||
* 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
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
* Preamble
|
||||
*****************************************************************************/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <vlc_common.h>
|
||||
#include <vlc_plugin.h>
|
||||
#include <vlc_stream.h>
|
||||
#include <vlc_url.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "rar.h"
|
||||
|
||||
/*****************************************************************************
|
||||
* Module descriptor
|
||||
*****************************************************************************/
|
||||
static int Open (vlc_object_t *);
|
||||
static void Close(vlc_object_t *);
|
||||
|
||||
vlc_module_begin()
|
||||
set_category(CAT_INPUT)
|
||||
set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
|
||||
set_description(N_("Uncompressed RAR"))
|
||||
set_capability("stream_filter", 1)
|
||||
set_callbacks(Open, Close)
|
||||
add_shortcut("rar")
|
||||
vlc_module_end()
|
||||
|
||||
/****************************************************************************
|
||||
* Local definitions/prototypes
|
||||
****************************************************************************/
|
||||
struct stream_sys_t {
|
||||
stream_t *payload;
|
||||
};
|
||||
|
||||
static int Read(stream_t *s, void *data, unsigned size)
|
||||
{
|
||||
return stream_Read(s->p_sys->payload, data, size);
|
||||
}
|
||||
|
||||
static int Peek( stream_t *s, const uint8_t **data, unsigned size)
|
||||
{
|
||||
return stream_Peek(s->p_sys->payload, data, size);
|
||||
}
|
||||
|
||||
static int Control(stream_t *s, int query, va_list args)
|
||||
{
|
||||
switch (query) {
|
||||
case STREAM_GET_CONTENT_TYPE: {
|
||||
char **mime = va_arg(args, char **);
|
||||
*mime = strdup("audio/x-mpegurl");
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
default:
|
||||
return stream_vaControl(s->p_sys->payload, query, args);
|
||||
}
|
||||
}
|
||||
|
||||
static int Open(vlc_object_t *object)
|
||||
{
|
||||
stream_t *s = (stream_t*)object;
|
||||
|
||||
if (RarProbe(s->p_source))
|
||||
return VLC_EGENERIC;
|
||||
|
||||
int count;
|
||||
rar_file_t **files;
|
||||
if (RarParse(s->p_source, &count, &files) || count <= 0) {
|
||||
msg_Err(s, "Invalid or unsupported RAR archive");
|
||||
free(files);
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
|
||||
/* TODO use xspf to have node for directories
|
||||
* Reusing WriteXSPF from the zip access is probably a good idea
|
||||
* (becareful about '\' and '/'.
|
||||
*/
|
||||
char *base;
|
||||
char *encoded = encode_URI_component(s->psz_path);
|
||||
if (!encoded || asprintf(&base, "rar://%s", encoded) < 0)
|
||||
base = NULL;
|
||||
free(encoded);
|
||||
|
||||
char *data = strdup("#EXTM3U\n");
|
||||
for (int i = 0; i < count; i++) {
|
||||
rar_file_t *f = files[i];
|
||||
char *next;
|
||||
if (base && data &&
|
||||
asprintf(&next, "%s"
|
||||
"#EXTINF:,,%s\n"
|
||||
"%s|%s\n",
|
||||
data, f->name, base, f->name) >= 0) {
|
||||
free(data);
|
||||
data = next;
|
||||
}
|
||||
RarFileDelete(f);
|
||||
}
|
||||
free(files);
|
||||
if (!data)
|
||||
return VLC_EGENERIC;
|
||||
stream_t *payload = stream_MemoryNew(s, (uint8_t*)data, strlen(data), false);
|
||||
if (!payload) {
|
||||
free(data);
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
|
||||
s->pf_read = Read;
|
||||
s->pf_peek = Peek;
|
||||
s->pf_control = Control;
|
||||
|
||||
stream_sys_t *sys = s->p_sys = malloc(sizeof(*sys));
|
||||
if (!sys) {
|
||||
stream_Delete(payload);
|
||||
return VLC_ENOMEM;
|
||||
}
|
||||
sys->payload = payload;
|
||||
|
||||
char *tmp;
|
||||
if (asprintf(&tmp, "%s.m3u", s->psz_path) < 0) {
|
||||
Close(object);
|
||||
return VLC_ENOMEM;
|
||||
}
|
||||
free(s->psz_path);
|
||||
s->psz_path = tmp;
|
||||
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
static void Close(vlc_object_t *object)
|
||||
{
|
||||
stream_t *s = (stream_t*)object;
|
||||
stream_sys_t *sys = s->p_sys;
|
||||
|
||||
stream_Delete(sys->payload);
|
||||
free(sys);
|
||||
}
|
||||
|
@ -1,10 +1,8 @@
|
||||
SOURCES_decomp = decomp.c
|
||||
SOURCES_stream_filter_record = record.c
|
||||
SOURCES_stream_filter_rar = rar.c
|
||||
|
||||
libvlc_LTLIBRARIES += \
|
||||
libstream_filter_record_plugin.la \
|
||||
libstream_filter_rar_plugin.la \
|
||||
$(NULL)
|
||||
if !HAVE_WIN32
|
||||
if !HAVE_WINCE
|
||||
|
@ -1,608 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* rar.c: uncompressed RAR stream filter (only the biggest file is extracted)
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2008 Laurent Aimar
|
||||
* $Id$
|
||||
*
|
||||
* Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
|
||||
*
|
||||
* 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
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
* Preamble
|
||||
*****************************************************************************/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <vlc_common.h>
|
||||
#include <vlc_plugin.h>
|
||||
#include <vlc_stream.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
/*****************************************************************************
|
||||
* Module descriptor
|
||||
*****************************************************************************/
|
||||
static int Open ( vlc_object_t * );
|
||||
static void Close( vlc_object_t * );
|
||||
|
||||
vlc_module_begin()
|
||||
set_category( CAT_INPUT )
|
||||
set_subcategory( SUBCAT_INPUT_STREAM_FILTER )
|
||||
set_description( N_("Uncompressed RAR") )
|
||||
set_capability( "stream_filter", 1 )
|
||||
set_callbacks( Open, Close )
|
||||
add_shortcut( "rar" )
|
||||
vlc_module_end()
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
*****************************************************************************/
|
||||
static const uint8_t p_rar_marker[] = {
|
||||
0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00
|
||||
};
|
||||
static const int i_rar_marker = sizeof(p_rar_marker);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint64_t i_offset;
|
||||
uint64_t i_size;
|
||||
uint64_t i_cummulated_size;
|
||||
} rar_file_chunk_t;
|
||||
typedef struct
|
||||
{
|
||||
char *psz_name;
|
||||
uint64_t i_size;
|
||||
bool b_complete;
|
||||
|
||||
int i_chunk;
|
||||
rar_file_chunk_t **pp_chunk;
|
||||
uint64_t i_real_size; /* Gathered size */
|
||||
} rar_file_t;
|
||||
|
||||
static void RarFileDelete( rar_file_t * );
|
||||
static int RarParse( stream_t *, int *pi_count, rar_file_t ***ppp_file );
|
||||
|
||||
struct stream_sys_t
|
||||
{
|
||||
rar_file_t *p_file;
|
||||
const rar_file_chunk_t *p_chunk;
|
||||
|
||||
uint64_t i_position;
|
||||
|
||||
uint8_t *p_peek_alloc;
|
||||
uint8_t *p_peek;
|
||||
unsigned int i_peek;
|
||||
};
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* Local prototypes
|
||||
****************************************************************************/
|
||||
static int Read ( stream_t *, void *p_read, unsigned int i_read );
|
||||
static int Peek ( stream_t *, const uint8_t **pp_peek, unsigned int i_peek );
|
||||
static int Control( stream_t *, int i_query, va_list );
|
||||
static int Seek ( stream_t *s, uint64_t i_position );
|
||||
|
||||
/****************************************************************************
|
||||
* Open
|
||||
****************************************************************************/
|
||||
static int Open ( vlc_object_t *p_this )
|
||||
{
|
||||
stream_t *s = (stream_t*)p_this;
|
||||
stream_sys_t *p_sys;
|
||||
|
||||
/* */
|
||||
const uint8_t *p_peek;
|
||||
if( stream_Peek( s->p_source, &p_peek, i_rar_marker ) < i_rar_marker )
|
||||
return VLC_EGENERIC;
|
||||
if( memcmp( p_peek, p_rar_marker, i_rar_marker ) )
|
||||
return VLC_EGENERIC;
|
||||
|
||||
/* */
|
||||
s->pf_read = Read;
|
||||
s->pf_peek = Peek;
|
||||
s->pf_control = Control;
|
||||
|
||||
s->p_sys = p_sys = malloc( sizeof( *p_sys ) );
|
||||
if( !p_sys )
|
||||
return VLC_ENOMEM;
|
||||
|
||||
/* */
|
||||
p_sys->p_file = NULL;
|
||||
p_sys->i_position = 0;
|
||||
p_sys->p_chunk = NULL;
|
||||
|
||||
p_sys->p_peek_alloc = NULL;
|
||||
p_sys->p_peek = NULL;
|
||||
p_sys->i_peek = 0;
|
||||
|
||||
/* */
|
||||
int i_count;
|
||||
rar_file_t **pp_file;
|
||||
if( RarParse( s, &i_count, &pp_file ) )
|
||||
{
|
||||
i_count = 0;
|
||||
pp_file = NULL;
|
||||
}
|
||||
|
||||
/* Select the longest file */
|
||||
p_sys->p_file = NULL;
|
||||
for( int i = 0; i < i_count; i++ )
|
||||
{
|
||||
if( !p_sys->p_file || p_sys->p_file->i_size < pp_file[i]->i_size )
|
||||
p_sys->p_file = pp_file[i];
|
||||
}
|
||||
for( int i = 0; i < i_count; i++ )
|
||||
{
|
||||
if( pp_file[i] != p_sys->p_file )
|
||||
RarFileDelete( pp_file[i] );
|
||||
}
|
||||
free( pp_file );
|
||||
|
||||
if( !p_sys->p_file || p_sys->p_file->i_chunk <= 0 || p_sys->p_file->i_size <= 0 )
|
||||
{
|
||||
msg_Err( s, "Invalid or unsupported RAR archive" );
|
||||
if( p_sys->p_file )
|
||||
RarFileDelete( p_sys->p_file );
|
||||
free( p_sys );
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
|
||||
/* */
|
||||
Seek( s, 0 );
|
||||
|
||||
/* */
|
||||
const rar_file_t *p_file = p_sys->p_file;
|
||||
msg_Dbg( s, "Using RAR stream filter for '%s' %"PRId64"(expected %"PRId64") bytes in %d chunks",
|
||||
p_file->psz_name, p_file->i_real_size, p_file->i_size, p_file->i_chunk );
|
||||
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Close
|
||||
****************************************************************************/
|
||||
static void Close( vlc_object_t *p_this )
|
||||
{
|
||||
stream_t *s = (stream_t*)p_this;
|
||||
stream_sys_t *p_sys = s->p_sys;
|
||||
|
||||
RarFileDelete( p_sys->p_file );
|
||||
free( p_sys->p_peek_alloc );
|
||||
free( p_sys );
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Stream filters functions
|
||||
****************************************************************************/
|
||||
static int Read( stream_t *s, void *p_read, unsigned int i_read )
|
||||
{
|
||||
stream_sys_t *p_sys = s->p_sys;
|
||||
uint8_t *p_data = p_read;
|
||||
unsigned int i_total = 0;
|
||||
|
||||
if( p_sys->i_peek > 0 && i_read > 0 )
|
||||
{
|
||||
const unsigned int i_copy = __MIN( i_read, p_sys->i_peek );
|
||||
|
||||
if( p_data )
|
||||
{
|
||||
memcpy( p_data, p_sys->p_peek, i_copy );
|
||||
p_data += i_copy;
|
||||
}
|
||||
|
||||
p_sys->i_peek -= i_copy;
|
||||
p_sys->p_peek += i_copy;
|
||||
i_total += i_copy;
|
||||
}
|
||||
|
||||
while( i_total < i_read )
|
||||
{
|
||||
const uint64_t i_chunk_end = p_sys->p_chunk->i_cummulated_size + p_sys->p_chunk->i_size;
|
||||
|
||||
int i_max = __MIN( i_read - i_total, i_chunk_end - p_sys->i_position );
|
||||
if( i_max <= 0 )
|
||||
break;
|
||||
|
||||
int i_real = stream_Read( s->p_source, p_data, i_max );
|
||||
if( i_real <= 0 )
|
||||
break;
|
||||
|
||||
i_total += i_real;
|
||||
if( p_data )
|
||||
p_data += i_real;
|
||||
p_sys->i_position += i_real;
|
||||
if( p_sys->i_position >= i_chunk_end )
|
||||
{
|
||||
if( Seek( s, p_sys->i_position ) )
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i_total;
|
||||
}
|
||||
|
||||
static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned int i_peek )
|
||||
{
|
||||
stream_sys_t *p_sys = s->p_sys;
|
||||
|
||||
if( i_peek <= p_sys->i_peek )
|
||||
{
|
||||
*pp_peek = p_sys->p_peek;
|
||||
return i_peek;
|
||||
}
|
||||
|
||||
/* */
|
||||
uint8_t *p_peek = malloc( i_peek );
|
||||
if( !p_peek )
|
||||
return 0;
|
||||
|
||||
/* XXX yes stream_Read on ourself */
|
||||
int i_read = stream_Read( s, p_peek, i_peek );
|
||||
if( i_read <= 0 )
|
||||
{
|
||||
free( p_peek );
|
||||
return i_read;
|
||||
}
|
||||
|
||||
free( p_sys->p_peek_alloc );
|
||||
|
||||
p_sys->p_peek_alloc =
|
||||
p_sys->p_peek = p_peek;
|
||||
p_sys->i_peek = i_read;
|
||||
|
||||
*pp_peek = p_sys->p_peek;
|
||||
return p_sys->i_peek;
|
||||
}
|
||||
|
||||
static int Control( stream_t *s, int i_query, va_list args )
|
||||
{
|
||||
stream_sys_t *p_sys = s->p_sys;
|
||||
|
||||
switch( i_query )
|
||||
{
|
||||
/* */
|
||||
case STREAM_SET_POSITION:
|
||||
{
|
||||
uint64_t i_position = va_arg( args, uint64_t );
|
||||
return Seek( s, i_position );
|
||||
}
|
||||
|
||||
case STREAM_GET_POSITION:
|
||||
{
|
||||
uint64_t *pi_position = va_arg( args, uint64_t* );
|
||||
*pi_position = p_sys->i_position - p_sys->i_peek;
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
case STREAM_GET_SIZE:
|
||||
{
|
||||
uint64_t *pi_size = (uint64_t*)va_arg( args, uint64_t* );
|
||||
*pi_size = p_sys->p_file->i_real_size;
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
/* */
|
||||
case STREAM_GET_CONTENT_TYPE: /* arg1= char ** */
|
||||
return VLC_EGENERIC;
|
||||
|
||||
case STREAM_UPDATE_SIZE: /* TODO maybe we should update i_real_size from file size and chunk offset ? */
|
||||
case STREAM_CONTROL_ACCESS:
|
||||
case STREAM_CAN_SEEK:
|
||||
case STREAM_CAN_FASTSEEK:
|
||||
case STREAM_SET_RECORD_STATE:
|
||||
return stream_vaControl( s->p_source, i_query, args );
|
||||
default:
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Helpers
|
||||
****************************************************************************/
|
||||
static int Seek( stream_t *s, uint64_t i_position )
|
||||
{
|
||||
stream_sys_t *p_sys = s->p_sys;
|
||||
|
||||
if( i_position > p_sys->p_file->i_real_size )
|
||||
i_position = p_sys->p_file->i_real_size;
|
||||
|
||||
/* Search the chunk */
|
||||
const rar_file_t *p_file = p_sys->p_file;
|
||||
for( int i = 0; i < p_file->i_chunk; i++ )
|
||||
{
|
||||
p_sys->p_chunk = p_file->pp_chunk[i];
|
||||
if( i_position < p_sys->p_chunk->i_cummulated_size + p_sys->p_chunk->i_size )
|
||||
break;
|
||||
}
|
||||
p_sys->i_position = i_position;
|
||||
p_sys->i_peek = 0;
|
||||
|
||||
const uint64_t i_seek = p_sys->p_chunk->i_offset +
|
||||
( i_position - p_sys->p_chunk->i_cummulated_size );
|
||||
return stream_Seek( s->p_source, i_seek );
|
||||
}
|
||||
|
||||
/* Rar parser */
|
||||
static void RarFileDelete( rar_file_t *p_file )
|
||||
{
|
||||
for( int i = 0; i < p_file->i_chunk; i++ )
|
||||
free( p_file->pp_chunk[i] );
|
||||
free( p_file->pp_chunk );
|
||||
free( p_file->psz_name );
|
||||
free( p_file );
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t i_crc;
|
||||
uint8_t i_type;
|
||||
uint16_t i_flags;
|
||||
uint16_t i_size;
|
||||
uint32_t i_add_size;
|
||||
} rar_block_t;
|
||||
|
||||
enum
|
||||
{
|
||||
RAR_BLOCK_MARKER = 0x72,
|
||||
RAR_BLOCK_ARCHIVE = 0x73,
|
||||
RAR_BLOCK_FILE = 0x74,
|
||||
RAR_BLOCK_END = 0x7b,
|
||||
};
|
||||
enum
|
||||
{
|
||||
RAR_BLOCK_END_HAS_NEXT = 0x0001,
|
||||
};
|
||||
enum
|
||||
{
|
||||
RAR_BLOCK_FILE_HAS_PREVIOUS = 0x0001,
|
||||
RAR_BLOCK_FILE_HAS_NEXT = 0x0002,
|
||||
RAR_BLOCK_FILE_HAS_HIGH = 0x0100,
|
||||
};
|
||||
|
||||
static int PeekBlock( stream_t *s, rar_block_t *p_hdr )
|
||||
{
|
||||
const uint8_t *p_peek;
|
||||
int i_peek = stream_Peek( s->p_source, &p_peek, 11 );
|
||||
|
||||
if( i_peek < 7 )
|
||||
return VLC_EGENERIC;
|
||||
|
||||
p_hdr->i_crc = GetWLE( &p_peek[0] );
|
||||
p_hdr->i_type = p_peek[2];
|
||||
p_hdr->i_flags = GetWLE( &p_peek[3] );
|
||||
p_hdr->i_size = GetWLE( &p_peek[5] );
|
||||
p_hdr->i_add_size = 0;
|
||||
if( p_hdr->i_flags & 0x8000 )
|
||||
{
|
||||
if( i_peek < 11 )
|
||||
return VLC_EGENERIC;
|
||||
p_hdr->i_add_size = GetDWLE( &p_peek[7] );
|
||||
}
|
||||
|
||||
if( p_hdr->i_size < 7 )
|
||||
return VLC_EGENERIC;
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
static int SkipBlock( stream_t *s, const rar_block_t *p_hdr )
|
||||
{
|
||||
uint64_t i_size = (uint64_t)p_hdr->i_size + p_hdr->i_add_size;
|
||||
|
||||
while( i_size > 0 )
|
||||
{
|
||||
int i_skip = __MIN( i_size, INT_MAX );
|
||||
if( stream_Read( s->p_source, NULL, i_skip ) < i_skip )
|
||||
return VLC_EGENERIC;
|
||||
|
||||
i_size -= i_skip;
|
||||
}
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
static int IgnoreBlock( stream_t *s, int i_block )
|
||||
{
|
||||
/* */
|
||||
rar_block_t bk;
|
||||
if( PeekBlock( s, &bk ) || bk.i_type != i_block )
|
||||
return VLC_EGENERIC;
|
||||
return SkipBlock( s, &bk );
|
||||
}
|
||||
|
||||
static int SkipEnd( stream_t *s, const rar_block_t *p_hdr )
|
||||
{
|
||||
if( !(p_hdr->i_flags & RAR_BLOCK_END_HAS_NEXT) )
|
||||
return VLC_EGENERIC;
|
||||
|
||||
if( SkipBlock( s, p_hdr ) )
|
||||
return VLC_EGENERIC;
|
||||
|
||||
/* Now, we need to look for a marker block,
|
||||
* It seems that there is garbage at EOF */
|
||||
for( ;; )
|
||||
{
|
||||
const uint8_t *p_peek;
|
||||
|
||||
if( stream_Peek( s->p_source, &p_peek, i_rar_marker ) < i_rar_marker )
|
||||
return VLC_EGENERIC;
|
||||
|
||||
if( !memcmp( p_peek, p_rar_marker, i_rar_marker ) )
|
||||
break;
|
||||
|
||||
if( stream_Read( s->p_source, NULL, 1 ) != 1 )
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
|
||||
/* Skip marker and archive blocks */
|
||||
if( IgnoreBlock( s, RAR_BLOCK_MARKER ) )
|
||||
return VLC_EGENERIC;
|
||||
if( IgnoreBlock( s, RAR_BLOCK_ARCHIVE ) )
|
||||
return VLC_EGENERIC;
|
||||
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
static int SkipFile( stream_t *s, int *pi_count, rar_file_t ***ppp_file, const rar_block_t *p_hdr )
|
||||
{
|
||||
const uint8_t *p_peek;
|
||||
|
||||
int i_min_size = 7+21;
|
||||
if( p_hdr->i_flags & RAR_BLOCK_FILE_HAS_HIGH )
|
||||
i_min_size += 8;
|
||||
if( p_hdr->i_size < (unsigned)i_min_size )
|
||||
return VLC_EGENERIC;
|
||||
|
||||
if( stream_Peek( s->p_source, &p_peek, i_min_size ) < i_min_size )
|
||||
return VLC_EGENERIC;
|
||||
|
||||
/* */
|
||||
uint32_t i_file_size_low = GetDWLE( &p_peek[7+4] );
|
||||
uint8_t i_method = p_peek[7+18];
|
||||
uint16_t i_name_size = GetWLE( &p_peek[7+19] );
|
||||
uint32_t i_file_size_high = 0;
|
||||
if( p_hdr->i_flags & RAR_BLOCK_FILE_HAS_HIGH )
|
||||
i_file_size_high = GetDWLE( &p_peek[7+25] );
|
||||
const uint64_t i_file_size = ((uint64_t)i_file_size_high << 32) | i_file_size_low;
|
||||
|
||||
char *psz_name = calloc( 1, i_name_size + 1 );
|
||||
if( !psz_name )
|
||||
return VLC_EGENERIC;
|
||||
|
||||
const int i_name_offset = (p_hdr->i_flags & RAR_BLOCK_FILE_HAS_HIGH) ? (7+33) : (7+25);
|
||||
if( i_name_offset + i_name_size <= p_hdr->i_size )
|
||||
{
|
||||
const int i_max_size = i_name_offset + i_name_size;
|
||||
if( stream_Peek( s->p_source, &p_peek, i_max_size ) < i_max_size )
|
||||
{
|
||||
free( psz_name );
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
memcpy( psz_name, &p_peek[i_name_offset], i_name_size );
|
||||
}
|
||||
|
||||
if( i_method != 0x30 )
|
||||
{
|
||||
msg_Warn( s, "Ignoring compressed file %s (method=0x%2.2x)", psz_name, i_method );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* */
|
||||
rar_file_t *p_current = *pi_count > 0 ? (*ppp_file)[*pi_count - 1] : NULL;
|
||||
if( p_current &&
|
||||
( p_current->b_complete ||
|
||||
p_current->i_size != i_file_size ||
|
||||
strcmp( p_current->psz_name, psz_name ) ||
|
||||
( p_hdr->i_flags & RAR_BLOCK_FILE_HAS_PREVIOUS ) == 0 ) )
|
||||
p_current = NULL;
|
||||
|
||||
if( !p_current )
|
||||
{
|
||||
p_current = malloc( sizeof( *p_current ) );
|
||||
if( !p_current )
|
||||
goto exit;
|
||||
TAB_APPEND( *pi_count, *ppp_file, p_current );
|
||||
|
||||
p_current->psz_name = psz_name;
|
||||
p_current->i_size = i_file_size;
|
||||
p_current->b_complete = false;
|
||||
p_current->i_real_size = 0;
|
||||
TAB_INIT( p_current->i_chunk, p_current->pp_chunk );
|
||||
|
||||
psz_name = NULL;
|
||||
}
|
||||
|
||||
/* Append chunks */
|
||||
rar_file_chunk_t *p_chunk = malloc( sizeof( *p_chunk ) );
|
||||
if( p_chunk )
|
||||
{
|
||||
p_chunk->i_offset = stream_Tell( s->p_source ) + p_hdr->i_size;
|
||||
p_chunk->i_size = p_hdr->i_add_size;
|
||||
p_chunk->i_cummulated_size = 0;
|
||||
if( p_current->i_chunk > 0 )
|
||||
{
|
||||
rar_file_chunk_t *p_previous = p_current->pp_chunk[p_current->i_chunk-1];
|
||||
|
||||
p_chunk->i_cummulated_size += p_previous->i_cummulated_size +
|
||||
p_previous->i_size;
|
||||
}
|
||||
|
||||
TAB_APPEND( p_current->i_chunk, p_current->pp_chunk, p_chunk );
|
||||
|
||||
p_current->i_real_size += p_hdr->i_add_size;
|
||||
}
|
||||
if( ( p_hdr->i_flags & RAR_BLOCK_FILE_HAS_NEXT ) == 0 )
|
||||
p_current->b_complete = true;
|
||||
|
||||
exit:
|
||||
/* */
|
||||
free( psz_name );
|
||||
|
||||
/* We stop on the first non empty file if we cannot seek */
|
||||
if( p_current )
|
||||
{
|
||||
bool b_can_seek = false;
|
||||
stream_Control( s->p_source, STREAM_CAN_SEEK, &b_can_seek );
|
||||
if( !b_can_seek && p_current->i_size > 0 )
|
||||
return VLC_EGENERIC;
|
||||
}
|
||||
|
||||
if( SkipBlock( s, p_hdr ) )
|
||||
return VLC_EGENERIC;
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
static int RarParse( stream_t *s, int *pi_count, rar_file_t ***ppp_file )
|
||||
{
|
||||
*pi_count = 0;
|
||||
*ppp_file = NULL;
|
||||
|
||||
/* Skip marker */
|
||||
if( IgnoreBlock( s, RAR_BLOCK_MARKER ) )
|
||||
return VLC_EGENERIC;
|
||||
|
||||
/* Skip archive */
|
||||
if( IgnoreBlock( s, RAR_BLOCK_ARCHIVE ) )
|
||||
return VLC_EGENERIC;
|
||||
|
||||
/* */
|
||||
for( ;; )
|
||||
{
|
||||
rar_block_t bk;
|
||||
int i_ret;
|
||||
|
||||
if( PeekBlock( s, &bk ) )
|
||||
break;
|
||||
|
||||
switch( bk.i_type )
|
||||
{
|
||||
case RAR_BLOCK_END:
|
||||
i_ret = SkipEnd( s, &bk );
|
||||
break;
|
||||
case RAR_BLOCK_FILE:
|
||||
i_ret = SkipFile( s, pi_count, ppp_file, &bk );
|
||||
break;
|
||||
default:
|
||||
i_ret = SkipBlock( s, &bk );
|
||||
break;
|
||||
}
|
||||
if( i_ret )
|
||||
break;
|
||||
}
|
||||
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user