#include #include #include #include #include #include #include #include #include "lint.h" #include "xconv.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) { size_t len = strlen((char *) str); if (len > 3 && str[1] == 'i' && str[2] == '7') { len = len * 2 + 8; char *dst = malloc(len); if (dst) { if (xconv((char *) (str + 3), dst, len)) return (uint8_t *) dst; else { free(dst); return str + 3; } } } return (uint8_t *) ""; } if (*str == 0x1f) return str + 2; return str + 1; } void parse_hmt(struct hmt *hmt) { STRING( HMT_FOLDER, directory, HMT_FOLDER_LEN); uint8_t *p = hmt->bin + HMT_FILENAME; if (!p[0] || (p[0] == p[1])) ++p; hmt->filename = (char *) strip_string(p); LE32( HMT_RECORDING_START, start); LE32( HMT_RECORDING_END, end); LE16( HMT_STORED_DURATION, duration); LE8( HMT_RECSTATUS, recstatus); LE8( HMT_FLAGS1, flags1); LE8( HMT_FLAGS2, flags2); LE8( HMT_FAILREASON, failreason); LE32( HMT_PLAYED_TIME, resume); LE16( HMT_BOOKMARKS_CNT, num_bookmarks); STRING( HMT_TITLE, mediatitle, HMT_TITLE_LEN); PTR32( HMT_BOOKMARKS, bookmarks); LE8( HMT_FLAGS3, flags3); LE8( HMT_GUIDETYPE, guidance_type); LE8( HMT_GUIDEMODE, guidance_mode); STRING( HMT_GUIDANCE, guidance, HMT_GUIDANCE_LEN + 1); LE8( HMT_COPYCOUNT, copycount); LE16( HMT_TZOFFSET, tzoffset); LE32( HMT_CHANNEL_NUM, lcn); LE8( HMT_RECTYPE, type); STRING( HMT_CHANNEL_NAME, channel, HMT_CHANNEL_NAME_LEN + 1); LE16( HMT_SID, service_id); LE16( HMT_TSID, tsid); LE16( HMT_ONID, onid); LE16( HMT_PMTPID, pmt_pid); LE16( HMT_VIDEOPID, video_pid); // LE8( HMT_HDFLAG, definition); LE8( HMT_HDFLAG2, definition); LE16( HMT_AUDIOPID, audio_pid); LE32( HMT_SCHEDULED_START, schedstart); LE32( HMT_SCHEDULED_DURATION, scheddur); LE8( HMT_GENRE, genre); STRING( HMT_ITITLE, title, HMT_ITITLE_LEN + 3); STRING( HMT_SYNOPSIS, synopsis, HMT_SYNOPSIS + 3); LE16( HMT_EVENTID, event_id); LE8( HMT_SHRUNK, xflags1); LE8( HMT_DEDUPED, xflags2); LE8( HMT_DETECTADS, xflags3); LE8( HMT_SERIES, series); LE8( HMT_EPISODE, episode); LE8( HMT_EPISODETOT, episodetot); } int hmt_is(struct hmt *hmt, enum hmt_attribute attr) { switch (attr) { case HMTA_ENCRYPTED: return hmt->flags2 & HMT_FLAGS2_ENCRYPTED; case HMTA_THUMBNAIL: return hmt->flags2 & HMT_FLAGS2_THUMBNAIL; case HMTA_GHOST: return hmt->flags2 & HMT_FLAGS2_GHOST; case HMTA_LOCKED: return hmt->flags1 & HMT_FLAGS1_LOCKED; case HMTA_NEW: return !(hmt->flags1 & HMT_FLAGS1_NEW); case HMTA_ENCFLAGGED: return hmt->flags3 != HMT_FLAGS3_UNPROTECTED; case HMTA_UNLIMITEDCOPY: return !(hmt->flags3 & HMT_FLAGS3_PROTECTED_COPY_LIMITED); 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 == HMT_RECTYPE_RADIO; 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; } }