|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* This file is part of libbluray
|
|
Packit |
5e46da |
* Copyright (C) 2014-2017 Petri Hintukainen <phintuka@users.sourceforge.net>
|
|
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 "disc.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "dec.h"
|
|
Packit |
5e46da |
#include "properties.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "util/logging.h"
|
|
Packit |
5e46da |
#include "util/macro.h"
|
|
Packit |
5e46da |
#include "util/mutex.h"
|
|
Packit |
5e46da |
#include "util/strutl.h"
|
|
Packit |
5e46da |
#include "file/file.h"
|
|
Packit |
5e46da |
#include "file/mount.h"
|
|
Packit |
5e46da |
#include "file/dirs.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include <ctype.h>
|
|
Packit |
5e46da |
#include <stdio.h>
|
|
Packit |
5e46da |
#include <string.h>
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "udf_fs.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
struct bd_disc {
|
|
Packit |
5e46da |
BD_MUTEX ovl_mutex; /* protect access to overlay root */
|
|
Packit |
5e46da |
BD_MUTEX properties_mutex; /* protect access to properties file */
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
char *disc_root; /* disc filesystem root (if disc is mounted) */
|
|
Packit |
5e46da |
char *overlay_root; /* overlay filesystem root (if set) */
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_DEC *dec;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
void *fs_handle;
|
|
Packit |
5e46da |
BD_FILE_H * (*pf_file_open_bdrom)(void *, const char *);
|
|
Packit |
5e46da |
BD_DIR_H * (*pf_dir_open_bdrom)(void *, const char *);
|
|
Packit |
5e46da |
void (*pf_fs_close)(void *);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
const char *udf_volid;
|
|
Packit |
5e46da |
char *properties_file; /* NULL if not yet used */
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
int8_t avchd; /* -1 - unknown. 0 - no. 1 - yes */
|
|
Packit |
5e46da |
};
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* BD-ROM filesystem
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static BD_FILE_H *_bdrom_open_path(void *p, const char *rel_path)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_DISC *disc = (BD_DISC *)p;
|
|
Packit |
5e46da |
BD_FILE_H *fp;
|
|
Packit |
5e46da |
char *abs_path;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
abs_path = str_printf("%s%s", disc->disc_root, rel_path);
|
|
Packit |
5e46da |
if (!abs_path) {
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
fp = file_open(abs_path, "rb");
|
|
Packit |
5e46da |
X_FREE(abs_path);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return fp;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static BD_DIR_H *_bdrom_open_dir(void *p, const char *dir)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_DISC *disc = (BD_DISC *)p;
|
|
Packit |
5e46da |
BD_DIR_H *dp;
|
|
Packit |
5e46da |
char *path;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
path = str_printf("%s%s", disc->disc_root, dir);
|
|
Packit |
5e46da |
if (!path) {
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
dp = dir_open(path);
|
|
Packit |
5e46da |
X_FREE(path);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return dp;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* AVCHD 8.3 filenames
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static char *_avchd_file_name(const char *rel_path)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
static const char map[][2][6] = {
|
|
Packit |
5e46da |
{ ".mpls", ".MPL" },
|
|
Packit |
5e46da |
{ ".clpi", ".CPI" },
|
|
Packit |
5e46da |
{ ".m2ts", ".MTS" },
|
|
Packit |
5e46da |
{ ".bdmv", ".BDM" },
|
|
Packit |
5e46da |
};
|
|
Packit |
5e46da |
char *avchd_path = str_dup(rel_path);
|
|
Packit |
5e46da |
char *name = avchd_path ? strrchr(avchd_path, DIR_SEP_CHAR) : NULL;
|
|
Packit |
5e46da |
char *dot = name ? strrchr(name, '.') : NULL;
|
|
Packit |
5e46da |
size_t i;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (dot) {
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* take up to 8 chars from file name */
|
|
Packit |
5e46da |
for (i = 0; *name && name < dot && i < 9; i++, name++) {
|
|
Packit |
5e46da |
*name = toupper(*name);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* convert extension */
|
|
Packit |
5e46da |
for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
|
|
Packit |
5e46da |
if (!strcmp(dot, map[i][0])) {
|
|
Packit |
5e46da |
strcpy(name, map[i][1]);
|
|
Packit |
5e46da |
return avchd_path;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* failed */
|
|
Packit |
5e46da |
X_FREE(avchd_path);
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* overlay filesystem
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static BD_FILE_H *_overlay_open_path(BD_DISC *p, const char *rel_path)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_FILE_H *fp = NULL;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
bd_mutex_lock(&p->ovl_mutex);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (p->overlay_root) {
|
|
Packit |
5e46da |
char *abs_path = str_printf("%s%s", p->overlay_root, rel_path);
|
|
Packit |
5e46da |
if (abs_path) {
|
|
Packit |
5e46da |
fp = file_open(abs_path, "rb");
|
|
Packit |
5e46da |
X_FREE(abs_path);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
bd_mutex_unlock(&p->ovl_mutex);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return fp;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static BD_DIR_H *_overlay_open_dir(BD_DISC *p, const char *dir)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_DIR_H *dp = NULL;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
bd_mutex_lock(&p->ovl_mutex);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (p->overlay_root) {
|
|
Packit |
5e46da |
char *abs_path = str_printf("%s%s", p->disc_root, dir);
|
|
Packit |
5e46da |
if (abs_path) {
|
|
Packit |
5e46da |
dp = dir_open_default()(abs_path);
|
|
Packit |
5e46da |
X_FREE(abs_path);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
bd_mutex_unlock(&p->ovl_mutex);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return dp;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* directory combining
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
typedef struct {
|
|
Packit |
5e46da |
unsigned int count;
|
|
Packit |
5e46da |
unsigned int pos;
|
|
Packit |
5e46da |
BD_DIRENT entry[1]; /* VLA */
|
|
Packit |
5e46da |
} COMB_DIR;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void _comb_dir_close(BD_DIR_H *dp)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
X_FREE(dp->internal);
|
|
Packit |
5e46da |
X_FREE(dp);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _comb_dir_read(BD_DIR_H *dp, BD_DIRENT *entry)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
COMB_DIR *priv = (COMB_DIR *)dp->internal;
|
|
Packit |
5e46da |
if (priv->pos < priv->count) {
|
|
Packit |
5e46da |
strcpy(entry->d_name, priv->entry[priv->pos++].d_name);
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void _comb_dir_append(BD_DIR_H *dp, BD_DIRENT *entry)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
COMB_DIR *priv = (COMB_DIR *)dp->internal;
|
|
Packit |
5e46da |
unsigned int i;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (!priv) {
|
|
Packit |
5e46da |
return;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* no duplicates */
|
|
Packit |
5e46da |
for (i = 0; i < priv->count; i++) {
|
|
Packit |
5e46da |
if (!strcmp(priv->entry[i].d_name, entry->d_name)) {
|
|
Packit |
5e46da |
return;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* append */
|
|
Packit |
5e46da |
priv = realloc(dp->internal, sizeof(*priv) + priv->count * sizeof(BD_DIRENT));
|
|
Packit |
5e46da |
if (!priv) {
|
|
Packit |
5e46da |
return;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
strcpy(priv->entry[priv->count].d_name, entry->d_name);
|
|
Packit |
5e46da |
priv->count++;
|
|
Packit |
5e46da |
dp->internal = (void*)priv;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static BD_DIR_H *_combine_dirs(BD_DIR_H *ovl, BD_DIR_H *rom)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_DIR_H *dp = calloc(1, sizeof(BD_DIR_H));
|
|
Packit |
5e46da |
BD_DIRENT entry;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (dp) {
|
|
Packit |
5e46da |
dp->read = _comb_dir_read;
|
|
Packit |
5e46da |
dp->close = _comb_dir_close;
|
|
Packit |
5e46da |
dp->internal = calloc(1, sizeof(COMB_DIR));
|
|
Packit |
5e46da |
if (!dp->internal) {
|
|
Packit |
5e46da |
X_FREE(dp);
|
|
Packit |
5e46da |
goto out;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
while (!dir_read(ovl, &entry)) {
|
|
Packit |
5e46da |
_comb_dir_append(dp, &entry);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
while (!dir_read(rom, &entry)) {
|
|
Packit |
5e46da |
_comb_dir_append(dp, &entry);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
out:
|
|
Packit |
5e46da |
dir_close(ovl);
|
|
Packit |
5e46da |
dir_close(rom);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return dp;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* disc open / close
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static BD_DISC *_disc_init()
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_DISC *p = calloc(1, sizeof(BD_DISC));
|
|
Packit |
5e46da |
if (p) {
|
|
Packit |
5e46da |
bd_mutex_init(&p->ovl_mutex);
|
|
Packit |
5e46da |
bd_mutex_init(&p->properties_mutex);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* default file access functions */
|
|
Packit |
5e46da |
p->fs_handle = (void*)p;
|
|
Packit |
5e46da |
p->pf_file_open_bdrom = _bdrom_open_path;
|
|
Packit |
5e46da |
p->pf_dir_open_bdrom = _bdrom_open_dir;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
p->avchd = -1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return p;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void _set_paths(BD_DISC *p, const char *device_path)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (device_path) {
|
|
Packit |
5e46da |
char *disc_root = mount_get_mountpoint(device_path);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* make sure path ends to slash */
|
|
Packit |
5e46da |
if (!disc_root || (disc_root[0] && disc_root[strlen(disc_root) - 1] == DIR_SEP_CHAR)) {
|
|
Packit |
5e46da |
p->disc_root = disc_root;
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
p->disc_root = str_printf("%s%c", disc_root, DIR_SEP_CHAR);
|
|
Packit |
5e46da |
X_FREE(disc_root);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_DISC *disc_open(const char *device_path,
|
|
Packit |
5e46da |
fs_access *p_fs,
|
|
Packit |
5e46da |
struct 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_DISC *p = _disc_init();
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (!p) {
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (p_fs && p_fs->open_dir) {
|
|
Packit |
5e46da |
p->fs_handle = p_fs->fs_handle;
|
|
Packit |
5e46da |
p->pf_file_open_bdrom = p_fs->open_file;
|
|
Packit |
5e46da |
p->pf_dir_open_bdrom = p_fs->open_dir;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
_set_paths(p, device_path);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* check if disc root directory can be opened. If not, treat it as device/image file. */
|
|
Packit |
5e46da |
BD_DIR_H *dp_img = device_path ? dir_open(device_path) : NULL;
|
|
Packit |
5e46da |
if (!dp_img) {
|
|
Packit |
5e46da |
void *udf = udf_image_open(device_path, p_fs ? p_fs->fs_handle : NULL, p_fs ? p_fs->read_blocks : NULL);
|
|
Packit |
5e46da |
if (!udf) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_FILE | DBG_CRIT, "failed opening UDF image %s\n", device_path);
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
p->fs_handle = udf;
|
|
Packit |
5e46da |
p->pf_fs_close = udf_image_close;
|
|
Packit |
5e46da |
p->pf_file_open_bdrom = udf_file_open;
|
|
Packit |
5e46da |
p->pf_dir_open_bdrom = udf_dir_open;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
p->udf_volid = udf_volume_id(udf);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* root not accessible with stdio */
|
|
Packit |
5e46da |
X_FREE(p->disc_root);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
dir_close(dp_img);
|
|
Packit |
5e46da |
BD_DEBUG(DBG_FILE, "%s does not seem to be image file or device node\n", device_path);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
struct dec_dev dev = { p->fs_handle, p->pf_file_open_bdrom, p, (file_openFp)disc_open_path, p->disc_root, device_path };
|
|
Packit |
5e46da |
p->dec = dec_init(&dev, enc_info, keyfile_path, regs, psr_read, psr_write);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return p;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
void disc_close(BD_DISC **pp)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (pp && *pp) {
|
|
Packit |
5e46da |
BD_DISC *p = *pp;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
dec_close(&p->dec);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (p->pf_fs_close) {
|
|
Packit |
5e46da |
p->pf_fs_close(p->fs_handle);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
bd_mutex_destroy(&p->ovl_mutex);
|
|
Packit |
5e46da |
bd_mutex_destroy(&p->properties_mutex);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
X_FREE(p->disc_root);
|
|
Packit |
5e46da |
X_FREE(p->properties_file);
|
|
Packit |
5e46da |
X_FREE(*pp);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
const char *disc_root(BD_DISC *p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
return p->disc_root;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
const char *disc_volume_id(BD_DISC *p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
return p ? p->udf_volid : NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_DIR_H *disc_open_bdrom_dir(BD_DISC *p, const char *rel_path)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
return p->pf_dir_open_bdrom(p->fs_handle, rel_path);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* VFS
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_FILE_H *disc_open_path(BD_DISC *p, const char *rel_path)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_FILE_H *fp;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (p->avchd > 0) {
|
|
Packit |
5e46da |
char *avchd_path = _avchd_file_name(rel_path);
|
|
Packit |
5e46da |
if (avchd_path) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_FILE, "AVCHD: %s -> %s\n", rel_path, avchd_path);
|
|
Packit |
5e46da |
fp = p->pf_file_open_bdrom(p->fs_handle, avchd_path);
|
|
Packit |
5e46da |
X_FREE(avchd_path);
|
|
Packit |
5e46da |
if (fp) {
|
|
Packit |
5e46da |
return fp;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* search file from overlay */
|
|
Packit |
5e46da |
fp = _overlay_open_path(p, rel_path);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* if not found, try BD-ROM */
|
|
Packit |
5e46da |
if (!fp) {
|
|
Packit |
5e46da |
fp = p->pf_file_open_bdrom(p->fs_handle, rel_path);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (!fp) {
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* AVCHD short filenames detection */
|
|
Packit |
5e46da |
if (p->avchd < 0 && !strcmp(rel_path, "BDMV" DIR_SEP "index.bdmv")) {
|
|
Packit |
5e46da |
fp = p->pf_file_open_bdrom(p->fs_handle, "BDMV" DIR_SEP "INDEX.BDM");
|
|
Packit |
5e46da |
if (fp) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_FILE | DBG_CRIT, "detected AVCHD 8.3 filenames\n");
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
p->avchd = !!fp;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (!fp) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_FILE | DBG_CRIT, "error opening file %s\n", rel_path);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return fp;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_FILE_H *disc_open_file(BD_DISC *p, const char *dir, const char *file)
|
|
Packit |
5e46da |
{
|
|
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 NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
fp = disc_open_path(p, path);
|
|
Packit |
5e46da |
X_FREE(path);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return fp;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_DIR_H *disc_open_dir(BD_DISC *p, const char *dir)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_DIR_H *dp_rom;
|
|
Packit |
5e46da |
BD_DIR_H *dp_ovl;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
dp_rom = p->pf_dir_open_bdrom(p->fs_handle, dir);
|
|
Packit |
5e46da |
dp_ovl = _overlay_open_dir(p, dir);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (!dp_ovl) {
|
|
Packit |
5e46da |
if (!dp_rom) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_FILE, "error opening dir %s\n", dir);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return dp_rom;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (!dp_rom) {
|
|
Packit |
5e46da |
return dp_ovl;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return _combine_dirs(dp_ovl, dp_rom);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
size_t disc_read_file(BD_DISC *disc, const char *dir, const char *file,
|
|
Packit |
5e46da |
uint8_t **data)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_FILE_H *fp;
|
|
Packit |
5e46da |
int64_t size;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
*data = NULL;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (dir) {
|
|
Packit |
5e46da |
fp = disc_open_file(disc, dir, file);
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
fp = disc_open_path(disc, file);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (!fp) {
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
size = file_size(fp);
|
|
Packit |
5e46da |
if (size > 0 && size < BD_MAX_SSIZE) {
|
|
Packit |
5e46da |
*data = malloc((size_t)size);
|
|
Packit |
5e46da |
if (*data) {
|
|
Packit |
5e46da |
int64_t got = file_read(fp, *data, size);
|
|
Packit |
5e46da |
if (got != size) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_FILE | DBG_CRIT, "Error reading file %s from %s\n", file, dir);
|
|
Packit |
5e46da |
X_FREE(*data);
|
|
Packit |
5e46da |
size = 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
size = 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
else {
|
|
Packit |
5e46da |
size = 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
file_close(fp);
|
|
Packit |
5e46da |
return (size_t)size;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* filesystem update
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
void disc_update(BD_DISC *p, const char *overlay_root)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
bd_mutex_lock(&p->ovl_mutex);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
X_FREE(p->overlay_root);
|
|
Packit |
5e46da |
if (overlay_root) {
|
|
Packit |
5e46da |
p->overlay_root = str_dup(overlay_root);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
bd_mutex_unlock(&p->ovl_mutex);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
int disc_cache_bdrom_file(BD_DISC *p, const char *rel_path, const char *cache_path)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_FILE_H *fp_in;
|
|
Packit |
5e46da |
BD_FILE_H *fp_out;
|
|
Packit |
5e46da |
int64_t got;
|
|
Packit |
5e46da |
size_t size;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (!cache_path || !cache_path[0]) {
|
|
Packit |
5e46da |
return -1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* make sure cache directory exists */
|
|
Packit |
5e46da |
if (file_mkdirs(cache_path) < 0) {
|
|
Packit |
5e46da |
return -1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* plain directory ? */
|
|
Packit |
5e46da |
size = strlen(rel_path);
|
|
Packit |
5e46da |
if (rel_path[size - 1] == '/' || rel_path[size - 1] == '\\') {
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* input file from BD-ROM */
|
|
Packit |
5e46da |
fp_in = p->pf_file_open_bdrom(p->fs_handle, rel_path);
|
|
Packit |
5e46da |
if (!fp_in) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_FILE | DBG_CRIT, "error caching file %s (does not exist ?)\n", rel_path);
|
|
Packit |
5e46da |
return -1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* output file in local filesystem */
|
|
Packit |
5e46da |
fp_out = file_open(cache_path, "wb");
|
|
Packit |
5e46da |
if (!fp_out) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_FILE | DBG_CRIT, "error creating cache file %s\n", cache_path);
|
|
Packit |
5e46da |
file_close(fp_in);
|
|
Packit |
5e46da |
return -1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
do {
|
|
Packit |
5e46da |
uint8_t buf[16*2048];
|
|
Packit |
5e46da |
got = file_read(fp_in, buf, sizeof(buf));
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* we'll call write(fp, buf, 0) after EOF. It is used to check for errors. */
|
|
Packit |
5e46da |
if (got < 0 || fp_out->write(fp_out, buf, got) != got) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_FILE | DBG_CRIT, "error caching file %s\n", rel_path);
|
|
Packit |
5e46da |
file_close(fp_out);
|
|
Packit |
5e46da |
file_close(fp_in);
|
|
Packit |
5e46da |
(void)file_unlink(cache_path);
|
|
Packit |
5e46da |
return -1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
} while (got > 0);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_DEBUG(DBG_FILE, "cached %s to %s\n", rel_path, cache_path);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
file_close(fp_out);
|
|
Packit |
5e46da |
file_close(fp_in);
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* persistent properties storage
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static char *_properties_file(BD_DISC *p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
const uint8_t *disc_id = NULL;
|
|
Packit |
5e46da |
uint8_t pseudo_id[20];
|
|
Packit |
5e46da |
char id_type, id_str[41];
|
|
Packit |
5e46da |
char *cache_home;
|
|
Packit |
5e46da |
char *properties_file;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
cache_home = file_get_cache_home();
|
|
Packit |
5e46da |
if (!cache_home) {
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* get disc ID */
|
|
Packit |
5e46da |
if (p->dec) {
|
|
Packit |
5e46da |
id_type = 'A';
|
|
Packit |
5e46da |
disc_id = dec_disc_id(p->dec);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (!disc_id) {
|
|
Packit |
5e46da |
id_type = 'P';
|
|
Packit |
5e46da |
disc_pseudo_id(p, pseudo_id);
|
|
Packit |
5e46da |
disc_id = pseudo_id;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
properties_file = str_printf("%s" DIR_SEP "bluray" DIR_SEP "properties" DIR_SEP "%c%s",
|
|
Packit |
5e46da |
cache_home, id_type,
|
|
Packit |
5e46da |
str_print_hex(id_str, disc_id, 20));
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
X_FREE(cache_home);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return properties_file;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _ensure_properties_file(BD_DISC *p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
bd_mutex_lock(&p->properties_mutex);
|
|
Packit |
5e46da |
if (!p->properties_file) {
|
|
Packit |
5e46da |
p->properties_file = _properties_file(p);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
bd_mutex_unlock(&p->properties_mutex);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return p->properties_file ? 0 : -1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
int disc_property_put(BD_DISC *p, const char *property, const char *val)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int result;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (_ensure_properties_file(p) < 0) {
|
|
Packit |
5e46da |
return -1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
bd_mutex_lock(&p->properties_mutex);
|
|
Packit |
5e46da |
result = properties_put(p->properties_file, property, val);
|
|
Packit |
5e46da |
bd_mutex_unlock(&p->properties_mutex);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return result;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
char *disc_property_get(BD_DISC *p, const char *property)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
char *result;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (_ensure_properties_file(p) < 0) {
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
bd_mutex_lock(&p->properties_mutex);
|
|
Packit |
5e46da |
result = properties_get(p->properties_file, property);
|
|
Packit |
5e46da |
bd_mutex_unlock(&p->properties_mutex);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return result;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* streams
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_FILE_H *disc_open_stream(BD_DISC *disc, const char *file)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_FILE_H *fp = disc_open_file(disc, "BDMV" DIR_SEP "STREAM", file);
|
|
Packit |
5e46da |
if (!fp) {
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (disc->dec) {
|
|
Packit |
5e46da |
BD_FILE_H *st = dec_open_stream(disc->dec, fp, atoi(file));
|
|
Packit |
5e46da |
if (st) {
|
|
Packit |
5e46da |
return st;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return fp;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
const uint8_t *disc_get_data(BD_DISC *disc, int type)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (disc->dec) {
|
|
Packit |
5e46da |
return dec_data(disc->dec, type);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
void disc_event(BD_DISC *disc, uint32_t event, uint32_t param)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (disc && disc->dec) {
|
|
Packit |
5e46da |
switch (event) {
|
|
Packit |
5e46da |
case DISC_EVENT_START:
|
|
Packit |
5e46da |
dec_start(disc->dec, param);
|
|
Packit |
5e46da |
return;
|
|
Packit |
5e46da |
case DISC_EVENT_TITLE:
|
|
Packit |
5e46da |
dec_title(disc->dec, param);
|
|
Packit |
5e46da |
return;
|
|
Packit |
5e46da |
case DISC_EVENT_APPLICATION:
|
|
Packit |
5e46da |
dec_application(disc->dec, param);
|
|
Packit |
5e46da |
return;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* Pseudo disc ID
|
|
Packit |
5e46da |
* This is used when AACS disc ID is not available
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#define ROTL64(k, n) (((k) << (n)) | ((k) >> (64 - (n))))
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static uint64_t _fmix64(uint64_t k)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
k ^= k >> 33;
|
|
Packit |
5e46da |
k *= UINT64_C(0xff51afd7ed558ccd);
|
|
Packit |
5e46da |
k ^= k >> 33;
|
|
Packit |
5e46da |
k *= UINT64_C(0xc4ceb9fe1a85ec53);
|
|
Packit |
5e46da |
k ^= k >> 33;
|
|
Packit |
5e46da |
return k;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void _murmurhash3_128(const uint8_t *in, size_t len, void *out)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
// original MurmurHash3 was written by Austin Appleby, and is placed in the public domain.
|
|
Packit |
5e46da |
// https://code.google.com/p/smhasher/wiki/MurmurHash3
|
|
Packit |
5e46da |
const uint64_t c1 = UINT64_C(0x87c37b91114253d5);
|
|
Packit |
5e46da |
const uint64_t c2 = UINT64_C(0x4cf5ad432745937f);
|
|
Packit |
5e46da |
uint64_t h[2] = {0, 0};
|
|
Packit |
5e46da |
size_t i;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* use only N * 16 bytes, ignore tail */
|
|
Packit |
5e46da |
len &= ~15;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (i = 0; i < len; i += 16) {
|
|
Packit |
5e46da |
uint64_t k1, k2;
|
|
Packit |
5e46da |
memcpy(&k1, in + i, sizeof(uint64_t));
|
|
Packit |
5e46da |
memcpy(&k2, in + i + 8, sizeof(uint64_t));
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
k1 *= c1; k1 = ROTL64(k1, 31); k1 *= c2; h[0] ^= k1;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
h[0] = ROTL64(h[0], 27); h[0] += h[1]; h[0] = h[0] * 5 + 0x52dce729;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
k2 *= c2; k2 = ROTL64(k2, 33); k2 *= c1; h[1] ^= k2;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
h[1] = ROTL64(h[1], 31); h[1] += h[0]; h[1] = h[1] * 5 + 0x38495ab5;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
h[0] ^= len;
|
|
Packit |
5e46da |
h[1] ^= len;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
h[0] += h[1];
|
|
Packit |
5e46da |
h[1] += h[0];
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
h[0] = _fmix64(h[0]);
|
|
Packit |
5e46da |
h[1] = _fmix64(h[1]);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
h[0] += h[1];
|
|
Packit |
5e46da |
h[1] += h[0];
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
memcpy(out, h, 2*sizeof(uint64_t));
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _hash_file(BD_DISC *p, const char *dir, const char *file, void *hash)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
uint8_t *data = NULL;
|
|
Packit |
5e46da |
size_t sz;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
sz = disc_read_file(p, dir, file, &data);
|
|
Packit |
5e46da |
if (sz > 16) {
|
|
Packit |
5e46da |
_murmurhash3_128(data, sz, hash);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
X_FREE(data);
|
|
Packit |
5e46da |
return sz > 16;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_PRIVATE void disc_pseudo_id(BD_DISC *p, uint8_t *id/*[20]*/)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
uint8_t h[2][20];
|
|
Packit |
5e46da |
int i;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
memset(h, 0, sizeof(h));
|
|
Packit |
5e46da |
_hash_file(p, "BDMV", "MovieObject.bdmv", h[0]);
|
|
Packit |
5e46da |
_hash_file(p, "BDMV", "index.bdmv", h[1]);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (i = 0; i < 20; i++) {
|
|
Packit |
5e46da |
id[i] = h[0][i] ^ h[1][i];
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|