summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--stream/stream_libarchive.c170
-rw-r--r--stream/stream_libarchive.h2
2 files changed, 156 insertions, 16 deletions
diff --git a/stream/stream_libarchive.c b/stream/stream_libarchive.c
index 21e1111671..7aa7feb073 100644
--- a/stream/stream_libarchive.c
+++ b/stream/stream_libarchive.c
@@ -18,31 +18,39 @@
#include <archive.h>
#include <archive_entry.h>
+#include "misc/bstr.h"
#include "common/common.h"
#include "stream.h"
#include "stream_libarchive.h"
+struct mp_archive_volume {
+ struct mp_archive *mpa;
+ struct stream *src;
+ char *url;
+};
+
static ssize_t read_cb(struct archive *arch, void *priv, const void **buffer)
{
- struct mp_archive *mpa = priv;
- int res = stream_read_partial(mpa->src, mpa->buffer, sizeof(mpa->buffer));
- *buffer = mpa->buffer;
+ struct mp_archive_volume *vol = priv;
+ int res = stream_read_partial(vol->src, vol->mpa->buffer,
+ sizeof(vol->mpa->buffer));
+ *buffer = vol->mpa->buffer;
return MPMAX(res, 0);
}
static int64_t seek_cb(struct archive *arch, void *priv,
int64_t offset, int whence)
{
- struct mp_archive *mpa = priv;
+ struct mp_archive_volume *vol = priv;
switch (whence) {
case SEEK_SET:
break;
case SEEK_CUR:
- offset += mpa->src->pos;
+ offset += vol->src->pos;
break;
case SEEK_END: ;
- int64_t size = stream_get_size(mpa->src);
+ int64_t size = stream_get_size(vol->src);
if (size < 0)
return -1;
offset += size;
@@ -50,15 +58,53 @@ static int64_t seek_cb(struct archive *arch, void *priv,
default:
return -1;
}
- return stream_seek(mpa->src, offset) ? offset : -1;
+ return stream_seek(vol->src, offset) ? offset : -1;
}
static int64_t skip_cb(struct archive *arch, void *priv, int64_t request)
{
- struct mp_archive *mpa = priv;
- int64_t old = stream_tell(mpa->src);
- stream_skip(mpa->src, request);
- return stream_tell(mpa->src) - old;
+ struct mp_archive_volume *vol = priv;
+ int64_t old = stream_tell(vol->src);
+ stream_skip(vol->src, request);
+ return stream_tell(vol->src) - old;
+}
+
+static int open_cb(struct archive *arch, void *priv)
+{
+ struct mp_archive_volume *vol = priv;
+ if (!vol->src) {
+ vol->src = stream_create(vol->url, STREAM_READ,
+ vol->mpa->primary_src->cancel,
+ vol->mpa->primary_src->global);
+ return vol->src ? ARCHIVE_OK : ARCHIVE_FATAL;
+ }
+
+ // just rewind the primary stream
+ return stream_seek(vol->src, 0) ? ARCHIVE_OK : ARCHIVE_FATAL;
+}
+
+static void volume_close(struct mp_archive_volume *vol)
+{
+ // don't close the primary stream
+ if (vol->src && vol->src != vol->mpa->primary_src) {
+ free_stream(vol->src);
+ vol->src = NULL;
+ }
+}
+
+static int close_cb(struct archive *arch, void *priv)
+{
+ struct mp_archive_volume *vol = priv;
+ volume_close(vol);
+ talloc_free(vol);
+ return ARCHIVE_OK;
+}
+
+static int switch_cb(struct archive *arch, void *oldpriv, void *newpriv)
+{
+ struct mp_archive_volume *oldvol = oldpriv;
+ volume_close(oldvol);
+ return open_cb(arch, newpriv);
}
void mp_archive_free(struct mp_archive *mpa)
@@ -70,16 +116,108 @@ void mp_archive_free(struct mp_archive *mpa)
talloc_free(mpa);
}
+static char *standard_volume_url(void *ctx, const char *format,
+ struct bstr base, int index)
+{
+ return talloc_asprintf(ctx, format, BSTR_P(base), index);
+}
+
+static char *old_rar_volume_url(void *ctx, const char *format,
+ struct bstr base, int index)
+{
+ return talloc_asprintf(ctx, format, BSTR_P(base),
+ 'r' + index / 100, index % 100);
+}
+
+struct file_pattern {
+ const char *match;
+ const char *format;
+ char *(*volume_url)(void *ctx, const char *format,
+ struct bstr base, int index);
+ int start;
+ int stop;
+};
+
+static const struct file_pattern patterns[] = {
+ { ".part1.rar", "%.*s.part%.1d.rar", standard_volume_url, 2, 9 },
+ { ".part01.rar", "%.*s.part%.2d.rar", standard_volume_url, 2, 99 },
+ { ".part001.rar", "%.*s.part%.3d.rar", standard_volume_url, 2, 999 },
+ { ".part0001.rar", "%.*s.part%.4d.rar", standard_volume_url, 2, 9999 },
+ { ".rar", "%.*s.%c%.2d", old_rar_volume_url, 0, 9999 },
+ { ".001", "%.*s.%.3d", standard_volume_url, 2, 9999 },
+ { NULL, NULL, NULL, 0, 0 },
+};
+
+static char **find_volumes(struct stream *primary_stream)
+{
+ char **res = talloc_new(NULL);
+ int num = 0;
+ struct bstr primary_url = bstr0(primary_stream->url);
+
+ const struct file_pattern *pattern = patterns;
+ while (pattern->match) {
+ if (bstr_endswith0(primary_url, pattern->match))
+ break;
+ pattern++;
+ }
+
+ if (!pattern->match)
+ goto done;
+
+ struct bstr base = bstr_splice(primary_url, 0, -strlen(pattern->match));
+ for (int i = pattern->start; i <= pattern->stop; i++) {
+ char* url = pattern->volume_url(res, pattern->format, base, i);
+ struct stream *s = stream_create(url, STREAM_READ | STREAM_SAFE_ONLY,
+ primary_stream->cancel,
+ primary_stream->global);
+ if (!s) {
+ talloc_free(url);
+ goto done;
+ }
+ free_stream(s);
+ MP_TARRAY_APPEND(res, res, num, url);
+ }
+
+done:
+ MP_TARRAY_APPEND(res, res, num, NULL);
+ return res;
+}
+
+
+static bool add_volume(struct mp_log *log, struct mp_archive *mpa,
+ struct stream *src, const char* url)
+{
+ struct mp_archive_volume *vol = talloc_zero(mpa, struct mp_archive_volume);
+ mp_verbose(log, "Adding volume %s\n", url);
+ vol->mpa = mpa;
+ vol->src = src;
+ vol->url = talloc_strdup(vol, url);
+ return archive_read_append_callback_data(mpa->arch, vol) == ARCHIVE_OK;
+}
+
struct mp_archive *mp_archive_new(struct mp_log *log, struct stream *src,
int flags)
{
struct mp_archive *mpa = talloc_zero(NULL, struct mp_archive);
- mpa->src = src;
- stream_seek(mpa->src, 0);
mpa->arch = archive_read_new();
+ mpa->primary_src = src;
if (!mpa->arch)
goto err;
+ // first volume is the primary streame
+ if (!add_volume(log ,mpa, src, src->url))
+ goto err;
+
+ // try to open other volumes
+ char** volumes = find_volumes(src);
+ for (int i = 0; volumes[i]; i++) {
+ if (!add_volume(log, mpa, NULL, volumes[i])) {
+ talloc_free(volumes);
+ goto err;
+ }
+ }
+ talloc_free(volumes);
+
archive_read_support_format_7zip(mpa->arch);
archive_read_support_format_iso9660(mpa->arch);
archive_read_support_format_rar(mpa->arch);
@@ -92,10 +230,12 @@ struct mp_archive *mp_archive_new(struct mp_log *log, struct stream *src,
archive_read_support_format_tar(mpa->arch);
}
- archive_read_set_callback_data(mpa->arch, mpa);
archive_read_set_read_callback(mpa->arch, read_cb);
archive_read_set_skip_callback(mpa->arch, skip_cb);
- if (mpa->src->seekable)
+ archive_read_set_switch_callback(mpa->arch, switch_cb);
+ archive_read_set_open_callback(mpa->arch, open_cb);
+ archive_read_set_close_callback(mpa->arch, close_cb);
+ if (mpa->primary_src->seekable)
archive_read_set_seek_callback(mpa->arch, seek_cb);
if (archive_read_open1(mpa->arch) < ARCHIVE_OK)
goto err;
diff --git a/stream/stream_libarchive.h b/stream/stream_libarchive.h
index f69faade72..ebded5b5ba 100644
--- a/stream/stream_libarchive.h
+++ b/stream/stream_libarchive.h
@@ -2,7 +2,7 @@ struct mp_log;
struct mp_archive {
struct archive *arch;
- struct stream *src;
+ struct stream *primary_src;
char buffer[4096];
};