initial hmt tool commit

This commit is contained in:
hummypkg 2011-04-30 00:57:30 +00:00 committed by HummyPkg
commit f6a5c33742
9 changed files with 1779 additions and 0 deletions

70
Makefile Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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[2] << 16 | p[1] << 8 | p[0];
else
return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
}
inline uint16_t
read_uint16(uint8_t *p, int le)
{
if (le)
return p[1] << 8 | p[0];
else
return p[0] << 8 | p[1];
}
inline void
write_uint32(uint8_t *p, uint32_t val)
{
*p++ = val & 0xff;
*p++ = val >> 8 & 0xff;
*p++ = val >> 16 & 0xff;
*p++ = val >> 24 & 0xff;
}
inline void
write_uint16(uint8_t *p, uint16_t val)
{
*p++ = val & 0xff;
*p++ = val >> 8 & 0xff;
}
uint8_t *
memmem(register uint8_t *mem, uint32_t mlen,
register uint8_t *pat, uint32_t plen)
{
while (mlen >= plen)
{
if (!memcmp(mem, pat, plen))
return mem;
mem++, mlen--;
}
return (uint8_t *)NULL;
}
void
hexdump(uint8_t *s, uint32_t len, uint32_t o)
{
uint16_t off;
if (!s)
return;
if (!len)
len = strlen((char *)s);
for (off = 0; off < len; off += 16)
{
uint32_t i;
printf("%08lx: ", (unsigned long)off + o);
for (i = off; i - off < 16; i++)
{
if (i < len)
printf("%02x ", s[i] & 0xff);
else
printf(" ");
}
printf(" ");
for (i = off; i < len && i - off < 16; i++)
printf("%c", isprint((int)s[i]) ? s[i] : '.');
printf("\n");
}
}
void
hexstr(uint8_t *s, uint32_t len)
{
uint16_t off;
if (!s)
return;
if (!len)
len = strlen((char *)s);
for (off = 0; off < len; off += 16)
{
uint32_t i;
for (i = off; i - off < 16; i++)
{
if (i < len)
printf("%02x", s[i] & 0xff);
}
}
}
void
hexdump_fd(int fd, uint32_t len)
{
off_t pos = lseek(fd, 0, SEEK_CUR);
uint8_t *buffer;
buffer = (uint8_t *)malloc(len);
read(fd, buffer, len);
hexdump(buffer, len, 0);
lseek(fd, pos, SEEK_SET);
}
void
error(char *fmt, ...)
{
char buf[0x400];
va_list argp;
va_start(argp, fmt);
vsprintf(buf, fmt, argp);
fprintf(stderr, "%s\n", buf);
va_end(argp);
exit(1);
}
#if defined(__MINGW32__) || defined(__linux__)
size_t
strlcpy(char *s, const char *t, size_t n)
{
const char *o = t;
if (n)
do
{
if (!--n)
{
*s = 0;
break;
}
} while ((*s++ = *t++));
if (!n)
while (*t++);
return t - o - 1;
}
#endif