#include #include #include #include #include #include #include #include #include "lint.h" #define STRING(addr, x, len) hmt->x = (char *)strip_string(hmt->bin + addr) #define LE32(addr, x) hmt->x = read_uint32(hmt->bin + addr, 1) #define LE16(addr, x) hmt->x = read_uint16(hmt->bin + addr, 1) #define LE8(addr, x) hmt->x = hmt->bin[addr] & 0xff #define PTR32(addr, x) hmt->x = (uint32_t *)(hmt->bin + addr); /* ETSI EN 300 468 Annex A.2 */ uint8_t * strip_string(uint8_t *str) { if (*str >= 0x20) return str; if (*str == 0x10) return str + 3; if (*str == 0x1f) return str + 2; return str + 1; } void parse_hmt(struct hmt *hmt) { STRING( 0x80, directory, 254); STRING( 0x17f, filename, 256); LE32( 0x280, start); LE32( 0x284, end); LE16( 0x288, duration); LE8( 0x28c, recstatus); LE8( 0x28d, flags1); LE8( 0x28e, flags2); LE8( 0x290, failreason); LE32( 0x294, resume); LE16( 0x298, num_bookmarks); STRING( 0x29a, mediatitle, 48); PTR32( 0x31c, bookmarks); LE8( 0x3dc, flags3); LE8( 0x3e0, guidance_type); LE8( 0x3e1, guidance_mode); STRING( 0x3e2, guidance, 75); LE8( 0x431, copycount); LE16( 0x434, tzoffset); LE32( 0x458, lcn); LE8( 0x4b8, type); STRING( 0x45c, channel, 43); LE16( 0x488, service_id); LE16( 0x48a, tsid); LE16( 0x48c, onid); LE16( 0x48e, pmt_pid); LE16( 0x490, video_pid); // LE8( 0x498, definition); LE8( 0x4bc, definition); LE16( 0x49c, audio_pid); LE32( 0x4f8, schedstart); LE32( 0x4fc, scheddur); LE8( 0x514, genre); STRING( 0x516, title, 51); STRING( 0x616, synopsis, 255); LE16( 0x716, event_id); LE8( 0x73d, xflags1); LE8( 0x73e, xflags2); LE8( 0x73f, xflags3); LE8( 0x10, series); LE8( 0x11, episode); LE8( 0x12, episodetot); } int hmt_is(struct hmt *hmt, enum hmt_attribute attr) { switch (attr) { case HMTA_ENCRYPTED: return hmt->flags2 & 0x1; case HMTA_THUMBNAIL: return hmt->flags2 & 0x2; case HMTA_GHOST: return hmt->flags2 & 0x8; case HMTA_LOCKED: return hmt->flags1 & 0x4; case HMTA_NEW: return !(hmt->flags1 & 0x8); case HMTA_ENCFLAGGED: return hmt->flags3 != 4; case HMTA_UNLIMITEDCOPY: return hmt->flags3 & 0x4; case HMTA_SHRUNK: return hmt->xflags1 == 'E'; case HMTA_DEDUPED: return hmt->xflags2 == 'N'; case HMTA_DETECTADS: return hmt->xflags3 == 'G'; case HMTA_RADIO: return hmt->type == 0x2; default: fprintf(stderr, "Unhandled hmt_is, %d\n", attr); break; } return 0; } char * hmt_flags(struct hmt *hmt) { static char buf[0x400]; int g; *buf = '\0'; if (hmt->definition == DEF_HD) strcat(buf, "HD,"); else strcat(buf, "SD,"); if (hmt_is(hmt, HMTA_LOCKED)) strcat(buf, "Locked,"); if (hmt_is(hmt, HMTA_NEW)) strcat(buf, "New,"); if (hmt_is(hmt, HMTA_UNLIMITEDCOPY)) strcat(buf, "Unlimited Copies,"); if (hmt_is(hmt, HMTA_ENCFLAGGED)) strcat(buf, "Encrypted,"); if ((g = guidance(hmt)) == 2) strcat(buf, "GGuidance,"); else if (g == 1) strcat(buf, "Guidance,"); if (hmt_is(hmt, HMTA_ENCRYPTED)) strcat(buf, "ODEncrypted,"); if (hmt_is(hmt, HMTA_THUMBNAIL)) strcat(buf, "Thumbnail,"); if (hmt_is(hmt, HMTA_SHRUNK)) strcat(buf, "Shrunk,"); if (hmt_is(hmt, HMTA_DEDUPED)) strcat(buf, "Deduped,"); if (hmt_is(hmt, HMTA_DETECTADS)) strcat(buf, "Addetection,"); if (hmt_is(hmt, HMTA_RADIO)) strcat(buf, "Radio,"); if (hmt_is(hmt, HMTA_GHOST)) strcat(buf, "Ghost,"); return buf; } struct { unsigned char genre; char *text; } genretab[] = { { 0x00, "Unclassified" }, { 0x10, "Film" }, { 0x20, "News & Factual" }, { 0x30, "Entertainment" }, { 0x40, "Sport" }, { 0x50, "Children" }, { 0x60, "Entertainment" }, { 0x70, "News & Factual" }, { 0x80, "News & Factual" }, { 0x90, "Education" }, { 0xa0, "Lifestyle" }, { 0xf0, "Drama" }, { 0, NULL }, }; const char * genredescr(unsigned char b) { int i; for (i = 0; genretab[i].text; i++) if (b == genretab[i].genre) return genretab[i].text; return "Unknown"; } unsigned char genrecode(char *s) { int i; for (i = 0; genretab[i].text; i++) if (!strncasecmp(genretab[i].text, s, strlen(s))) return genretab[i].genre; return 0; } const char * recordingstatus(struct hmt *hmt) { switch (hmt->recstatus) { case 0: return "Zero length"; case 1: return "Loss of power"; case 2: return "Valid"; case 3: return "Scrambled"; case 4: return "Failed"; default: break; } return "Unknown"; } const char * failurereason(struct hmt *hmt) { switch (hmt->failreason) { case 0: return "OK"; case 1: return "Failed: Disk was full"; case 8: case 2: return "Failed: Conflicted with another recording"; case 3: return "Failed: Maximum number of files per folder"; case 4: return "Recording less than 30 seconds"; case 5: return "Failed: Lack of signal"; case 13: return "Incomplete: Disk was full"; case 15: return "Failed: No storage device detected"; case 16: return "Incomplete: USB storage device was removed"; case 17: return "Failed: Appears not to have been broadcast"; case 19: return "Failed: Conflicted with a higher priority recording"; case 20: return "Failed: Unable to track"; default: break; } return "Unknown"; } int guidance(struct hmt *hmt) { #if 0 printf("GUIDANCE TYPE/MODE: %d/%d\n", hmt->guidance_type, hmt->guidance_mode); #endif switch (hmt->guidance_type) { case 1: switch (hmt->guidance_mode) { case 0: /* General guidance */ return 2; default: /* Adult content */ return 1; } break; default: switch (hmt->guidance_mode) { case 0: /* No guidance */ return 0; default: /* Adult content */ return 1; } break; } }