[vlc-commits] Fixed multi-volume RAR support.

Laurent Aimar git at videolan.org
Sun Dec 11 20:20:00 CET 2011


vlc | branch: master | Laurent Aimar <fenrir at videolan.org> | Sun Dec 11 20:13:18 2011 +0100| [25262a5ae65b2ce815a938fc53bab0602944921f] | committer: Laurent Aimar

Fixed multi-volume RAR support.

The access now open itself the following rar volumes (only if needed). This
also allows to support rar embeded in rar or using other protocals than file.

It has been broken since the rar modules have been modified to
support multiple files.

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=25262a5ae65b2ce815a938fc53bab0602944921f
---

 modules/access/rar/access.c |   21 ++++++-
 modules/access/rar/rar.c    |  133 ++++++++++++++++++++++++++++++++++--------
 modules/access/rar/rar.h    |    1 +
 3 files changed, 126 insertions(+), 29 deletions(-)

diff --git a/modules/access/rar/access.c b/modules/access/rar/access.c
index 92dd943..cce9ba1 100644
--- a/modules/access/rar/access.c
+++ b/modules/access/rar/access.c
@@ -72,6 +72,7 @@ static int Seek(access_t *access, uint64_t position)
         position = file->real_size;
 
     /* 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)
@@ -82,7 +83,13 @@ static int Seek(access_t *access, uint64_t position)
 
     const uint64_t offset = sys->chunk->offset +
                             (position - sys->chunk->cummulated_size);
-    return stream_Seek(sys->s, offset);
+
+    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, uint8_t *data, size_t size)
@@ -96,7 +103,7 @@ static ssize_t Read(access_t *access, uint8_t *data, size_t size)
         if (max <= 0)
             break;
 
-        int r = stream_Read(sys->s, data, max);
+        int r = sys->s ? stream_Read(sys->s, data, max) : -1;
         if (r <= 0)
             break;
 
@@ -117,6 +124,9 @@ static ssize_t Read(access_t *access, uint8_t *data, size_t size)
 static int Control(access_t *access, int query, va_list args)
 {
     stream_t *s = access->p_sys->s;
+    if (!s)
+        return VLC_EGENERIC;
+
     switch (query) {
     case ACCESS_CAN_SEEK: {
         bool *b = va_arg(args, bool *);
@@ -190,6 +200,10 @@ static int Open(vlc_object_t *object)
     access_InitFields(access);
     access->info.i_size = file->size;
 
+    rar_file_chunk_t dummy = {
+        .mrl = base,
+    };
+    sys->chunk = &dummy;
     Seek(access, 0);
 
     free(base);
@@ -207,7 +221,8 @@ static void Close(vlc_object_t *object)
     access_t *access = (access_t*)object;
     access_sys_t *sys = access->p_sys;
 
-    stream_Delete(sys->s);
+    if (sys->s)
+        stream_Delete(sys->s);
     RarFileDelete(sys->file);
     free(sys);
 }
diff --git a/modules/access/rar/rar.c b/modules/access/rar/rar.c
index c2f605e..05dcaf6 100644
--- a/modules/access/rar/rar.c
+++ b/modules/access/rar/rar.c
@@ -45,8 +45,10 @@ static const int rar_marker_size = sizeof(rar_marker);
 
 void RarFileDelete(rar_file_t *file)
 {
-    for (int i = 0; i < file->chunk_count; i++)
+    for (int i = 0; i < file->chunk_count; i++) {
+        free(file->chunk[i]->mrl);
         free(file->chunk[i]);
+    }
     free(file->chunk);
     free(file->name);
     free(file);
@@ -153,7 +155,8 @@ static int SkipEnd(stream_t *s, const rar_block_t *hdr)
     return VLC_SUCCESS;
 }
 
-static int SkipFile(stream_t *s, int *count, rar_file_t ***file, const rar_block_t *hdr)
+static int SkipFile(stream_t *s, int *count, rar_file_t ***file,
+                    const rar_block_t *hdr, const char *volume_mrl)
 {
     const uint8_t *peek;
 
@@ -223,6 +226,7 @@ static int SkipFile(stream_t *s, int *count, rar_file_t ***file, const rar_block
     /* Append chunks */
     rar_file_chunk_t *chunk = malloc(sizeof(*chunk));
     if (chunk) {
+        chunk->mrl = strdup(volume_mrl);
         chunk->offset = stream_Tell(s) + hdr->size;
         chunk->size = hdr->add_size;
         chunk->cummulated_size = 0;
@@ -267,42 +271,119 @@ int RarProbe(stream_t *s)
     return VLC_SUCCESS;
 }
 
+typedef struct {
+    const char *match;
+    const char *format;
+    int start;
+    int stop;
+} rar_pattern_t;
+
+static const rar_pattern_t *FindVolumePattern(const char *location)
+{
+    static const rar_pattern_t patterns[] = {
+        { ".part1.rar",   "%s.part%.1d.rar", 2,   9 },
+        { ".part01.rar",  "%s.part%.2d.rar", 2,  99, },
+        { ".part001.rar", "%s.part%.3d.rar", 2, 999 },
+        { ".rar",         "%s.r%.2d",        0,  99 },
+        { NULL, NULL, 0, 0 },
+    };
+
+    const size_t location_size = strlen(location);
+    for (int i = 0; patterns[i].match != NULL; i++) {
+        const size_t match_size = strlen(patterns[i].match);
+
+        if (location_size < match_size)
+            continue;
+        if (!strcmp(&location[location_size - match_size], patterns[i].match))
+            return &patterns[i];
+    }
+    return NULL;
+}
+
 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;
+    const rar_pattern_t *pattern = FindVolumePattern(s->psz_path);
+    int volume_offset = 0;
 
-    /* Skip archive  */
-    if (IgnoreBlock(s, RAR_BLOCK_ARCHIVE))
+    char *volume_mrl;
+    if (asprintf(&volume_mrl, "%s://%s",
+                 s->psz_access, s->psz_path) < 0)
         return VLC_EGENERIC;
 
-    /* */
+    stream_t *vol = s;
     for (;;) {
-        rar_block_t bk;
-        int ret;
+        /* Skip marker & archive */
+        if (IgnoreBlock(vol, RAR_BLOCK_MARKER) ||
+            IgnoreBlock(vol, RAR_BLOCK_ARCHIVE)) {
+            if (vol != s)
+                stream_Delete(vol);
+            free(volume_mrl);
+            return VLC_EGENERIC;
+        }
 
-        if (PeekBlock(s, &bk))
-            break;
+        /* */
+        bool has_next = false;
+        for (;;) {
+            rar_block_t bk;
+            int ret;
+
+            if (PeekBlock(vol, &bk))
+                break;
+
+            switch(bk.type) {
+            case RAR_BLOCK_END:
+                ret = SkipEnd(vol, &bk);
+                has_next = ret && (bk.flags & RAR_BLOCK_END_HAS_NEXT);
+                break;
+            case RAR_BLOCK_FILE:
+                ret = SkipFile(vol, count, file, &bk, volume_mrl);
+                break;
+            default:
+                ret = SkipBlock(vol, &bk);
+                break;
+            }
+            if (ret)
+                break;
+        }
+        if (vol != s)
+            stream_Delete(vol);
 
-        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 (!has_next) {
+            free(volume_mrl);
+            return VLC_SUCCESS;
         }
-        if (ret)
-            break;
-    }
 
-    return VLC_SUCCESS;
+        /* Open next volume */
+        const int volume_index = pattern->start + volume_offset++;
+        if (volume_index > pattern->stop) {
+            free(volume_mrl);
+            return VLC_SUCCESS;
+        }
+
+        char *volume_base;
+        if (asprintf(&volume_base, "%s://%.*s",
+                     s->psz_access,
+                     (int)(strlen(s->psz_path) - strlen(pattern->match)), s->psz_path) < 0) {
+            free(volume_mrl);
+            return VLC_SUCCESS;
+        }
+
+        free(volume_mrl);
+        if (asprintf(&volume_mrl, pattern->format, volume_base, volume_index) < 0)
+            volume_mrl = NULL;
+        free(volume_base);
+
+        if (!volume_mrl)
+            return VLC_SUCCESS;
+        vol = stream_UrlNew(s, volume_mrl);
+
+        if (!vol) {
+            free(volume_mrl);
+            return VLC_SUCCESS;
+        }
+    }
 }
 
diff --git a/modules/access/rar/rar.h b/modules/access/rar/rar.h
index 7506934..174879c 100644
--- a/modules/access/rar/rar.h
+++ b/modules/access/rar/rar.h
@@ -22,6 +22,7 @@
  *****************************************************************************/
 
 typedef struct {
+    char     *mrl;
     uint64_t offset;
     uint64_t size;
     uint64_t cummulated_size;



More information about the vlc-commits mailing list