/*
* Some code freely inspired from VobSub <URL:http://vobsub.edensrising.com>,
* with kind permission from Gabest <gabest@freemail.hu>
*/
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "config.h"
#include "version.h"
#include "vobsub.h"
#include "spudec.h"
#include "mp_msg.h"
#ifdef USE_UNRARLIB
#include "unrarlib.h"
#endif
#include "libavutil/common.h"
extern int vobsub_id;
/**********************************************************************
* RAR stream handling
* The RAR file must have the same basename as the file to open
* See <URL:http://www.unrarlib.org/>
**********************************************************************/
#ifdef USE_UNRARLIB
typedef struct {
FILE *file;
unsigned char *data;
unsigned long size;
unsigned long pos;
} rar_stream_t;
static rar_stream_t *
rar_open(const char *const filename, const char *const mode)
{
rar_stream_t *stream;
/* unrarlib can only read */
if (strcmp("r", mode) && strcmp("rb", mode)) {
errno = EINVAL;
return NULL;
}
stream = malloc(sizeof(rar_stream_t));
if (stream == NULL)
return NULL;
/* first try normal access */
stream->file = fopen(filename, mode);
if (stream->file == NULL) {
char *rar_filename;
const char *p;
int rc;
/* Guess the RAR archive filename */
rar_filename = NULL;
p = strrchr(filename, '.');
if (p) {
ptrdiff_t l = p - filename;
rar_filename = malloc(l + 5);
if (rar_filename == NULL) {
free(stream);
return NULL;
}
strncpy(rar_filename, filename, l);
strcpy(rar_filename + l, ".rar");
}
else {
rar_filename = malloc(strlen(filename) + 5);
if (rar_filename == NULL) {
free(stream);
return NULL;
}
strcpy(rar_filename, filename);
strcat(rar_filename, ".rar");
}
/* get rid of the path if there is any */
if ((p = strrchr(filename, '/')) == NULL) {
p = filename;
} else {
p++;
}
rc = urarlib_get(&stream->data, &stream->size, (char*) p, rar_filename, "");
if (!rc) {
/* There is no matching filename in the archive. However, sometimes
* the files we are looking for have been given arbitrary names in the archive.
* Let's look for a file with an exact match in the extension only. */
int i, num_files, name_len;
ArchiveList_struct *list, *lp;
/* the cast in the next line is a hack to overcome a design flaw (IMHO) in unrarlib */
num_files = urarlib_list (rar_filename, (ArchiveList_struct *)&list);
if (num_files > 0) {
char *demanded_ext;
demanded_ext = strrchr (p, '.');
if (demanded_ext) {
int demanded_ext_len = strlen (demanded_ext);
for (i=0, lp=list; i<num_files; i++, lp=lp->next) {
name_len = strlen (lp->item.Name);
if (name_len >= demanded_ext_len && !strcasecmp (lp->item.Name + name_len - demanded_ext_len, demanded_ext)) {
if ((rc = urarlib_get(&stream->data, &stream->size, lp->item.Name, rar_filename, ""))) {
break;
}
}
}
}
urarlib_freelist (list);
}
if (!rc) {
free(rar_filename);
free(stream);
return NULL;
}
}
free(rar_filename);
stream->pos = 0;
}
return stream;
}
static int
rar_close(rar_stream_t *stream)
{
if (stream->file)
return fclose(stream->file);
free(stream->data);
return 0;
}
static int
rar_eof(rar_stream_t *stream)
{
if (stream->file)
return feof(stream->file);
return stream->pos >= stream->size;
}
static long
rar_tell(rar_stream_t *stream)
{
if (stream->file)
return ftell(stream->file);
return stream->pos;
}
static int
rar_seek(rar_stream_t *stream, long offset, int whence)
{
if (stream->file)
return fseek(stream->file, offset, whence);
switch (whence) {
case SEEK_SET:
if (offset < 0) {
errno = EINVAL;
return -1;
}
stream->pos = offset;
break;
case SEEK_CUR:
if (offset < 0 && stream->pos < (unsigned long) -offset) {
errno = EINVAL;
return -1;
}
stream->pos += offset;
break;
case SEEK_END:
if (offset < 0 && stream->size < (unsigned long) -offset) {
errno = EINVAL;
return -1;
}
stream->pos = stream->size + offset;
break;
default:
errno = EINVAL;
return -1;
}
return 0;
}
static int
rar_getc(rar_stream_t *stream)
{
if (stream->file)
return getc(stream->file);
if (rar_eof(stream))
return EOF;
return stream->data[stream->pos++];
}
static size_t
rar_read(void *ptr, size_t size, size_t nmemb, rar_stream_t *stream)
{
size_t res;
unsigned long remain;
if (stream->file)
return fread(ptr, size, nmemb, stream->file);
if (rar_eof(stream))
return 0;
res = size * nmemb;
remain = stream->size - stream->pos;
if (res > remain)
res = remain / size * size;
memcpy(ptr, stream->data + stream->pos, res);
stream->pos += res;
res /= size;
return res;
}
#else
typedef FILE rar_stream_t;
#define rar_open fopen
#define rar_close fclose
#define rar_eof feof
#define rar_tell ftell
#define rar_seek fseek
#define rar_getc getc
#define rar_read fread
#endif
/**********************************************************************/
static ssize_t
vobsub_getline(char **lineptr, size_t *n, rar_stream_t *stream)
{
size_t res = 0;
int c;
if (*lineptr == NULL) {
*lineptr = malloc(4096);
if (*lineptr)
*n = 4096;
}
else if (*n == 0) {
char *tmp = realloc(*lineptr, 4096);
if (tmp) {
*lineptr = tmp;
*n = 4096;
}
}
if (*lineptr == NULL || *n == 0)
return -1;
for (c = rar_getc(stream); c != EOF; c = rar_getc(stream)) {
if (res + 1 >= *n) {
char *tmp = realloc(*lineptr, *n * 2);
if (tmp == NULL)
return -1;
*lineptr = tmp;
*n *= 2;
}
(*lineptr)[res++] = c;
if (c == '\n') {
(*lineptr)[res] = 0;
return res;
}
}
if (res == 0)
return -1;
(*lineptr)[res] = 0;
return res;
}
/**********************************************************************
* MPEG parsing
**********************************************************************/
typedef struct {
rar_stream_t *stream;
unsigned int pts;
int aid;
unsigned char *packet;
unsigned int packet_reserve;
unsigned int packet_size;
} mpeg_t;
static mpeg_t *
mpeg_open(const char *filename)
{
mpeg_t *res = malloc(sizeof(mpeg_t));
int err = res == NULL;
if (!err) {
res->pts = 0;
res->aid = -1;
res->packet = NULL;
res->packet_size = 0;
res->packet_reserve = 0;
res->stream = rar_open(filename, "rb");
err = res->stream == NULL;
if (err)
perror("fopen Vobsub file failed");
if (err)
free(res);
}
return err ? NULL : res;
}
static void
mpeg_free(mpeg_t *mpeg)
{
if (mpeg->packet)
free(mpeg->packet);
if (mpeg->stream)
rar_close(mpeg->stream);
free(mpeg);
}
static int
mpeg_eof(mpeg_t *mpeg)
{
return rar_eof(mpeg->stream);
}
static off_t
mpeg_tell(mpeg_t *mpeg)
{
return rar_tell(mpeg->stream);
}
static int
mpeg_run(mpeg_t *mpeg)
{
unsigned int len, idx, version;
int c;
/* Goto start of a packet, it starts with 0x000001?? */
const unsigned char wanted[] = { 0, 0, 1 };
|