]> git.sesse.net Git - vlc/commitdiff
Zip: add a stream_filter and an access
authorJean-Philippe Andre <jpeg@via.ecp.fr>
Sun, 18 Jan 2009 18:34:32 +0000 (19:34 +0100)
committerJean-Philippe Andre <jpeg@via.ecp.fr>
Tue, 27 Jan 2009 22:09:32 +0000 (23:09 +0100)
The stream_filter does the following job:
[zip stream] -->filter--> [xspf playlist]

The access is called zip or unzip and takes MRLs of the form:
zip://[web-encoded-path]![file-in-zip]

As there is some automatic web-form decoding of the MRL,
the MRLs in the playlist generated by zipstream are encoded
two times.

modules/access/zip/Modules.am [new file with mode: 0644]
modules/access/zip/zip.h [new file with mode: 0644]
modules/access/zip/zipaccess.c [new file with mode: 0644]
modules/access/zip/zipstream.c [new file with mode: 0644]

diff --git a/modules/access/zip/Modules.am b/modules/access/zip/Modules.am
new file mode 100644 (file)
index 0000000..303c29f
--- /dev/null
@@ -0,0 +1,6 @@
+if HAVE_ZLIB
+SOURCES_zip = \
+       zipstream.c \
+       zipaccess.c \
+       $(NULL)
+endif
diff --git a/modules/access/zip/zip.h b/modules/access/zip/zip.h
new file mode 100644 (file)
index 0000000..e6dde5a
--- /dev/null
@@ -0,0 +1,73 @@
+/*****************************************************************************
+ * zip.h: Module (access+demux) to extract different archives, based on zlib
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Jean-Philippe André <jpeg@videolan.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.
+ *****************************************************************************/
+
+/** **************************************************************************
+ * Common includes and shared headers
+ *****************************************************************************/
+
+#ifndef ZIP_ACCESSDEMUX_H
+#define ZIP_ACCESSDEMUX_H
+
+#include <vlc/vlc.h>
+#include <vlc_common.h>
+#include <vlc_url.h>
+#include <vlc_strings.h>
+#include <vlc_arrays.h>
+#include <vlc_plugin.h>
+#include <vlc_stream.h>
+#include "unzip.h"
+#include "ioapi.h"
+
+#include <assert.h>
+
+#define ZIP_FILENAME_LEN 512
+#define ZIP_BUFFER_LEN 32768
+#define ZIP_SEP      "|"
+#define ZIP_SEP_CHAR '|'
+
+
+/** **************************************************************************
+ * Module access points: stream_filter
+ *****************************************************************************/
+int StreamOpen( vlc_object_t* );
+void StreamClose( vlc_object_t* );
+
+/** **************************************************************************
+ * Module access points: access
+ *****************************************************************************/
+int AccessOpen( vlc_object_t *p_this );
+void AccessClose( vlc_object_t *p_this );
+
+/** **************************************************************************
+ * zipIO function headers : how to use vlc_stream to read the zip
+ * Note: static because the implementations differ
+ *****************************************************************************/
+static void* ZCALLBACK ZipIO_Open( void* opaque, const char* filename, int m );
+static uLong ZCALLBACK ZipIO_Read( void*, void* stream, void* buf, uLong sz );
+static uLong ZCALLBACK ZipIO_Write( void*, void* stream, const void*, uLong );
+static long ZCALLBACK ZipIO_Tell( void*, void* stream );
+static long ZCALLBACK ZipIO_Seek( void*, void* stream, uLong offset, int ori );
+static int ZCALLBACK ZipIO_Close( void*, void* stream );
+static int ZCALLBACK ZipIO_Error( void*, void* stream );
+
+#endif /* ZIP_ACCESSDEMUX_H */
diff --git a/modules/access/zip/zipaccess.c b/modules/access/zip/zipaccess.c
new file mode 100644 (file)
index 0000000..a30e696
--- /dev/null
@@ -0,0 +1,425 @@
+/*****************************************************************************
+ * zipaccess.c: Module (access) to extract different archives, based on zlib
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Jean-Philippe André <jpeg@videolan.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.
+ *****************************************************************************/
+
+/** @todo:
+ * - implement crypto (using url zip://user:password@path-to-archive#ZIP#file
+ * - read files in zip with long name (use unz_file_info.size_filename)
+ * - multi-volume archive support ?
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_ZLIB_H
+
+#include "zip.h"
+#include <vlc_access.h>
+
+/** **************************************************************************
+ * This is our own access_sys_t for zip files
+ *****************************************************************************/
+struct access_sys_t
+{
+    /* zlib / unzip members */
+    unzFile            zipFile;
+    zlib_filefunc_def *fileFunctions;
+
+    /* file in zip information */
+    char              *psz_fileInzip;
+};
+
+static int AccessControl( access_t *p_access, int i_query, va_list args );
+static ssize_t AccessRead( access_t *, uint8_t *, size_t );
+static int AccessSeek( access_t *, int64_t );
+static int OpenFileInZip( access_t *p_access, int i_pos );
+
+/** **************************************************************************
+ * \brief Open access
+ *****************************************************************************/
+int AccessOpen( vlc_object_t *p_this )
+{
+    access_t     *p_access = (access_t*)p_this;
+    access_sys_t *p_sys;
+    int i_ret              = VLC_EGENERIC;
+    unzFile file           = 0;
+
+    char *psz_pathToZip = NULL, *psz_path = NULL, *psz_sep = NULL;
+
+    p_access->p_sys = p_sys = (access_sys_t*)
+            calloc( sizeof( access_sys_t ), 1 );
+    if( !p_sys )
+        return VLC_ENOMEM;
+
+    /* Split the MRL */
+    psz_path = strdup( p_access->psz_path );
+    psz_sep = strchr( psz_path, ZIP_SEP_CHAR );
+    if( !psz_sep )
+        return VLC_EGENERIC;
+
+    *psz_sep = '\0';
+    psz_pathToZip = unescape_URI_duplicate( psz_path );
+    p_sys->psz_fileInzip = strdup( psz_sep + 1 );
+
+    /* Define IO functions */
+    zlib_filefunc_def *p_func = (zlib_filefunc_def*)
+                                    calloc( 1, sizeof( zlib_filefunc_def ) );
+    p_func->zopen_file   = ZipIO_Open;
+    p_func->zread_file   = ZipIO_Read;
+    p_func->zwrite_file  = ZipIO_Write; // see comment
+    p_func->ztell_file   = ZipIO_Tell;
+    p_func->zseek_file   = ZipIO_Seek;
+    p_func->zclose_file  = ZipIO_Close;
+    p_func->zerror_file  = ZipIO_Error;
+    p_func->opaque       = p_access;
+
+    /* Open zip archive */
+    file = p_access->p_sys->zipFile = unzOpen2( psz_pathToZip, p_func );
+    if( !file )
+    {
+        msg_Err( p_access, "not a valid zip archive: '%s'", psz_pathToZip );
+        goto exit;
+    }
+
+    /* Open file in zip */
+    OpenFileInZip( p_access, 0 );
+
+    /* Set callback */
+    ACCESS_SET_CALLBACKS( AccessRead, NULL, AccessControl, AccessSeek );
+
+    /* Get some infos about current file. Maybe we could want some more ? */
+    unz_file_info z_info;
+    unzGetCurrentFileInfo( file, &z_info, NULL, 0, NULL, 0, NULL, 0 );
+
+    /* Set access informations: size is needed for AccessSeek */
+    p_access->info.i_size = z_info.uncompressed_size;
+    p_access->info.i_pos  = 0;
+    p_access->info.b_eof  = false;
+
+    i_ret = VLC_SUCCESS;
+
+exit:
+    if( i_ret != VLC_SUCCESS )
+    {
+        if( file )
+        {
+            unzCloseCurrentFile( file );
+            unzClose( file );
+        }
+        free( p_sys->psz_fileInzip );
+        free( p_sys->fileFunctions );
+        free( p_sys );
+    }
+
+    free( psz_pathToZip );
+    free( psz_path );
+    return i_ret;
+}
+
+/** **************************************************************************
+ * \brief Close access: free structures
+ *****************************************************************************/
+void AccessClose( vlc_object_t *p_this )
+{
+    access_t     *p_access = (access_t*)p_this;
+    access_sys_t *p_sys = p_access->p_sys;
+    if( p_sys )
+    {
+        unzFile file = p_sys->zipFile;
+        if( file )
+        {
+            unzCloseCurrentFile( file );
+            unzClose( file );
+        }
+        free( p_sys->psz_fileInzip );
+        free( p_sys->fileFunctions );
+        free( p_sys );
+    }
+    var_Destroy( p_access, "zip-no-xspf" );
+}
+
+/** **************************************************************************
+ * \brief Control access
+ *****************************************************************************/
+static int AccessControl( access_t *p_access, int i_query, va_list args )
+{
+    bool         *pb_bool;
+    int          *pi_int;
+    int64_t      *pi_64;
+
+    switch( i_query )
+    {
+        /* */
+        case ACCESS_CAN_SEEK:
+        case ACCESS_CAN_PAUSE:
+        case ACCESS_CAN_CONTROL_PACE:
+            pb_bool = (bool*)va_arg( args, bool* );
+            *pb_bool = true;
+            break;
+
+        case ACCESS_CAN_FASTSEEK:
+            pb_bool = (bool*)va_arg( args, bool* );
+            *pb_bool = false;
+            break;
+
+        /* */
+        case ACCESS_GET_MTU:
+            pi_int = (int*)va_arg( args, int * );
+            *pi_int = 0;
+            break;
+
+        case ACCESS_GET_PTS_DELAY:
+            pi_64 = (int64_t*)va_arg( args, int64_t * );
+            *pi_64 = DEFAULT_PTS_DELAY;
+            break;
+
+        /* */
+        case ACCESS_SET_PAUSE_STATE:
+            /* Nothing to do */
+            break;
+
+        case ACCESS_GET_TITLE_INFO:
+        case ACCESS_SET_TITLE:
+        case ACCESS_SET_SEEKPOINT:
+        case ACCESS_SET_PRIVATE_ID_STATE:
+        case ACCESS_GET_META:
+        case ACCESS_GET_PRIVATE_ID_STATE:
+        case ACCESS_GET_CONTENT_TYPE:
+            return VLC_EGENERIC;
+
+        default:
+            msg_Warn( p_access, "unimplemented query %d in control", i_query );
+            return VLC_EGENERIC;
+
+    }
+    return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief Read access
+ * Reads current opened file in zip. This does not open the file in zip.
+ * Return -1 if no data yet, 0 if no more data, else real data read
+ *****************************************************************************/
+static ssize_t AccessRead( access_t *p_access, uint8_t *p_buffer, size_t sz )
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    assert( p_sys );
+    unzFile file = p_sys->zipFile;
+    if( !file )
+    {
+        msg_Err( p_access, "archive not opened !" );
+        return VLC_EGENERIC;
+    }
+
+    int i_read = 0;
+    i_read = unzReadCurrentFile( file, p_buffer, sz );
+
+    p_access->info.i_pos = unztell( file );
+    return ( i_read >= 0 ? i_read : VLC_EGENERIC );
+}
+
+/** **************************************************************************
+ * \brief Seek inside zip file
+ *****************************************************************************/
+static int AccessSeek( access_t *p_access, int64_t seek_len )
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    assert( p_sys );
+    unzFile file = p_sys->zipFile;
+    if( !file )
+    {
+        msg_Err( p_access, "archive not opened !" );
+        return VLC_EGENERIC;
+    }
+
+    /* Reopen file in zip if needed */
+    if( p_access->info.i_pos != 0 )
+    {
+        OpenFileInZip( p_access, p_access->info.i_pos + seek_len );
+    }
+
+    /* Read seek_len data and drop it */
+    int i_seek = 0;
+    int i_read = 1;
+    char *p_buffer = ( char* ) calloc( 1, ZIP_BUFFER_LEN );
+    while( ( i_seek < seek_len ) && ( i_read > 0 ) )
+    {
+        i_read = ( seek_len - i_seek < ZIP_BUFFER_LEN )
+               ? ( seek_len - i_seek ) : ZIP_BUFFER_LEN;
+        i_read = unzReadCurrentFile( file, p_buffer, i_read );
+        if( i_read < 0 )
+        {
+            msg_Warn( p_access, "could not seek in file" );
+            free( p_buffer );
+            return VLC_EGENERIC;
+        }
+        else
+        {
+            i_seek += i_read;
+        }
+    }
+    free( p_buffer );
+
+    p_access->info.i_pos = unztell( file );
+    return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief Open file in zip
+ *****************************************************************************/
+static int OpenFileInZip( access_t *p_access, int i_pos )
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    unzFile file = p_sys->zipFile;
+    if( !p_sys->psz_fileInzip )
+    {
+        return VLC_EGENERIC;
+    }
+
+    i_pos = __MIN( i_pos, 0 );
+    p_access->info.i_pos = 0;
+
+    unzCloseCurrentFile( file ); /* returns UNZ_PARAMERROR if file not opened */
+    if( unzLocateFile( file, p_sys->psz_fileInzip, 0 ) != UNZ_OK )
+    {
+        msg_Err( p_access, "could not [re]locate file in zip: '%s'",
+                 p_sys->psz_fileInzip );
+        return VLC_EGENERIC;
+    }
+    if( unzOpenCurrentFile( file ) != UNZ_OK )
+    {
+        msg_Err( p_access, "could not [re]open file in zip: '%s'",
+                 p_sys->psz_fileInzip );
+        return VLC_EGENERIC;
+    }
+    if( i_pos > 0 )
+        return AccessSeek( p_access, i_pos );
+    else
+        return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: open (read only)
+ *****************************************************************************/
+static void* ZCALLBACK ZipIO_Open( void* opaque, const char* file, int mode )
+{
+    assert(opaque != NULL);
+    assert(mode == (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING));
+
+    access_t *p_access = (access_t*) opaque;
+
+    return stream_UrlNew( p_access, file );
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: read
+ *****************************************************************************/
+static uLong ZCALLBACK ZipIO_Read( void* opaque, void* stream,
+                                   void* buf, uLong size )
+{
+    (void)opaque;
+    //access_t *p_access = (access_t*) opaque;
+    //msg_Dbg(p_access, "read %d", size);
+    return stream_Read( (stream_t*) stream, buf, size );
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: write (assert insteadof segfault)
+ *****************************************************************************/
+static uLong ZCALLBACK ZipIO_Write( void* opaque, void* stream,
+                                    const void* buf, uLong size )
+{
+    (void)opaque; (void)stream; (void)buf; (void)size;
+    int zip_access_cannot_write_this_should_not_happen = 0;
+    assert(zip_access_cannot_write_this_should_not_happen);
+    return 0;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: tell
+ *****************************************************************************/
+static long ZCALLBACK ZipIO_Tell( void* opaque, void* stream )
+{
+    (void)opaque;
+    int64_t i64_tell = stream_Tell( (stream_t*) stream );
+    //access_t *p_access = (access_t*) opaque;
+    //msg_Dbg(p_access, "tell %" PRIu64, i64_tell);
+    return (long)i64_tell;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: seek
+ *****************************************************************************/
+static long ZCALLBACK ZipIO_Seek( void* opaque, void* stream,
+                                  uLong offset, int origin )
+{
+    (void)opaque;
+    //access_t *p_access = (access_t*) opaque;
+    int64_t pos = offset;
+    switch( origin )
+    {
+        case SEEK_CUR:
+            pos += stream_Tell( (stream_t*) stream );
+            break;
+        case SEEK_SET:
+            break;
+        case SEEK_END:
+            pos += stream_Size( (stream_t*) stream );
+            break;
+        default:
+            return -1;
+    }
+    //msg_Dbg( p_access, "seek (%d,%d): %" PRIu64, offset, origin, pos );
+    stream_Seek( (stream_t*) stream, pos );
+    /* Note: in unzip.c, unzlocal_SearchCentralDir seeks to the end of
+             the stream, which is doable but returns an error in VLC.
+             That's why we always assume this was OK. FIXME */
+    return 0;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: close
+ *****************************************************************************/
+static int ZCALLBACK ZipIO_Close( void* opaque, void* stream )
+{
+    (void)opaque;
+    stream_Delete( (stream_t*) stream );
+    return 0;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: test error (man 3 ferror)
+ *****************************************************************************/
+static int ZCALLBACK ZipIO_Error( void* opaque, void* stream )
+{
+    (void)opaque;
+    (void)stream;
+    //msg_Dbg( p_access, "error" );
+    return 0;
+}
+
+
+
+#else
+# error Can not compile zip demuxer without zlib support
+#endif
diff --git a/modules/access/zip/zipstream.c b/modules/access/zip/zipstream.c
new file mode 100644 (file)
index 0000000..af2adcc
--- /dev/null
@@ -0,0 +1,794 @@
+/*****************************************************************************
+ * zipstream.c: stream_filter that creates a XSPF playlist from a Zip archive
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Jean-Philippe André <jpeg@videolan.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 "zip.h"
+#include <stddef.h>
+
+/* FIXME remove */
+#include <vlc_input.h>
+
+#define FILENAME_TEXT N_( "Media in Zip" )
+#define FILENAME_LONGTEXT N_( "Path to the media in the Zip archive" )
+
+/** **************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+vlc_module_begin()
+    set_shortname( "Zip" )
+    set_category( CAT_INPUT )
+    set_subcategory( SUBCAT_INPUT_STREAM_FILTER )
+    set_description( _( "Zip files filter" ) )
+    set_capability( "stream_filter", 1 )
+    set_callbacks( StreamOpen, StreamClose )
+    add_submodule()
+        set_subcategory( SUBCAT_INPUT_ACCESS )
+        set_description( _( "Zip access" ) )
+        set_capability( "access", 70 )
+        add_shortcut( "unzip" )
+        add_shortcut( "zip" )
+        set_callbacks( AccessOpen, AccessClose )
+vlc_module_end()
+
+/** *************************************************************************
+ * 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 );
+
+typedef struct node node;
+typedef struct item item;
+
+static int CreatePlaylist( stream_t *s, char **pp_buffer );
+static int GetFilesInZip( stream_t*, unzFile, vlc_array_t*, vlc_array_t* );
+static node* findOrCreateParentNode( node *root, const char *fullpath );
+static int WriteXSPF( char **pp_buffer, vlc_array_t *p_filenames,
+                      const char *psz_zippath );
+static int nodeToXSPF( char **pp_buffer, node *n, bool b_root );
+static node* findOrCreateParentNode( node *root, const char *fullpath );
+
+/** **************************************************************************
+ * Struct definitions
+ *****************************************************************************/
+struct stream_sys_t
+{
+    /* zlib / unzip members */
+    unzFile zipFile;
+    zlib_filefunc_def *fileFunctions;
+    char *psz_path;
+
+    /* xspf data */
+    char *psz_xspf;
+    size_t i_len;
+    size_t i_pos;
+};
+
+struct item {
+    int id;
+    item *next;
+};
+
+struct node {
+    char *name;
+    item *media;
+    node *child;
+    node *next;
+};
+
+/** **************************************************************************
+ * Some helpers
+ *****************************************************************************/
+
+inline static node* new_node( char *name )
+{
+    node *n = (node*) calloc( 1, sizeof(node) );
+    n->name = convert_xml_special_chars( name );
+    return n;
+}
+
+inline static item* new_item( int id )
+{
+    item *media = (item*) calloc( 1, sizeof(item) );
+    media->id = id;
+    return media;
+}
+
+inline static void free_all_node( node *root )
+{
+    while( root )
+    {
+        free_all_node( root->child );
+        free( root->name );
+        node *tmp = root->next;
+        free( root );
+        root = tmp;
+    }
+}
+
+/* Allocate strcat and format */
+static int astrcatf( char **ppsz_dest, const char *psz_fmt_src, ... )
+{
+    va_list args;
+    va_start( args, psz_fmt_src );
+
+    char *psz_tmp;
+    int i_ret = vasprintf( &psz_tmp, psz_fmt_src, args );
+    if( i_ret == -1 ) return -1;
+
+    va_end( args );
+
+    int i_len = strlen( *ppsz_dest ) + strlen( psz_tmp ) + 1;
+    char *psz_out = realloc( *ppsz_dest, i_len );
+    if( !psz_out ) return -1;
+
+    strcat( psz_out, psz_tmp );
+    free( psz_tmp );
+
+    *ppsz_dest = psz_out;
+    return i_len;
+}
+
+/** **************************************************************************
+ * Zip file identifier
+ *****************************************************************************/
+static const uint8_t p_zip_marker[] = { 0x50, 0x4b, 0x03, 0x04 }; // "PK^C^D"
+static const int i_zip_marker = 4;
+
+
+/** **************************************************************************
+ * Open
+ *****************************************************************************/
+int StreamOpen( vlc_object_t *p_this )
+{
+    stream_t *s = (stream_t*) p_this;
+    stream_sys_t *p_sys;
+
+    /* Verify file format */
+    const uint8_t *p_peek;
+    if( stream_Peek( s->p_source, &p_peek, i_zip_marker ) < i_zip_marker )
+        return VLC_EGENERIC;
+    if( memcmp( p_peek, p_zip_marker, i_zip_marker ) )
+        return VLC_EGENERIC;
+
+    s->p_sys = p_sys = calloc( sizeof( *p_sys ), 1 );
+    if( !p_sys )
+        return VLC_ENOMEM;
+
+    s->pf_read = Read;
+    s->pf_peek = Peek;
+    s->pf_control = Control;
+
+    p_sys->fileFunctions = ( zlib_filefunc_def * )
+            calloc( 1, sizeof( zlib_filefunc_def ) );
+    if( !p_sys->fileFunctions )
+    {
+        free( p_sys );
+        return VLC_ENOMEM;
+    }
+    p_sys->fileFunctions->zopen_file   = ZipIO_Open;
+    p_sys->fileFunctions->zread_file   = ZipIO_Read;
+    p_sys->fileFunctions->zwrite_file  = ZipIO_Write;
+    p_sys->fileFunctions->ztell_file   = ZipIO_Tell;
+    p_sys->fileFunctions->zseek_file   = ZipIO_Seek;
+    p_sys->fileFunctions->zclose_file  = ZipIO_Close;
+    p_sys->fileFunctions->zerror_file  = ZipIO_Error;
+    p_sys->fileFunctions->opaque       = ( void * ) s;
+    p_sys->zipFile = unzOpen2( NULL /* path */, p_sys->fileFunctions );
+    if( !p_sys->zipFile )
+    {
+        msg_Warn( s, "unable to open file" );
+        free( p_sys );
+        free( p_sys->fileFunctions );
+        return VLC_EGENERIC;
+    }
+
+    /* Find the stream uri */
+    /* FIXME FIXME FIXME */
+    input_thread_t *p_input_thread = (input_thread_t*)
+            vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
+    if( !p_input_thread )
+    {
+        free( p_sys );
+        free( p_sys->fileFunctions );
+        return VLC_EGENERIC;
+    }
+    input_item_t *p_input_item = input_GetItem( p_input_thread );
+    if( !p_input_item )
+    {
+        free( p_sys );
+        free( p_sys->fileFunctions );
+        return VLC_EGENERIC;
+    }
+    s->p_sys->psz_path = strdup( p_input_item->psz_uri );
+    vlc_gc_decref( p_input_item );
+//     vlc_object_release( p_input_thread );
+    /* FIXME FIXME FIXME */
+
+    return VLC_SUCCESS;
+}
+
+/** *************************************************************************
+ * Close
+ ****************************************************************************/
+void StreamClose( vlc_object_t *p_this )
+{
+    stream_t *s = (stream_t*)p_this;
+    stream_sys_t *p_sys = s->p_sys;
+
+    free( p_sys->fileFunctions );
+    free( p_sys->psz_xspf );
+    free( p_sys->psz_path );
+    free( p_sys );
+}
+
+/** *************************************************************************
+ * Stream filters functions
+ ****************************************************************************/
+
+/** *************************************************************************
+ * Read
+ ****************************************************************************/
+static int Read( stream_t *s, void *p_read, unsigned int i_read )
+{
+    stream_sys_t *p_sys = s->p_sys;
+
+    if( !p_read ) return 0;
+
+    /* Fill the buffer */
+    if( p_sys->psz_xspf == NULL )
+    {
+        int i_ret = CreatePlaylist( s, &p_sys->psz_xspf );
+        if( i_ret < 0 )
+            return -1;
+        p_sys->i_len = strlen( p_sys->psz_xspf );
+        p_sys->i_pos = 0;
+    }
+
+    /* Read the buffer */
+    int i_len = __MIN( i_read, p_sys->i_len - p_sys->i_pos );
+    memcpy( p_read, p_sys->psz_xspf + p_sys->i_pos, i_len );
+    p_sys->i_pos += i_len;
+
+    return i_len;
+}
+
+/** *************************************************************************
+ * Peek
+ ****************************************************************************/
+static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned int i_peek )
+{
+    stream_sys_t *p_sys = s->p_sys;
+
+    /* Fill the buffer */
+    if( p_sys->psz_xspf == NULL )
+    {
+        int i_ret = CreatePlaylist( s, &p_sys->psz_xspf );
+        if( i_ret < 0 )
+            return -1;
+        p_sys->i_len = strlen( p_sys->psz_xspf );
+        p_sys->i_pos = 0;
+    }
+
+
+    /* Point to the buffer */
+    int i_len = __MIN( i_peek, p_sys->i_len - p_sys->i_pos );
+    *pp_peek = (uint8_t*) p_sys->psz_xspf + p_sys->i_pos;
+
+    return i_len;
+}
+
+/** *************************************************************************
+ * Control
+ ****************************************************************************/
+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:
+        {
+            int64_t i_position = (int64_t)va_arg( args, int64_t );
+            if( i_position >= p_sys->i_len )
+                return VLC_EGENERIC;
+            else
+            {
+                p_sys->i_len = (size_t) i_position;
+                return VLC_SUCCESS;
+            }
+        }
+
+        case STREAM_GET_POSITION:
+        {
+            int64_t *pi_position = (int64_t*)va_arg( args, int64_t* );
+            *pi_position = p_sys->i_pos;
+            return VLC_SUCCESS;
+        }
+
+        case STREAM_GET_SIZE:
+        {
+            int64_t *pi_size = (int64_t*)va_arg( args, int64_t* );
+            *pi_size = (int64_t) p_sys->i_len;
+            return VLC_SUCCESS;
+        }
+
+        case STREAM_GET_MTU:
+        {
+            int *pi_mtu = (int*)va_arg( args, int* );
+            *pi_mtu = 0;
+            return VLC_SUCCESS;
+        }
+
+        case STREAM_GET_CONTENT_TYPE:
+            return VLC_EGENERIC;
+
+        case STREAM_UPDATE_SIZE:
+        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;
+    }
+}
+
+static int CreatePlaylist( stream_t *s, char **pp_buffer )
+{
+    /* Get some infos about zip archive */
+    int i_ret = 0;
+    unzFile file = s->p_sys->zipFile;
+    vlc_array_t *p_filenames = vlc_array_new(); /* Will contain char* */
+
+    /* List all file names in Zip archive */
+    i_ret = GetFilesInZip( s, file, p_filenames, NULL );
+    if( i_ret < 0 )
+    {
+        unzClose( file );
+        i_ret = -1;
+        goto exit;
+    }
+
+    // msg_Dbg( s, "%d files in Zip", vlc_array_count( p_filenames ) );
+
+    /* Close archive */
+    unzClose( file );
+    s->p_sys->zipFile = NULL;
+
+    /* Construct the xspf playlist */
+    i_ret = WriteXSPF( pp_buffer, p_filenames, s->p_sys->psz_path );
+    if( i_ret > 0 )
+        i_ret = 1;
+    else if( i_ret < 0 )
+        i_ret = -1;
+
+exit:
+    for( int i = 0; i < vlc_array_count( p_filenames ); i++ )
+    {
+        free( vlc_array_item_at_index( p_filenames, i ) );
+    }
+    vlc_array_destroy( p_filenames );
+    return i_ret;
+}
+
+
+/** **************************************************************************
+ * Zip utility functions
+ *****************************************************************************/
+
+/** **************************************************************************
+ * \brief List files in zip and append their names to p_array
+ * \param p_this
+ * \param file Opened zip file
+ * \param p_array vlc_array_t which will receive all filenames
+ *
+ * In case of error, returns VLC_EGENERIC.
+ * In case of success, returns number of files found, and goes back to first file.
+ *****************************************************************************/
+static int GetFilesInZip( stream_t *p_this, unzFile file,
+                          vlc_array_t *p_filenames, vlc_array_t *p_fileinfos )
+{
+    if( !p_filenames || !p_this )
+        return VLC_EGENERIC;
+
+    int i_ret = 0;
+
+    /* Get global info */
+    unz_global_info info;
+
+    if( unzGetGlobalInfo( file, &info ) != UNZ_OK )
+    {
+        msg_Warn( p_this, "this is not a valid zip archive" );
+        return VLC_EGENERIC;
+    }
+
+    /* Go to first file in archive */
+    unzGoToFirstFile( file );
+
+    /* Get info about each file */
+    for( unsigned long i = 0; i < info.number_entry; i++ )
+    {
+        char *psz_fileName = calloc( ZIP_FILENAME_LEN, 1 );
+        unz_file_info *p_fileInfo = calloc( 1, sizeof( unz_file_info ) );
+
+        if( !p_fileInfo || !psz_fileName )
+        {
+            msg_Warn( p_this, "not enough memory" );
+            return VLC_ENOMEM;
+        }
+
+        if( unzGetCurrentFileInfo( file, p_fileInfo, psz_fileName,
+                                   ZIP_FILENAME_LEN, NULL, 0, NULL, 0 )
+            != UNZ_OK )
+        {
+            msg_Warn( p_this, "can't get info about file in zip" );
+            return VLC_EGENERIC;
+        }
+
+        if( p_filenames )
+            vlc_array_append( p_filenames, strdup( psz_fileName ) );
+        free( psz_fileName );
+
+        if( p_fileinfos )
+            vlc_array_append( p_fileinfos, p_fileInfo );
+        else
+            free( p_fileInfo );
+
+        if( i < ( info.number_entry - 1 ) )
+        {
+            /* Go the next file in the archive */
+            if( unzGoToNextFile( file ) != UNZ_OK )
+            {
+                msg_Warn( p_this, "can't go to next file in zip" );
+                return VLC_EGENERIC;
+            }
+        }
+
+        i_ret++;
+    }
+
+    /* i_ret should be equal to info.number_entry */
+    unzGoToFirstFile( file );
+    return i_ret;
+}
+
+
+/** **************************************************************************
+ * XSPF generation functions
+ *****************************************************************************/
+
+/** **************************************************************************
+ * \brief Write the XSPF playlist given the list of files
+ *****************************************************************************/
+static int WriteXSPF( char **pp_buffer, vlc_array_t *p_filenames,
+                      const char *psz_zippath )
+{
+    char *psz_zip = strrchr( psz_zippath, DIR_SEP_CHAR );
+    psz_zip = convert_xml_special_chars( psz_zip ? (psz_zip+1) : psz_zippath );
+
+    if( asprintf( pp_buffer, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+        "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\" "
+                "xmlns:vlc=\"http://www.videolan.org/vlc/playlist/ns/0/\">\n"
+                " <title>%s</title>\n"
+                " <trackList>\n", psz_zip ) == -1)
+        return -1;
+
+    /* Root node */
+    node *playlist = new_node( psz_zip );
+
+    /* Web-Encode the URI and append '!' */
+    char *psz_pathtozip = vlc_UrlEncode( psz_zippath );
+    if( astrcatf( &psz_pathtozip, ZIP_SEP ) < 0 ) return -1;
+
+    int i_track = 0;
+    for( int i = 0; i < vlc_array_count( p_filenames ); ++i )
+    {
+        char *psz_name = (char*) vlc_array_item_at_index( p_filenames, i );
+        int i_len = strlen( psz_name );
+
+        if( !i_len ) continue;
+
+        /* Is it a folder ? */
+        if( psz_name[i_len-1] == '/' )
+        {
+            /* Do nothing */
+        }
+        else /* File */
+        {
+            /* Extract file name */
+            char *psz_file = strrchr( psz_name, '/' );
+            psz_file = convert_xml_special_chars( psz_file ?
+                    (psz_file+1) : psz_name );
+
+            /* Build full MRL */
+            char *psz_path = strdup( psz_pathtozip );
+            if( astrcatf( &psz_path, psz_name ) < 0 ) return -1;
+
+            /* Double url-encode */
+            char *psz_tmp = psz_path;
+            psz_path = vlc_UrlEncode( psz_tmp );
+            free( psz_tmp );
+
+            /* Track information */
+            if( astrcatf( pp_buffer,
+                        "  <track>\n"
+                        "   <location>zip://%s</location>\n"
+                        "   <title>%s</title>\n"
+                        "   <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n"
+                        "    <vlc:id>%d</vlc:id>\n"
+                        "   </extension>\n"
+                        "  </track>\n",
+                        psz_path, psz_file, i_track ) < 0 ) return -1;
+
+            free( psz_file );
+            free( psz_path );
+
+            /* Find the parent node */
+            node *parent = findOrCreateParentNode( playlist, psz_name );
+            assert( parent );
+
+            /* Add the item to this node */
+            item *tmp = parent->media;
+            if( !tmp )
+            {
+                parent->media = new_item( i_track );
+            }
+            else
+            {
+                while( tmp->next )
+                {
+                    tmp = tmp->next;
+                }
+                tmp->next = new_item( i_track );
+            }
+
+            ++i_track;
+        }
+    }
+
+    free( psz_pathtozip );
+
+    /* Close tracklist, open the extension */
+    if( astrcatf( pp_buffer,
+        " </trackList>\n"
+        " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n"
+                ) < 0 ) return -1;
+
+    /* Write the tree */
+    if( nodeToXSPF( pp_buffer, playlist, true ) < 0 ) return -1;
+
+    /* Close extension and playlist */
+    if( astrcatf( pp_buffer, " </extension>\n</playlist>\n" ) < 0 ) return -1;
+
+    /* printf( "%s", *pp_buffer ); */
+
+    free_all_node( playlist );
+
+    return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief Recursively convert a node to its XSPF representation
+ *****************************************************************************/
+static int nodeToXSPF( char **pp_buffer, node *n, bool b_root )
+{
+    if( !b_root )
+    {
+        if( astrcatf( pp_buffer, "  <vlc:node title=\"%s\">\n", n->name ) < 0 )
+            return -1;
+    }
+    if( n->child )
+        nodeToXSPF( pp_buffer, n->child, false );
+    item *i = n->media;
+    while( i )
+    {
+        if( astrcatf( pp_buffer, "   <vlc:item tid=\"%d\" />\n", i->id ) < 0 )
+            return -1;
+        i = i->next;
+    }
+    if( !b_root )
+    {
+        if( astrcatf( pp_buffer, "  </vlc:node>\n" ) < 0 )
+            return -1;
+    }
+    return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief Either create or find the parent node of the item
+ *****************************************************************************/
+static node* findOrCreateParentNode( node *root, const char *fullpath )
+{
+    char *folder;
+    char *path = strdup( fullpath );
+    folder = path;
+
+    assert( root );
+
+    char *sep = strchr( folder, '/' );
+    if( !sep )
+    {
+        free( path );
+        return root;
+    }
+
+    *sep = '\0';
+    ++sep;
+
+    node *current = root->child;
+
+    while( current )
+    {
+        if( !strcmp( current->name, folder ) )
+        {
+            /* We found the folder, go recursively deeper */
+            return findOrCreateParentNode( current, sep );
+        }
+        current = current->next;
+    }
+
+    /* If we are here, then it means that we did not find the parent */
+    node *ret = new_node( folder );
+    if( !root->child )
+        root->child = ret;
+    else
+    {
+        current = root->child;
+        while( current->next )
+        {
+            current = current->next;
+        }
+        current->next = ret;
+    }
+
+    /* And now, create the subfolders */
+    ret = findOrCreateParentNode( ret, sep );
+
+    free( path );
+    return ret;
+}
+
+
+/** **************************************************************************
+ * ZipIO function definitions : how to use vlc_stream to read the zip
+ *****************************************************************************/
+
+/** **************************************************************************
+ * \brief interface for unzip module to open a file using a vlc_stream
+ * \param opaque
+ * \param filename
+ * \param mode how to open the file (read/write ?). We support only read
+ * \return opaque
+ *****************************************************************************/
+static void ZCALLBACK *ZipIO_Open( void *opaque, const char *file, int mode )
+{
+    (void) file;
+    stream_t *s = (stream_t*) opaque;
+    if( mode & ( ZLIB_FILEFUNC_MODE_CREATE | ZLIB_FILEFUNC_MODE_WRITE ) )
+    {
+        msg_Dbg( s, "ZipIO_Open: we cannot write into zip files" );
+        return NULL;
+    }
+    return s;
+}
+
+/** **************************************************************************
+ * \brief read something from stream into buffer
+ * \param opaque should be the stream
+ * \param stream stream created by ZipIO_Open
+ * \param buf buffer to read the file
+ * \param size length of this buffer
+ * \return return the number of bytes read (<= size)
+ *****************************************************************************/
+static unsigned long ZCALLBACK ZipIO_Read( void *opaque, void *stream,
+                                           void *buf, unsigned long size )
+{
+    (void) stream;
+    stream_t *s = (stream_t*) opaque;
+    return (unsigned long) stream_Read( s->p_source, buf, (int) size );
+}
+
+/** **************************************************************************
+ * \brief tell size of stream
+ * \param opaque should be the stream
+ * \param stream stream created by ZipIO_Open
+ * \return size of the file / stream
+ * ATTENTION: this is not stream_Tell, but stream_Size !
+ *****************************************************************************/
+static long ZCALLBACK ZipIO_Tell( void *opaque, void *stream )
+{
+    (void) stream;
+    stream_t *s = (stream_t*) opaque;
+    return (long) stream_Size( s->p_source ); /* /!\ not stream_Tell /!\ */
+}
+
+/** **************************************************************************
+ * \brief seek in the stream
+ * \param opaque should be the stream
+ * \param stream stream created by ZipIO_Open
+ * \param offset positive offset to seek
+ * \param origin current position in stream
+ * \return ¿ VLC_SUCCESS or an error code ?
+ *****************************************************************************/
+static long ZCALLBACK ZipIO_Seek ( void *opaque, void *stream,
+                                   unsigned long offset, int origin )
+{
+    (void) stream;
+    stream_t *s = (stream_t*) opaque;
+    long l_ret;
+
+    uint64_t pos = offset + origin;
+    l_ret = (long) stream_Seek( s->p_source, pos );
+    return l_ret;
+}
+
+/** **************************************************************************
+ * \brief close the stream
+ * \param opaque should be the stream
+ * \param stream stream created by ZipIO_Open
+ * \return always VLC_SUCCESS
+ * This closes zip archive
+ *****************************************************************************/
+static int ZCALLBACK ZipIO_Close ( void *opaque, void *stream )
+{
+    (void) stream;
+    (void) opaque;
+//     stream_t *s = (stream_t*) opaque;
+//    if( p_demux->p_sys && p_demux->p_sys->zipFile )
+//        p_demux->p_sys->zipFile = NULL;
+//     stream_Seek( s->p_source, 0 );
+    return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: write (assert insteadof segfault)
+ *****************************************************************************/
+static uLong ZCALLBACK ZipIO_Write( void* opaque, void* stream,
+                                    const void* buf, uLong size )
+{
+    (void)opaque; (void)stream; (void)buf; (void)size;
+    int ERROR_zip_cannot_write_this_should_not_happen = 0;
+    assert( ERROR_zip_cannot_write_this_should_not_happen );
+    return 0;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: test error (man 3 ferror)
+ *****************************************************************************/
+static int ZCALLBACK ZipIO_Error( void* opaque, void* stream )
+{
+    (void)opaque;
+    (void)stream;
+    //msg_Dbg( p_access, "error" );
+    return 0;
+}
+