Blame src/libbluray/disc/bdplus.c

Packit 5e46da
/*
Packit 5e46da
 * This file is part of libbluray
Packit 5e46da
 * Copyright (C) 2013-2015  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 "bdplus.h"
Packit 5e46da
Packit 5e46da
#include "file/dl.h"
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 <stdlib.h>
Packit 5e46da
#include <string.h>
Packit 5e46da
Packit 5e46da
Packit 5e46da
struct bd_bdplus {
Packit 5e46da
    void           *h_libbdplus; /* library handle from dlopen */
Packit 5e46da
Packit 5e46da
    void           *bdplus;      /* bdplus handle from bdplus_open() */
Packit 5e46da
Packit 5e46da
    /* functions */
Packit 5e46da
    fptr_int32     event;
Packit 5e46da
    fptr_p_void    m2ts;
Packit 5e46da
    fptr_int32     m2ts_close;
Packit 5e46da
    fptr_int32     seek;
Packit 5e46da
    fptr_int32     fixup;
Packit 5e46da
Packit 5e46da
    /* old API */
Packit 5e46da
    fptr_p_void    title;
Packit 5e46da
Packit 5e46da
    int impl_id;
Packit 5e46da
};
Packit 5e46da
Packit 5e46da
Packit 5e46da
static void _libbdplus_close(BD_BDPLUS *p)
Packit 5e46da
{
Packit 5e46da
    if (p->bdplus) {
Packit 5e46da
        DL_CALL(p->h_libbdplus, bdplus_free, p->bdplus);
Packit 5e46da
        p->bdplus = NULL;
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _unload(BD_BDPLUS *p)
Packit 5e46da
{
Packit 5e46da
    _libbdplus_close(p);
Packit 5e46da
Packit 5e46da
    if (p->h_libbdplus) {
Packit 5e46da
        dl_dlclose(p->h_libbdplus);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void libbdplus_unload(BD_BDPLUS **p)
Packit 5e46da
{
Packit 5e46da
    if (p && *p) {
Packit 5e46da
        _unload(*p);
Packit 5e46da
        X_FREE(*p);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int libbdplus_required(void *have_file_handle, int (*have_file)(void *, const char *, const char *))
Packit 5e46da
{
Packit 5e46da
    if (have_file(have_file_handle, "BDSVM", "00000.svm")) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY, "BDSVM" DIR_SEP "00000.svm found. Disc seems to be BD+ protected.\n");
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BLURAY, "BDSVM" DIR_SEP "00000.svm not found. No BD+ protection.\n");
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
#define IMPL_USER       0
Packit 5e46da
#define IMPL_LIBBDPLUS  1
Packit 5e46da
#define IMPL_LIBMMBD    2
Packit 5e46da
Packit 5e46da
static void *_libbdplus_open(int *impl_id)
Packit 5e46da
{
Packit 5e46da
    const char * const libbdplus[] = {
Packit 5e46da
      getenv("LIBBDPLUS_PATH"),
Packit 5e46da
      "libbdplus",
Packit 5e46da
      "libmmbd",
Packit 5e46da
    };
Packit 5e46da
    unsigned ii;
Packit 5e46da
Packit 5e46da
    for (ii = *impl_id; ii < sizeof(libbdplus) / sizeof(libbdplus[0]); ii++) {
Packit 5e46da
        if (libbdplus[ii]) {
Packit 5e46da
            void *handle = dl_dlopen(libbdplus[ii], "0");
Packit 5e46da
            if (handle) {
Packit 5e46da
                *impl_id = ii;
Packit 5e46da
                BD_DEBUG(DBG_BLURAY, "Using %s for BD+\n", libbdplus[ii]);
Packit 5e46da
                return handle;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BLURAY | DBG_CRIT, "No usable BD+ libraries found!\n");
Packit 5e46da
    return NULL;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int libbdplus_is_mmbd(BD_BDPLUS *p)
Packit 5e46da
{
Packit 5e46da
    return p && (p->impl_id == IMPL_LIBMMBD);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static BD_BDPLUS *_load(int impl_id)
Packit 5e46da
{
Packit 5e46da
    BD_BDPLUS *p = calloc(1, sizeof(BD_BDPLUS));
Packit 5e46da
    if (!p) {
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
    p->impl_id = impl_id;
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BDPLUS, "attempting to load libbdplus\n");
Packit 5e46da
Packit 5e46da
    p->h_libbdplus = _libbdplus_open(&p->impl_id);
Packit 5e46da
    if (!p->h_libbdplus) {
Packit 5e46da
        X_FREE(p);
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BLURAY, "Loading libbdplus (%p)\n", p->h_libbdplus);
Packit 5e46da
Packit 5e46da
    *(void **)(&p->event)      = dl_dlsym(p->h_libbdplus, "bdplus_event");
Packit 5e46da
    *(void **)(&p->m2ts)       = dl_dlsym(p->h_libbdplus, "bdplus_m2ts");
Packit 5e46da
    *(void **)(&p->seek)       = dl_dlsym(p->h_libbdplus, "bdplus_seek");
Packit 5e46da
    *(void **)(&p->fixup)      = dl_dlsym(p->h_libbdplus, "bdplus_fixup");
Packit 5e46da
    *(void **)(&p->m2ts_close) = dl_dlsym(p->h_libbdplus, "bdplus_m2ts_close");
Packit 5e46da
    if (!p->m2ts) {
Packit 5e46da
        /* Old API */
Packit 5e46da
        *(void **)(&p->title)  = dl_dlsym(p->h_libbdplus, "bdplus_set_title");
Packit 5e46da
        if (!p->title) {
Packit 5e46da
            *(void **)(&p->title)  = dl_dlsym(p->h_libbdplus, "bdplus_set_m2ts");
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!p->seek || !p->fixup || !((p->m2ts && p->m2ts_close) || p->title)) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "libbdplus dlsym failed! (%p)\n", p->h_libbdplus);
Packit 5e46da
        libbdplus_unload(&p);
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_BLURAY, "Loaded libbdplus (%p)\n", p->h_libbdplus);
Packit 5e46da
    return p;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
BD_BDPLUS *libbdplus_load()
Packit 5e46da
{
Packit 5e46da
    return _load(0);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int libbdplus_init(BD_BDPLUS *p, const char *root, const char *device,
Packit 5e46da
                   void *file_open_handle, void *file_open_fp,
Packit 5e46da
                   const uint8_t *vid, const uint8_t *mk)
Packit 5e46da
{
Packit 5e46da
    fptr_p_void    bdplus_init;
Packit 5e46da
    fptr_void      set_fopen;
Packit 5e46da
Packit 5e46da
    _libbdplus_close(p);
Packit 5e46da
Packit 5e46da
    /* force libmmbd BD+ if no AACS media key:
Packit 5e46da
     * - libbdplus requires media key
Packit 5e46da
     * - libmmbd does not export media key
Packit 5e46da
     *   (=> libbdplus won't work with libmmbd AACS)
Packit 5e46da
     */
Packit 5e46da
    if (mk == NULL && p->impl_id == IMPL_LIBBDPLUS) {
Packit 5e46da
        BD_BDPLUS *p2 = _load(IMPL_LIBMMBD);
Packit 5e46da
        if (p2) {
Packit 5e46da
            if (!libbdplus_init(p2, root, device, file_open_handle, file_open_fp, vid, mk)) {
Packit 5e46da
                /* succeed - swap implementations */
Packit 5e46da
                _unload(p);
Packit 5e46da
                *p = *p2;
Packit 5e46da
                X_FREE(p2);
Packit 5e46da
                return 0;
Packit 5e46da
            }
Packit 5e46da
            /* failed - continue with original bd+ implementation */
Packit 5e46da
            libbdplus_unload(&p2;;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* */
Packit 5e46da
Packit 5e46da
    *(void **)(&bdplus_init) = dl_dlsym(p->h_libbdplus, "bdplus_init");
Packit 5e46da
    *(void **)(&set_fopen)   = dl_dlsym(p->h_libbdplus, "bdplus_set_fopen");
Packit 5e46da
Packit 5e46da
    if (!bdplus_init) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "libbdplus dlsym(bdplus_init) failed! (%p)\n", p->h_libbdplus);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (set_fopen) {
Packit 5e46da
        /* New libbdplus. Use libbluray for file I/O */
Packit 5e46da
        p->bdplus = bdplus_init(NULL, NULL, vid);
Packit 5e46da
        set_fopen(p->bdplus, file_open_handle, file_open_fp);
Packit 5e46da
    } else if (root) {
Packit 5e46da
        /* Old libbdplus or libmmbd. Disc is mounted. */
Packit 5e46da
        p->bdplus = bdplus_init(root, NULL, vid);
Packit 5e46da
    } else if (device) {
Packit 5e46da
        /* Unmounted device */
Packit 5e46da
        if (p->impl_id == IMPL_LIBMMBD && !strncmp(device, "/dev/", 5)) {
Packit 5e46da
            char *tmp = str_printf("dev:%s", device);
Packit 5e46da
            if (tmp) {
Packit 5e46da
                p->bdplus = bdplus_init(tmp, NULL, vid);
Packit 5e46da
                X_FREE(tmp);
Packit 5e46da
            }
Packit 5e46da
        } else {
Packit 5e46da
            BD_DEBUG(DBG_BLURAY | DBG_CRIT, "Too old libbdplus detected. Disc must be mounted first.\n");
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!p->bdplus) {
Packit 5e46da
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "bdplus_init() failed! (%p)\n", p->h_libbdplus);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    DL_CALL(p->h_libbdplus, bdplus_set_mk, p->bdplus, mk);
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static uint32_t _bdplus_get(BD_BDPLUS *p, const char *func)
Packit 5e46da
{
Packit 5e46da
    if (p && p->bdplus) {
Packit 5e46da
        fptr_int32 fp;
Packit 5e46da
        *(void **)(&fp) = dl_dlsym(p->h_libbdplus, func);
Packit 5e46da
        if (fp) {
Packit 5e46da
            return fp(p->bdplus);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int libbdplus_get_gen(BD_BDPLUS *p)
Packit 5e46da
{
Packit 5e46da
    return _bdplus_get(p, "bdplus_get_code_gen");
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int libbdplus_get_date(BD_BDPLUS *p)
Packit 5e46da
{
Packit 5e46da
    return _bdplus_get(p, "bdplus_get_code_date");
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
const uint8_t *libbdplus_get_data(BD_BDPLUS *p, int type)
Packit 5e46da
{
Packit 5e46da
    switch (type) {
Packit 5e46da
        case BD_BDPLUS_TYPE:
Packit 5e46da
            if (libbdplus_is_mmbd(p)) {
Packit 5e46da
                return (const uint8_t *)"mmbd";
Packit 5e46da
            }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return NULL;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void libbdplus_event(BD_BDPLUS *p, uint32_t event, uint32_t param1, uint32_t param2)
Packit 5e46da
{
Packit 5e46da
    if (p && p->bdplus && p->event) {
Packit 5e46da
        p->event(p->bdplus, event, param1, param2);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void libbdplus_mmap(BD_BDPLUS *p, uint32_t region_id, void *mem)
Packit 5e46da
{
Packit 5e46da
    if (p && p->bdplus) {
Packit 5e46da
        DL_CALL(p->h_libbdplus, bdplus_mmap, p->bdplus, region_id, mem);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void libbdplus_psr(BD_BDPLUS *p, void *regs, void *read, void *write)
Packit 5e46da
{
Packit 5e46da
    if (p && p->bdplus) {
Packit 5e46da
        DL_CALL(p->h_libbdplus, bdplus_psr, p->bdplus, regs, read, write);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void libbdplus_start(BD_BDPLUS *p)
Packit 5e46da
{
Packit 5e46da
    if (p && p->bdplus) {
Packit 5e46da
        DL_CALL(p->h_libbdplus, bdplus_start, p->bdplus);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 *  stream layer
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
struct bd_bdplus_st {
Packit 5e46da
    BD_BDPLUS *lib;
Packit 5e46da
    void      *st;
Packit 5e46da
};
Packit 5e46da
Packit 5e46da
BD_BDPLUS_ST *libbdplus_m2ts(BD_BDPLUS *p, uint32_t clip_id, uint64_t pos)
Packit 5e46da
{
Packit 5e46da
    if (p && p->bdplus) {
Packit 5e46da
Packit 5e46da
        if (!p->m2ts) {
Packit 5e46da
            /* use old API */
Packit 5e46da
            BD_BDPLUS_ST *ret = calloc(1, sizeof(BD_BDPLUS_ST));
Packit 5e46da
            if (ret) {
Packit 5e46da
                ret->lib = p;
Packit 5e46da
                ret->st  = NULL;
Packit 5e46da
                p->title(p->bdplus, clip_id);
Packit 5e46da
                p->seek(p->bdplus, pos);
Packit 5e46da
            }
Packit 5e46da
            return ret;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        void *st = p->m2ts(p->bdplus, clip_id);
Packit 5e46da
Packit 5e46da
        if (!st) {
Packit 5e46da
            BD_DEBUG(DBG_BLURAY | DBG_CRIT, "BD+ failed for clip %05d.m2ts\n", clip_id);
Packit 5e46da
Packit 5e46da
        } else if (p->seek(st, pos) < 0) {
Packit 5e46da
            BD_DEBUG(DBG_BLURAY | DBG_CRIT, "BD+ seek failed for clip %05d.m2ts\n", clip_id);
Packit 5e46da
            p->m2ts_close(st);
Packit 5e46da
        } else {
Packit 5e46da
            BD_BDPLUS_ST *ret = calloc(1, sizeof(BD_BDPLUS_ST));
Packit 5e46da
            if (ret) {
Packit 5e46da
                ret->lib = p;
Packit 5e46da
                ret->st  = st;
Packit 5e46da
                BD_DEBUG(DBG_BLURAY | DBG_CRIT, "BD+ active for clip %05d.m2ts\n", clip_id);
Packit 5e46da
            }
Packit 5e46da
            return ret;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return NULL;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int libbdplus_m2ts_close(BD_BDPLUS_ST **p)
Packit 5e46da
{
Packit 5e46da
    int result = -1;
Packit 5e46da
    if (p && *p) {
Packit 5e46da
        if ((*p)->lib && (*p)->st) {
Packit 5e46da
            result = (*p)->lib->m2ts_close((*p)->st);
Packit 5e46da
        }
Packit 5e46da
        X_FREE(*p);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int libbdplus_seek(BD_BDPLUS_ST *p, uint64_t pos)
Packit 5e46da
{
Packit 5e46da
    if (p) {
Packit 5e46da
        if (p->st) {
Packit 5e46da
            return p->lib->seek(p->st, pos);
Packit 5e46da
        } else {
Packit 5e46da
            /* use old API */
Packit 5e46da
            return p->lib->seek(p->lib->bdplus, pos);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int libbdplus_fixup(BD_BDPLUS_ST *p, uint8_t *buf, int len)
Packit 5e46da
{
Packit 5e46da
    if (p && !p->lib->m2ts) {
Packit 5e46da
        /* use old API */
Packit 5e46da
        return p->lib->fixup(p->lib->bdplus, len, buf);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (p && p->st) {
Packit 5e46da
        int32_t numFixes;
Packit 5e46da
        numFixes = p->lib->fixup(p->st, len, buf);
Packit 5e46da
#if 1
Packit 5e46da
        if (numFixes) {
Packit 5e46da
            BD_DEBUG(DBG_BDPLUS, "BD+ did %d fixups\n", numFixes);
Packit 5e46da
        }
Packit 5e46da
#endif
Packit 5e46da
        return numFixes;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return -1;
Packit 5e46da
}