epg/epg.c

459 lines
9.7 KiB
C

/*
* Humax EPG Tool
* by af123, 2011
*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <time.h>
#include <strings.h>
#include "lint.h"
struct section *
read_section(struct epg *epg)
{
static struct section h;
if (epg->binsize - epg->offset < sizeof(h))
return NULL;
memcpy(&h, epg->bin + epg->offset, sizeof(h));
if (memcmp(h.magic, SECTION_MAGIC, sizeof(h.magic) - 1))
{
printf("Section header magic mismatch.\n");
hexdump(epg->bin + epg->offset, 64, 0);
return NULL;
}
h.total_length = _swap16(h.total_length);
h.service_id = _swap16(h.service_id);
h.transport_stream_id = _swap16(h.transport_stream_id);
h.original_network_id = _swap16(h.original_network_id);
h.u1.comp = _swap16(h.u1.comp);
epg->offset += sizeof(h);
return &h;
}
void
dump_section(struct section *h)
{
printf("Section header:\n");
DUMPINT(h, total_length);
DUMPINT(h, table_id);
DUMPINT(h, u1.u.syntax_indicator);
DUMPINT(h, u1.u.reserved);
DUMPINT(h, u1.u.length);
DUMPINT(h, service_id);
DUMPINT(h, reserved2);
DUMPINT(h, version_number);
DUMPINT(h, current_next_indicator);
DUMPINT(h, section_number);
DUMPINT(h, last_section_number);
DUMPINT(h, transport_stream_id);
DUMPINT(h, original_network_id);
DUMPINT(h, segment_last_section_number);
DUMPINT(h, last_table_id);
}
struct data *
read_data(struct epg *epg)
{
static struct data d;
if (epg->binsize - epg->offset < sizeof(d))
return NULL;
memcpy(&d, epg->bin + epg->offset, sizeof(d));
d.event_id = _swap16(d.event_id);
d.start_date = _swap16(d.start_date);
d.u1.comp = _swap16(d.u1.comp);
d.start_hour = bcd(d.start_hour);
d.start_min = bcd(d.start_min);
d.start_sec = bcd(d.start_sec);
d.dur_hour = bcd(d.dur_hour);
d.dur_min = bcd(d.dur_min);
d.dur_sec = bcd(d.dur_sec);
epg->offset += sizeof(d);
return &d;
}
void
dump_data(struct data *d)
{
time_t tm;
printf("Data:\n");
DUMPINT(d, event_id);
tm = mjd(d->start_date, d->start_hour, d->start_min, d->start_sec);
printf(" %30s: %#x %d:%02d:%02d (%s) [%ld]\n", "start_date",
d->start_date, d->start_hour, d->start_min, d->start_sec,
ctime_nl(&tm), tm);
printf(" %30s: %d:%02d:%02d\n", "duration",
d->dur_hour, d->dur_min, d->dur_sec);
DUMPINT(d, u1.u.running_status);
DUMPINT(d, u1.u.free_CA_mode);
DUMPINT(d, u1.u.descriptors_loop_length);
}
static inline int
check_filter_timerange(struct epgfilter *f, enum epgfiltertype type,
unsigned long idl, unsigned long idh)
{
if (!f)
return 1;
for (; f; f = f->next)
{
if (f->type != type)
continue;
if (!(
(idl > f->num && idl < f->num2) ||
(idh > f->num && idh < f->num2) ||
(idl < f->num && idh > f->num))
)
return 0;
}
return 1;
}
static inline int
check_filter_range(struct epgfilter *f, enum epgfiltertype type,
unsigned long idl, unsigned long idh)
{
if (!f)
return 1;
for (; f; f = f->next)
{
if (f->type != type)
continue;
if (f->match == FT_RANGE)
{
if (idl < f->num || idl > f->num2)
return 0;
}
else if (f->num < idl || f->num > idh)
return 0;
}
return 1;
}
static inline int
check_filter(struct epgfilter *f, enum epgfiltertype type,
unsigned long id, char *str)
{
if (!f)
return 1;
for (; f; f = f->next)
{
if (f->type == type)
{
if (type == FILTER_CRID || type == FILTER_SCRID)
{
if (strcmp(str, f->str))
return 0;
}
else if (f->num != id)
return 0;
}
}
return 1;
}
void
parse(char *epgpath,
void (*callback)(struct epg *, struct section *, struct data *,
struct descriptor **, void *), void *val, struct epgfilter *filter)
{
struct descriptor *dslist[PARSER_MAX];
struct epg *epg;
int i;
if (!(epg = open_file(epgpath)))
return;
while (epg->offset < epg->binsize)
{
struct section *s;
uint32_t send;
if (!(s = read_section(epg)))
break;
send = epg->offset - 11 + s->u1.u.length - 4;
if ((filterflags & FILTER_SERVICE) &&
!check_filter(filter, FILTER_SERVICE, s->service_id, NULL))
{
/* Skip this service. */
epg->offset += s->total_length - 14;
/* Skip padding bytes... */
while (epg->bin[epg->offset] == 'U')
epg->offset++;
continue;
}
while (epg->offset < send)
{
struct data *d;
uint32_t dstart, dend;
dstart = epg->offset;
if (!(d = read_data(epg)))
break;
dend = epg->offset + d->u1.u.descriptors_loop_length;
if ((filterflags & FILTER_EVENT) &&
!check_filter(filter, FILTER_EVENT, d->event_id,
NULL))
{
/* Skip this event. */
epg->offset = dend;
continue;
}
if ((filterflags &
(FILTER_TIMESTAMP | FILTER_TIMERANGE)))
{
time_t tm;
int dur;
tm = mjd(d->start_date, d->start_hour,
d->start_min, d->start_sec);
dur = d->dur_hour * 3600 + d->dur_min * 60 +
d->dur_sec;
if ((filterflags & FILTER_TIMESTAMP))
{
if (!check_filter_range(filter,
FILTER_TIMESTAMP, tm, tm + dur))
{
/* Skip this event. */
epg->offset = dend;
continue;
}
}
if ((filterflags & FILTER_TIMERANGE))
{
if (!check_filter_timerange(filter,
FILTER_TIMERANGE, tm, tm + dur))
{
/* Skip this event. */
epg->offset = dend;
continue;
}
}
}
if (debug)
hexdump(epg->bin + dstart, dend - dstart,
dstart);
for (i = 0; i < PARSER_MAX; i++)
dslist[i] = NULL;
while (epg->offset < dend)
{
unsigned int dstag = epg->bin[epg->offset];
unsigned int dslen = epg->bin[epg->offset + 1];
struct descriptor *ds;
if ((filterflags & FILTER_DESCRIPTOR) &&
!check_filter(filter, FILTER_DESCRIPTOR,
dstag, NULL))
{
epg->offset += dslen + 2;
continue;
}
if (debug)
printf("Checking descriptor: "
"%d/%#x (%d)\n",
dstag, dstag, dslen);
if (debug > 3)
hexdump(epg->bin + epg->offset,
dslen + 2, 0);
switch (dstag)
{
case DS_SHORT_EVENT:
if (!(ds = read_descriptor_header(epg)))
break;
read_descriptor(epg, ds);
dslist[PARSER_SHORT_EVENT] = ds;
break;
case DS_EXTENDED_EVENT:
if (!(ds = read_descriptor_header(epg)))
break;
read_descriptor(epg, ds);
dslist[PARSER_EXTENDED_EVENT] = ds;
break;
case DS_CONTENT_IDENTIFIER:
if (!(ds = read_descriptor_header(epg)))
break;
read_descriptor(epg, ds);
switch (ds->content.d118.crids[0].type)
{
case CRIDT_EVENT:
dslist[PARSER_CRID_EVENT] = ds;
break;
case CRIDT_SERIES:
dslist[PARSER_CRID_SERIES] = ds;
break;
case CRIDT_REC:
dslist[PARSER_CRID_REC] = ds;
break;
}
break;
case DS_USER_DEFINED:
if (!(ds = read_descriptor_header(epg)))
break;
read_descriptor(epg, ds);
dslist[PARSER_USER_DEFINED] = ds;
break;
case DS_CONTENT:
if (!(ds = read_descriptor_header(epg)))
break;
read_descriptor(epg, ds);
dslist[PARSER_CONTENT] = ds;
break;
/*
case DS_FTA_CONTENT_MGMT:
read_descriptor(epg, ds);
dump_descriptor(ds, 1);
free_descriptor(ds);
break;
case DS_LINKAGE:
read_descriptor(epg, ds);
dump_descriptor(ds, 1);
free_descriptor(ds);
break;
*/
default:
if (debug > 2)
printf("! Unhandled.\n");
epg->offset += dslen + 2;
break;
}
}
/* Move synopsis from extended event descriptor into
* short description if appropriate. */
if (dslist[PARSER_SHORT_EVENT] &&
dslist[PARSER_EXTENDED_EVENT] &&
dslist[PARSER_EXTENDED_EVENT]->content.d78.textlen
> 0 &&
dslist[PARSER_SHORT_EVENT]->content.d77.textlen
== 2 &&
dslist[PARSER_SHORT_EVENT]->content.d77.text[1]
== '\0')
{
if (debug > 3)
printf("Moving event text from "
"extended to short event.\n");
free(dslist[PARSER_SHORT_EVENT]
->content.d77.text);
dslist[PARSER_SHORT_EVENT]->content.d77.text
= dslist[PARSER_EXTENDED_EVENT]->
content.d78.text;
dslist[PARSER_SHORT_EVENT]->content.d77.textlen
= dslist[PARSER_EXTENDED_EVENT]->
content.d78.textlen;
dslist[PARSER_EXTENDED_EVENT]->content.d78.text
= (char *)NULL;
}
if (
(
!(filterflags & FILTER_CRID) ||
(dslist[PARSER_CRID_EVENT] &&
check_filter(filter, FILTER_CRID, 0,
dslist[PARSER_CRID_EVENT]->content.d118.
crids[0].crid))
) && (
!(filterflags & FILTER_SCRID) ||
(dslist[PARSER_CRID_SERIES] &&
check_filter(filter, FILTER_SCRID, 0,
dslist[PARSER_CRID_SERIES]->content.d118.
crids[0].crid))
) && (
!(filterflags & FILTER_CONTENT) ||
(dslist[PARSER_CONTENT] &&
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);
for (i = 0; i < PARSER_MAX; i++)
if (dslist[i])
free_descriptor(dslist[i]);
}
/* Skip CRC */
epg->offset += 4;
/* Skip padding bytes... */
while (epg->offset < epg->binsize &&
epg->bin[epg->offset] == 'U')
epg->offset++;
}
close_file(epg);
}
void
add_epgfilter(struct epgfilter **filter, enum epgfiltertype type,
unsigned long num, unsigned long num2, char *str, enum matchtype match)
{
struct epgfilter *f = malloc(sizeof(struct epgfilter));
f->type = type;
f->num = num;
f->num2 = num2;
f->str = str;
f->match = match;
filterflags |= type;
f->next = *filter;
*filter = f;
}