diff options
Diffstat (limited to 'stream/stream_rar.c')
-rw-r--r-- | stream/stream_rar.c | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/stream/stream_rar.c b/stream/stream_rar.c new file mode 100644 index 0000000000..d3ccfa3bf3 --- /dev/null +++ b/stream/stream_rar.c @@ -0,0 +1,198 @@ +// Major parts based on: +/***************************************************************************** + * access.c: uncompressed RAR access + ***************************************************************************** + * Copyright (C) 2008-2010 Laurent Aimar + * $Id: dcd973529e0029abe326d31f8d58cd13bbcc276c $ + * + * 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. + *****************************************************************************/ + +#include "config.h" + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#include "osdep/io.h" + +#include "mpvcore/mp_msg.h" +#include "stream.h" +#include "mpvcore/m_option.h" +#include "rar.h" + +/* +This works as follows: + +- stream_open() with file01.rar + - is opened as normal file (stream_file.c or others) first + - stream_info_rar_filter stream filter applies + - leads to rar_filter_open() + - if multi-part, opens file02.rar, file03.rar, etc. as actual streams + (recursive opening is prevented with the STREAM_NO_FILTERS flag) + - read accesses return a m3u playlist with entries like: + rar://bla01.rar|subfile.mkv + (one such entry for each file contained in the rar) +- stream_open() with the playlist entry, e.g. rar://bla01.rar|subfile.mkv + - leads to rar_entry_open() + - opens bla01.rar etc. again as actual streams + (again, STREAM_NO_FILTERS to open the actual files) + - read accesses go into subfile.mkv contained in the rar file(s) +*/ + +static int rar_entry_fill_buffer(stream_t *s, char *buffer, int max_len) +{ + rar_file_t *rar_file = s->priv; + return RarRead(rar_file, buffer, max_len); +} + +static int rar_entry_seek(stream_t *s, int64_t newpos) +{ + rar_file_t *rar_file = s->priv; + return RarSeek(rar_file, newpos); +} + +static void rar_entry_close(stream_t *s) +{ + rar_file_t *rar_file = s->priv; + RarFileDelete(rar_file); +} + +static int rar_entry_open(stream_t *stream, int mode) +{ + if (!strchr(stream->path, '|')) + return STREAM_ERROR; + + char *base = talloc_strdup(stream, stream->path); + char *name = strchr(base, '|'); + *name++ = '\0'; + mp_url_unescape_inplace(base); + + struct stream *rar = + stream_create(base, STREAM_READ | STREAM_NO_FILTERS, stream->opts); + if (!rar) + return STREAM_ERROR; + + int count; + rar_file_t **files; + if (RarProbe(rar) || RarParse(rar, &count, &files)) { + free_stream(rar); + return STREAM_ERROR; + } + + rar_file_t *file = NULL; + for (int i = 0; i < count; i++) { + if (!file && strcmp(files[i]->name, name) == 0) + file = files[i]; + else + RarFileDelete(files[i]); + } + talloc_free(files); + if (!file) { + free_stream(rar); + return STREAM_ERROR; + } + + rar_file_chunk_t dummy = { + .mrl = base, + }; + file->current_chunk = &dummy; + file->s = rar; // transfer ownership + file->opts = stream->opts; + RarSeek(file, 0); + + stream->priv = file; + stream->end_pos = file->size; + stream->fill_buffer = rar_entry_fill_buffer; + stream->seek = rar_entry_seek; + stream->close = rar_entry_close; + + return STREAM_OK; +} + +static int rar_filter_fill_buffer(stream_t *s, char *buffer, int max_len) +{ + struct stream *m = s->priv; + return stream_read_partial(m, buffer, max_len); +} + +static int rar_filter_seek(stream_t *s, int64_t newpos) +{ + struct stream *m = s->priv; + return stream_seek(m, newpos); +} + +static void rar_filter_close(stream_t *s) +{ + struct stream *m = s->priv; + free_stream(m); +} + +static int rar_filter_open(stream_t *stream, int mode) +{ + if (mode != STREAM_READ) + return STREAM_UNSUPPORTED; + + struct stream *rar = stream->source; + if (!rar) + return STREAM_UNSUPPORTED; + + int count; + rar_file_t **files; + if (!rar || RarProbe(rar) || RarParse(rar, &count, &files)) + return STREAM_UNSUPPORTED; + + void *tmp = talloc_new(NULL); + + // Create a playlist containing all entries of the .rar file. The URLs + // link to rar_entry_open(). + char *prefix = mp_url_escape(tmp, stream->url, "~|"); + char *pl = talloc_strdup(tmp, "#EXTM3U\n"); + for (int n = 0; n < count; n++) { + pl = talloc_asprintf_append_buffer(pl, "rar://%s|%s\n", + prefix, files[n]->name); + RarFileDelete(files[n]); + } + talloc_free(files); + + struct stream *m = open_memory_stream(pl, strlen(pl)); + + stream->priv = m; + stream->end_pos = m->end_pos; + stream->fill_buffer = rar_filter_fill_buffer; + stream->seek = rar_filter_seek; + stream->close = rar_filter_close; + stream->safe_origin = true; + + talloc_free(tmp); + return STREAM_OK; +} + +const stream_info_t stream_info_rar_entry = { + .name = "rar_entry", + .open = rar_entry_open, + .protocols = (const char*[]){ "rar", NULL }, +}; + +const stream_info_t stream_info_rar_filter = { + .name = "rar_filter", + .open = rar_filter_open, + .stream_filter = true, +}; |