/*
* This file is part of libbluray
* Copyright (C) 2013-2015 VideoLAN
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* .
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "bdplus.h"
#include "file/dl.h"
#include "file/file.h"
#include "util/logging.h"
#include "util/macro.h"
#include "util/strutl.h"
#include
#include
struct bd_bdplus {
void *h_libbdplus; /* library handle from dlopen */
void *bdplus; /* bdplus handle from bdplus_open() */
/* functions */
fptr_int32 event;
fptr_p_void m2ts;
fptr_int32 m2ts_close;
fptr_int32 seek;
fptr_int32 fixup;
/* old API */
fptr_p_void title;
int impl_id;
};
static void _libbdplus_close(BD_BDPLUS *p)
{
if (p->bdplus) {
DL_CALL(p->h_libbdplus, bdplus_free, p->bdplus);
p->bdplus = NULL;
}
}
static void _unload(BD_BDPLUS *p)
{
_libbdplus_close(p);
if (p->h_libbdplus) {
dl_dlclose(p->h_libbdplus);
}
}
void libbdplus_unload(BD_BDPLUS **p)
{
if (p && *p) {
_unload(*p);
X_FREE(*p);
}
}
int libbdplus_required(void *have_file_handle, int (*have_file)(void *, const char *, const char *))
{
if (have_file(have_file_handle, "BDSVM", "00000.svm")) {
BD_DEBUG(DBG_BLURAY, "BDSVM" DIR_SEP "00000.svm found. Disc seems to be BD+ protected.\n");
return 1;
}
BD_DEBUG(DBG_BLURAY, "BDSVM" DIR_SEP "00000.svm not found. No BD+ protection.\n");
return 0;
}
#define IMPL_USER 0
#define IMPL_LIBBDPLUS 1
#define IMPL_LIBMMBD 2
static void *_libbdplus_open(int *impl_id)
{
const char * const libbdplus[] = {
getenv("LIBBDPLUS_PATH"),
"libbdplus",
"libmmbd",
};
unsigned ii;
for (ii = *impl_id; ii < sizeof(libbdplus) / sizeof(libbdplus[0]); ii++) {
if (libbdplus[ii]) {
void *handle = dl_dlopen(libbdplus[ii], "0");
if (handle) {
*impl_id = ii;
BD_DEBUG(DBG_BLURAY, "Using %s for BD+\n", libbdplus[ii]);
return handle;
}
}
}
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "No usable BD+ libraries found!\n");
return NULL;
}
int libbdplus_is_mmbd(BD_BDPLUS *p)
{
return p && (p->impl_id == IMPL_LIBMMBD);
}
static BD_BDPLUS *_load(int impl_id)
{
BD_BDPLUS *p = calloc(1, sizeof(BD_BDPLUS));
if (!p) {
return NULL;
}
p->impl_id = impl_id;
BD_DEBUG(DBG_BDPLUS, "attempting to load libbdplus\n");
p->h_libbdplus = _libbdplus_open(&p->impl_id);
if (!p->h_libbdplus) {
X_FREE(p);
return NULL;
}
BD_DEBUG(DBG_BLURAY, "Loading libbdplus (%p)\n", p->h_libbdplus);
*(void **)(&p->event) = dl_dlsym(p->h_libbdplus, "bdplus_event");
*(void **)(&p->m2ts) = dl_dlsym(p->h_libbdplus, "bdplus_m2ts");
*(void **)(&p->seek) = dl_dlsym(p->h_libbdplus, "bdplus_seek");
*(void **)(&p->fixup) = dl_dlsym(p->h_libbdplus, "bdplus_fixup");
*(void **)(&p->m2ts_close) = dl_dlsym(p->h_libbdplus, "bdplus_m2ts_close");
if (!p->m2ts) {
/* Old API */
*(void **)(&p->title) = dl_dlsym(p->h_libbdplus, "bdplus_set_title");
if (!p->title) {
*(void **)(&p->title) = dl_dlsym(p->h_libbdplus, "bdplus_set_m2ts");
}
}
if (!p->seek || !p->fixup || !((p->m2ts && p->m2ts_close) || p->title)) {
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "libbdplus dlsym failed! (%p)\n", p->h_libbdplus);
libbdplus_unload(&p);
return NULL;
}
BD_DEBUG(DBG_BLURAY, "Loaded libbdplus (%p)\n", p->h_libbdplus);
return p;
}
BD_BDPLUS *libbdplus_load()
{
return _load(0);
}
int libbdplus_init(BD_BDPLUS *p, const char *root, const char *device,
void *file_open_handle, void *file_open_fp,
const uint8_t *vid, const uint8_t *mk)
{
fptr_p_void bdplus_init;
fptr_void set_fopen;
_libbdplus_close(p);
/* force libmmbd BD+ if no AACS media key:
* - libbdplus requires media key
* - libmmbd does not export media key
* (=> libbdplus won't work with libmmbd AACS)
*/
if (mk == NULL && p->impl_id == IMPL_LIBBDPLUS) {
BD_BDPLUS *p2 = _load(IMPL_LIBMMBD);
if (p2) {
if (!libbdplus_init(p2, root, device, file_open_handle, file_open_fp, vid, mk)) {
/* succeed - swap implementations */
_unload(p);
*p = *p2;
X_FREE(p2);
return 0;
}
/* failed - continue with original bd+ implementation */
libbdplus_unload(&p2);
}
}
/* */
*(void **)(&bdplus_init) = dl_dlsym(p->h_libbdplus, "bdplus_init");
*(void **)(&set_fopen) = dl_dlsym(p->h_libbdplus, "bdplus_set_fopen");
if (!bdplus_init) {
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "libbdplus dlsym(bdplus_init) failed! (%p)\n", p->h_libbdplus);
return -1;
}
if (set_fopen) {
/* New libbdplus. Use libbluray for file I/O */
p->bdplus = bdplus_init(NULL, NULL, vid);
set_fopen(p->bdplus, file_open_handle, file_open_fp);
} else if (root) {
/* Old libbdplus or libmmbd. Disc is mounted. */
p->bdplus = bdplus_init(root, NULL, vid);
} else if (device) {
/* Unmounted device */
if (p->impl_id == IMPL_LIBMMBD && !strncmp(device, "/dev/", 5)) {
char *tmp = str_printf("dev:%s", device);
if (tmp) {
p->bdplus = bdplus_init(tmp, NULL, vid);
X_FREE(tmp);
}
} else {
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Too old libbdplus detected. Disc must be mounted first.\n");
}
}
if (!p->bdplus) {
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "bdplus_init() failed! (%p)\n", p->h_libbdplus);
return -1;
}
DL_CALL(p->h_libbdplus, bdplus_set_mk, p->bdplus, mk);
return 0;
}
static uint32_t _bdplus_get(BD_BDPLUS *p, const char *func)
{
if (p && p->bdplus) {
fptr_int32 fp;
*(void **)(&fp) = dl_dlsym(p->h_libbdplus, func);
if (fp) {
return fp(p->bdplus);
}
}
return 0;
}
int libbdplus_get_gen(BD_BDPLUS *p)
{
return _bdplus_get(p, "bdplus_get_code_gen");
}
int libbdplus_get_date(BD_BDPLUS *p)
{
return _bdplus_get(p, "bdplus_get_code_date");
}
const uint8_t *libbdplus_get_data(BD_BDPLUS *p, int type)
{
switch (type) {
case BD_BDPLUS_TYPE:
if (libbdplus_is_mmbd(p)) {
return (const uint8_t *)"mmbd";
}
}
return NULL;
}
void libbdplus_event(BD_BDPLUS *p, uint32_t event, uint32_t param1, uint32_t param2)
{
if (p && p->bdplus && p->event) {
p->event(p->bdplus, event, param1, param2);
}
}
void libbdplus_mmap(BD_BDPLUS *p, uint32_t region_id, void *mem)
{
if (p && p->bdplus) {
DL_CALL(p->h_libbdplus, bdplus_mmap, p->bdplus, region_id, mem);
}
}
void libbdplus_psr(BD_BDPLUS *p, void *regs, void *read, void *write)
{
if (p && p->bdplus) {
DL_CALL(p->h_libbdplus, bdplus_psr, p->bdplus, regs, read, write);
}
}
void libbdplus_start(BD_BDPLUS *p)
{
if (p && p->bdplus) {
DL_CALL(p->h_libbdplus, bdplus_start, p->bdplus);
}
}
/*
* stream layer
*/
struct bd_bdplus_st {
BD_BDPLUS *lib;
void *st;
};
BD_BDPLUS_ST *libbdplus_m2ts(BD_BDPLUS *p, uint32_t clip_id, uint64_t pos)
{
if (p && p->bdplus) {
if (!p->m2ts) {
/* use old API */
BD_BDPLUS_ST *ret = calloc(1, sizeof(BD_BDPLUS_ST));
if (ret) {
ret->lib = p;
ret->st = NULL;
p->title(p->bdplus, clip_id);
p->seek(p->bdplus, pos);
}
return ret;
}
void *st = p->m2ts(p->bdplus, clip_id);
if (!st) {
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "BD+ failed for clip %05d.m2ts\n", clip_id);
} else if (p->seek(st, pos) < 0) {
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "BD+ seek failed for clip %05d.m2ts\n", clip_id);
p->m2ts_close(st);
} else {
BD_BDPLUS_ST *ret = calloc(1, sizeof(BD_BDPLUS_ST));
if (ret) {
ret->lib = p;
ret->st = st;
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "BD+ active for clip %05d.m2ts\n", clip_id);
}
return ret;
}
}
return NULL;
}
int libbdplus_m2ts_close(BD_BDPLUS_ST **p)
{
int result = -1;
if (p && *p) {
if ((*p)->lib && (*p)->st) {
result = (*p)->lib->m2ts_close((*p)->st);
}
X_FREE(*p);
}
return result;
}
int libbdplus_seek(BD_BDPLUS_ST *p, uint64_t pos)
{
if (p) {
if (p->st) {
return p->lib->seek(p->st, pos);
} else {
/* use old API */
return p->lib->seek(p->lib->bdplus, pos);
}
}
return -1;
}
int libbdplus_fixup(BD_BDPLUS_ST *p, uint8_t *buf, int len)
{
if (p && !p->lib->m2ts) {
/* use old API */
return p->lib->fixup(p->lib->bdplus, len, buf);
}
if (p && p->st) {
int32_t numFixes;
numFixes = p->lib->fixup(p->st, len, buf);
#if 1
if (numFixes) {
BD_DEBUG(DBG_BDPLUS, "BD+ did %d fixups\n", numFixes);
}
#endif
return numFixes;
}
return -1;
}