/* * Humax EPG Tool * by af123, 2011 */ #include #include #include #include #include #include #include #include #include "lint.h" struct descriptor * read_descriptor_header(struct epg *epg) { struct descriptor *d; d = (struct descriptor *)calloc(sizeof(struct descriptor), 1); d->loaded = 0; if (epg->binsize - epg->offset < 2) return NULL; memcpy(d, epg->bin + epg->offset, 2); epg->offset += 2; return d; } static char * string_to_end(struct epg *epg, struct descriptor *d, int sofar, unsigned int *len) { int rest = d->len - sofar; char *str; *len = rest; if (rest <= 0) return NULL;; str = (char *)malloc(rest + 1); memcpy(str, epg->bin + epg->offset, rest); str[rest] = '\0'; epg->offset += rest; return str; } static char * read_lstring(struct epg *epg, unsigned int *len) { unsigned int l = epg->bin[epg->offset++]; char *c; /* Check that there is enough file left. */ if (epg->binsize - epg->offset < l) l = epg->binsize - epg->offset; if (l < 1) return strdup("Short file"); c = malloc(l + 1); memcpy(c, epg->bin + epg->offset, l); c[l] = '\0'; epg->offset += l; if (len) *len = l; return c; } void read_descriptor(struct epg *epg, struct descriptor *d) { d->loaded = 1; switch (d->tag) { case DS_SHORT_EVENT: memcpy(d->content.d77.lang, epg->bin + epg->offset, 3); epg->offset += 3; d->content.d77.name = read_lstring(epg, &d->content.d77.namelen); d->content.d77.text = read_lstring(epg, &d->content.d77.textlen); break; case DS_EXTENDED_EVENT: { unsigned int i; epg->offset++; /* Skip descriptor number and last descriptor */ memcpy(d->content.d78.lang, epg->bin + epg->offset, 3); epg->offset += 3; d->content.d78.items = epg->bin[epg->offset++]; if (debug > 1 && d->content.d78.items > 0) printf("Extended event items: %d\n", d->content.d78.items); /* Skip items. */ for (i = 0; i < d->content.d78.items; i++) { int l = epg->bin[epg->offset++]; epg->offset += l; } d->content.d78.text = read_lstring(epg, &d->content.d78.textlen); break; } case DS_COMPONENT: memcpy(&d->content.d80, epg->bin + epg->offset, 6); epg->offset += 6; d->content.d80.text = string_to_end(epg, d, 6, &d->content.d80.textlen); break; case DS_USER_DEFINED: { 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; 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, sizeof(d->content.d74)); epg->offset += d->len; d->content.d74.tsid = _swap16(d->content.d74.tsid); d->content.d74.orig_netid = _swap16(d->content.d74.orig_netid); d->content.d74.service_id = _swap16(d->content.d74.service_id); if (d->content.d74.linkage_type == 8) d->content.d74.l.l8.id = _swap16(d->content.d74.l.l8.id); else d->content.d74.l.ld.event_id = _swap16(d->content.d74.l.ld.event_id); break; case DS_CONTENT: { unsigned int end = epg->offset + d->len; int cnt = 0; /* A content descriptor can contain multiple types. Only * save the first one at the moment. */ while (epg->offset < end) { if (!cnt++) memcpy(&d->content.d84, epg->bin + epg->offset, sizeof(d->content.d84)); epg->offset += sizeof(d->content.d84); } break; } case DS_FTA_CONTENT_MGMT: memcpy(&d->content.d126, epg->bin + epg->offset, sizeof(d->content.d126)); epg->offset += d->len; break; case DS_CONTENT_IDENTIFIER: { unsigned int end = epg->offset + d->len; d->content.d118.i = 0; while (epg->offset < end) { struct crid *crid = &d->content.d118.crids[d->content.d118.i]; memcpy(crid, epg->bin + epg->offset, 1); epg->offset++; crid->cridlen = crid->ref = 0; switch (crid->location) { case 0: crid->crid = read_lstring(epg, &crid->cridlen); break; case 1: crid->ref = _swap16((uint16_t)epg->bin[epg->offset]); /* Not safe to use post increment in macro */ epg->offset++; break; default: printf("Unknown CRID location - %d\n", crid->location); } if (d->content.d118.i++ >= 3) { printf("Too many content IDs.\n"); exit(0); } } break; } default: printf("WARNING: Default descriptor: %d\n", d->tag); case DS_PRIVATE_DATA_SPECIFIER: d->content.unknown.text = string_to_end(epg, d, 0, &d->content.unknown.textlen); break; } } inline void skip_descriptor(struct epg *epg, struct descriptor *d) { epg->offset += d->len; } void free_descriptor(struct descriptor *d) { if (d->loaded) { switch (d->tag) { case DS_SHORT_EVENT: if (d->content.d77.name) free(d->content.d77.name); if (d->content.d77.text) free(d->content.d77.text); break; case DS_EXTENDED_EVENT: if (d->content.d78.text) free(d->content.d78.text); break; case DS_COMPONENT: if (d->content.d80.text) free(d->content.d80.text); break; case DS_USER_DEFINED: if (d->content.d137.warning) free(d->content.d137.warning); break; case DS_CONTENT_IDENTIFIER: { int i; for (i = 0; i < d->content.d118.i; i++) { struct crid *crid = &d->content.d118.crids[i]; if (crid->cridlen) free(crid->crid); } break; } case DS_CONTENT: case DS_FTA_CONTENT_MGMT: case DS_LINKAGE: break; default: if (d->content.unknown.text) free(d->content.unknown.text); break; } } free(d); } const char * descriptor_name(struct descriptor *d) { switch (d->tag) { case DS_LINKAGE: return "linkage"; case DS_SHORT_EVENT: return "short event"; case DS_COMPONENT: return "component"; case DS_CONTENT: return "content"; case DS_PRIVATE_DATA_SPECIFIER: return "private data spec."; case DS_CONTENT_IDENTIFIER: return "content id"; case DS_FTA_CONTENT_MGMT: return "content mgmt"; case DS_USER_DEFINED: return "user defined"; } return "Unknown"; } const char * content_type(struct descriptor *d) { switch (d->content.d84.level1) { case 0x1: return "Film/Drama"; case 0x2: return "News/Current affairs"; case 0x3: return "Show/Game show"; case 0x4: return "Sport"; case 0x5: return "Children"; case 0x6: return "Music/Ballet/Dance"; case 0x7: return "Arts/Culture"; case 0x8: return "Social/Political/Economic"; case 0x9: return "Education/Science/Factual"; case 0xa: return "Leisure"; case 0xf: return "Drama"; } return "Undefined"; } void dump_descriptor(struct descriptor *d, int content) { printf("Descriptor header:\n"); printf(" %30s: %#x [%d] (%s)\n", "descriptor", d->tag, d->tag, descriptor_name(d)); DUMPINT(d, len); if (!content || !d->loaded) return; switch (d->tag) { case DS_SHORT_EVENT: DUMPNSTR(d, content.d77.lang, 3); DUMPINT(d, content.d77.namelen); DUMPHEX(d, content.d77.name, d->content.d77.namelen); DUMPINT(d, content.d77.textlen); DUMPHEX(d, content.d77.text, d->content.d77.textlen); break; case DS_EXTENDED_EVENT: DUMPNSTR(d, content.d78.lang, 3); DUMPINT(d, content.d78.items); DUMPINT(d, content.d78.textlen); DUMPHEX(d, content.d78.text, d->content.d78.textlen); break; case DS_COMPONENT: DUMPINT(d, content.d80.stream_content); DUMPINT(d, content.d80.reserved); DUMPINT(d, content.d80.type); DUMPINT(d, content.d80.tag); DUMPNSTR(d, content.d80.lang, 3); if (d->content.d80.textlen) DUMPHEX(d, content.d80.text, d->content.d80.textlen); break; case DS_USER_DEFINED: DUMPINT(d, content.d137.guidance_type); DUMPINT(d, content.d137.guidance_mode); DUMPNSTR(d, content.d137.lang, 3); DUMPHEX(d, content.d137.warning, d->content.d137.warninglen); break; case DS_CONTENT_IDENTIFIER: { int i; DUMPINT(d, content.d118.i); for (i = 0; i < d->content.d118.i; i++) { struct crid *crid = &d->content.d118.crids[i]; DUMPINT(crid, location); DUMPINT(crid, type); DUMPINT(crid, cridlen); if (crid->cridlen) DUMPHEX(crid, crid, crid->cridlen); DUMPINT(crid, ref); } break; } case DS_CONTENT: DUMPINT(d, content.d84.level1); DUMPINT(d, content.d84.level2); DUMPINT(d, content.d84.user); printf("%30s: %s\n", "Type", content_type(d)); break; case DS_FTA_CONTENT_MGMT: DUMPINT(d, content.d126.reserved); DUMPINT(d, content.d126.no_scramble); DUMPINT(d, content.d126.control_remote_access); DUMPINT(d, content.d126.no_revocation); break; case DS_LINKAGE: DUMPINT(d, content.d74.tsid); DUMPINT(d, content.d74.orig_netid); DUMPINT(d, content.d74.service_id); DUMPINT(d, content.d74.linkage_type); if (d->content.d74.linkage_type == 8) { DUMPINT(d, content.d74.l.l8.handover_type); DUMPINT(d, content.d74.l.l8.reserved); DUMPINT(d, content.d74.l.l8.origin_type); DUMPINT(d, content.d74.l.l8.id); } else if (d->content.d74.linkage_type == 13) { DUMPINT(d, content.d74.l.ld.event_id); DUMPINT(d, content.d74.l.ld.listed); DUMPINT(d, content.d74.l.ld.simulcast); DUMPINT(d, content.d74.l.ld.reserved); } break; default: if (d->content.unknown.textlen) DUMPHEX(d, content.unknown.text, d->content.unknown.textlen); break; } }