Blame src/libbluray/bdnav/meta_parse.c

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