254 lines
6.7 KiB
C
254 lines
6.7 KiB
C
|
#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);
|
||
|
}
|