You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
249 lines
7.1 KiB
249 lines
7.1 KiB
/*****************************************************************************
|
|
* access.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 Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser 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.
|
|
*****************************************************************************/
|
|
|
|
#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"
|
|
|
|
struct access_sys_t {
|
|
stream_t *s;
|
|
rar_file_t *file;
|
|
const rar_file_chunk_t *chunk;
|
|
uint64_t position;
|
|
};
|
|
|
|
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;
|
|
sys->position = position;
|
|
|
|
/* Search the chunk */
|
|
const rar_file_chunk_t *old_chunk = sys->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;
|
|
}
|
|
|
|
const uint64_t offset = sys->chunk->offset +
|
|
(position - sys->chunk->cummulated_size);
|
|
|
|
if (strcmp(old_chunk->mrl, sys->chunk->mrl)) {
|
|
if (sys->s)
|
|
stream_Delete(sys->s);
|
|
sys->s = stream_UrlNew(access, sys->chunk->mrl);
|
|
}
|
|
return sys->s ? stream_Seek(sys->s, offset) : VLC_EGENERIC;
|
|
}
|
|
|
|
static ssize_t Read(access_t *access, void *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 - sys->position)), INT_MAX);
|
|
if (max <= 0)
|
|
break;
|
|
|
|
int r = sys->s ? stream_Read(sys->s, data, max) : -1;
|
|
if (r <= 0)
|
|
break;
|
|
|
|
total += r;
|
|
if( data )
|
|
data = ((char *)data) + r;
|
|
sys->position += r;
|
|
if (sys->position >= chunk_end &&
|
|
Seek(access, sys->position))
|
|
break;
|
|
}
|
|
return total;
|
|
|
|
}
|
|
|
|
static int Control(access_t *access, int query, va_list args)
|
|
{
|
|
access_sys_t *sys = access->p_sys;
|
|
stream_t *s = sys->s;
|
|
if (!s)
|
|
return VLC_EGENERIC;
|
|
|
|
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_SIZE:
|
|
*va_arg(args, uint64_t *) = sys->file->size;
|
|
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;
|
|
}
|
|
}
|
|
|
|
int RarAccessOpen(vlc_object_t *object)
|
|
{
|
|
access_t *access = (access_t*)object;
|
|
|
|
const char *name = strchr(access->psz_location, '|');
|
|
if (name == NULL)
|
|
return VLC_EGENERIC;
|
|
|
|
char *base = strndup(access->psz_location, name - access->psz_location);
|
|
if (unlikely(base == NULL))
|
|
return VLC_ENOMEM;
|
|
|
|
name++;
|
|
vlc_uri_decode(base);
|
|
|
|
stream_t *s = stream_UrlNew(access, base);
|
|
if (!s || RarProbe(s))
|
|
goto error;
|
|
|
|
struct
|
|
{
|
|
int filescount;
|
|
rar_file_t **files;
|
|
unsigned int i_nbvols;
|
|
} newscheme = { 0, NULL, 0 }, oldscheme = { 0, NULL, 0 }, *p_scheme;
|
|
|
|
if (RarParse(s, &newscheme.filescount, &newscheme.files, &newscheme.i_nbvols, false)
|
|
|| newscheme.filescount < 1 || newscheme.i_nbvols < 2 )
|
|
{
|
|
/* We might want to lookup old naming scheme, could be a part1.rar,part1.r00 */
|
|
stream_Seek(s, 0);
|
|
RarParse(s, &oldscheme.filescount, &oldscheme.files, &oldscheme.i_nbvols, true);
|
|
}
|
|
|
|
if (oldscheme.filescount >= newscheme.filescount && oldscheme.i_nbvols > newscheme.i_nbvols)
|
|
{
|
|
for (int i = 0; i < newscheme.filescount; i++)
|
|
RarFileDelete(newscheme.files[i]);
|
|
free(newscheme.files);
|
|
p_scheme = &oldscheme;
|
|
msg_Dbg(s, "using rar old naming for %d files nbvols %u", p_scheme->filescount, oldscheme.i_nbvols);
|
|
}
|
|
else if (newscheme.filescount)
|
|
{
|
|
for (int i = 0; i < oldscheme.filescount; i++)
|
|
RarFileDelete(oldscheme.files[i]);
|
|
free(oldscheme.files);
|
|
p_scheme = &newscheme;
|
|
msg_Dbg(s, "using rar new naming for %d files nbvols %u", p_scheme->filescount, oldscheme.i_nbvols);
|
|
}
|
|
else
|
|
{
|
|
msg_Info(s, "Invalid or unsupported RAR archive");
|
|
for (int i = 0; i < oldscheme.filescount; i++)
|
|
RarFileDelete(oldscheme.files[i]);
|
|
free(oldscheme.files);
|
|
for (int i = 0; i < newscheme.filescount; i++)
|
|
RarFileDelete(newscheme.files[i]);
|
|
free(newscheme.files);
|
|
goto error;
|
|
}
|
|
|
|
rar_file_t *file = NULL;
|
|
for (int i = 0; i < p_scheme->filescount; i++) {
|
|
if (!file && !strcmp(p_scheme->files[i]->name, name))
|
|
file = p_scheme->files[i];
|
|
else
|
|
RarFileDelete(p_scheme->files[i]);
|
|
}
|
|
free(p_scheme->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);
|
|
|
|
rar_file_chunk_t dummy = {
|
|
.mrl = base,
|
|
};
|
|
sys->chunk = &dummy;
|
|
Seek(access, 0);
|
|
|
|
free(base);
|
|
return VLC_SUCCESS;
|
|
|
|
error:
|
|
if (s)
|
|
stream_Delete(s);
|
|
free(base);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
void RarAccessClose(vlc_object_t *object)
|
|
{
|
|
access_t *access = (access_t*)object;
|
|
access_sys_t *sys = access->p_sys;
|
|
|
|
if (sys->s)
|
|
stream_Delete(sys->s);
|
|
RarFileDelete(sys->file);
|
|
free(sys);
|
|
}
|
|
|