Compare commits

...

No commits in common. "master" and "foxsat" have entirely different histories.

10 changed files with 644 additions and 1097 deletions

2
.gitignore vendored
View File

@ -1,2 +0,0 @@
*.o
hmt

View File

@ -1,5 +1,5 @@
MAKE=make
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
@ -15,17 +15,22 @@ DEFS=-D_REENTRANT -D_TS_ERRNO -DHMT_PROTECT
SRCS= cmd.c \
display.c \
file.c \
hmt.c \
main.c \
util.c
OBJS= $(SRCS:.c=.o)
#CC=mipsel-linux-gcc
#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 -std=c99
CFLAGS=-g
INCS=
LIBS=-lxconv
LIBS=
ifeq ($(PLATFORM),CYGWIN_NT)
CFLAGS=-g -mno-cygwin
endif
@ -47,6 +52,7 @@ hmt: ${OBJS}
${CFLAGS} -o $@ \
${OBJS} \
${LIBS}
${STRIP} hmt
@echo "Done..."
clean:
@ -62,9 +68,5 @@ tags:
@echo " $<"
@$(CC) $(CFLAGS) ${WARN} ${DEFS} ${INCS} -c $< -o $@
install: hmt
strip hmt
cp hmt /mod/bin/hmt
${OBJS}: lint.h hmt.h
${OBJS}: lint.h

467
cmd.c
View File

@ -2,422 +2,149 @@
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.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 (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;
hmt->modified++;
}
void
cmd_lock(struct hmt *hmt, int flag)
{
CHECK_OFFSET(HMT_FLAGS1)
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;
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_GUIDETYPE, HMT_GUIDETYPE_ON);
patch_byte(hmt, HMT_GUIDEMODE, HMT_GUIDEMODE_ON);
patch_byte(hmt, HMT_GUIDETYPE2, HMT_GUIDETYPE_ON);
patch_byte(hmt, HMT_GUIDEMODE2, HMT_GUIDEMODE_ON);
}
else
{
patch_byte(hmt, HMT_GUIDETYPE, HMT_GUIDETYPE_OFF);
patch_byte(hmt, HMT_GUIDEMODE, HMT_GUIDEMODE_OFF);
patch_byte(hmt, HMT_GUIDETYPE2, HMT_GUIDETYPE_OFF);
patch_byte(hmt, HMT_GUIDEMODE2, HMT_GUIDEMODE_OFF);
}
hmt->modified++;
}
void
cmd_protect(struct hmt *hmt, int flag)
{
CHECK_OFFSET(HMT_FLAGS3)
if (flag)
hmt->bin[HMT_FLAGS3] = HMT_FLAGS3_PROTECTED_COPY_UNLIMITED;
else
hmt->bin[HMT_FLAGS3] = HMT_FLAGS3_UNPROTECTED;
hmt->modified++;
}
void
cmd_encrypted(struct hmt *hmt, int flag)
{
CHECK_OFFSET(HMT_FLAGS2);
if (flag)
hmt->bin[HMT_FLAGS2] |= HMT_FLAGS2_ENCRYPTED;
else
hmt->bin[HMT_FLAGS2] &= ~HMT_FLAGS2_ENCRYPTED;
hmt->modified++;
}
void
cmd_thumbnail(struct hmt *hmt, int flag)
{
CHECK_OFFSET(HMT_FLAGS2);
if (flag)
hmt->bin[HMT_FLAGS2] |= HMT_FLAGS2_THUMBNAIL;
else
hmt->bin[HMT_FLAGS2] &= ~HMT_FLAGS2_THUMBNAIL;
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] = (uint8_t)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 = strtol(resume, (char **)NULL, 10);
CHECK_OFFSET(HMT_PLAYED_TIME)
if (r < 0)
if (hmt->binsize < HMT_FLAGS1)
{
uint32_t d;
d = hmt->end - hmt->start;
printf("Recording Duration:%u\n", d);
r += d;
printf("File too small to patch.\n");
return;
}
printf("Setting resume point to: %u second(s) in.\n", r);
write_uint32(hmt->bin + HMT_PLAYED_TIME, r);
hmt->modified++;
if (flag)
hmt->bin[HMT_FLAGS1] |= HMT_FLAGS1_PROTECTED;
else
hmt->bin[HMT_FLAGS1] &= ~HMT_FLAGS1_PROTECTED;
}
static void
patch_epg_offset(struct hmt *hmt, uint32_t offset, uint32_t len, char *str)
void
patch_string(struct hmt *hmt, uint32_t offset, char *str, int ReportErrs)
{
if (hmt->binsize > 0x1001)
int i;
if (hmt->binsize < offset + strlen(str))
{
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_utf8(hmt, k + offset, len, str);
}
if (ReportErrs==1) {
printf("File too small to patch.\n");
}
}
}
void
cmd_settitle(struct hmt *hmt, char *str)
{
patch_string(hmt, HMT_TITLE, HMT_TITLE_LEN, str);
patch_string_utf8(hmt, HMT_ITITLE, HMT_ITITLE_LEN, str);
patch_epg_offset(hmt, HMT_EPG_TITLE, HMT_TITLE_LEN, str);
}
void
cmd_setsynopsis(struct hmt *hmt, char *str)
{
patch_string_utf8(hmt, HMT_SYNOPSIS, HMT_SYNOPSIS_LEN, str);
patch_epg_offset(hmt, HMT_EPG_SYNOPSIS, HMT_SYNOPSIS_LEN, str);
}
void
cmd_setguidance(struct hmt *hmt, char *str)
{
cmd_guidance(hmt, !0);
patch_string_utf8(hmt, HMT_GUIDANCE, HMT_GUIDANCE_LEN, str);
patch_string_utf8(hmt, HMT_GUIDANCE2, HMT_GUIDANCE_LEN, str);
patch_epg_offset(hmt, HMT_EPG_GUIDANCE, HMT_GUIDANCE_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] = (uint8_t)series;
hmt->bin[HMT_EPISODE] = (uint8_t)episode;
hmt->bin[HMT_EPISODETOT] = (uint8_t)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++;
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
patch_string(struct hmt *hmt, uint32_t offset, uint32_t len, char *str)
cmd_getstring(struct hmt *hmt, uint32_t offset)
{
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++;
printf("%s\n", strip_string(hmt->bin + offset));
}
void
patch_string_utf8(struct hmt *hmt, uint32_t offset, uint32_t len, char *str)
expand_hmt(struct hmt *hmt, uint32_t new_size)
{
CHECK_OFFSET(offset + strlen(str))
if (*str > 0x1F)
hmt->bin[offset++] = 0x15;
patch_string(hmt, offset, len, str);
}
void
patch_uint16(struct hmt *hmt, uint32_t offset, uint16_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(%u) = %#x(%u)\n", width,
offset, offset, val, val);
switch(width)
{
case 8:
patch_byte(hmt, offset, (uint8_t)val);
break;
case 16:
patch_uint16(hmt, offset, (uint16_t)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;
}
/*void *new_bin = malloc(new_size);*/
if (debug)
printf("Reading width %d - %#x(%u)\n", width,
offset, offset);
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);
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("%d %#x\n", val, val);
break;
}
default:
printf("Unknown patch width.\n");
break;
}
/*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);
}

288
display.c
View File

@ -1,161 +1,203 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "lint.h"
char *
ctimenl(time_t tm)
const char *
genre(unsigned char b)
{
static char buf[32];
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;
p = ctime(&tm);
memset(buf, '\0', sizeof(buf));
if (p)
{
strncpy(buf, p, sizeof(buf) - 1);
if ((p = strpbrk(buf, "\r\n")))
*p = '\0';
}
strcpy(buf, ctime(tm));
if ((p = strpbrk(buf, "\r\n")))
*p = '\0';
return buf;
}
void
display_bookmarks(struct hmt *hmt)
uint8_t *
strip_string(uint8_t *str)
{
int i;
for (i = 0; i < hmt->num_bookmarks && i < 32; i++)
printf("%d ", hmt->bookmarks[i]);
printf("\n");
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)
{
int g;
uint16_t i;
uint16_t j;
time_t tm;
char time_buf[256];
g = guidance(hmt);
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", hmt->mediatitle);
printf("%s\t", hmt->synopsis);
printf("%s\t", hmt->definition == DEF_HD ? "HD" : "SD");
printf("%u\t%s\t", hmt->lcn, hmt->channel);
printf("%u\t", hmt->start);
printf("%u\t", hmt->end);
printf("%s\t", hmt_flags(hmt));
if (g)
printf("%s\t", hmt->guidance);
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");
printf("%d\t", hmt->num_bookmarks);
printf("%u\t", hmt->schedstart);
printf("%u\t", hmt->scheddur);
printf("%d\t", hmt->genre);
printf("%u\t", hmt->resume);
printf("%s/%s\t", recordingstatus(hmt), failurereason(hmt));
printf("%u\t%u\t%u\t", hmt->series, hmt->episode,
hmt->episodetot);
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;
}
printf("Format:%s\n", hmt->definition == DEF_HD ? "HD" : "SD");
printf("Title:%s\n", hmt->mediatitle);
printf("ITitle:%s\n", hmt->title);
printf("Channel:%u (%s)\n", hmt->lcn, hmt->channel);
printf("Episode:%u,%u/%u\n",
hmt->series, hmt->episode, hmt->episodetot);
i = read_uint16(hmt->bin + HMT_CHANNEL_NUM, 0);
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);
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("Genre:%s (%d)\n", genredescr(hmt->genre), hmt->genre);
printf("EPG:%s\n", hmt->synopsis);
if (g)
printf("Guidance:%s\n", hmt->guidance);
printf("Format: %s\n", hmt->bin[HMT_FLAGS2] & HMT_FLAGS2_HD ? "HD" : "SD");
printf("Title: %s\n", strip_string(hmt->bin + (HMT_TITLE)));
if (hmt_is(hmt, HMTA_ENCRYPTED))
printf("Raw file is encrypted on disk.\n");
printf("Channel: %i (%s)\n", i, hmt->bin + HMT_CHANNEL_NAME);
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:%u (%s)\n", hmt->schedstart,
ctimenl(hmt->schedstart));
printf("Scheduled duration:%u\n", hmt->scheddur);
printf("Recording start:%u (%s)\n", hmt->start,
ctimenl(hmt->start));
printf("Recording end:%u (%s)\n", hmt->end, ctimenl(hmt->end));
printf("Duration:%u\n", hmt->end - hmt->start);
printf("Stored duration:%u\n", hmt->duration);
printf("Play resumes at:%u 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;
uint32_t 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:%u", 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 + HMT_EPG_TITLE));
}
/* Synopsis is off + 0x13e */
}
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);
}

200
file.c
View File

@ -5,18 +5,30 @@
#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)
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))))
{
@ -24,8 +36,11 @@ open_file(char *filename, int readonly)
return NULL;
}
hmt->epgguidance = NULL;
hmt->epgstart = NULL;
if (debug)
printf("Opening file '%s' (readonly=%d)\n", filename, readonly);
printf("Opening file '%s'\n", filename);
strcpy(hmt->fname, filename);
@ -60,54 +75,141 @@ open_file(char *filename, int readonly)
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)
if ((hmt->fd = open(hmt->fname, iOpenMode, 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 (iOpenMode == O_RDONLY)
{
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;
hmt->bin = (uint8_t *)mmap(NULL, hmt->binsize, PROT_READ,
MAP_SHARED, hmt->fd, 0);
}
else
{
if (debug)
printf("File mapped into memory.\n");
hmt->mmapped = 1;
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 != 0x1701) */
if (magic != 0x0000)
{
printf("Invalid HMT file, %s\n", hmt->fname);
printf("Invalid HMT file, %s", hmt->fname);
close_file(hmt);
return NULL;
}
hmt->modified = 0;
parse_hmt(hmt);
/* 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;
}
@ -117,19 +219,35 @@ 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);
}
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);
}

137
hmt.c
View File

@ -8,7 +8,6 @@
#include <time.h>
#include "lint.h"
#include "xconv.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)
@ -23,26 +22,7 @@ strip_string(uint8_t *str)
if (*str >= 0x20)
return str;
if (*str == 0x10)
{
size_t len = strlen((char *) str);
if (len > 3 && str[1] == 'i' && str[2] == '7')
{
len = len * 2 + 8;
char *dst = malloc(len);
if (dst)
{
if (xconv((char *) (str + 3), dst, len))
return (uint8_t *) dst;
else
{
free(dst);
return str + 3;
}
}
}
return (uint8_t *) "";
}
return str + 3;
if (*str == 0x1f)
return str + 2;
return str + 1;
@ -51,54 +31,49 @@ strip_string(uint8_t *str)
void
parse_hmt(struct hmt *hmt)
{
STRING( HMT_FOLDER, directory, HMT_FOLDER_LEN);
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);
uint8_t *p = hmt->bin + HMT_FILENAME;
if (!p[0] || (p[0] == p[1]))
++p;
hmt->filename = (char *) strip_string(p);
LE32( HMT_RECORDING_START, start);
LE32( HMT_RECORDING_END, end);
LE16( HMT_STORED_DURATION, duration);
LE8( HMT_RECSTATUS, recstatus);
LE8( HMT_FLAGS1, flags1);
LE8( HMT_FLAGS2, flags2);
LE8( HMT_FAILREASON, failreason);
LE32( HMT_PLAYED_TIME, resume);
LE16( HMT_BOOKMARKS_CNT, num_bookmarks);
STRING( HMT_TITLE, mediatitle, HMT_TITLE_LEN);
PTR32( HMT_BOOKMARKS, bookmarks);
LE8( HMT_FLAGS3, flags3);
LE8( HMT_GUIDETYPE, guidance_type);
LE8( HMT_GUIDEMODE, guidance_mode);
STRING( HMT_GUIDANCE, guidance, HMT_GUIDANCE_LEN + 1);
LE8( HMT_COPYCOUNT, copycount);
LE16( HMT_TZOFFSET, tzoffset);
LE32( HMT_CHANNEL_NUM, lcn);
LE8( HMT_RECTYPE, type);
STRING( HMT_CHANNEL_NAME, channel, HMT_CHANNEL_NAME_LEN + 1);
LE16( HMT_SID, service_id);
LE16( HMT_TSID, tsid);
LE16( HMT_ONID, onid);
LE16( HMT_PMTPID, pmt_pid);
LE16( HMT_VIDEOPID, video_pid);
// LE8( HMT_HDFLAG, definition);
LE8( HMT_HDFLAG2, definition);
LE16( HMT_AUDIOPID, audio_pid);
LE32( HMT_SCHEDULED_START, schedstart);
LE32( HMT_SCHEDULED_DURATION, scheddur);
LE8( HMT_GENRE, genre);
STRING( HMT_ITITLE, title, HMT_ITITLE_LEN + 3);
STRING( HMT_SYNOPSIS, synopsis, HMT_SYNOPSIS + 3);
LE16( HMT_EVENTID, event_id);
LE8( HMT_SHRUNK, xflags1);
LE8( HMT_DEDUPED, xflags2);
LE8( HMT_DETECTADS, xflags3);
LE8( HMT_SERIES, series);
LE8( HMT_EPISODE, episode);
LE8( HMT_EPISODETOT, episodetot);
LE8( 0x10, series);
LE8( 0x11, episode);
LE8( 0x12, episodetot);
}
int
@ -107,19 +82,19 @@ hmt_is(struct hmt *hmt, enum hmt_attribute attr)
switch (attr)
{
case HMTA_ENCRYPTED:
return hmt->flags2 & HMT_FLAGS2_ENCRYPTED;
return hmt->flags2 & 0x1;
case HMTA_THUMBNAIL:
return hmt->flags2 & HMT_FLAGS2_THUMBNAIL;
return hmt->flags2 & 0x2;
case HMTA_GHOST:
return hmt->flags2 & HMT_FLAGS2_GHOST;
return hmt->flags2 & 0x8;
case HMTA_LOCKED:
return hmt->flags1 & HMT_FLAGS1_LOCKED;
return hmt->flags1 & 0x4;
case HMTA_NEW:
return !(hmt->flags1 & HMT_FLAGS1_NEW);
return !(hmt->flags1 & 0x8);
case HMTA_ENCFLAGGED:
return hmt->flags3 != HMT_FLAGS3_UNPROTECTED;
return hmt->flags3 != 4;
case HMTA_UNLIMITEDCOPY:
return !(hmt->flags3 & HMT_FLAGS3_PROTECTED_COPY_LIMITED);
return hmt->flags3 & 0x4;
case HMTA_SHRUNK:
return hmt->xflags1 == 'E';
case HMTA_DEDUPED:
@ -127,7 +102,7 @@ hmt_is(struct hmt *hmt, enum hmt_attribute attr)
case HMTA_DETECTADS:
return hmt->xflags3 == 'G';
case HMTA_RADIO:
return hmt->type == HMT_RECTYPE_RADIO;
return hmt->type == 0x2;
default:
fprintf(stderr, "Unhandled hmt_is, %d\n", attr);
break;
@ -182,15 +157,14 @@ struct {
{ 0x00, "Unclassified" },
{ 0x10, "Film" },
{ 0x20, "News & Factual" },
{ 0x30, "Entertainment (Show)" },
{ 0x30, "Entertainment" },
{ 0x40, "Sport" },
{ 0x50, "Children" },
{ 0x60, "Entertainment (Music)" },
{ 0x70, "News & Factual (Arts)" },
{ 0x80, "News & Factual (Social)" },
{ 0x60, "Entertainment" },
{ 0x70, "News & Factual" },
{ 0x80, "News & Factual" },
{ 0x90, "Education" },
{ 0xa0, "Lifestyle" },
{ 0xc0, "Adult" },
{ 0xf0, "Drama" },
{ 0, NULL },
};
@ -288,3 +262,4 @@ guidance(struct hmt *hmt)
break;
}
}

193
hmt.h
View File

@ -1,175 +1,66 @@
#ifdef _WIN32
#include <windows.h>
#define MAXPATHLEN MAX_PATH
#else
#include <strings.h>
#include <sys/param.h>
#endif
enum hmt_attribute {
HMTA_ENCRYPTED = 0,
HMTA_LOCKED,
HMTA_NEW,
HMTA_ENCFLAGGED,
HMTA_SHRUNK,
HMTA_DEDUPED,
HMTA_DETECTADS,
HMTA_UNLIMITEDCOPY,
HMTA_RADIO,
HMTA_GHOST,
HMTA_THUMBNAIL,
};
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;
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;
};
#define HMT_EPG_BLOCK_LENGTH 0x220
#define HMT_EPG_TITLE 0x14
#define HMT_BLOCK_LENGTH 0x1003
void parse_hmt(struct hmt *);
uint8_t *strip_string(uint8_t *);
int guidance(struct hmt *);
const char *genredescr(unsigned char b);
unsigned char genrecode(char *);
const char *genre(unsigned 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_HEADER_PROG_EV_ID 0x036b
#define HMT_FOLDER 0x80
#define HMT_FOLDER_LEN 254
#define HMT_FOLDER 0x21
#define HMT_FOLDER_LEN 512
#define HMT_FILENAME 0x17f
#define HMT_FILENAME_LEN 256
#define HMT_FILENAME 0x21
#define HMT_FILENAME_LEN 512
#define HMT_TITLE 0x29a
#define HMT_TITLE_LEN 48
#define HMT_ITITLE 0x516
#define HMT_ITITLE_LEN 48
#define HMT_TITLE 0x221
#define HMT_TITLE_LEN 255
#define HMT_SYNOPSIS_LEN 255
#define HMT_CHANNEL_NUM 0x458 /* 4 bytes */
#define HMT_CHANNEL_NAME 0x45c
#define HMT_CHANNEL_NAME_LEN 42
#define HMT_SYNOPSIS 0x616
#define HMT_SYNOPSIS_LEN 252
#define HMT_CHANNEL_NUM 0x11 /* 2 bytes */
#define HMT_CHANNEL_NAME 0x321
#define HMT_EPG 0x114
#define HMT_RECSTATUS 0x28c
#define HMT_FAILREASON 0x290
#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_RECTYPE 0x4b8
#define HMT_RECTYPE_RADIO 0x2
#define HMT_FLAGS2 0x368
#define HMT_FLAGS2_ENCRYPTED 0x10 /* As in "keep encrypted" */
#define HMT_FLAGS2_HD 0x80
#define HMT_FLAGS1 0x28d
#define HMT_FLAGS1_LOCKED 0x4
#define HMT_FLAGS1_NEW 0x8
#define HMT_FLAGS3 0x369
#define HMT_FLAGS3_LIMITCOPY 0x20
#define HMT_FLAGS2 0x28e
#define HMT_FLAGS2_ENCRYPTED 0x1
#define HMT_FLAGS2_THUMBNAIL 0x2
#define HMT_FLAGS2_GHOST 0x8
#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_BOOKMARKS_CNT 0x298
#define HMT_BOOKMARKS 0x31c
#define HMT_FLAGS3 0x3dc
#define HMT_FLAGS3_PROTECTED_COPY_UNLIMITED 0
#define HMT_FLAGS3_PROTECTED_COPY_LIMITED 0x2
#define HMT_FLAGS3_UNPROTECTED 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_GUIDETYPE 0x3e0
#define HMT_GUIDETYPE_ON 0x01
#define HMT_GUIDETYPE_OFF 0xff
#define HMT_GUIDEMODE 0x3e1
#define HMT_GUIDEMODE_ON 0x01
#define HMT_GUIDEMODE_OFF 0
#define HMT_GUIDEFLAG 0x3e0
#define HMT_GUIDEFLAG_ON 0x01
#define HMT_GUIDANCE 0x3e2
#define HMT_GUIDANCE_LEN 74
#define HMT_GUIDETYPE2 0x73b
#define HMT_GUIDEMODE2 0x73c
#define HMT_GUIDANCE2 0x741
#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_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_TZOFFSET 0x434
/* 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
#define HMT_EPG_TITLE 0x3e
#define HMT_EPG_SYNOPSIS 0x13e
#define HMT_EPG_GUIDANCE 0x269
#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)

59
lint.h
View File

@ -7,64 +7,21 @@ extern int debug;
extern const char *version;
extern unsigned long sysopts;
uint16_t read_uint16(uint8_t *, int);
uint32_t read_uint32(uint8_t *, int);
void write_uint32(uint8_t *, uint32_t);
void write_uint16(uint8_t *, uint16_t);
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);
void display_bookmarks(struct hmt *hmt);
uint8_t * strip_string(uint8_t *);
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_thumbnail(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_setguidance(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 patch_string_utf8(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 *);
#ifdef _WIN32
#include <io.h>
#include <windows.h>
#define MAXPATHLEN MAX_PATH
#define open(a,b,c) _open(a,b,c)
#define close(a) _close(a)
#define read(a,b,c) _read(a,b,c)
#define write(a,b,c) _write(a,b,c)
#define lseek(a,b,c) _lseek(a,b,c)
#define MAP_FAILED 0
#define mmap(a,b,c,d,e,f) MAP_FAILED
#define munmap(a,b)
#define strncasecmp _strnicmp
typedef long off_t;
#else /* _WIN32 */
#include <sys/mman.h>
#include <sys/param.h>
#include <unistd.h>
#endif /* _WIN32 */
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);

353
main.c
View File

@ -1,91 +1,52 @@
/*
* Humax HMT Tool
* (c) af123, 2011-2022
* 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.0.12";
const char *version = "2.5";
unsigned long sysopts = 0;
int
syntax()
{
fprintf(stderr, "Humax HMT Tool v%s, by af123, 2011-2024.\n\n",
version);
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");
"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"
" 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 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"
" +/-thumbnail Mark/unmark recording as thumbnail present.\n"
" +/-protect Mark/unmark recording as protected.\n"
);
#endif
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 preceded 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 preceded 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"
" +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;
@ -94,33 +55,20 @@ syntax()
int
main(int argc, char **argv)
{
int iFileOpenMode = O_RDONLY;
enum {
CMD_LIST = 0,
CMD_NEW,
CMD_LOCK,
#ifdef HMT_PROTECT
CMD_PROTECT,
CMD_GUIDANCE,
CMD_ENCRYPTED,
CMD_THUMBNAIL,
CMD_SHRUNK,
CMD_DEDUP,
CMD_DETECTADS,
#endif
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_GETPATHANDFILENAME
} cmd = CMD_LIST;
char *newstr = "";
char *newstr;
int i, toggle;
if (argc > 1 && !strncmp(argv[1], "-d", 2))
@ -129,11 +77,8 @@ main(int argc, char **argv)
debug = 1;
else
debug = atoi(argv[1] + 2);
if (debug > 0)
{
printf("Set debug level to %d\n", debug);
argc--, argv++;
}
printf("Set debug level to %d\n", debug);
argc--, argv++;
}
if (argc > 1 && !strcmp(argv[1], "-p"))
@ -142,48 +87,42 @@ main(int argc, char **argv)
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 '+':
case '+':
toggle = 1;
/* Fall-through */
case '-':
if (!strcmp(argv[1] + 1, "list"))
cmd = CMD_LIST;
else if (!strcmp(argv[1] + 1, "new"))
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;
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, "thumbnail"))
cmd = CMD_THUMBNAIL;
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;
iFileOpenMode = O_RDWR;
}
#endif
else if (!strncmp(argv[1], "+settitle=", 10))
{
newstr = argv[1] + 10;
@ -193,6 +132,7 @@ main(int argc, char **argv)
return 0;
}
cmd = CMD_SETTITLE;
iFileOpenMode = O_RDWR;
}
else if (!strncmp(argv[1], "+setsynopsis=", 13))
{
@ -203,31 +143,7 @@ main(int argc, char **argv)
return 0;
}
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;
iFileOpenMode = O_RDWR;
}
else if (!strncmp(argv[1], "+setfilename=", 13))
{
@ -238,30 +154,7 @@ main(int argc, char **argv)
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;
iFileOpenMode = O_RDWR;
}
else
{
@ -272,118 +165,64 @@ main(int argc, char **argv)
break;
}
if (debug)
if (debug) {
printf("Command %d\n", cmd);
printf("File open mode %d\n", iFileOpenMode);
}
for (i = 1; i < argc; i++)
{
struct hmt *hmt;
if (!(hmt = open_file(argv[i],
cmd == CMD_LIST || cmd == CMD_BOOKMARKS)))
continue;
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_NEW:
cmd_new(hmt, toggle);
break;
case CMD_LOCK:
cmd_lock(hmt, toggle);
break;
case CMD_LOCK:
cmd_lock(hmt, toggle);
break;
case CMD_PROTECT:
cmd_protect(hmt, toggle);
break;
#ifdef HMT_PROTECT
case CMD_PROTECT:
cmd_protect(hmt, toggle);
break;
#endif
case CMD_ENCRYPTED:
cmd_encrypted(hmt, toggle);
break;
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_SHRUNK:
cmd_shrunk(hmt, toggle);
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_DEDUP:
cmd_dedup(hmt, toggle);
break;
case CMD_SETFILENAME:
patch_string(hmt, HMT_FILENAME, newstr,1);
break;
case CMD_DETECTADS:
cmd_detectads(hmt, toggle);
break;
case CMD_GETPATHANDFILENAME:
cmd_getstring(hmt, HMT_FILENAME);
break;
case CMD_THUMBNAIL:
cmd_thumbnail(hmt, toggle);
break;
case CMD_SETTITLE:
cmd_settitle(hmt, newstr);
break;
case CMD_SETSYNOPSIS:
cmd_setsynopsis(hmt, newstr);
break;
case CMD_SETGUIDANCE:
cmd_setguidance(hmt, 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:
hmt->bin[HMT_FILENAME] = '\0';
patch_string(hmt, HMT_FILENAME + 1, 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);
default:
display_hmt(hmt);
}
close_file(hmt);

20
util.c
View File

@ -1,6 +1,6 @@
/*
* HMT Tool
* (c) af123, 2011
* af123, 2011
*/
#include <stdio.h>
@ -9,16 +9,14 @@
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <time.h>
#include <strings.h>
#include <stdarg.h>
#include "lint.h"
uint32_t
inline uint32_t
read_uint32(uint8_t *p, int le)
{
if (le)
@ -27,7 +25,7 @@ read_uint32(uint8_t *p, int le)
return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
}
uint16_t
inline uint16_t
read_uint16(uint8_t *p, int le)
{
if (le)
@ -36,20 +34,20 @@ read_uint16(uint8_t *p, int le)
return p[0] << 8 | p[1];
}
void
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;
*p++ = val >> 16 & 0xff;
*p++ = val >> 8 & 0xff;
*p++ = val & 0xff;
}
void
inline void
write_uint16(uint8_t *p, uint16_t val)
{
*p++ = val & 0xff;
*p++ = val >> 8 & 0xff;
*p++ = val & 0xff;
}
uint8_t *