diff --git a/descriptor.c b/descriptor.c index 66d82b7..8b87425 100644 --- a/descriptor.c +++ b/descriptor.c @@ -19,7 +19,7 @@ read_descriptor_header(struct epg *epg) { struct descriptor *d; - d = (struct descriptor *)malloc(sizeof(struct descriptor)); + d = (struct descriptor *)calloc(sizeof(struct descriptor), 1); d->loaded = 0; if (epg->binsize - epg->offset < 2) @@ -120,15 +120,37 @@ read_descriptor(struct epg *epg, struct descriptor *d) break; case DS_USER_DEFINED: - d->content.d137.text = - read_lstring(epg, &d->content.d137.textlen); + { + int sofar = 0; + + /* reserved:6 guidance_type:2 */ + d->content.d137.guidance_type = epg->bin[epg->offset++] & 0x3; + sofar++; + switch (d->content.d137.guidance_type) + { + case 0: + /* guidance_type 0 means unsuitable for broadcast + * prior to the watershed so set mode to 1. */ + d->content.d137.guidance_mode = 1; + break; + case 1: + /* reserved:7 guidance_mode:1 */ + d->content.d137.guidance_mode = + epg->bin[epg->offset++] & 0x1; + sofar++; + break; + default: + /* Unknown guidance type.. */ + break; + } memcpy(d->content.d137.lang, epg->bin + epg->offset, 3); epg->offset += 3; - d->content.d137.warning = string_to_end(epg, d, - 4 + d->content.d137.textlen, + sofar += 3; + d->content.d137.warning = string_to_end(epg, d, sofar, &d->content.d137.warninglen); break; + } case DS_LINKAGE: memcpy(&d->content.d74, epg->bin + epg->offset, @@ -252,8 +274,6 @@ free_descriptor(struct descriptor *d) break; case DS_USER_DEFINED: - if (d->content.d137.text) - free(d->content.d137.text); if (d->content.d137.warning) free(d->content.d137.warning); break; @@ -373,9 +393,9 @@ dump_descriptor(struct descriptor *d, int content) break; case DS_USER_DEFINED: - DUMPINT(d, content.d137.textlen); + DUMPINT(d, content.d137.guidance_type); + DUMPINT(d, content.d137.guidance_mode); DUMPNSTR(d, content.d137.lang, 3); - DUMPHEX(d, content.d137.text, d->content.d137.textlen); DUMPHEX(d, content.d137.warning, d->content.d137.warninglen); break; diff --git a/descriptor.h b/descriptor.h index c5ddb96..e0e6799 100644 --- a/descriptor.h +++ b/descriptor.h @@ -9,9 +9,9 @@ #define DS_COMPONENT 80 /* 0x50 */ #define DS_CONTENT 84 /* 0x54 */ #define DS_PRIVATE_DATA_SPECIFIER 95 /* 0x5f */ -#define DS_CONTENT_IDENTIFIER 118 -#define DS_FTA_CONTENT_MGMT 126 -#define DS_USER_DEFINED 137 +#define DS_CONTENT_IDENTIFIER 118 /* 0x76 */ +#define DS_FTA_CONTENT_MGMT 126 /* 0x7e */ +#define DS_USER_DEFINED 137 /* 0x89 */ #define PARSER_SHORT_EVENT 0 #define PARSER_USER_DEFINED 1 @@ -67,12 +67,12 @@ struct descriptor { unsigned int user:8; } d84; /* CONTENT */ struct { - unsigned int textlen; - char *text; + unsigned int guidance_type; + unsigned int guidance_mode; char lang[3]; char *warning; unsigned int warninglen; - } d137; /* USER_DEFINED - content warnings? */ + } d137; /* USER_DEFINED - content warnings */ struct { unsigned int no_revocation:1; unsigned int control_remote_access:2; diff --git a/epg.c b/epg.c index 01fc19a..edfddbf 100644 --- a/epg.c +++ b/epg.c @@ -410,6 +410,13 @@ parse(char *epgpath, check_filter(filter, FILTER_CONTENT, dslist[PARSER_CONTENT]->content.d84.level1, NULL)) + ) && ( + !(filterflags & FILTER_GUIDETYPE) || + (dslist[PARSER_USER_DEFINED] && + check_filter(filter, FILTER_GUIDETYPE, + dslist[PARSER_USER_DEFINED]-> + content.d137.guidance_type, + NULL)) ) ) callback(epg, s, d, dslist, val); diff --git a/epg.h b/epg.h index ca06b16..1a1858a 100644 --- a/epg.h +++ b/epg.h @@ -71,7 +71,8 @@ enum epgfiltertype { FILTER_CRID = 0x10, FILTER_SCRID = 0x20, FILTER_CONTENT = 0x40, - FILTER_TIMERANGE = 0x80 + FILTER_TIMERANGE = 0x80, + FILTER_GUIDETYPE = 0x100 }; enum matchtype { FT_EQUAL, FT_RANGE, FT_GREATER, FT_LESS }; diff --git a/main.c b/main.c index 5641dac..7424430 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,6 @@ /* * Humax EPG Tool - * by af123, 2011 - 2013 + * by af123, 2011 - 2015 */ #include @@ -21,7 +21,7 @@ #include "lint.h" int debug = 0; -const char *version = "1.0.14"; +const char *version = "1.0.15"; unsigned long sysopts = 0; unsigned long filterflags = 0; static time_t latest_stamp = 0; @@ -34,7 +34,7 @@ sqlite3_stmt *stmt; int syntax() { - fprintf(stderr, "Humax EPG Tool v%s, by af123, 2011-2013.\n\n", + fprintf(stderr, "Humax EPG Tool v%s, by af123, 2011-2015.\n\n", version); fprintf(stderr, "Syntax: epg [options] [filters] ...\n\n"); @@ -44,7 +44,7 @@ syntax() " -d[level] Set debug level.\n" " -f Specify alternate EPG data file.\n" " -h Show help text.\n" - " -p Parsable output.\n" + " -p Parsable output (see *).\n" "\n" ); fprintf(stderr, @@ -54,6 +54,8 @@ syntax() " -E Show only selected event.\n" " -R Show only events with this Series ID.\n" " -S Show only selected service.\n" + " -G Show only records with this guidance " + "type\n" ); fprintf(stderr, " -T Show only selected content types.\n" @@ -83,6 +85,14 @@ syntax() " searchall " "Search programme names/descriptions for text.\n" ); + + fprintf(stderr, "\n" +"* Parseable output is tab delimited and contains the following fields:\n" +" Service ID, Event ID, Start, Duration, Encrypted,\n" +" Title, Synopsis, Warning, Content Code, Content Type,\n" +" Event CRID, Series CRID, Recommended CRID, Guidance Mode\n" + ); + fprintf(stderr, "\n"); return 0; } @@ -249,8 +259,8 @@ sqlitedumpstart(char *file) "create table epg (" "[service_id] integer, [event_id] integer, " "[start] integer, [end] integer, [duration] integer, " - "[name] text, [text] text, [warning] text, " - "[content_type] integer, [content] text, " + "[name] text, [text] text, [warning] text, [warning_mode] integer, " + "[content_code] integer, [content_type] text, " "[event_crid] text, [series_crid] text, [rec_crid] text)" ); EXEC("create index tm on epg(start,end)"); @@ -314,33 +324,34 @@ sqlitedump(struct epg *epg __attribute__((unused)), DECOMPRESS(d->content.d137.warning, d->content.d137.warninglen); sqlite3_bind_text(stmt, 8, d->content.d137.warning, -1, NULL); + sqlite3_bind_int(stmt, 9, d->content.d137.guidance_mode); } if (ds[PARSER_CONTENT]) { - sqlite3_bind_int(stmt, 9, + sqlite3_bind_int(stmt, 10, ds[PARSER_CONTENT]->content.d84.level1); - sqlite3_bind_text(stmt, 10, content_type(ds[PARSER_CONTENT]), + sqlite3_bind_text(stmt, 11, content_type(ds[PARSER_CONTENT]), -1, NULL); } if (ds[PARSER_CRID_EVENT]) { struct descriptor *d = ds[PARSER_CRID_EVENT]; - sqlite3_bind_text(stmt, 11, d->content.d118.crids[0].crid, + sqlite3_bind_text(stmt, 12, d->content.d118.crids[0].crid, -1, NULL); } if (ds[PARSER_CRID_SERIES]) { struct descriptor *d = ds[PARSER_CRID_SERIES]; - sqlite3_bind_text(stmt, 12, d->content.d118.crids[0].crid, + sqlite3_bind_text(stmt, 13, d->content.d118.crids[0].crid, -1, NULL); } if (ds[PARSER_CRID_REC]) { struct descriptor *d = ds[PARSER_CRID_REC]; - sqlite3_bind_text(stmt, 13, d->content.d118.crids[0].crid, + sqlite3_bind_text(stmt, 14, d->content.d118.crids[0].crid, -1, NULL); } @@ -420,12 +431,16 @@ json(struct epg *epg __attribute__((unused)), printf(",\n \"warning\": \"%s\"", json_escape_len(d->content.d137.warninglen, d->content.d137.warning)); + printf(",\n \"guidance_type\": \"%d\"", + d->content.d137.guidance_type); + printf(",\n \"guidance_mode\": \"%d\"", + d->content.d137.guidance_mode); } if (ds[PARSER_CONTENT]) { - printf(",\n \"content_type\": \"%d\"", + printf(",\n \"content_code\": \"%d\"", ds[PARSER_CONTENT]->content.d84.level1); - printf(",\n \"content\": \"%s\"", + printf(",\n \"content_type\": \"%s\"", content_type(ds[PARSER_CONTENT])); } @@ -469,8 +484,9 @@ sqldumpstart() printf("name varchar(255),\n"); printf("text varchar(255),\n"); printf("warning varchar(255),\n"); - printf("content_type bigint unsigned not null,\n"); - printf("content varchar(255),\n"); + printf("guidance_mode tinyint unsigned not null,\n"); + printf("content_code bigint unsigned not null,\n"); + printf("content_type varchar(255),\n"); printf("event_crid varchar(255),\n"); printf("series_crid varchar(255),\n"); printf("rec_crid varchar(255),\n"); @@ -534,12 +550,15 @@ sqldump(struct epg *epg __attribute__((unused)), printf(",\n warning = '%s'", sql_escape_len(d->content.d137.warninglen, d->content.d137.warning)); + printf(",\n guidance_mode = '%d'", + d->content.d137.guidance_mode); } if (ds[PARSER_CONTENT]) { - printf(",\n content_type = %d", + printf(",\n content_code = %d", ds[PARSER_CONTENT]->content.d84.level1); - printf(",\n content = '%s'", content_type(ds[PARSER_CONTENT])); + printf(",\n content_type = '%s'", + content_type(ds[PARSER_CONTENT])); } if (ds[PARSER_CRID_EVENT]) @@ -593,7 +612,8 @@ dump(struct epg *epg __attribute__((unused)), { /* service_id, event_id, start, duration, encrypted, name, text * warning, content code, content type, - * event CRID, series CRID, rec CRID + * event CRID, series CRID, rec CRID, + * guidance mode */ printf("%d\t%d\t%ld\t%d\t%d\t", s->service_id, d->event_id, tm, @@ -656,6 +676,15 @@ dump(struct epg *epg __attribute__((unused)), else printf("\t"); + if (ds[PARSER_USER_DEFINED]) + { + struct descriptor *d137 = ds[PARSER_USER_DEFINED]; + + printf("%d\t", d137->content.d137.guidance_mode); + } + else + printf("\t"); + printf("\n"); return; } @@ -717,6 +746,10 @@ dump(struct epg *epg __attribute__((unused)), safeprintf("%30s: %.*s\n", "Warning", d137->content.d137.warninglen, d137->content.d137.warning); + printf("%30s: %d\n", "Guidance Type", + d137->content.d137.guidance_type); + printf("%30s: %d\n", "Guidance Mode", + d137->content.d137.guidance_mode); } if (ds[PARSER_CONTENT]) @@ -920,6 +953,12 @@ main(int argc, char **argv) add_epgfilter(&filter, FILTER_CONTENT, atoi(cp), 0, NULL, FT_EQUAL); goto nextopt; + + case 'G': + GETOPT; + add_epgfilter(&filter, FILTER_GUIDETYPE, + atoi(cp), 0, NULL, FT_EQUAL); + goto nextopt; } } nextopt: