From: Adam Sutton Date: Thu, 2 Aug 2012 16:43:41 +0000 (+0100) Subject: Initial stab at new transparent file bundling. X-Git-Tag: 3.3~183 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=58209cd184c8154805ca56dcf2c7968476fa00e5;p=thirdparty%2Ftvheadend.git Initial stab at new transparent file bundling. --- diff --git a/Makefile b/Makefile index e33cab639..c47348a91 100644 --- a/Makefile +++ b/Makefile @@ -74,6 +74,7 @@ SRCS = src/main.c \ src/iptv_input.c \ src/avc.c \ src/huffman.c \ + src/filebundle.c \ SRCS += src/epggrab/module.c\ src/epggrab/channel.c\ @@ -157,7 +158,7 @@ OBJS_EXTRA = $(SRCS_EXTRA:%.c=$(BUILDDIR)/%.so) DEPS= ${OBJS:%.o=%.d} # File bundles -BUNDLE_SRCS=$(BUNDLES:%=$(BUILDDIR)/bundles/%.c) +BUNDLE_SRCS=$(BUILDDIR)/bundle.c BUNDLE_DEPS=$(BUNDLE_SRCS:%.c=%.d) BUNDLE_OBJS=$(BUNDLE_SRCS:%.c=%.o) .PRECIOUS: ${BUNDLE_SRCS} @@ -168,10 +169,10 @@ CFLAGS_com = -g -funsigned-char -O2 CFLAGS_com += -D_FILE_OFFSET_BITS=64 CFLAGS_com += -I${BUILDDIR} -I${CURDIR}/src -I${CURDIR} -MKBUNDLE = $(CURDIR)/support/mkbundle +MKBUNDLE = $(CURDIR)/support/mkbundle.py ifndef V -ECHO = printf "$(1)\t%s\n" $(2) +ECHO = printf "$(1)\t\t%s\n" $(2) BRIEF = CC MKBUNDLE CXX MSG = $@ $(foreach VAR,$(BRIEF), \ @@ -207,7 +208,7 @@ ${BUILDDIR}/%.so: ${SRCS_EXTRA} ${CC} -O -fbuiltin -fomit-frame-pointer -fPIC -shared -o $@ $< -ldl clean: - rm -rf ${BUILDDIR}/src ${BUILDDIR}/bundles + rm -rf ${BUILDDIR}/src ${BUILDDIR}/bundle* find . -name "*~" | xargs rm -f distclean: clean @@ -227,10 +228,10 @@ FORCE: include support/${OSENV}.mk # Bundle files -$(BUILDDIR)/bundles/%.o: $(BUILDDIR)/bundles/%.c +$(BUILDDIR)/bundle.o: $(BUILDDIR)/bundle.c @mkdir -p $(dir $@) $(CC) -I${CURDIR}/src -c -o $@ $< -$(BUILDDIR)/bundles/%.c: % +$(BUILDDIR)/bundle.c: @mkdir -p $(dir $@) - $(MKBUNDLE) -o $@ -s $< -d ${BUILDDIR}/bundles/$<.d -p $< -z + $(MKBUNDLE) -o $@ -d ${BUILDDIR}/bundle.d -z $(BUNDLES) diff --git a/src/cwc.c b/src/cwc.c index a45008bb9..2a872e319 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -1547,7 +1547,7 @@ cwc_emm_viaccess(cwc_t *cwc, uint8_t *data, int mlen) ass[2] = len & 0xff; len += 3; - crc = crc32(ass, len, 0xffffffff); + crc = tvh_crc32(ass, len, 0xffffffff); if (!cwc_emm_cache_lookup(cwc, crc)) { tvhlog(LOG_DEBUG, "cwc", "Send EMM " diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 00a845fad..3cd5b53ae 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -152,7 +152,7 @@ dvb_proc_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt, uint8_t *sec, /* It seems some hardware (or is it the dvb API?) does not honour the DMX_CHECK_CRC flag, so we check it again */ - if(chkcrc && crc32(sec, r, 0xffffffff)) + if(chkcrc && tvh_crc32(sec, r, 0xffffffff)) return; r -= 3; diff --git a/src/filebundle.c b/src/filebundle.c new file mode 100644 index 000000000..99dba70d5 --- /dev/null +++ b/src/filebundle.c @@ -0,0 +1,365 @@ +/* + * TV headend - File bundles + * Copyright (C) 2012 Adam Sutton + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "filebundle.h" +#include "tvheadend.h" + +#ifdef TEST +#define MIN(x,y) ((xtype == FB_DIR && !strcmp(fb->name, tmp2)) { + tmp2 = strtok(NULL, "/"); + if (tmp2) fb = fb->d.child; + } else { + fb = fb->next; + } + } + free(tmp1); + + /* Found */ + if (fb) { + ret = calloc(1, sizeof(fb_dir)); + ret->type = FB_BUNDLE; + ret->b.root = fb; + ret->b.cur = fb->d.child; + } + + /* Direct */ + } else { + DIR *dir; + char buf[512]; + snprintf(buf, sizeof(buf), "%s%s%s", root, *root ? "" : "/", path); + if ((dir = opendir(buf))) { + ret = calloc(1, sizeof(fb_dir)); + ret->type = FB_DIRECT; + ret->d.root = strdup(buf); + ret->d.cur = dir; + } + } + + return ret; +} + +/* Close directory */ +void fb_closedir ( fb_dir *dir ) +{ + if (dir->type == FB_DIRECT) { + closedir(dir->d.cur); + free(dir->d.root); + } + free(dir); +} + +/* Iterate through entries */ +fb_dirent *fb_readdir ( fb_dir *dir ) +{ + fb_dirent *ret = NULL; + if (dir->type == FB_BUNDLE) { + if (dir->b.cur) { + dir->dirent.name = dir->b.cur->name; + dir->dirent.type = dir->b.cur->type; + dir->b.cur = dir->b.cur->next; + ret = &dir->dirent; + } + + } else { + struct dirent *de = readdir(dir->d.cur); + if (de) { + struct stat st; + char buf[512]; + snprintf(buf, sizeof(buf), "%s/%s", dir->d.root, de->d_name); + dir->dirent.name = de->d_name; + dir->dirent.type = FB_UNKNOWN; + if (!lstat(buf, &st)) + dir->dirent.type = S_ISDIR(st.st_mode) ? FB_DIR : FB_FILE; + ret = &dir->dirent; + } + } + return ret; +} + +/* ************************************************************************** + * Directory processing + * *************************************************************************/ + +/* Open file (with dir and name) */ +// Note: decompress is only used on bundled (not direct) files that were +// compressed at the time the bundle was generated, 1 can safely +// be passed in though and will be ignored if this is not the case +// Note: compress will work on EITHER type (but will be ignored for already +// compressed bundles) +fb_file *fb_open2 ( const fb_dir *dir, const char *name, int decompress, int compress ) +{ + assert(!decompress || !compress); + fb_file *ret = NULL; + if (dir->type == FB_BUNDLE) { + const filebundle_entry_t *fb = dir->b.root->d.child; + while (fb) { + if (!strcmp(name, fb->name)) break; + fb = fb->next; + } + if (fb) { + ret = calloc(1, sizeof(fb_file)); + ret->type = FB_BUNDLE; + ret->size = fb->f.size; + ret->gzip = fb->f.orig != -1; + ret->b.root = fb; + + /* Inflate the file */ + if (fb->f.orig != -1 && decompress) { + ret->gzip = 0; + ret->size = fb->f.orig; + int err; + z_stream zstr; + uint8_t *bufin, *bufout; + + /* Setup buffers */ + bufin = malloc(fb->f.size); + bufout = malloc(fb->f.orig); + memcpy(bufin, fb->f.data, fb->f.size); + + /* Setup zlib */ + zstr.zalloc = Z_NULL; + zstr.zfree = 0; + zstr.opaque = Z_NULL; + zstr.avail_in = 0; + zstr.next_in = Z_NULL; + inflateInit2(&zstr, 31); + zstr.avail_in = fb->f.size; + zstr.next_in = bufin; + zstr.avail_out = fb->f.orig; + zstr.next_out = bufout; + + /* Decompress */ + err = inflate(&zstr, Z_NO_FLUSH); + if ( err == Z_STREAM_END && zstr.avail_out == 0 ) { + ret->b.buf = bufout; + + /* Error */ + } else { + free(bufout); + free(ret); + ret = NULL; + } + + /* Cleanup */ + free(bufin); + inflateEnd(&zstr); + } + } + } else { + + } + return ret; +} + +/* Open file */ +fb_file *fb_open ( const char *path, int decompress, int compress ) +{ + fb_file *ret = NULL; + fb_dir *dir = NULL; + char *tmp = strdup(path); + char *pos = strrchr(tmp, '/'); + if (!pos) { + free(tmp); + return NULL; + } + + /* Find directory */ + *pos = '\0'; + dir = fb_opendir(tmp); + if (!dir) { + free(tmp); + return NULL; + } + + /* Open */ + ret = fb_open2(dir, pos+1, decompress, compress); + free(tmp); + return ret; +} + +/* Close file */ +void fb_close ( fb_file *fp ) +{ + if (fp->type == FB_DIRECT) + fclose(fp->d.cur); + else if (fp->b.buf) + free(fp->b.buf); + free(fp); +} + +/* Get the files size */ +size_t fb_size ( fb_file *fp ) +{ + return fp->size; +} + +/* Check if compressed */ +int fb_gzipped ( fb_file *fp ) +{ + return fp->gzip; +} + +/* Check for EOF */ +int fb_eof ( fb_file *fp ) +{ + if (fp->type == FB_DIRECT) { + return feof(fp->d.cur); + } else { + return fp->b.cur >= fp->size; + } +} + +/* Read some data */ +ssize_t fb_read ( fb_file *fp, void *buf, size_t count ) +{ + if (fp->type == FB_DIRECT) { + return fread(buf, 1, count, fp->d.cur); + } else if (fb_eof(fp)) { + return -1; + } else if (fp->b.buf) { + count = MIN(count, fp->b.root->f.orig - fp->b.cur); + memcpy(buf, fp->b.buf + fp->b.cur, count); + fp->b.cur += count; + } else { + count = MIN(count, fp->b.root->f.size - fp->b.cur); + memcpy(buf, fp->b.root->f.data + fp->b.cur, count); + fp->b.cur += count; + } + return count; +} + +/* Read a line */ +char *fb_gets ( fb_file *fp, void *buf, size_t count ) +{ + ssize_t c = 0, err; + while ((err = fb_read(fp, buf+c, 1)) && c < (count-1)) { + char b = ((char*)buf)[c]; + c++; + if (b == '\n' || b == '\0') break; + } + if (err < 0) return NULL; + ((char*)buf)[c] = '\0'; + return buf; +} + +#ifdef TEST +int main ( int argc, char **argv ) +{ + uint8_t buf[100000]; + fb_dirent *de; + fb_dir *d = fb_opendir(argv[1]); + if (d) { + while ((de = fb_readdir(d))) { + printf("name = %s\n", de->name); + fb_file *fp = fb_open2(d, de->name, 1, 0); + printf("size = %lu\n", fb_size(fp)); + while (!fb_eof(fp)) { + if (fb_gets(fp, buf, sizeof(buf)) == NULL) + return 1; + printf("LINE: %s", buf); + } + fb_close(fp); + } + fb_closedir(d); + } +} +#endif diff --git a/src/filebundle.h b/src/filebundle.h index e745253da..315bc3fbd 100644 --- a/src/filebundle.h +++ b/src/filebundle.h @@ -1,12 +1,85 @@ -struct filebundle_entry { - const char *filename; - const unsigned char *data; - int size; - int original_size; // -1 if file is not compressed -}; +/* + * TV headend - File bundles + * Copyright (C) 2008 Andreas Öman, Adam Sutton + * + * 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 3 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, see . + */ + +#ifndef __TVH_FILE_BUNDLE_H__ +#define __TVH_FILE_BUNDLE_H__ + +#include +#include +#include +#include -struct filebundle { - struct filebundle *next; - const struct filebundle_entry *entries; - const char *prefix; +/* File bundle entry type */ +enum filebundle_type +{ + FB_UNKNOWN, + FB_FILE, + FB_DIR }; + +/* File bundle entry */ +typedef struct filebundle_entry +{ + enum filebundle_type type; + const char *name; + union { + struct { + struct filebundle_entry *child; + } d; + struct { + const uint8_t *data; + size_t size; + ssize_t orig; + } f; + }; + struct filebundle_entry *next; +} filebundle_entry_t; + +/* File bundle directory entry */ +typedef struct filebundle_dirent +{ + const char *name; + enum filebundle_type type; +} fb_dirent; + +/* Opaque types */ +typedef struct filebundle_dir fb_dir; +typedef struct filebundle_file fb_file; + +/* Root of bundle */ +extern filebundle_entry_t *filebundle_root; + +/* Directory processing wrappers */ +fb_dir *fb_opendir ( const char *path ); +fb_dirent *fb_readdir ( fb_dir *fb ); +void fb_closedir ( fb_dir *fb ); + +/* File processing wrappers */ +// Note: all access is read-only +// Note: decompress is only for compressed filebundles, +// not direct disk access +fb_file *fb_open2 ( const fb_dir *dir, const char *name, int decompress, int compress ); +fb_file *fb_open ( const char *path, int decompress, int compress ); +void fb_close ( fb_file *fp ); +size_t fb_size ( fb_file *fp ); +int fb_gzipped ( fb_file *fp ); +int fb_eof ( fb_file *fp ); +ssize_t fb_read ( fb_file *fp, void *buf, size_t count ); +char *fb_gets ( fb_file *fp, void *buf, size_t count ); + +#endif /* __TVH_FILE_BUNDLE_H__ */ diff --git a/src/htsbuf.c b/src/htsbuf.c index 9dc08e0bc..ff859c18f 100644 --- a/src/htsbuf.c +++ b/src/htsbuf.c @@ -323,7 +323,7 @@ htsbuf_crc32(htsbuf_queue_t *hq, uint32_t crc) htsbuf_data_t *hd; TAILQ_FOREACH(hd, &hq->hq_q, hd_link) - crc = crc32(hd->hd_data + hd->hd_data_off, + crc = tvh_crc32(hd->hd_data + hd->hd_data_off, hd->hd_data_len - hd->hd_data_off, crc); return crc; diff --git a/src/psi.c b/src/psi.c index d27a5a9e1..277c0426d 100644 --- a/src/psi.c +++ b/src/psi.c @@ -59,7 +59,7 @@ psi_section_reassemble0(psi_section_t *ps, const uint8_t *data, excess = ps->ps_offset - tsize; - if(crc && crc32(ps->ps_data, tsize, 0xffffffff)) + if(crc && tvh_crc32(ps->ps_data, tsize, 0xffffffff)) return -1; cb(ps->ps_data, tsize - (crc ? 4 : 0), opaque); @@ -159,14 +159,14 @@ psi_append_crc32(uint8_t *buf, int offset, int maxlen) if(offset + 4 > maxlen) return -1; - crc = crc32(buf, offset, 0xffffffff); + crc = tvh_crc32(buf, offset, 0xffffffff); buf[offset + 0] = crc >> 24; buf[offset + 1] = crc >> 16; buf[offset + 2] = crc >> 8; buf[offset + 3] = crc; - assert(crc32(buf, offset + 4, 0xffffffff) == 0); + assert(tvh_crc32(buf, offset + 4, 0xffffffff) == 0); return offset + 4; } diff --git a/src/tvheadend.h b/src/tvheadend.h index b5bff53bc..98772b85e 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -417,7 +417,7 @@ int tvh_socket(int domain, int type, int protocol); void hexdump(const char *pfx, const uint8_t *data, int len); -uint32_t crc32(uint8_t *data, size_t datalen, uint32_t crc); +uint32_t tvh_crc32(uint8_t *data, size_t datalen, uint32_t crc); int base64_decode(uint8_t *out, const char *in, int out_size); diff --git a/src/utils.c b/src/utils.c index 391597b1e..ca88294f7 100644 --- a/src/utils.c +++ b/src/utils.c @@ -23,7 +23,6 @@ #include #include "tvheadend.h" - /** * CRC32 */ @@ -74,7 +73,7 @@ static uint32_t crc_tab[256] = { }; uint32_t -crc32(uint8_t *data, size_t datalen, uint32_t crc) +tvh_crc32(uint8_t *data, size_t datalen, uint32_t crc) { while(datalen--) crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff]; diff --git a/src/webui/webui.c b/src/webui/webui.c index cb0d990ef..8ad7d24da 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -41,8 +42,6 @@ #include "plumbing/globalheaders.h" #include "epg.h" -struct filebundle *filebundles; - /** * */ @@ -85,12 +84,13 @@ page_root(http_connection_t *hc, const char *remain, void *opaque) static int page_static_file(http_connection_t *hc, const char *remain, void *opaque) { + int ret = 0; const char *base = opaque; - - int fd; char path[500]; - struct stat st; + ssize_t size; const char *content = NULL, *postfix; + char buf[4096]; + const char *gzip; if(remain == NULL) return 404; @@ -98,6 +98,9 @@ page_static_file(http_connection_t *hc, const char *remain, void *opaque) if(strstr(remain, "..")) return HTTP_STATUS_BAD_REQUEST; + snprintf(path, sizeof(path), "%s/%s", base, remain); + printf("path = %s\n", path); + postfix = strrchr(remain, '.'); if(postfix != NULL) { postfix++; @@ -105,24 +108,30 @@ page_static_file(http_connection_t *hc, const char *remain, void *opaque) content = "text/javascript; charset=UTF-8"; } - snprintf(path, sizeof(path), "%s/%s", base, remain); - - if((fd = tvh_open(path, O_RDONLY, 0)) < 0) { - tvhlog(LOG_ERR, "webui", - "Unable to open file %s -- %s", path, strerror(errno)); - return 404; + // TODO: handle compression + fb_file *fp = fb_open(path, 0, 1); + if (!fp) { + tvhlog(LOG_ERR, "webui", "failed to open %s", path); + return 500; } - if(fstat(fd, &st) < 0) { - tvhlog(LOG_ERR, "webui", - "Unable to stat file %s -- %s", path, strerror(errno)); - close(fd); - return 404; + size = fb_size(fp); + gzip = fb_gzipped(fp) ? "gzip" : NULL; + + http_send_header(hc, 200, content, size, gzip, NULL, 10, 0, NULL); + while (!fb_eof(fp)) { + ssize_t c = fb_read(fp, buf, sizeof(buf)); + if (c < 0) { + ret = 500; + break; + } + if (write(hc->hc_fd, buf, c) != c) { + ret = 500; + break; + } } + fb_close(fp); - http_send_header(hc, 200, content, st.st_size, NULL, NULL, 10, 0, NULL); - sendfile(hc->hc_fd, fd, NULL, st.st_size); - close(fd); - return 0; + return ret; } /** @@ -613,6 +622,7 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque) /** * Static download of a file from an embedded filebundle */ +#if 0 static int page_static_bundle(http_connection_t *hc, const char *remain, void *opaque) { @@ -644,6 +654,7 @@ page_static_bundle(http_connection_t *hc, const char *remain, void *opaque) } return 404; } +#endif /** @@ -766,34 +777,7 @@ static void webui_static_content(const char *content_path, const char *http_path, const char *source) { - char path[256]; - struct stat st; - struct filebundle *fb; - - if(content_path != NULL) { - snprintf(path, sizeof(path), "%s/%s", content_path, source); - - if(!stat(path, &st) && S_ISDIR(st.st_mode)) { - - http_path_add(http_path, strdup(path), - page_static_file, ACCESS_WEB_INTERFACE); - return; - } - } - - for(fb = filebundles; fb != NULL; fb = fb->next) { - if(!strcmp(source, fb->prefix)) { - http_path_add(http_path, fb, - page_static_bundle, ACCESS_WEB_INTERFACE); - return; - } - } - - tvhlog(LOG_ERR, "webui", - "No source path providing HTTP content: \"%s\". " - "Checked in \"%s\" and in the binary's embedded file system. " - , http_path, content_path); - + http_path_add(http_path, strdup(source), page_static_file, ACCESS_WEB_INTERFACE); } diff --git a/support/mkbundle.py b/support/mkbundle.py new file mode 100755 index 000000000..e1fb053ee --- /dev/null +++ b/support/mkbundle.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python +# +# Replacement for old mkbundle script that creates a full file heirarchy +# + +import os, sys, re +import gzip, cStringIO +from optparse import OptionParser + +# Add reverse path split +def rsplit ( p ): + i = p.find(os.path.sep) + if i != -1: + return (p[:i], p[i+1:]) + else: + return (p, '') + +# Process command line +optp = OptionParser() +optp.add_option('-z', '--gzip', action='store_true', + help='Compress the files with gzip') +optp.add_option('-l', '--gzlevel', type='int', default=9, + help='Specify compression level if using gzip') +optp.add_option('-o', '--output', default=None, + help='Specify output file (default /dev/stdout)') +optp.add_option('-d', '--deps', default=None, + help='Specify deps file to update during run') +(opts, args) = optp.parse_args() + +# Setup +outf = sys.stdout +if opts.output: + outf = open(opts.output, 'w') +depf = None +if opts.deps: + depf = open(opts.deps, 'w') + print >>depf, '%s: \\' % opts.output + +# Build heirarchy +root = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '..')) +ents = {} +for path in args: + for (p, ds, fs) in os.walk(path): + p = os.path.abspath(p) + n = p.replace(root+'/', '') + t = ents + while True: + (d,n) = rsplit(n) + if d not in t: + t[d] = {} + t = t[d] + if not n: break + for f in fs: + t[f] = None + +# Output a file +def output_file ( path, name, idx, next = -1 ): + n = 'NULL' + if next >= 0: n = '&filebundle_entry_%06d' % next + p = os.path.join(root, path, name); + + # Dep file + if depf: print >>depf, ' %s\\' % p + + # First the data + print >>outf, '// FILE : %s %s %d %d' % (path, name, idx, next) + print >>outf, 'static const uint8_t filebundle_data_%06d[] = {' % idx, + o = -1 + d = open(p, 'rb').read() + if opts.gzip: + o = len(d) + l = opts.gzlevel + t = cStringIO.StringIO() + if l < 0: l = 1 + if l > 9: l = 9 + z = gzip.GzipFile(filename=name, mode='w', compresslevel=l, fileobj=t) + z.write(d) + z.close() + d = t.getvalue() + t.close() + i = 0 + for b in d: + if not (i % 12): print >>outf, '\n ', + print >>outf, '0x%02x,' % ord(b), + i = i + 1 + print >>outf, '' + print >>outf, '};' + + print >>outf, 'static filebundle_entry_t filebundle_entry_%06d = {' % idx + print >>outf, ' .type = FB_FILE,' + print >>outf, ' .name = "%s",' % name + print >>outf, ' .next = %s,' % n + print >>outf, ' .f.size = %d,' % len(d) + print >>outf, ' .f.orig = %d,' % o + print >>outf, ' .f.data = filebundle_data_%06d,' % idx + print >>outf, '};' + print >>outf, '' + +# Output a directory +def output_dir ( path, name, idx, child, next = -1 ): + n = 'NULL' + if next >= 0: n = '&filebundle_entry_%06d' % next + print >>outf, '// DIR: %s %s %d %d %d' % (path, name, idx, child, next) + print >>outf, 'static filebundle_entry_t filebundle_entry_%06d = {' % idx + print >>outf, ' .type = FB_DIR,' + print >>outf, ' .name = "%s",' % name + print >>outf, ' .next = %s,' % n + print >>outf, ' .d.child = &filebundle_entry_%06d,' % child + print >>outf, '};' + print >>outf, '' + +# Create output +def add_entry ( ents, path = "", name = "", idx = -1, next = -1 ): + + # Add children + d = os.path.join(path, name) + p = -1 + for k in ents: + + # File + if not ents[k]: + output_file(d, k, idx+1, p) + p = idx = idx + 1 + + # Directory + else: + tmp = add_entry(ents[k], d, k, idx, p) + if tmp != idx: + p = idx = tmp + + # Add directory + if p >= 0: + if name: + output_dir(path, name, idx+1, p, next) + idx = idx + 1 + + return idx + +# Output header +print >>outf, '// Auto-generated - DO NOT EDIT' +print >>outf, '// COMMAND: [%s]' % (' '.join(sys.argv)) +print >>outf, '' +print >>outf, '#include "filebundle.h"' +print >>outf, '' + +# Output entries +idx = add_entry(ents) + +# Output top link +print >>outf, 'filebundle_entry_t *filebundle_root = &filebundle_entry_%06d;' % idx