/* * Humax EPG Tool * by af123, 2011 */ #include #include #include #include #include #include #include #include #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; } int 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 0; 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->offset < epg->binsize && 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, 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, (unsigned long) tm, (unsigned long) (tm + dur))) { /* Skip this event. */ epg->offset = dend; continue; } } if ((filterflags & FILTER_TIMERANGE)) { if (!check_filter_timerange(filter, FILTER_TIMERANGE, (unsigned long) tm, (unsigned long) (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: if (!(ds = read_descriptor_header(epg))) break; read_descriptor(epg, ds); dslist[PARSER_FTA_CONTENT_MGMT] = 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); return 1; } 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; }