forked from hummypkg/hmt
Modify iPlate guidance also. Add/correct/use manifest constants. Update filename location to offset 0x180.
290 lines
6.6 KiB
C
290 lines
6.6 KiB
C
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <time.h>
|
|
|
|
#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;
|
|
}
|
|
}
|