/*
* This file is part of libbluray
* Copyright (C) 2010 fraxinas
*
* 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 "meta_parse.h"
#include "meta_data.h"
#include "disc/disc.h"
#include "file/file.h"
#include "util/bits.h"
#include "util/logging.h"
#include "util/macro.h"
#include "util/strutl.h"
#include
#include
#ifdef HAVE_STRINGS_H
#include
#endif
#ifdef HAVE_LIBXML2
#include
#include
#include
#endif
#define DEFAULT_LANGUAGE "eng"
#define BAD_CAST_CONST (const xmlChar *)
#define XML_FREE(p) (xmlFree(p), p = NULL)
#define MAX_META_FILE_SIZE 0xfffff
struct meta_root {
uint8_t dl_count;
META_DL * dl_entries;
};
#ifdef HAVE_LIBXML2
static void _parseManifestNode(xmlNode * a_node, META_DL *disclib)
{
xmlNode *cur_node = NULL;
xmlChar *tmp;
for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
if (cur_node->type == XML_ELEMENT_NODE) {
if (xmlStrEqual(cur_node->parent->name, BAD_CAST_CONST "title")) {
if (xmlStrEqual(cur_node->name, BAD_CAST_CONST "name")) {
disclib->di_name = (char*)xmlNodeGetContent(cur_node);
}
if (xmlStrEqual(cur_node->name, BAD_CAST_CONST "alternative")) {
disclib->di_alternative = (char*)xmlNodeGetContent(cur_node);
}
if (xmlStrEqual(cur_node->name, BAD_CAST_CONST "numSets")) {
disclib->di_num_sets = atoi((char*)(tmp = xmlNodeGetContent(cur_node)));
XML_FREE(tmp);
}
if (xmlStrEqual(cur_node->name, BAD_CAST_CONST "setNumber")) {
disclib->di_set_number = atoi((char*)(tmp = xmlNodeGetContent(cur_node)));
XML_FREE(tmp);
}
}
else if (xmlStrEqual(cur_node->parent->name, BAD_CAST_CONST "tableOfContents")) {
if (xmlStrEqual(cur_node->name, BAD_CAST_CONST "titleName") && (tmp = xmlGetProp(cur_node, BAD_CAST_CONST "titleNumber"))) {
META_TITLE *new_entries = realloc(disclib->toc_entries, ((disclib->toc_count + 1)*sizeof(META_TITLE)));
if (new_entries) {
int i = disclib->toc_count;
disclib->toc_count++;
disclib->toc_entries = new_entries;
disclib->toc_entries[i].title_number = atoi((const char*)tmp);
disclib->toc_entries[i].title_name = (char*)xmlNodeGetContent(cur_node);
}
XML_FREE(tmp);
}
}
else if (xmlStrEqual(cur_node->parent->name, BAD_CAST_CONST "description")) {
if (xmlStrEqual(cur_node->name, BAD_CAST_CONST "thumbnail") && (tmp = xmlGetProp(cur_node, BAD_CAST_CONST "href"))) {
META_THUMBNAIL *new_thumbnails = realloc(disclib->thumbnails, ((disclib->thumb_count + 1)*sizeof(META_THUMBNAIL)));
if (new_thumbnails) {
uint8_t i = disclib->thumb_count;
disclib->thumb_count++;
disclib->thumbnails = new_thumbnails;
disclib->thumbnails[i].path = (char *)tmp;
if ((tmp = xmlGetProp(cur_node, BAD_CAST_CONST "size"))) {
int x = 0, y = 0;
sscanf((const char*)tmp, "%ix%i", &x, &y);
disclib->thumbnails[i].xres = x;
disclib->thumbnails[i].yres = y;
XML_FREE(tmp);
}
else {
disclib->thumbnails[i].xres = disclib->thumbnails[i].yres = -1;
}
}
}
}
}
_parseManifestNode(cur_node->children, disclib);
}
}
static void _findMetaXMLfiles(META_ROOT *meta, BD_DISC *disc)
{
BD_DIR_H *dir;
BD_DIRENT ent;
dir = disc_open_dir(disc, "BDMV" DIR_SEP "META" DIR_SEP "DL");
if (dir == NULL) {
BD_DEBUG(DBG_DIR, "Failed to open meta dir BDMV/META/DL/\n");
return;
}
int res;
for (res = dir_read(dir, &ent); !res; res = dir_read(dir, &ent)) {
if (ent.d_name[0] == '.')
continue;
else if (strncasecmp(ent.d_name, "bdmt_", 5) == 0) {
META_DL *new_dl_entries = realloc(meta->dl_entries, ((meta->dl_count + 1)*sizeof(META_DL)));
if (new_dl_entries) {
uint8_t i = meta->dl_count;
meta->dl_count++;
meta->dl_entries = new_dl_entries;
memset(&meta->dl_entries[i], 0, sizeof(meta->dl_entries[i]));
meta->dl_entries[i].filename = str_dup(ent.d_name);
strncpy(meta->dl_entries[i].language_code, ent.d_name+5,3);
meta->dl_entries[i].language_code[3] = '\0';
str_tolower(meta->dl_entries[i].language_code);
}
}
}
dir_close(dir);
}
#endif
META_ROOT *meta_parse(BD_DISC *disc)
{
#ifdef HAVE_LIBXML2
META_ROOT *root = calloc(1, sizeof(META_ROOT));
unsigned i;
if (!root) {
BD_DEBUG(DBG_CRIT, "out of memory\n");
return NULL;
}
root->dl_count = 0;
_findMetaXMLfiles(root, disc);
for (i = 0; i < root->dl_count; i++) {
uint8_t *data = NULL;
size_t size;
size = disc_read_file(disc, "BDMV" DIR_SEP "META" DIR_SEP "DL",
root->dl_entries[i].filename,
&data);
if (!data || size == 0) {
BD_DEBUG(DBG_DIR, "Failed to read BDMV/META/DL/%s\n", root->dl_entries[i].filename);
} else {
xmlDocPtr doc;
doc = xmlReadMemory((char*)data, (int)size, NULL, NULL, 0);
if (doc == NULL) {
BD_DEBUG(DBG_DIR, "Failed to parse BDMV/META/DL/%s\n", root->dl_entries[i].filename);
} else {
xmlNode *root_element = NULL;
root_element = xmlDocGetRootElement(doc);
root->dl_entries[i].di_name = root->dl_entries[i].di_alternative = NULL;
root->dl_entries[i].di_num_sets = root->dl_entries[i].di_set_number = -1;
root->dl_entries[i].toc_count = root->dl_entries[i].thumb_count = 0;
root->dl_entries[i].toc_entries = NULL;
root->dl_entries[i].thumbnails = NULL;
_parseManifestNode(root_element, &root->dl_entries[i]);
xmlFreeDoc(doc);
}
X_FREE(data);
}
}
xmlCleanupParser();
return root;
#else
(void)disc;
BD_DEBUG(DBG_DIR, "configured without libxml2 - can't parse meta info\n");
return NULL;
#endif
}
const META_DL *meta_get(const META_ROOT *meta_root, const char *language_code)
{
#ifdef HAVE_LIBXML2
unsigned i;
if (meta_root == NULL || meta_root->dl_count == 0) {
BD_DEBUG(DBG_DIR, "meta_get not possible, no info available!\n");
return NULL;
}
if (language_code) {
for (i = 0; i < meta_root->dl_count; i++) {
if (strcmp(language_code, meta_root->dl_entries[i].language_code) == 0) {
return &meta_root->dl_entries[i];
}
}
BD_DEBUG(DBG_DIR, "requested disclib language '%s' not found\n", language_code);
}
for (i = 0; i < meta_root->dl_count; i++) {
if (strcmp(DEFAULT_LANGUAGE, meta_root->dl_entries[i].language_code) == 0) {
BD_DEBUG(DBG_DIR, "using default disclib language '"DEFAULT_LANGUAGE"'\n");
return &meta_root->dl_entries[i];
}
}
BD_DEBUG(DBG_DIR, "requested disclib language '%s' or default '"DEFAULT_LANGUAGE"' not found, using '%s' instead\n", language_code, meta_root->dl_entries[0].language_code);
return &meta_root->dl_entries[0];
#else
(void)meta_root;
(void)language_code;
return NULL;
#endif
}
void meta_free(META_ROOT **p)
{
(void)p;
#ifdef HAVE_LIBXML2
if (p && *p)
{
uint8_t i;
for (i = 0; i < (*p)->dl_count; i++) {
uint32_t t;
for (t = 0; t < (*p)->dl_entries[i].toc_count; t++) {
XML_FREE((*p)->dl_entries[i].toc_entries[t].title_name);
}
for (t = 0; t < (*p)->dl_entries[i].thumb_count; t++) {
XML_FREE((*p)->dl_entries[i].thumbnails[t].path);
}
X_FREE((*p)->dl_entries[i].toc_entries);
X_FREE((*p)->dl_entries[i].thumbnails);
X_FREE((*p)->dl_entries[i].filename);
XML_FREE((*p)->dl_entries[i].di_name);
XML_FREE((*p)->dl_entries[i].di_alternative);
}
X_FREE((*p)->dl_entries);
X_FREE(*p);
}
#endif
}