initial hmt tool commit
This commit is contained in:
commit
7080bd92b0
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
|
@ -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);
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue