263 lines
5.6 KiB
C
263 lines
5.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 <strings.h>
|
|
#include <time.h>
|
|
|
|
#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_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_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;
|
|
}
|
|
}
|
|
|