initial hmt tool commit

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

72
Makefile Normal file
View File

@ -0,0 +1,72 @@
MAKE=gmake
# 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 \
main.c \
util.c
OBJS= $(SRCS:.c=.o)
#CC=mips-linux-gcc
#CC=gcc
#STRIP=mips-linux-strip
#STRIP=strip
CC=mips-linux-gcc
STRIP=mips-linux-strip
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}
${STRIP} hmt
@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 $@
${OBJS}: lint.h

150
cmd.c Normal file
View File

@ -0,0 +1,150 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include "lint.h"
void
cmd_new(struct hmt *hmt, int flag)
{
if (hmt->binsize < HMT_FLAGS1)
{
printf("File too small to patch.\n");
return;
}
if (flag)
hmt->bin[HMT_FLAGS1] &= ~HMT_FLAGS1_NEW;
else
hmt->bin[HMT_FLAGS1] |= HMT_FLAGS1_NEW;
}
void
cmd_lock(struct hmt *hmt, int flag)
{
if (hmt->binsize < HMT_FLAGS1)
{
printf("File too small to patch.\n");
return;
}
if (flag)
hmt->bin[HMT_FLAGS1] |= HMT_FLAGS1_LOCKED;
else
hmt->bin[HMT_FLAGS1] &= ~HMT_FLAGS1_LOCKED;
}
void
cmd_protect(struct hmt *hmt, int flag)
{
if (hmt->binsize < HMT_FLAGS1)
{
printf("File too small to patch.\n");
return;
}
if (flag)
hmt->bin[HMT_FLAGS1] |= HMT_FLAGS1_PROTECTED;
else
hmt->bin[HMT_FLAGS1] &= ~HMT_FLAGS1_PROTECTED;
}
void
patch_string(struct hmt *hmt, uint32_t offset, char *str, int ReportErrs)
{
int i;
if (hmt->binsize < offset + strlen(str))
{
if (ReportErrs==1) {
printf("File too small to patch.\n");
}
return;
}
if (offset != HMT_FILENAME) {
for (i=0; i<255; i++) {hmt->bin[offset+i] = 0x00;}
hmt->bin[offset] = 0x15;
strcpy((char *)(hmt->bin + offset+1), (const char *)str);
} else {
for (i=0; i<512; i++) {hmt->bin[offset+i] = 0x00;}
strcpy((char *)(hmt->bin + offset), (const char *)str);
}
}
void
cmd_getstring(struct hmt *hmt, uint32_t offset)
{
printf("%s\n", strip_string(hmt->bin + offset));
}
void
expand_hmt(struct hmt *hmt, uint32_t new_size)
{
/*void *new_bin = malloc(new_size);*/
if (debug)
printf("expand_hmt(0x%08X, %d) {\n", (void *)hmt, (int)new_size);
/*memset(new_bin, 0, new_size);*/
/*memcpy(new_bin, hmt->bin, hmt->binsize);*/
munmap((void *)hmt->bin, hmt->binsize);
hmt->binsize = new_size;
/* Will this work when size is larger than file? */
hmt->bin = (uint8_t *)mmap(NULL, hmt->binsize, PROT_READ|PROT_WRITE,
MAP_SHARED, hmt->fd, 0);
/*free(hmt->bin);*/
/*hmt->bin = new_bin;*/
hmt->epgstart = hmt->bin + 0x1004;
if (debug)
printf("expand_hmt(0x%08X, %d) }\n", (void *)hmt, (int)new_size);
}
void
init_hmt_epg_block(struct hmt *hmt)
{
uint32_t duration = 0;
if (debug)
printf("init_hmt_epg_block(0x%08X) {\n", (void *)hmt);
hmt->bin[HMT_BLOCK_LENGTH] = 0x1;
if (debug)
printf(
"Zeroing 0x%04X bytes from 0x%04X\n",
HMT_EPG_BLOCK_LENGTH + HMT_GUIDE_BLOCK_MIN_SIZE,
hmt->epgstart - hmt->bin
);
memset(hmt->epgstart, 0, HMT_EPG_BLOCK_LENGTH + HMT_GUIDE_BLOCK_MIN_SIZE);
if (debug)
printf(
"Copying 0x%04X bytes from 0x%04X to 0x%04X\n",
4, HMT_RECORDING_START, hmt->epgstart + HMT_SCHEDULED_START - hmt->bin
);
memcpy(hmt->epgstart + HMT_SCHEDULED_START, hmt->bin + HMT_RECORDING_START, 4);
if (debug)
printf("1\n");
duration =
*(uint32_t*)(hmt->bin + HMT_RECORDING_END) -
*(uint32_t*)(hmt->bin + HMT_RECORDING_START);
if (debug)
printf(
"Setting duration %d in 0x%04X\n",
duration, hmt->epgstart + HMT_SCHEDULED_DURATION - hmt->bin
);
memcpy(hmt->epgstart + HMT_SCHEDULED_DURATION, &duration, 4);
*(hmt->epgstart + 0x0c) = 0x04;
*(hmt->epgstart + 0x0e) = 0x01;
/**(hmt->epgstart + 0x14) = 0x15;*/
if (debug)
printf(
"Copying 0x%04X bytes from 0x%04X to 0x%04X\n",
0xff, HMT_TITLE, hmt->epgstart + 0x14 - hmt->bin
);
memcpy(hmt->epgstart + 0x14, hmt->bin + HMT_TITLE, 0xff /*53*/);
/* *(hmt->epgstart + 0x114) = 0x15; */
/* Synopsis, up to 255 bytes. */
/* No guide block. */
if (debug)
printf("init_hmt_epg_block(0x%08X) }\n", (void *)hmt);
}

203
display.c Normal file
View File

@ -0,0 +1,203 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "lint.h"
const char *
genre(unsigned char b)
{
switch (b)
{
case 0:
return "Unclassified";
case 0x10:
return "Film";
case 0x20:
return "News & Factual";
case 0x30:
return "Entertainment";
case 0x40:
return "Sport";
case 0x50:
return "Children";
case 0x60:
return "Education";
case 0xa0:
return "Lifestyle";
case 0xf0:
return "Drama";
default:
return "Unknown";
}
}
char *
ctimenl(time_t *tm)
{
static char buf[25];
char *p;
strcpy(buf, ctime(tm));
if ((p = strpbrk(buf, "\r\n")))
*p = '\0';
return buf;
}
uint8_t *
strip_string(uint8_t *str)
{
if (str[0] == 0x10 && str[1] == 0x69 && str[2] == 0x37)
return str + 3;
if (*str == 0x15)
return str + 1;
/* non freesat recordings seems to have a 0x05 as the header string */
if (*str == 0x05)
return str + 1;
return str;
}
void
display_hmt(struct hmt *hmt)
{
uint16_t i;
uint16_t j;
time_t tm;
char time_buf[256];
char fullpath[512]={0};
char filename[512]={0};
int posslash = -1;
/* title, epg, def, channel num, channel name, start, end, flags,
* guidance
*/
if (sysopts & SYSOPT_PARSABLE)
{
printf("%s\t", strip_string(hmt->bin + (HMT_TITLE)));
if (hmt->epgstart != NULL)
printf("%s\t", strip_string(hmt->epgstart + HMT_EPG));
else
printf("\t");
printf("%s\t", hmt->bin[HMT_FLAGS2] & HMT_FLAGS2_HD ? "HD" : "SD");
i = read_uint16(hmt->bin + HMT_CHANNEL_NUM, 0);
printf("%i\t%s\t", i,
strip_string(hmt->bin + HMT_CHANNEL_NAME));
tm = read_uint32(hmt->bin + HMT_RECORDING_START, 0);
(void) strftime(time_buf, sizeof(time_buf), "%a %b %d %H:%M:%S %Y", gmtime(&tm));
printf("%s\t", time_buf);
printf("%lu\t", tm);
tm = read_uint32(hmt->bin + HMT_RECORDING_END, 0);
(void) strftime(time_buf, sizeof(time_buf), "%a %b %d %H:%M:%S %Y", gmtime(&tm));
printf("%s\t", time_buf);
printf("%lu\t", tm);
printf("%s%s%s%s%s",
hmt->bin[HMT_FLAGS1] & HMT_FLAGS1_LOCKED ? "Locked," : "",
hmt->bin[HMT_FLAGS1] & HMT_FLAGS1_NEW ? "" : "New,",
hmt->bin[HMT_FLAGS1] & HMT_FLAGS1_PROTECTED? "Protected," : "",
hmt->bin[HMT_FLAGS2] & HMT_FLAGS2_ENCRYPTED ? "Encrypted," : "",
hmt->epgguidance != NULL ? "Guidance," : ""
);
if (hmt->bin[HMT_CONVERT_FLAG] == 'P')
printf("Converting,\t");
else
if (hmt->bin[HMT_CONVERT_FLAG] == 'F')
printf("Converted,\t");
else
printf("Unconverted,\t");
if (hmt->epgguidance != NULL)
printf("%s\t", strip_string(hmt->epgguidance));
printf("\n");
return;
}
i = read_uint16(hmt->bin + HMT_CHANNEL_NUM, 0);
memcpy(fullpath, hmt->bin + HMT_FOLDER, sizeof(fullpath));
posslash = (strrchr(fullpath, '/') - fullpath);
memcpy(filename, hmt->bin + HMT_FOLDER + posslash + 1, (sizeof(fullpath) - posslash));
fullpath[posslash+1]=0;
printf("Format: %s\n", hmt->bin[HMT_FLAGS2] & HMT_FLAGS2_HD ? "HD" : "SD");
printf("Title: %s\n", strip_string(hmt->bin + (HMT_TITLE)));
printf("Channel: %i (%s)\n", i, hmt->bin + HMT_CHANNEL_NAME);
printf("Folder: %s\n", fullpath);
printf("Filename: %s.ts\n", filename);
if (hmt->epgstart != NULL) {
printf("EPG: %s\n", strip_string(hmt->epgstart + HMT_EPG));
} else {
printf("EPG:\n");
}
if (hmt->epgguidance != NULL)
printf("Guidance: %s\n", strip_string(hmt->epgguidance));
printf("\n");
printf("Flags: %s%s%s%s%s%s",
hmt->bin[HMT_FLAGS2] & HMT_FLAGS2_HD ? "HD," : "SD,",
hmt->bin[HMT_FLAGS1] & HMT_FLAGS1_LOCKED ? "Locked," : "",
hmt->bin[HMT_FLAGS1] & HMT_FLAGS1_NEW ? "" : "New,",
hmt->bin[HMT_FLAGS1] & HMT_FLAGS1_PROTECTED? "Protected," : "",
hmt->bin[HMT_FLAGS2] & HMT_FLAGS2_ENCRYPTED ? "Encrypted," : "",
hmt->epgguidance != NULL ? "Guidance," : ""
);
if (hmt->bin[HMT_CONVERT_FLAG] == 'P')
printf("Converting,\n");
else
if (hmt->bin[HMT_CONVERT_FLAG] == 'F')
printf("Converted,\n");
else
printf("Unconverted,\n");
printf("Copy count: %d\n", hmt->bin[HMT_COPYCOUNT]);
printf("\n");
tm = read_uint32(hmt->epgstart + HMT_SCHEDULED_START, 0);
(void) strftime(time_buf, sizeof(time_buf), "%a %b %d %H:%M:%S %Y", gmtime(&tm));
printf("Scheduled start: %s\n", time_buf);
/* printf("Scheduled start:%lu (%s)\n", tm, ctimenl(&tm)); */
tm = read_uint32(hmt->epgstart + HMT_SCHEDULED_DURATION, 0);
printf("Scheduled duration: %lu\n", tm);
tm = read_uint32(hmt->bin + HMT_RECORDING_START, 0);
(void) strftime(time_buf, sizeof(time_buf), "%a %b %d %H:%M:%S %Y", gmtime(&tm));
printf("Recording start: %s\n", time_buf);
/* printf("Recording start:%lu (%s)\n", tm, ctimenl(&tm)); */
tm = read_uint32(hmt->bin + HMT_RECORDING_END, 0);
(void) strftime(time_buf, sizeof(time_buf), "%a %b %d %H:%M:%S %Y", gmtime(&tm));
printf("Recording end: %s\n", time_buf);
/* printf("Recording end:%lu (%s)\n", tm, ctimenl(&tm)); */
/* printf("Play resumes at: %i seconds in.\n", read_uint16(hmt->bin + HMT_PLAYED_TIME, 0)); */
printf("\n");
j = read_uint16(hmt->bin + HMT_SID, 0);
printf("Service ID (SID): %u\n", j);
j = read_uint16(hmt->bin + HMT_TSID, 0);
printf("Transport Stream ID (TSID): %u\n", j);
j = read_uint16(hmt->bin + HMT_ONID, 0);
printf("Originating Network ID (ONID): %u\n", j);
j = read_uint16(hmt->bin + HMT_PMTPID, 0);
printf("Programme Map Table PID (PMTPID): %u\n", j);
j = read_uint16(hmt->bin + HMT_VIDEOPID, 0);
printf("Video PID: %u\n", j);
j = read_uint16(hmt->bin + HMT_AUDIOPID, 0);
printf("Audio PID: %u\n", j);
}

253
file.c Normal file
View File

@ -0,0 +1,253 @@
#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 iOpenMode)
{
struct hmt *hmt;
struct stat st;
char *ext;
uint16_t magic;
uint16_t numepgblocks = 0;
uint16_t thisepgblock = 0;
uint8_t numtransportblocks = 0;
uint8_t numguidanceblocks = 0;
int i;
uint16_t thisblocklength;
uint16_t eventid;
int foundit;
if (!(hmt = malloc(sizeof(struct hmt))))
{
perror("malloc");
return NULL;
}
hmt->epgguidance = NULL;
hmt->epgstart = NULL;
if (debug)
printf("Opening file '%s'\n", filename);
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);
if ((hmt->fd = open(hmt->fname, iOpenMode, 0)) == -1)
{
perror(hmt->fname);
free(hmt);
return NULL;
}
if (iOpenMode == O_RDONLY)
{
hmt->bin = (uint8_t *)mmap(NULL, hmt->binsize, PROT_READ,
MAP_SHARED, hmt->fd, 0);
}
else
{
hmt->bin = (uint8_t *)mmap(NULL, hmt->binsize, PROT_READ|PROT_WRITE,
MAP_SHARED, hmt->fd, 0);
}
if (hmt->bin == MAP_FAILED)
{
perror("mmap");
close(hmt->fd);
free(hmt);
return NULL;
}
magic = read_uint16(hmt->bin, 0);
/* if (magic != 0x1701) */
if (magic != 0x0000)
{
printf("Invalid HMT file, %s", hmt->fname);
close_file(hmt);
return NULL;
}
/* Check for non-standard/non-freesat HMTs
Don't try to find the EPG data if this is one of them!
*/
if (hmt->binsize < 0x1004)
{
hmt->epgstart=NULL;
} else {
/*
Need to find the correct EPG entry after the main HMT Block at the start of the file
If the recording spans multiple EPG entries there will be the same number of EPG blocks
each of which may have one or more Guidance Blocks and/or one or more Transport Stream
blocks.
Two possible ways of doing this:
1. If the recording was an EPG based recording then the target event ID will be located in the header block
at offset 0x36b (2 bytes) and also in the EPG block at offset 0x00. This can be used for a definite match.
2. For manual record on demand and manual timed recordings the event ID is not present in the header so have to
find the correct EPG block using a text based comparison of the programme titles.
*/
/* First EPG Block starts at the end of the HMT Block */
hmt->epgstart = (hmt->bin + 0x1004);
numepgblocks = read_uint16(hmt->bin + (HMT_BLOCK_LENGTH - 1), 0);
thisepgblock=1;
foundit = 0;
eventid = read_uint16(hmt->bin + HMT_HEADER_PROG_EV_ID, 0);
if (eventid == 0) {
/* This is a manual recording where the eventid is not present so use string matching to find the EPG block */
while ((strcmp((char *)hmt->epgstart+HMT_EPG_TITLE, (char *)hmt->bin+HMT_TITLE)!=0) && (thisepgblock <= numepgblocks)) {
/* This is not the correct epg entry so skip to next one */
numtransportblocks = hmt->epgstart[(HMT_EPG_BLOCK_LENGTH - 11)];
numguidanceblocks = hmt->epgstart[(HMT_EPG_BLOCK_LENGTH - 10)];
hmt->epgstart = hmt->epgstart + HMT_EPG_BLOCK_LENGTH;
for (i=0; i< (numtransportblocks+numguidanceblocks); i++) {
/* Skip this guidance or transport block as it's not the correct EPG entry */
thisblocklength = read_uint16(hmt->epgstart+2, 0);
hmt->epgstart = hmt->epgstart + thisblocklength + 4;
}
thisepgblock++;
}
if (strcmp((char *)hmt->epgstart+HMT_EPG_TITLE, (char *)hmt->bin+HMT_TITLE) == 0) {
foundit = 1;
}
} else {
/* This is an Event Based (EPG Based) recording so find the EPG Block using the Event ID */
while ((read_uint16(hmt->epgstart, 0) != eventid) && (thisepgblock <= numepgblocks)) {
/* This is not the correct epg entry so skip to next one */
numtransportblocks = hmt->epgstart[(HMT_EPG_BLOCK_LENGTH - 11)];
numguidanceblocks = hmt->epgstart[(HMT_EPG_BLOCK_LENGTH - 10)];
hmt->epgstart = hmt->epgstart + HMT_EPG_BLOCK_LENGTH;
for (i=0; i< (numtransportblocks+numguidanceblocks); i++) {
/* Skip this guidance or transport block as it's not the correct EPG entry */
thisblocklength = read_uint16(hmt->epgstart+2, 0);
hmt->epgstart = hmt->epgstart + thisblocklength + 4;
}
thisepgblock++;
}
if (read_uint16(hmt->epgstart, 0) == eventid) {
foundit = 1;
}
}
/* Check that we've found the EPG block ..... if not, revert to the first EPG block in the file.
This can happen if, for example, the recording was a manual recording which has been renamed so
there's no event ID and the title strings between the header and the EPG block don't match.
*/
if (foundit == 0) {
hmt->epgstart = (hmt->bin + 0x1004);
}
/* So now we have the start of the correct EPG record.
See if there's any guidance blocks and if so, set the relevant pointer
for the start of the Guidance test.
Note - Foxsat HDR HMT files can contain multiple Guidance Blocks - return the first one */
numguidanceblocks = hmt->epgstart[(HMT_EPG_BLOCK_LENGTH - 10)];
if (numguidanceblocks != 0) {
hmt->epgguidance = hmt->epgstart + HMT_EPG_BLOCK_LENGTH;
/* Skip any transport blocks first */
numtransportblocks = hmt->epgstart[(HMT_EPG_BLOCK_LENGTH - 11)];
if (numtransportblocks != 0) {
for(i=0; i<numtransportblocks; i++) {
thisblocklength = read_uint16(hmt->epgguidance+2, 0);
hmt->epgguidance = hmt->epgguidance + thisblocklength + 4;
}
}
/* Pointer now at the start of the guidance block
so now set it to the Guidance Text itself */
hmt->epgguidance = hmt->epgguidance + 21;
}
}
return hmt;
}
void
close_file(struct hmt *hmt)
{
if (debug)
printf("Closing file.\n");
munmap((void *)hmt->bin, hmt->binsize);
if (hmt->fd > 0)
close(hmt->fd);
hmt->fd = -1;
}
struct hmt *
extend_file(struct hmt *hmt, uint32_t extra_size)
{
FILE *file = NULL;
void *nul = NULL;
/*
* No portable way of extending a memory mapped file, just close it, append
* extra bytes and reopen.
*/
close_file(hmt);
if (debug)
printf("Extending file by %d byte(s)\n", extra_size);
if (file = fopen(hmt->fname, "ab")) {
nul = malloc(extra_size);
memset(nul, 0, extra_size);
fwrite(nul, 1, extra_size, file);
fclose(file);
free(nul);
nul = NULL;
}
return open_file(hmt->fname, O_RDWR);
}

265
hmt.c Normal file
View File

@ -0,0 +1,265 @@
#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"
#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_THUMBNAIL:
return hmt->flags2 & 0x2;
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_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;
}
}

66
hmt.h Normal file
View File

@ -0,0 +1,66 @@
#include <sys/param.h>
struct hmt {
char fname[MAXPATHLEN + 1];
int fd;
uint8_t *bin;
uint8_t *epgstart; /* Start of the correct EPG entry in the HMT file */
uint8_t *epgguidance; /* Start of the guidance text for the relevant EPG entry */
uint32_t binsize;
};
#define HMT_EPG_BLOCK_LENGTH 0x220
#define HMT_EPG_TITLE 0x14
#define HMT_BLOCK_LENGTH 0x1003
#define HMT_HEADER_PROG_EV_ID 0x036b
#define HMT_FOLDER 0x21
#define HMT_FOLDER_LEN 512
#define HMT_FILENAME 0x21
#define HMT_FILENAME_LEN 512
#define HMT_TITLE 0x221
#define HMT_TITLE_LEN 255
#define HMT_SYNOPSIS_LEN 255
#define HMT_CHANNEL_NUM 0x11 /* 2 bytes */
#define HMT_CHANNEL_NAME 0x321
#define HMT_EPG 0x114
#define HMT_FLAGS1 0x367
#define HMT_FLAGS1_LOCKED 0x80
#define HMT_FLAGS1_PROTECTED 0x40
#define HMT_FLAGS1_NEW 0x20
#define HMT_CONVERT_FLAG 0x500
#define HMT_FLAGS2 0x368
#define HMT_FLAGS2_ENCRYPTED 0x10 /* As in "keep encrypted" */
#define HMT_FLAGS2_HD 0x80
#define HMT_FLAGS3 0x369
#define HMT_FLAGS3_LIMITCOPY 0x20
#define HMT_RECORDING_START 0x19 /* 4 bytes */
#define HMT_RECORDING_END 0x1D /* 4 bytes */
#define HMT_SCHEDULED_START 0x04 /* 4 bytes */
#define HMT_SCHEDULED_DURATION 0x08 /* 4 bytes */
#define HMT_PLAYED_TIME 0x05 /* 4 bytes */
#define HMT_GUIDEFLAG 0x3e0
#define HMT_GUIDEFLAG_ON 0x01
#define HMT_GUIDANCE 0x3e2
#define HMT_SID 0x417 /* 2 bytes */
#define HMT_TSID 0x425 /* 2 bytes */
#define HMT_ONID 0x427 /* 2 bytes */
#define HMT_PMTPID 0x419 /* 2 bytes */
#define HMT_VIDEOPID 0x41b /* 2 bytes */
#define HMT_AUDIOPID 0x41d /* 2 bytes */
#define HMT_COPYCOUNT 0x431
#define HMT_EPG_SYNOPSIS 0x114 /* 0x115 */
#define HMT_GUIDE_BLOCK_MIN_SIZE 0x15
#define HMT_WITH_EPG_BLOCK_MIN_SIZE (HMT_BLOCK_LENGTH + HMT_EPG_BLOCK_LENGTH + HMT_GUIDE_BLOCK_MIN_SIZE)

27
lint.h Normal file
View File

@ -0,0 +1,27 @@
#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);
void hexdump(uint8_t *, uint32_t, uint32_t);
struct hmt *open_file(char *, int);
void close_file(struct hmt *);
struct hmt *extend_file(struct hmt *hmt, uint32_t extra_size);
uint8_t * strip_string(uint8_t *);
void display_hmt(struct hmt *hmt);
void cmd_protect(struct hmt *, int);
void cmd_new(struct hmt *, int);
void cmd_lock(struct hmt *, int);
void patch_string(struct hmt *, uint32_t, char *, int);
void cmd_getstring(struct hmt *, uint32_t);
void expand_hmt(struct hmt *hmt, uint32_t new_size);
void init_hmt_epg_block(struct hmt *hmt);

232
main.c Normal file
View File

@ -0,0 +1,232 @@
/*
* Humax HMT Tool
* af123, 2011
*/
#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 <string.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <time.h>
#include "lint.h"
int debug = 0;
const char *version = "2.5";
unsigned long sysopts = 0;
int
syntax()
{
fprintf(stderr, "Humax HMT Tool v%s, by af123, 2012.\n", version);
fprintf(stderr, "Modified for Foxsat HDR by adrianf36 2012 and MofTot 2018-20.\n\n");
fprintf(stderr,
"Syntax: hmt [command] <filename> [filename] ...\n");
fprintf(stderr,
" Commands:\n"
" -p Read HMT and produce parseable output for use by the WebIf\n"
" -f Read HMT and return path and filename (without extension) to TS file\n"
" +/-new Mark/unmark recording as new.\n"
" +/-lock Mark/unmark recording as locked.\n"
);
#ifdef HMT_PROTECT
fprintf(stderr,
" +/-protect Mark/unmark recording as protected.\n"
);
#endif
fprintf(stderr,
" +settitle=<new title>\n"
" +setsynopsis=<new synopsis>\n"
" +setfilename=<new filename>\n"
" where <new filename> = Full Path and filename of related TS file\n"
" WITHOUT the .TS extension. e.g. /mnt/hd3/Video/File123\n"
" Note. File paths MUST be /mnt/hd3/... and NOT /media/...\n"
);
fprintf(stderr, "\n");
return 0;
}
int
main(int argc, char **argv)
{
int iFileOpenMode = O_RDONLY;
enum {
CMD_LIST = 0,
CMD_NEW,
CMD_LOCK,
#ifdef HMT_PROTECT
CMD_PROTECT,
#endif
CMD_SETTITLE,
CMD_SETSYNOPSIS,
CMD_SETFILENAME,
CMD_GETPATHANDFILENAME
} 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);
printf("Set debug level to %d\n", debug);
argc--, argv++;
}
if (argc > 1 && !strcmp(argv[1], "-p"))
{
sysopts |= SYSOPT_PARSABLE;
argc--, argv++;
}
if (argc > 1 && !strcmp(argv[1], "-f"))
{
cmd = CMD_GETPATHANDFILENAME;
argc--, argv++;
}
if (argc < 2)
return syntax();
toggle = 0;
switch (*argv[1])
{
case '+':
toggle = 1;
/* Fall-through */
case '-':
if (debug)
printf("argv[1] = \"%s\"\n", argv[1]);
if (!strcmp(argv[1] + 1, "new"))
{
cmd = CMD_NEW;
iFileOpenMode = O_RDWR;
}
else if (!strcmp(argv[1] + 1, "lock"))
{
cmd = CMD_LOCK;
iFileOpenMode = O_RDWR;
}
#ifdef HMT_PROTECT
else if (!strcmp(argv[1] + 1, "protect"))
{
cmd = CMD_PROTECT;
iFileOpenMode = O_RDWR;
}
#endif
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;
iFileOpenMode = O_RDWR;
}
else if (!strncmp(argv[1], "+setsynopsis=", 13))
{
newstr = argv[1] + 13;
if (strlen(newstr) >= HMT_SYNOPSIS_LEN)
{
fprintf(stderr, "New synopsis too long.\n");
return 0;
}
cmd = CMD_SETSYNOPSIS;
iFileOpenMode = O_RDWR;
}
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;
iFileOpenMode = O_RDWR;
}
else
{
printf("Unknown command, %s\n", argv[1] + 1);
return syntax();
}
argc--, argv++;
break;
}
if (debug) {
printf("Command %d\n", cmd);
printf("File open mode %d\n", iFileOpenMode);
}
for (i = 1; i < argc; i++)
{
struct hmt *hmt;
if (strlen(argv[i]) > 4 &&
(
!strcmp(argv[i] + strlen(argv[i]) - 4, ".hmt") ||
!strcmp(argv[i] + strlen(argv[i]) - 3, ".ts")
)) {
if (!(hmt = open_file(argv[i], iFileOpenMode)))
continue;
}
else continue;
switch (cmd)
{
case CMD_NEW:
cmd_new(hmt, toggle);
break;
case CMD_LOCK:
cmd_lock(hmt, toggle);
break;
#ifdef HMT_PROTECT
case CMD_PROTECT:
cmd_protect(hmt, toggle);
break;
#endif
case CMD_SETTITLE:
patch_string(hmt, HMT_TITLE, newstr,1);
patch_string(hmt, (hmt->epgstart - hmt->bin) + HMT_EPG_TITLE, newstr,0);
break;
case CMD_SETSYNOPSIS:
if (!hmt->epgstart || hmt->binsize < HMT_WITH_EPG_BLOCK_MIN_SIZE) {
hmt = extend_file(hmt, HMT_WITH_EPG_BLOCK_MIN_SIZE - hmt->binsize);
init_hmt_epg_block(hmt);
}
patch_string(hmt, (hmt->epgstart - hmt->bin) + HMT_EPG_SYNOPSIS, newstr, 1 /*0*/);
break;
case CMD_SETFILENAME:
patch_string(hmt, HMT_FILENAME, newstr,1);
break;
case CMD_GETPATHANDFILENAME:
cmd_getstring(hmt, HMT_FILENAME);
break;
default:
display_hmt(hmt);
}
close_file(hmt);
}
return 0;
}

174
util.c Normal file
View File

@ -0,0 +1,174 @@
/*
* HMT Tool
* 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 >> 24 & 0xff;
*p++ = val >> 16 & 0xff;
*p++ = val >> 8 & 0xff;
*p++ = val & 0xff;
}
inline void
write_uint16(uint8_t *p, uint16_t val)
{
*p++ = val >> 8 & 0xff;
*p++ = val & 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