|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* This file is part of libbluray
|
|
Packit |
5e46da |
* Copyright (C) 2014 VideoLAN
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
* This library is free software; you can redistribute it and/or
|
|
Packit |
5e46da |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
5e46da |
* License as published by the Free Software Foundation; either
|
|
Packit |
5e46da |
* version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
5e46da |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
5e46da |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
5e46da |
* Lesser General Public License for more details.
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
5e46da |
* License along with this library. If not, see
|
|
Packit |
5e46da |
* <http://www.gnu.org/licenses/>.
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#if HAVE_CONFIG_H
|
|
Packit |
5e46da |
#include "config.h"
|
|
Packit |
5e46da |
#endif
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "dec.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "enc_info.h"
|
|
Packit |
5e46da |
#include "aacs.h"
|
|
Packit |
5e46da |
#include "bdplus.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "file/file.h"
|
|
Packit |
5e46da |
#include "util/logging.h"
|
|
Packit |
5e46da |
#include "util/macro.h"
|
|
Packit |
5e46da |
#include "util/strutl.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include <string.h>
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
struct bd_dec {
|
|
Packit |
5e46da |
int use_menus;
|
|
Packit |
5e46da |
BD_AACS *aacs;
|
|
Packit |
5e46da |
BD_BDPLUS *bdplus;
|
|
Packit |
5e46da |
};
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* stream
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
typedef struct {
|
|
Packit |
5e46da |
BD_FILE_H *fp;
|
|
Packit |
5e46da |
BD_AACS *aacs;
|
|
Packit |
5e46da |
BD_BDPLUS_ST *bdplus;
|
|
Packit |
5e46da |
} DEC_STREAM;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int64_t _stream_read(BD_FILE_H *fp, uint8_t *buf, int64_t size)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
DEC_STREAM *st = (DEC_STREAM *)fp->internal;
|
|
Packit |
5e46da |
int64_t result;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (size != 6144) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_CRIT, "read size != unit size\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
result = st->fp->read(st->fp, buf, size);
|
|
Packit |
5e46da |
if (result <= 0) {
|
|
Packit |
5e46da |
return result;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (st->aacs) {
|
|
Packit |
5e46da |
if (libaacs_decrypt_unit(st->aacs, buf)) {
|
|
Packit |
5e46da |
/* failure is detected from TP header */
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (st->bdplus) {
|
|
Packit |
5e46da |
if (libbdplus_fixup(st->bdplus, buf, (int)size) < 0) {
|
|
Packit |
5e46da |
/* there's no way to verify if the stream was decoded correctly */
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return result;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int64_t _stream_seek(BD_FILE_H *fp, int64_t offset, int32_t origin)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
DEC_STREAM *st = (DEC_STREAM *)fp->internal;
|
|
Packit |
5e46da |
int64_t result = st->fp->seek(st->fp, offset, origin);
|
|
Packit |
5e46da |
if (result >= 0 && st->bdplus) {
|
|
Packit |
5e46da |
libbdplus_seek(st->bdplus, st->fp->tell(st->fp));
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return result;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int64_t _stream_tell(BD_FILE_H *fp)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
DEC_STREAM *st = (DEC_STREAM *)fp->internal;
|
|
Packit |
5e46da |
return st->fp->tell(st->fp);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void _stream_close(BD_FILE_H *fp)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
DEC_STREAM *st = (DEC_STREAM *)fp->internal;
|
|
Packit |
5e46da |
if (st->bdplus) {
|
|
Packit |
5e46da |
libbdplus_m2ts_close(&st->bdplus);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
st->fp->close(st->fp);
|
|
Packit |
5e46da |
X_FREE(fp->internal);
|
|
Packit |
5e46da |
X_FREE(fp);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_FILE_H *dec_open_stream(BD_DEC *dec, BD_FILE_H *fp, uint32_t clip_id)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
DEC_STREAM *st;
|
|
Packit |
5e46da |
BD_FILE_H *p = calloc(1, sizeof(BD_FILE_H));
|
|
Packit |
5e46da |
if (!p) {
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
st = calloc(1, sizeof(DEC_STREAM));
|
|
Packit |
5e46da |
if (!st) {
|
|
Packit |
5e46da |
X_FREE(p);
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
st->fp = fp;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (dec->bdplus) {
|
|
Packit |
5e46da |
st->bdplus = libbdplus_m2ts(dec->bdplus, clip_id, 0);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (dec->aacs) {
|
|
Packit |
5e46da |
st->aacs = dec->aacs;
|
|
Packit |
5e46da |
if (!dec->use_menus) {
|
|
Packit |
5e46da |
/* There won't be title events --> need to manually reset AACS CPS */
|
|
Packit |
5e46da |
libaacs_select_title(dec->aacs, 0xffff);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
p->internal = st;
|
|
Packit |
5e46da |
p->read = _stream_read;
|
|
Packit |
5e46da |
p->seek = _stream_seek;
|
|
Packit |
5e46da |
p->tell = _stream_tell;
|
|
Packit |
5e46da |
p->close = _stream_close;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return p;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _bdrom_have_file(void *p, const char *dir, const char *file)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
struct dec_dev *dev = (struct dec_dev *)p;
|
|
Packit |
5e46da |
BD_FILE_H *fp;
|
|
Packit |
5e46da |
char *path;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
path = str_printf("%s" DIR_SEP "%s", dir, file);
|
|
Packit |
5e46da |
if (!path) {
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
fp = dev->pf_file_open_bdrom(dev->file_open_bdrom_handle, path);
|
|
Packit |
5e46da |
X_FREE(path);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (fp) {
|
|
Packit |
5e46da |
file_close(fp);
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _libaacs_init(BD_DEC *dec, struct dec_dev *dev,
|
|
Packit |
5e46da |
BD_ENC_INFO *i, const char *keyfile_path)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int result;
|
|
Packit |
5e46da |
const uint8_t *disc_id;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (!dec->aacs) {
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
result = libaacs_open(dec->aacs, dev->device, dev->file_open_vfs_handle, (void*)dev->pf_file_open_vfs, keyfile_path);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
i->aacs_error_code = result;
|
|
Packit |
5e46da |
i->aacs_handled = !result;
|
|
Packit |
5e46da |
i->aacs_mkbv = libaacs_get_mkbv(dec->aacs);
|
|
Packit |
5e46da |
disc_id = libaacs_get_aacs_data(dec->aacs, BD_AACS_DISC_ID);
|
|
Packit |
5e46da |
if (disc_id) {
|
|
Packit |
5e46da |
memcpy(i->disc_id, disc_id, 20);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (result) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "aacs_open() failed: %d!\n", result);
|
|
Packit |
5e46da |
libaacs_unload(&dec->aacs);
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_DEBUG(DBG_BLURAY, "Opened libaacs\n");
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _libbdplus_init(BD_DEC *dec, struct dec_dev *dev,
|
|
Packit |
5e46da |
BD_ENC_INFO *i,
|
|
Packit |
5e46da |
void *regs, void *psr_read, void *psr_write)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (!dec->bdplus) {
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
const uint8_t *vid = libaacs_get_aacs_data(dec->aacs, BD_AACS_MEDIA_VID);
|
|
Packit |
5e46da |
const uint8_t *mk = libaacs_get_aacs_data(dec->aacs, BD_AACS_MEDIA_KEY);
|
|
Packit |
5e46da |
if (!vid) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "BD+ initialization failed (no AACS ?)\n");
|
|
Packit |
5e46da |
libbdplus_unload(&dec->bdplus);
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (libbdplus_init(dec->bdplus, dev->root, dev->device, dev->file_open_bdrom_handle, (void*)dev->pf_file_open_bdrom, vid, mk)) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "bdplus_init() failed\n");
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
i->bdplus_handled = 0;
|
|
Packit |
5e46da |
libbdplus_unload(&dec->bdplus);
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_DEBUG(DBG_BLURAY, "libbdplus initialized\n");
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* map player memory regions */
|
|
Packit |
5e46da |
libbdplus_mmap(dec->bdplus, 0, regs);
|
|
Packit |
5e46da |
libbdplus_mmap(dec->bdplus, 1, (void*)((uint8_t *)regs + sizeof(uint32_t) * 128));
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* connect registers */
|
|
Packit |
5e46da |
libbdplus_psr(dec->bdplus, regs, psr_read, psr_write);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
i->bdplus_gen = libbdplus_get_gen(dec->bdplus);
|
|
Packit |
5e46da |
i->bdplus_date = libbdplus_get_date(dec->bdplus);
|
|
Packit |
5e46da |
i->bdplus_handled = 1;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (i->bdplus_date == 0) {
|
|
Packit |
5e46da |
// libmmbd -> no menu support
|
|
Packit |
5e46da |
BD_DEBUG(DBG_BLURAY | DBG_CRIT, "WARNING: using libmmbd for BD+. On-disc menus will not work.\n");
|
|
Packit |
5e46da |
//i->no_menu_support = 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _dec_detect(struct dec_dev *dev, BD_ENC_INFO *i)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
/* Check for AACS */
|
|
Packit |
5e46da |
i->aacs_detected = libaacs_required((void*)dev, _bdrom_have_file);
|
|
Packit |
5e46da |
if (!i->aacs_detected) {
|
|
Packit |
5e46da |
/* No AACS (=> no BD+) */
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* check for BD+ */
|
|
Packit |
5e46da |
i->bdplus_detected = libbdplus_required((void*)dev, _bdrom_have_file);
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void _dec_load(BD_DEC *dec, BD_ENC_INFO *i)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int force_mmbd_aacs = 0;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (i->bdplus_detected) {
|
|
Packit |
5e46da |
/* load BD+ library and check BD+ library type. libmmbd doesn't work with libaacs */
|
|
Packit |
5e46da |
dec->bdplus = libbdplus_load();
|
|
Packit |
5e46da |
force_mmbd_aacs = dec->bdplus && libbdplus_is_mmbd(dec->bdplus);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* load AACS library */
|
|
Packit |
5e46da |
dec->aacs = libaacs_load(force_mmbd_aacs);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
i->libaacs_detected = !!dec->aacs;
|
|
Packit |
5e46da |
i->libbdplus_detected = !!dec->bdplus;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_DEC *dec_init(struct dec_dev *dev, BD_ENC_INFO *enc_info,
|
|
Packit |
5e46da |
const char *keyfile_path,
|
|
Packit |
5e46da |
void *regs, void *psr_read, void *psr_write)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_DEC *dec = NULL;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
memset(enc_info, 0, sizeof(*enc_info));
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* detect AACS/BD+ */
|
|
Packit |
5e46da |
if (!_dec_detect(dev, enc_info)) {
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
dec = calloc(1, sizeof(BD_DEC));
|
|
Packit |
5e46da |
if (!dec) {
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* load compatible libraries */
|
|
Packit |
5e46da |
_dec_load(dec, enc_info);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* init decoding libraries */
|
|
Packit |
5e46da |
/* BD+ won't help unless AACS works ... */
|
|
Packit |
5e46da |
if (_libaacs_init(dec, dev, enc_info, keyfile_path)) {
|
|
Packit |
5e46da |
_libbdplus_init(dec, dev, enc_info, regs, psr_read, psr_write);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (!enc_info->aacs_handled) {
|
|
Packit |
5e46da |
/* AACS failed, clean up */
|
|
Packit |
5e46da |
dec_close(&dec;;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* BD+ failure may be non-fatal (not all titles in disc use BD+).
|
|
Packit |
5e46da |
* Keep working AACS decoder even if BD+ init failed
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return dec;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
void dec_close(BD_DEC **pp)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (pp && *pp) {
|
|
Packit |
5e46da |
BD_DEC *p = *pp;
|
|
Packit |
5e46da |
libaacs_unload(&p->aacs);
|
|
Packit |
5e46da |
libbdplus_unload(&p->bdplus);
|
|
Packit |
5e46da |
X_FREE(*pp);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
const uint8_t *dec_data(BD_DEC *dec, int type)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
const uint8_t *ret = NULL;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (type >= 0x1000) {
|
|
Packit |
5e46da |
if (dec->bdplus) {
|
|
Packit |
5e46da |
ret = libbdplus_get_data(dec->bdplus, type);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
if (dec->aacs) {
|
|
Packit |
5e46da |
ret = libaacs_get_aacs_data(dec->aacs, type);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return ret;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
const uint8_t *dec_disc_id(BD_DEC *dec)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
return dec_data(dec, BD_AACS_DISC_ID);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
void dec_start(BD_DEC *dec, uint32_t num_titles)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (num_titles == 0) {
|
|
Packit |
5e46da |
dec->use_menus = 1;
|
|
Packit |
5e46da |
if (dec->bdplus) {
|
|
Packit |
5e46da |
libbdplus_start(dec->bdplus);
|
|
Packit |
5e46da |
libbdplus_event(dec->bdplus, 0x110, 0xffff, 0);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
if (dec->bdplus) {
|
|
Packit |
5e46da |
libbdplus_event(dec->bdplus, 0xffffffff, num_titles, 0);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
void dec_title(BD_DEC *dec, uint32_t title)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (dec->aacs) {
|
|
Packit |
5e46da |
libaacs_select_title(dec->aacs, title);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (dec->bdplus) {
|
|
Packit |
5e46da |
libbdplus_event(dec->bdplus, 0x110, title, 0);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
void dec_application(BD_DEC *dec, uint32_t data)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (dec->bdplus) {
|
|
Packit |
5e46da |
libbdplus_event(dec->bdplus, 0x210, data, 0);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|