Compare commits
No commits in common. "master" and "foxsat" have entirely different histories.
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
*.o
|
||||
hmt
|
22
Makefile
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
|
||||
|
||||
|
467
cmd.c
467
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)
|
||||
{
|
||||
/* 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
288
display.c
@ -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
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);
|
||||
}
|
||||
|
137
hmt.c
137
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);
|
||||
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
193
hmt.h
@ -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
59
lint.h
@ -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
353
main.c
@ -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
20
util.c
@ -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 *
|
||||
|
Loading…
Reference in New Issue
Block a user