Compare commits

...

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

  1. 22
      Makefile
  2. 453
      cmd.c
  3. 274
      display.c
  4. 200
      file.c
  5. 128
      hmt.c
  6. 193
      hmt.h
  7. 59
      lint.h
  8. 367
      main.c
  9. 20
      util.c

22
Makefile

@ -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

453
cmd.c

@ -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)
cmd_protect(struct hmt *hmt, int flag)
{
/* 0x03E0 2 byte 'Guide' flag for Media List.
0xFF00 = Guide off. 0x0101 = Guide on. */
if (flag)
if (hmt->binsize < HMT_FLAGS1)
{
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);
printf("File too small to patch.\n");
return;
}
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';
hmt->bin[HMT_FLAGS1] |= HMT_FLAGS1_PROTECTED;
else
hmt->bin[HMT_SHRUNK] = 'e';
hmt->modified++;
hmt->bin[HMT_FLAGS1] &= ~HMT_FLAGS1_PROTECTED;
}
void
cmd_dedup(struct hmt *hmt, int flag)
patch_string(struct hmt *hmt, uint32_t offset, char *str, int ReportErrs)
{
CHECK_OFFSET(HMT_DEDUPED);
int i;
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)
{
uint32_t d;
d = hmt->end - hmt->start;
printf("Recording Duration:%u\n", d);
r += d;
}
printf("Setting resume point to: %u second(s) in.\n", r);
write_uint32(hmt->bin + HMT_PLAYED_TIME, r);
hmt->modified++;
}
static void
patch_epg_offset(struct hmt *hmt, uint32_t offset, uint32_t len, char *str)
{
if (hmt->binsize > 0x1001)
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))
/*void *new_bin = malloc(new_size);*/
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;
}
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
cmd_unpatch(struct hmt *hmt, char *str)
init_hmt_epg_block(struct hmt *hmt)
{
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;
}
uint32_t duration = 0;
if (debug)
printf("Reading width %d - %#x(%u)\n", width,
offset, offset);
switch(width)
{
case 8:
{
uint8_t val = hmt->bin[offset] & 0xff;
printf("%d %#x\n", val, val);
break;
}
case 16:
{
uint16_t val = read_uint16(hmt->bin + offset, 1);
printf("%d %#x\n", val, val);
break;
}
case 32:
{
uint32_t val = read_uint32(hmt->bin + offset, 1);
printf("%d %#x\n", val, val);
break;
}
default:
printf("Unknown patch width.\n");
break;
}
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);
}

274
display.c

@ -1,161 +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)
ctimenl(time_t *tm)
{
static char buf[32];
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;
g = guidance(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", 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");
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");
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);
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("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");
}
printf("Recording Status: %s (%s)\n",
recordingstatus(hmt), failurereason(hmt));
printf("Flags: %s\n", hmt_flags(hmt));
printf("Copy count:%d\n", hmt->copycount);
if (hmt->epgguidance != NULL)
printf("Guidance: %s\n", strip_string(hmt->epgguidance));
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("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("Play resumes at:%u second%s in.\n", hmt->resume,
hmt->resume == 1 ? "" : "s");
printf("Timezone offset:%d\n", hmt->tzoffset);
printf("Copy count: %d\n", hmt->bin[HMT_COPYCOUNT]);
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;
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)); */
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 */
}
}
}
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

@ -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);
}

128
hmt.c

@ -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);
uint8_t *p = hmt->bin + HMT_FILENAME;
if (!p[0] || (p[0] == p[1]))
++p;
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);
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;
@ -287,3 +262,4 @@ guidance(struct hmt *hmt)
break;
}
}

193
hmt.h

@ -1,175 +1,66 @@