Browse Source

initial hmt tool commit

master
hummypkg 11 years ago
committed by HummyPkg
commit
f6a5c33742
  1. 70
      Makefile
  2. 396
      cmd.c
  3. 155
      display.c
  4. 139
      file.c
  5. 263
      hmt.c
  6. 151
      hmt.h
  7. 44
      lint.h
  8. 387
      main.c
  9. 174
      util.c

70
Makefile

@ -0,0 +1,70 @@
MAKE=make
# Setting these makes sure that the subsequent include files know that
# we are going to be threaded so they should do whatever they need to
# support this. In particular, this makes the global variable 'errno'
# become thread safe by replacing it with a call to the internal function
# ___errno() on Solaris - and should do something similar on other operating
# systems. Solaris also supports _TS_ERRNO to enable thread-safe errno
# _REENTRANT is also required for the tempnam() function to be properly
# thread-safe.
DEFS=-D_REENTRANT -D_TS_ERRNO -DHMT_PROTECT
# -DWITH_MPATROL
SRCS= cmd.c \
display.c \
file.c \
hmt.c \
main.c \
util.c
OBJS= $(SRCS:.c=.o)
#CC=mipsel-linux-gcc
PLATFORM=$(shell uname -s | cut -d- -f1)
PROCESSOR=$(shell uname -p)
CFLAGS=-g
INCS=
LIBS=
ifeq ($(PLATFORM),CYGWIN_NT)
CFLAGS=-g -mno-cygwin
endif
#ifeq ($(PLATFORM),Linux)
#CFLAGS=-static -g
#endif
#CFLAGS=-g -O3 -fno-strict-aliasing
#WARN=-pedantic -Wall -Wnested-externs -Wpointer-arith -Werror -Wno-unused
WARN=-pedantic -Wall -W -Wnested-externs -Wpointer-arith -Wno-long-long
all: tags hmt
hmt: ${OBJS}
@echo "Linking..."
@-[ -f $@ ] && mv $@ $@~ || exit 0
${CC} -static-libgcc \
${WARN} \
${DEFS} \
${CFLAGS} -o $@ \
${OBJS} \
${LIBS}
@echo "Done..."
clean:
@-touch core
rm -f hmt hmt~ *.exe core tags asm ${OBJS} ${AROBJS}
rm -f *.raw *.raw.sum *.lh5
rm -rf hmt-*
tags:
@-ctags *.[ch] 2>> /dev/null
.c.o:
@echo " $<"
@$(CC) $(CFLAGS) ${WARN} ${DEFS} ${INCS} -c $< -o $@
install: hmt
strip hmt
cp hmt /mod/bin/hmt
${OBJS}: lint.h

396
cmd.c

@ -0,0 +1,396 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "lint.h"
#define CHECK_OFFSET(xxx) \
if (hmt->binsize < (xxx)) \
{ \
printf("File too small to patch.\n"); \
return; \
}
void
cmd_new(struct hmt *hmt, int flag)
{
CHECK_OFFSET(HMT_FLAGS1)
if (flag)
hmt->bin[HMT_FLAGS1] &= ~HMT_FLAGS1_NEW;
else
hmt->bin[HMT_FLAGS1] |= HMT_FLAGS1_NEW;
hmt->modified++;
}
void
cmd_lock(struct hmt *hmt, int flag)
{
CHECK_OFFSET(HMT_FLAGS1)
if (flag)
hmt->bin[HMT_FLAGS1] |= HMT_FLAGS1_LOCKED;
else
hmt->bin[HMT_FLAGS1] &= ~HMT_FLAGS1_LOCKED;
hmt->modified++;
}
void
cmd_guidance(struct hmt *hmt, int flag)
{
/* 0x03E0 2 byte 'Guide' flag for Media List.
0xFF00 = Guide off. 0x0101 = Guide on. */
if (flag)
{
patch_byte(hmt, HMT_GUIDEFLAG, 0x01);
patch_byte(hmt, HMT_GUIDEFLAG + 1, 0x01);
}
else
{
patch_byte(hmt, HMT_GUIDEFLAG, 0xff);
patch_byte(hmt, HMT_GUIDEFLAG + 1, 0x00);
}
hmt->modified++;
}
void
cmd_protect(struct hmt *hmt, int flag)
{
CHECK_OFFSET(HMT_FLAGS2)
if (flag)
hmt->bin[HMT_FLAGS2] = 0;
else
hmt->bin[HMT_FLAGS2] = 4;
hmt->modified++;
}
void
cmd_encrypted(struct hmt *hmt, int flag)
{
CHECK_OFFSET(HMT_ENCRYPTED);
if (flag)
hmt->bin[HMT_ENCRYPTED] |= HMT_IS_ENCRYPTED;
else
hmt->bin[HMT_ENCRYPTED] &= ~HMT_IS_ENCRYPTED;
hmt->modified++;
}
void
cmd_shrunk(struct hmt *hmt, int flag)
{
CHECK_OFFSET(HMT_SHRUNK);
if (flag)
hmt->bin[HMT_SHRUNK] = 'E';
else
hmt->bin[HMT_SHRUNK] = 'e';
hmt->modified++;
}
void
cmd_dedup(struct hmt *hmt, int flag)
{
CHECK_OFFSET(HMT_DEDUPED);
if (flag)
hmt->bin[HMT_DEDUPED] = 'N';
else
hmt->bin[HMT_DEDUPED] = 'n';
hmt->modified++;
}
void
cmd_detectads(struct hmt *hmt, int flag)
{
CHECK_OFFSET(HMT_DETECTADS);
if (flag)
hmt->bin[HMT_DETECTADS] = 'G';
else
hmt->bin[HMT_DETECTADS] = 'g';
hmt->modified++;
}
void
cmd_setgenre(struct hmt *hmt, char *g)
{
CHECK_OFFSET(HMT_GENRE)
printf("Current genre: %s\n", genredescr(hmt->bin[HMT_GENRE]));
if (*g == '-')
hmt->bin[HMT_GENRE] = atoi(g + 1);
else
hmt->bin[HMT_GENRE] = genrecode(g);
printf("Set genre to: %s\n", genredescr(hmt->bin[HMT_GENRE]));
hmt->modified++;
}
void
cmd_setresume(struct hmt *hmt, char *resume)
{
int32_t r = strtoll(resume, (char **)NULL, 10);
CHECK_OFFSET(HMT_PLAYED_TIME)
if (r < 0)
{
uint32_t d;
d = hmt->end - hmt->start;
printf("Recording Duration:%lu\n", d);
r += d;
}
printf("Setting resume point to: %lu second(s) in.\n", r);
write_uint32(hmt->bin + HMT_PLAYED_TIME, r);
hmt->modified++;
}
static void
patch_epg_offset(struct hmt *hmt, uint32_t offset, uint32_t len, char *str)
{
if (hmt->binsize > 0x1001)
{
uint32_t j, k;
uint32_t n;
/* Read the number of EPG blocks. */
j = read_uint16(hmt->bin + 0x1000, 1);
/* Loop through them. */
for (n = 1; n <= j; n++)
{
/* Offset of block header. */
uint32_t off = 0x1004 + n * 32;
if (hmt->binsize < off)
break;
/* Read offset of EPG data. */
k = read_uint32(hmt->bin + off + 8, 1);
if (k < off)
continue;
if (hmt->binsize >= k + 704)
{
if (debug)
printf("Patching EPG block %d.\n", n);
patch_string(hmt, k + offset, len, str);
}
}
}
}
void
cmd_settitle(struct hmt *hmt, char *str)
{
patch_string(hmt, HMT_TITLE, HMT_TITLE_LEN, str);
patch_string(hmt, HMT_ITITLE, HMT_ITITLE_LEN, str);
patch_epg_offset(hmt, 0x3e, HMT_TITLE_LEN, str);
}
void
cmd_setsynopsis(struct hmt *hmt, char *str)
{
CHECK_OFFSET(HMT_EPG)
if (strlen(str) >= HMT_EPG_LEN)
{
fprintf(stderr, "New synopsis too long.\n");
return;
}
patch_string(hmt, HMT_EPG, HMT_EPG_LEN, str);
patch_epg_offset(hmt, 0x141, HMT_EPG_LEN, str);
}
void
cmd_setseries(struct hmt *hmt, char *g)
{
unsigned int series, episode, episodes;
if (sscanf(g, "%u,%u,%u", &series, &episode, &episodes) >= 2)
{
hmt->bin[HMT_SERIES] = series;
hmt->bin[HMT_EPISODE] = episode;
hmt->bin[HMT_EPISODETOT] = episodes;
hmt->modified++;
}
else
printf("Invalid episode '%s'\n", g);
}
static int
intcomp(const void *a, const void *b)
{
return (*(uint32_t *)a - *(uint32_t *)b);
}
void
cmd_bookmarks(struct hmt *hmt, char *str, int add)
{
uint32_t *b = (uint32_t *)(hmt->bin + HMT_BOOKMARKS);
uint16_t n = read_uint16(hmt->bin + HMT_BOOKMARKS_CNT, 1);
char *p;
if (!add)
n = 0;
if (!str || !(p = strtok(str, ":")))
{
/* No bookmarks provided. */
if (add)
return;
*(uint16_t *)(hmt->bin + HMT_BOOKMARKS_CNT) = 0;
hmt->modified++;
return;
}
do
{
if (n >= 32)
{
fprintf(stderr, "Too many bookmarks, maximum 32.\n");
return;
}
b[n++] = (uint32_t)strtoul(p, (char **)NULL, 10);
hmt->modified++;
} while ((p = strtok((char *)NULL, ":")));
*(uint16_t *)(hmt->bin + HMT_BOOKMARKS_CNT) = n;
qsort(b, n, sizeof(uint32_t), intcomp);
}
void
patch_byte(struct hmt *hmt, uint32_t offset, uint8_t val)
{
CHECK_OFFSET(offset)
if (hmt->bin[offset] != val)
{
hmt->bin[offset] = val;
hmt->modified++;
}
}
void
patch_string(struct hmt *hmt, uint32_t offset, uint32_t len, char *str)
{
CHECK_OFFSET(offset + strlen(str))
if (debug > 3)
fprintf(stderr, "patch_string(%#x, %d, %s)\n",
offset, len, str);
memset(hmt->bin + offset, '\0', len - 1);
strcpy((char *)(hmt->bin + offset), (const char *)str);
hmt->modified++;
}
void
patch_uint16(struct hmt *hmt, uint32_t offset, uint32_t val)
{
CHECK_OFFSET(offset)
write_uint16(hmt->bin + offset, val);
hmt->modified++;
}
void
patch_uint32(struct hmt *hmt, uint32_t offset, uint32_t val)
{
CHECK_OFFSET(offset)
write_uint32(hmt->bin + offset, val);
hmt->modified++;
}
void
cmd_patch(struct hmt *hmt, char *str)
{
int width;
uint32_t offset;
uint32_t val;
char offsets[0x20], vals[0x20];
if (sscanf(str, "%d=%[^:]:%s", &width, offsets, vals) != 3)
{
printf("Syntax error.\n");
return;
}
offset = strtoul(offsets, (char **)NULL, 0);
val = strtoul(vals, (char **)NULL, 0);
printf("Patching width %d - %#x(%lu) = %#x(%lu)\n", width,
offset, offset, val, val);
switch(width)
{
case 8:
patch_byte(hmt, offset, val);
break;
case 16:
patch_uint16(hmt, offset, val);
break;
case 32:
patch_uint32(hmt, offset, val);
break;
default:
printf("Unknown patch width.\n");
break;
}
}
void
cmd_unpatch(struct hmt *hmt, char *str)
{
int width;
uint32_t offset;
char offsets[0x20];
if (sscanf(str, "%d=%[^:]", &width, offsets) != 2)
{
printf("Syntax error.\n");
return;
}
offset = strtoul(offsets, (char **)NULL, 0);
if (hmt->binsize < offset)
{
printf("Offset too large for file.\n");
return;
}
if (debug)
printf("Reading width %d - %#x(%lu)\n", width,
offset, offset);
switch(width)
{
case 8:
{
uint8_t val = hmt->bin[offset] & 0xff;
printf("%d %#x\n", val, val);
break;
}
case 16:
{
uint16_t val = read_uint16(hmt->bin + offset, 1);
printf("%d %#x\n", val, val);
break;
}
case 32:
{
uint32_t val = read_uint32(hmt->bin + offset, 1);
printf("%ld %#x\n", val, val);
break;
}
default:
printf("Unknown patch width.\n");
break;
}
}

155
display.c

@ -0,0 +1,155 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "lint.h"
char *
ctimenl(uint32_t *tm)
{
static char buf[25];
char *p;
strcpy(buf, ctime((time_t *)tm));
if ((p = strpbrk(buf, "\r\n")))
*p = '\0';
return buf;
}
void
display_bookmarks(struct hmt *hmt)
{
int i;
for (i = 0; i < hmt->num_bookmarks && i < 32; i++)
printf("%d ", hmt->bookmarks[i]);
printf("\n");
}
void
display_hmt(struct hmt *hmt)
{
int g;
g = guidance(hmt);
if (sysopts & SYSOPT_PARSABLE)
{
printf("%s\t", hmt->mediatitle);
printf("%s\t", hmt->synopsis);
printf("%s\t", hmt->definition == DEF_HD ? "HD" : "SD");
printf("%lu\t%s\t", hmt->lcn, hmt->channel);
printf("%lu\t", hmt->start);
printf("%lu\t", hmt->end);
printf("%s\t", hmt_flags(hmt));
if (g)
printf("%s\t", hmt->guidance);
else
printf("\t");
printf("%d\t", hmt->num_bookmarks);
printf("%lu\t", hmt->schedstart);
printf("%lu\t", hmt->scheddur);
printf("%d\t", hmt->genre);
printf("%lu\t", hmt->resume);
printf("%s/%s\t", recordingstatus(hmt), failurereason(hmt));
printf("%u\t%u\t%u\t", hmt->series, hmt->episode,
hmt->episodetot);
printf("\n");
return;
}
printf("Format:%s\n", hmt->definition == DEF_HD ? "HD" : "SD");
printf("Title:%s\n", hmt->mediatitle);
printf("ITitle:%s\n", hmt->title);
printf("Channel:%lu (%s)\n", hmt->lcn, hmt->channel);
printf("Episode:%u,%u/%u\n",
hmt->series, hmt->episode, hmt->episodetot);
printf("Folder:%s\n", hmt->directory);
if (*hmt->filename == '\0' && hmt->filename[1] != '\0')
printf("Filename:%s\n", hmt->filename + 1);
else
printf("Filename:%s\n", hmt->filename);
printf("Genre:%s (%d)\n", genredescr(hmt->genre), hmt->genre);
printf("EPG:%s\n", hmt->synopsis);
if (g)
printf("Guidance:%s\n", hmt->guidance);
if (hmt_is(hmt, HMTA_ENCRYPTED))
printf("Raw file is encrypted on disk.\n");
printf("\n");
printf("Recording Status: %s (%s)\n",
recordingstatus(hmt), failurereason(hmt));
printf("Flags: %s\n", hmt_flags(hmt));
printf("Copy count:%d\n", hmt->copycount);
printf("\n");
printf("Scheduled start:%lu (%s)\n", hmt->schedstart,
ctimenl(&hmt->schedstart));
printf("Scheduled duration:%lu\n", hmt->scheddur);
printf("Recording start:%lu (%s)\n", hmt->start,
ctimenl(&hmt->start));
printf("Recording end:%lu (%s)\n", hmt->end, ctimenl(&hmt->end));
printf("Duration:%lu\n", hmt->end - hmt->start);
printf("Stored duration:%lu\n", hmt->duration);
printf("Play resumes at:%lu second%s in.\n", hmt->resume,
hmt->resume == 1 ? "" : "s");
printf("Timezone offset:%d\n", hmt->tzoffset);
printf("\n");
printf("Service ID (SID):%u\n", hmt->service_id);
printf("Event ID:%u\n", hmt->event_id);
printf("Transport Stream ID (TSID):%u\n", hmt->tsid);
printf("Originating Network ID (ONID):%u\n", hmt->onid);
printf("Programme Map Table PID (PMTPID):%u\n", hmt->pmt_pid);
printf("Video PID:%u\n", hmt->video_pid);
printf("Audio PID:%u\n", hmt->audio_pid);
printf("Bookmarks:%d = ", hmt->num_bookmarks);
display_bookmarks(hmt);
/* Display basic information EPG blocks... TBC */
if (hmt->binsize > 0x1001)
{
uint16_t j, k;
int n;
printf("\n");
j = read_uint16(hmt->bin + 0x1000, 1);
printf("EPG Blocks:%u\n", j);
for (n = 1; n <= j; n++)
{
uint32_t off = 0x1004 + n * 32;
if (hmt->binsize < off)
break;
printf(" Block:%d", n);
k = read_uint32(hmt->bin + off + 4, 1);
printf(" Time:%lu", k);
k = read_uint32(hmt->bin + off + 8, 1);
printf(" Offset:%#x\n", k);
if (k < off)
continue;
if (hmt->binsize >= k + 704)
{
/* Offset to EPG block. */
off = k;
printf(" Block%d_Title:%s\n", n,
strip_string(hmt->bin + off + 0x3e));
}
/* Synopsis is off + 0x141 */
}
}
}

139
file.c

@ -0,0 +1,139 @@
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <time.h>
#include <strings.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "lint.h"
struct hmt *
open_file(char *filename, int readonly)
{
struct hmt *hmt;
struct stat st;
char *ext;
uint16_t magic;
if (!(hmt = malloc(sizeof(struct hmt))))
{
perror("malloc");
return NULL;
}
if (debug)
printf("Opening file '%s' (readonly=%d)\n", filename, readonly);
strcpy(hmt->fname, filename);
if ((ext = strrchr(hmt->fname, '.')))
{
ext++;
if (debug)
printf(" Extension: '%s'", ext);
if (strcmp(ext, "hmt"))
{
*ext = '\0';
strcat(hmt->fname, "hmt");
}
}
else
strcat(hmt->fname, ".hmt");
if (debug)
printf(" Actual: '%s'\n", hmt->fname);
if (stat(hmt->fname, &st) == -1)
{
perror(hmt->fname);
free(hmt);
return NULL;
}
hmt->binsize = st.st_size;
if (debug)
printf("Opening %s, %lu bytes.\n", hmt->fname,
(unsigned long)hmt->binsize);
hmt->readonly = readonly;
if ((hmt->fd = open(hmt->fname, readonly ? O_RDONLY : O_RDWR, 0)) == -1)
{
perror(hmt->fname);
free(hmt);
return NULL;
}
hmt->bin = (uint8_t *)mmap(NULL, hmt->binsize,
readonly ? PROT_READ : PROT_READ|PROT_WRITE,
MAP_SHARED, hmt->fd, 0);
if (hmt->bin == MAP_FAILED)
{
if (debug)
printf("mmap() failed, falling back to read.\n");
/* This occurs if the underlying filesystem does not support
* mmap, e.g. NTFS. Fallback to holding the file in memory.
*/
if (!(hmt->bin = (uint8_t *)malloc(hmt->binsize)))
{
perror("malloc");
free(hmt);
close(hmt->fd);
return NULL;
}
read(hmt->fd, hmt->bin, hmt->binsize);
hmt->mmapped = 0;
}
else
{
if (debug)
printf("File mapped into memory.\n");
hmt->mmapped = 1;
}
magic = read_uint16(hmt->bin, 0);
if (magic != 0x1701)
{
printf("Invalid HMT file, %s\n", hmt->fname);
close_file(hmt);
return NULL;
}
hmt->modified = 0;
parse_hmt(hmt);
return hmt;
}
void
close_file(struct hmt *hmt)
{
if (debug)
printf("Closing file.\n");
if (hmt->mmapped)
munmap((void *)hmt->bin, hmt->binsize);
else
{
if (hmt->modified && !hmt->readonly)
{
lseek(hmt->fd, SEEK_SET, 0);
write(hmt->fd, hmt->bin, hmt->binsize);
}
free(hmt->bin);
}
if (hmt->fd > 0)
close(hmt->fd);
hmt->fd = -1;
}

263
hmt.c

@ -0,0 +1,263 @@
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <time.h>
#include <strings.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 */
inline 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";
}
int
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;
}
}

151
hmt.h

@ -0,0 +1,151 @@
#include <sys/param.h>
enum hmt_attribute {
HMTA_ENCRYPTED = 0,
HMTA_LOCKED,
HMTA_NEW,
HMTA_ENCFLAGGED,
HMTA_SHRUNK,
HMTA_DEDUPED,
HMTA_DETECTADS,
HMTA_UNLIMITEDCOPY,
HMTA_RADIO,
HMTA_GHOST,
};
struct hmt {
char fname[MAXPATHLEN + 1];
int fd;
uint8_t *bin;
uint32_t binsize;
int mmapped;
int modified;
int readonly;
/* Recording information */
char *directory;
char *filename;
uint8_t type;
uint32_t start;
uint32_t end;
uint16_t duration;
uint8_t recstatus;
uint8_t flags1;
uint8_t flags2;
uint8_t failreason;
uint32_t resume;
uint16_t num_bookmarks;
char *mediatitle;
uint32_t *bookmarks;
uint8_t flags3;
uint8_t guidance_type;
uint8_t guidance_mode;
char *guidance;
uint8_t copycount;
uint16_t tzoffset;
uint32_t lcn;
char *channel;
uint16_t service_id;
uint16_t tsid;
uint16_t onid;
uint16_t pmt_pid;
uint16_t video_pid;
enum { DEF_SD = 1, DEF_HD } definition;
uint16_t audio_pid;
uint32_t schedstart;
uint32_t scheddur;
uint8_t genre;
char *title;
char *synopsis;
uint16_t event_id;
uint8_t xflags1;
uint8_t xflags2;
uint8_t xflags3;
int series;
int episode;
int episodetot;
};
void parse_hmt(struct hmt *);
uint8_t *strip_string(uint8_t *);
int guidance(struct hmt *);
const char *genredescr(unsigned char b);
int genrecode(char *);
const char *recordingstatus(struct hmt *);
const char *failurereason(struct hmt *);
char *hmt_flags(struct hmt *);
int hmt_is(struct hmt *, enum hmt_attribute);
#define HMT_FOLDER 0x80
#define HMT_FOLDER_LEN 254
#define HMT_FILENAME 0x17f
#define HMT_FILENAME_LEN 256
#define HMT_TITLE 0x29a
#define HMT_TITLE_LEN 48
#define HMT_ITITLE 0x516
#define HMT_ITITLE_LEN 48
#define HMT_CHANNEL_NUM 0x458 /* 4 bytes */
#define HMT_CHANNEL_NAME 0x45c
#define HMT_EPG 0x616
#define HMT_EPG_LEN 252
#define HMT_RECSTATUS 0x28c
#define HMT_FAILREASON 0x290
#define HMT_RECTYPE 0x4b8
#define HMT_FLAGS1 0x28d
#define HMT_FLAGS1_LOCKED 0x4
#define HMT_FLAGS1_NEW 0x8
#define HMT_ENCRYPTED 0x28e
#define HMT_IS_ENCRYPTED 0x1
#define HMT_BOOKMARKS_CNT 0x298
#define HMT_BOOKMARKS 0x31c
#define HMT_FLAGS2 0x3dc
#define HMT_FLAGS2_ENCRYPTED 0x2 /* As in "keep encrypted" */
#define HMT_FLAGS2_UNLIMITCOPY 0x4
#define HMT_RECORDING_START 0x280 /* 4 bytes */
#define HMT_RECORDING_END 0x284 /* 4 bytes */
#define HMT_STORED_DURATION 0x288 /* 2 bytes */
#define HMT_SCHEDULED_START 0x4f8 /* 4 bytes */
#define HMT_SCHEDULED_DURATION 0x4fc /* 4 bytes */
#define HMT_PLAYED_TIME 0x294 /* 4 bytes */
#define HMT_GENRE 0x514
#define HMT_EVENTID 0x716
#define HMT_GUIDEFLAG 0x3e0
#define HMT_GUIDEFLAG_ON 0x01
#define HMT_GUIDANCE 0x3e2
#define HMT_GUIDANCE_LEN 74
#define HMT_SID 0x488 /* 2 bytes */
#define HMT_TSID 0x48a /* 2 bytes */
#define HMT_ONID 0x48c /* 2 bytes */
#define HMT_PMTPID 0x48e /* 2 bytes */
#define HMT_VIDEOPID 0x490 /* 2 bytes */
#define HMT_AUDIOPID 0x49c /* 2 bytes */
#define HMT_HDFLAG 0x498 /* 1 == SD, 2 == HD */
#define HMT_HDFLAG2 0x4bc /* 1 == SD, 0 == HD */
#define HMT_COPYCOUNT 0x431
/* Section of HMT used for 'eng' string from guidance text. */
#define HMT_SHRUNK 0x73d /* Set to E for shrunk.. */
#define HMT_DEDUPED 0x73e /* Set to N for deduped.. */
#define HMT_DETECTADS 0x73f /* Set to G for detectads.. */
#define HMT_SERIES 0x10
#define HMT_EPISODE 0x11
#define HMT_EPISODETOT 0x12

44
lint.h

@ -0,0 +1,44 @@
#define SYSOPT_PARSABLE 0x1
#include "hmt.h"
extern int debug;
extern const char *version;
extern unsigned long sysopts;
inline uint16_t read_uint16(uint8_t *, int);
inline uint32_t read_uint32(uint8_t *, int);
inline void write_uint32(uint8_t *, uint32_t);
inline void write_uint16(uint8_t *, uint16_t);
void hexdump(uint8_t *, uint32_t, uint32_t);
struct hmt *open_file(char *, int);
void close_file(struct hmt *);
void display_bookmarks(struct hmt *hmt);
void display_hmt(struct hmt *hmt);
void cmd_protect(struct hmt *, int);
void cmd_encrypted(struct hmt *, int);
void cmd_shrunk(struct hmt *, int);
void cmd_dedup(struct hmt *, int);
void cmd_detectads(struct hmt *, int);
void cmd_new(struct hmt *, int);
void cmd_lock(struct hmt *, int);
void cmd_guidance(struct hmt *, int);
void cmd_setresume(struct hmt *, char *);
void cmd_setsynopsis(struct hmt *, char *);
void cmd_settitle(struct hmt *, char *);
void cmd_setgenre(struct hmt *, char *);
void cmd_bookmarks(struct hmt *, char *, int);
void patch_byte(struct hmt *, uint32_t, uint8_t);
void patch_string(struct hmt *, uint32_t, uint32_t, char *);
void cmd_patch(struct hmt *, char *);
void cmd_unpatch(struct hmt *, char *);
void cmd_setseries(struct hmt *, char *);
const char *genre(unsigned char);
int genrecode(char *);

387
main.c

@ -0,0 +1,387 @@
/*
* Humax HMT Tool
* (c) af123, 2011-2016
*/
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <time.h>
#include "lint.h"
int debug = 0;
const char *version = "2.0.10";
unsigned long sysopts = 0;
int
syntax()
{
fprintf(stderr, "Humax HMT Tool v%s, by af123, 2011-2016.\n\n",
version);
fprintf(stderr,
"Syntax: hmt [command] <filename> [filename] ...\n");
fprintf(stderr,
" Commands:\n"
" +/-new Mark/unmark recording as new.\n"
" +/-lock Mark/unmark recording as locked.\n"
" +/-guidance Mark/unmark recording as having guidance.\n"
);
fprintf(stderr,
" +/-protect Enable/disable protection"
" (prevents decryption on copy).\n"
" +/-encrypted Mark/unmark recording as encrypted.\n"
" +/-shrunk Mark/unmark recording as shrunk.\n"
" +/-dedup Mark/unmark recording as deduped.\n"
" +/-detectads Mark/unmark recording as ad-detection-done.\n"
);
fprintf(stderr,
" -p Display parseable file information (see *).\n"
" -list Display file information (default).\n"
" -bookmarks Display bookmarks.\n"
" +addbookmark=<seconds>[:<seconds>]...\n"
" +setbookmarks=<seconds>[:<seconds>]...\n"
" +clearbookmarks\n"
" +settitle=<new title>\n"
" +setsynopsis=<new synopsis>\n"
" +setguidance=<new guidance>\n"
);
fprintf(stderr,
" +setseries=<series,episode,episodes>\n"
" +setfolder=<new folder> (patch hmt only)\n"
" +setfilename=<new filename> (patch hmt only)\n"
" +setgenre=<genre> (can just specifiy initial part)\n"
" +setresume=<resume point (seconds)> "
"(-seconds to set from end)\n"
);
fprintf(stderr,
"\n"
"Generic patch commands:\n"
" +patch8=offset:value patch 8-bit value\n"
" +patch16=offset:value patch 16-bit value\n"
" +patch32=offset:value patch 32-bit value\n"
" Offset and value can be preceeded with 0x to indicate hex.\n"
);
fprintf(stderr,
"\n"
"Generic read commands:\n"
" +read8=offset read 8-bit value\n"
" +read16=offset read 16-bit value\n"
" +read32=offset read 32-bit value\n"
" Offset can be preceeded with 0x to indicate hex.\n"
);
fprintf(stderr,
"\n"
"* Parseable output is tab delimited and contains the following"
" fields:\n"
" Title, Synopsis, HD/SD, LCN, Channel Name,\n"
" Start time, End time, Flags, Guidance, Bookmark count,\n"
" Scheduled start, Scheduled duration, Genre code,\n"
" Resume point, Status/Reason.\n"
);
fprintf(stderr, "\n");
return 0;
}
int
main(int argc, char **argv)
{
enum {
CMD_LIST = 0,
CMD_NEW,
CMD_LOCK,
CMD_PROTECT,
CMD_GUIDANCE,
CMD_ENCRYPTED,
CMD_SHRUNK,
CMD_DEDUP,
CMD_DETECTADS,
CMD_SETTITLE,
CMD_SETSYNOPSIS,
CMD_SETGUIDANCE,
CMD_SETFOLDER,
CMD_SETSERIES,
CMD_SETFILENAME,
CMD_SETGENRE,
CMD_SETRESUME,
CMD_BOOKMARKS,
CMD_ADDBOOKMARK,
CMD_SETBOOKMARKS,
CMD_CLEARBOOKMARKS,
CMD_PATCH,
CMD_UNPATCH
} cmd = CMD_LIST;
char *newstr;
int i, toggle;
if (argc > 1 && !strncmp(argv[1], "-d", 2))
{
if (argv[1][2] == '\0')
debug = 1;
else
debug = atoi(argv[1] + 2);
if (debug > 0)
{
printf("Set debug level to %d\n", debug);
argc--, argv++;
}
}
if (argc > 1 && !strcmp(argv[1], "-p"))
{
sysopts |= SYSOPT_PARSABLE;
argc--, argv++;
}
if (argc < 2)
return syntax();
toggle = 0;
switch (*argv[1])
{
case '+':
toggle = 1;
/* Fall-through */
case '-':
if (!strcmp(argv[1] + 1, "list"))
cmd = CMD_LIST;
else if (!strcmp(argv[1] + 1, "new"))
cmd = CMD_NEW;
else if (!strcmp(argv[1] + 1, "lock"))
cmd = CMD_LOCK;
else if (!strcmp(argv[1] + 1, "protect"))
cmd = CMD_PROTECT;
else if (!strcmp(argv[1] + 1, "guidance"))
cmd = CMD_GUIDANCE;
else if (!strcmp(argv[1] + 1, "encrypted"))
cmd = CMD_ENCRYPTED;
else if (!strcmp(argv[1] + 1, "shrunk"))
cmd = CMD_SHRUNK;
else if (!strcmp(argv[1] + 1, "dedup"))
cmd = CMD_DEDUP;
else if (!strcmp(argv[1] + 1, "detectads"))
cmd = CMD_DETECTADS;
else if (!strcmp(argv[1] + 1, "bookmarks"))
cmd = CMD_BOOKMARKS;
else if (!strncmp(argv[1], "+patch", 6))
{
newstr = argv[1] + 6;
cmd = CMD_PATCH;
}
else if (!strncmp(argv[1], "+read", 5))
{
newstr = argv[1] + 5;
cmd = CMD_UNPATCH;
}
else if (!strncmp(argv[1], "+settitle=", 10))
{
newstr = argv[1] + 10;
if (strlen(newstr) >= HMT_TITLE_LEN)
{
fprintf(stderr, "New title too long.\n");
return 0;
}
cmd = CMD_SETTITLE;
}
else if (!strncmp(argv[1], "+setsynopsis=", 13))
{
newstr = argv[1] + 13;
cmd = CMD_SETSYNOPSIS;
}
else if (!strncmp(argv[1], "+setguidance=", 13))
{
newstr = argv[1] + 13;
if (strlen(newstr) >= HMT_GUIDANCE_LEN)
{
fprintf(stderr, "New guidance too long.\n");
return 0;
}
cmd = CMD_SETGUIDANCE;
}
else if (!strncmp(argv[1], "+setseries=", 11))
{
newstr = argv[1] + 11;
cmd = CMD_SETSERIES;
}
else if (!strncmp(argv[1], "+setfolder=", 11))
{
newstr = argv[1] + 11;
if (strlen(newstr) >= HMT_FOLDER_LEN)
{
fprintf(stderr, "New folder too long.\n");
return 0;
}
cmd = CMD_SETFOLDER;
}
else if (!strncmp(argv[1], "+setfilename=", 13))
{
newstr = argv[1] + 13;
if (strlen(newstr) >= HMT_FILENAME_LEN)
{
fprintf(stderr, "New filename too long.\n");
return 0;
}
cmd = CMD_SETFILENAME;
}
else if (!strncmp(argv[1], "+setgenre=", 10))
{
newstr = argv[1] + 10;
cmd = CMD_SETGENRE;
}
else if (!strncmp(argv[1], "+setresume=", 11))
{
newstr = argv[1] + 11;
cmd = CMD_SETRESUME;
}
else if (!strncmp(argv[1], "+addbookmark=", 13))
{
newstr = argv[1] + 13;
cmd = CMD_ADDBOOKMARK;
}
else if (!strncmp(argv[1], "+setbookmarks=", 14))
{
newstr = argv[1] + 14;
cmd = CMD_SETBOOKMARKS;
}
else if (!strncmp(argv[1], "+clearbookmarks", 15))
{
cmd = CMD_CLEARBOOKMARKS;
}
else
{
printf("Unknown command, %s\n", argv[1] + 1);
return syntax();
}
argc--, argv++;
break;
}
if (debug)
printf("Command %d\n", cmd);
for (i = 1; i < argc; i++)
{
struct hmt *hmt;
if (!(hmt = open_file(argv[i],
cmd == CMD_LIST || cmd == CMD_BOOKMARKS)))
continue;
switch (cmd)
{
case CMD_NEW:
cmd_new(hmt, toggle);
break;
case CMD_LOCK:
cmd_lock(hmt, toggle);
break;
case CMD_PROTECT:
cmd_protect(hmt, toggle);
break;
case CMD_ENCRYPTED:
cmd_encrypted(hmt, toggle);
break;
case CMD_SHRUNK:
cmd_shrunk(hmt, toggle);
break;
case CMD_DEDUP:
cmd_dedup(hmt, toggle);
break;
case CMD_DETECTADS:
cmd_detectads(hmt, toggle);
break;
case CMD_SETTITLE:
cmd_settitle(hmt, newstr);
break;
case CMD_SETSYNOPSIS:
cmd_setsynopsis(hmt, newstr);
break;
case CMD_SETGUIDANCE:
/* 0x03E0 2 byte 'Guide' flag for Media List.
* 0xFF00 = Guide off. 0x0101 = Guide on. */
patch_byte(hmt, HMT_GUIDEFLAG, HMT_GUIDEFLAG_ON);
patch_byte(hmt, HMT_GUIDEFLAG + 1, HMT_GUIDEFLAG_ON);
patch_string(hmt, HMT_GUIDANCE, HMT_GUIDANCE_LEN,
newstr);
break;
case CMD_SETSERIES:
cmd_setseries(hmt, newstr);
break;
case CMD_SETFOLDER:
patch_string(hmt, HMT_FOLDER, HMT_FOLDER_LEN, newstr);
break;
case CMD_SETFILENAME:
patch_string(hmt, HMT_FILENAME, HMT_FILENAME_LEN,
newstr);
break;
case CMD_BOOKMARKS:
display_bookmarks(hmt);
break;
case CMD_GUIDANCE:
cmd_guidance(hmt, toggle);
break;
case CMD_SETGENRE:
cmd_setgenre(hmt, newstr);
break;
case CMD_SETRESUME:
cmd_setresume(hmt, newstr);
break;
case CMD_ADDBOOKMARK:
cmd_bookmarks(hmt, newstr, 1);
display_bookmarks(hmt);
break;
case CMD_SETBOOKMARKS:
cmd_bookmarks(hmt, newstr, 0);
display_bookmarks(hmt);
break;
case CMD_CLEARBOOKMARKS:
cmd_bookmarks(hmt, NULL, 0);
display_bookmarks(hmt);
break;
case CMD_PATCH:
cmd_patch(hmt, newstr);
break;
case CMD_UNPATCH:
cmd_unpatch(hmt, newstr);
break;
default:
display_hmt(hmt);
}
close_file(hmt);
}
return 0;
}

174
util.c

@ -0,0 +1,174 @@
/*
* HMT Tool
* (c) af123, 2011
*/
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <time.h>
#include <strings.h>
#include <stdarg.h>
#include "lint.h"
inline uint32_t
read_uint32(uint8_t *p, int le)
{
if (le)
return p[3] << 24 | p