You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
256 lines
7.2 KiB
C++
256 lines
7.2 KiB
C++
/*
|
|
* MPD SACD Decoder plugin
|
|
* Copyright (c) 2011-2023 Maxim V.Anisiutkin <maxim.anisiutkin@gmail.com>
|
|
*
|
|
* This program 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 program 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 FFmpeg; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include "sacd_metabase.h"
|
|
#include "lib/crypto/Base64.hxx"
|
|
#include "lib/crypto/MD5.hxx"
|
|
#include "util/ASCII.hxx"
|
|
#include "tag/Handler.hxx"
|
|
#include "tag/Names.hxx"
|
|
#include <vector>
|
|
|
|
static auto utf2xml = [](auto src) {
|
|
auto dst{ std::string() };
|
|
for (auto i = 0; src[i] != 0; i++) {
|
|
if (src[i] == '\r') {
|
|
dst += " ";
|
|
}
|
|
else if (src[i] == '\n') {
|
|
dst += " ";
|
|
}
|
|
else {
|
|
dst += string(&src[i], 1);
|
|
}
|
|
}
|
|
return dst;
|
|
};
|
|
|
|
static auto xml2utf = [](auto src) {
|
|
auto dst{ std::string() };
|
|
for (auto i = 0; src[i] != 0; i++) {
|
|
if (strncmp(&src[i], " ", 5) == 0) {
|
|
dst += "\r";
|
|
i += 4;
|
|
}
|
|
else if (strncmp(&src[i], " ", 5) == 0) {
|
|
dst += "\n";
|
|
i += 4;
|
|
}
|
|
else {
|
|
dst += std::string(&src[i], 1);
|
|
}
|
|
}
|
|
return dst;
|
|
};
|
|
|
|
static std::string get_md5(sacd_disc_t* p_disc) {
|
|
auto md5_string{ std::string()};
|
|
std::vector<std::byte> md5_source(MASTER_TOC_LEN * SACD_LSN_SIZE);
|
|
if (p_disc->read_blocks_raw(START_OF_MASTER_TOC, MASTER_TOC_LEN, (uint8_t*)md5_source.data())) {
|
|
GlobalInitMD5();
|
|
for (auto md5_hash_value : MD5(md5_source)) {
|
|
char hex_byte[3];
|
|
sprintf(hex_byte, "%02X", (uint8_t)md5_hash_value);
|
|
md5_string += hex_byte;
|
|
}
|
|
}
|
|
return md5_string;
|
|
}
|
|
|
|
sacd_metabase_t::sacd_metabase_t(sacd_disc_t* sacd_disc, const char* tags_path, const char* tags_file) {
|
|
initialized = false;
|
|
store_id = get_md5(sacd_disc);
|
|
if (store_id.empty()) {
|
|
return;
|
|
}
|
|
if (tags_path) {
|
|
store_path = tags_path;
|
|
store_file = store_path;
|
|
store_file += "/";
|
|
store_file += store_id;
|
|
store_file += ".xml";
|
|
if (access(store_file.c_str(), F_OK) == 0) {
|
|
if (tags_file && access(tags_file, F_OK) == -1) {
|
|
auto s = fopen(store_file.c_str(), "rb");
|
|
auto d = fopen(tags_file, "wb");
|
|
if (s) {
|
|
if (d) {
|
|
char buf[64];
|
|
size_t size;
|
|
while ((size = fread(buf, 1, sizeof(buf), s)) > 0) {
|
|
fwrite(buf, 1, size, d);
|
|
}
|
|
fclose(d);
|
|
}
|
|
fclose(s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
xmlfile = tags_file ? tags_file : store_file;
|
|
initialized = init_xmldoc();
|
|
}
|
|
|
|
sacd_metabase_t::~sacd_metabase_t() {
|
|
if (xmldoc) {
|
|
ixmlDocument_free(xmldoc);
|
|
}
|
|
}
|
|
|
|
bool sacd_metabase_t::get_track_info(unsigned track_number, TagHandler& handler) {
|
|
if (!initialized) {
|
|
return false;
|
|
}
|
|
auto node_track {get_node(MB_TAG_TRACK, std::to_string(track_number).c_str())};
|
|
if (!node_track) {
|
|
return false;
|
|
}
|
|
auto list_tags {ixmlNode_getChildNodes(node_track)};
|
|
if (!list_tags) {
|
|
return false;
|
|
}
|
|
for (auto tag_index = 0u; tag_index < ixmlNodeList_length(list_tags); tag_index++) {
|
|
auto node_tag {ixmlNodeList_item(list_tags, tag_index)};
|
|
if (node_tag) {
|
|
std::string node_name {ixmlNode_getNodeName(node_tag)};
|
|
if (node_name == MB_TAG_META) {
|
|
auto attr_tag {ixmlNode_getAttributes(node_tag)};
|
|
if (attr_tag) {
|
|
std::string tag_name;
|
|
std::string tag_value;
|
|
auto att_name {ixmlNamedNodeMap_getNamedItem(attr_tag, MB_ATT_NAME)};
|
|
if (att_name) {
|
|
tag_name = ixmlNode_getNodeValue(att_name);
|
|
}
|
|
auto att_value {ixmlNamedNodeMap_getNamedItem(attr_tag, MB_ATT_VALUE)};
|
|
if (att_value) {
|
|
tag_value = xml2utf(ixmlNode_getNodeValue(att_value));
|
|
}
|
|
if (tag_name.length() > 0) {
|
|
TagType tag_type = TAG_NUM_OF_ITEM_TYPES;
|
|
for (auto i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) {
|
|
if (StringEqualsCaseASCII(tag_item_names[i], tag_name.c_str())) {
|
|
tag_type = static_cast<TagType>(i);
|
|
break;
|
|
}
|
|
}
|
|
if (tag_type != TAG_NUM_OF_ITEM_TYPES) {
|
|
handler.OnTag(tag_type, {tag_value.c_str(), tag_value.size()});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool sacd_metabase_t::get_albumart(TagHandler& handler) {
|
|
if (!initialized) {
|
|
return false;
|
|
}
|
|
IXML_Node* node_albumart = nullptr;
|
|
for (auto& att_id : {"3", "4", "6", "2", "8"}) {
|
|
node_albumart = get_node(MB_TAG_ALBUMART , att_id);
|
|
if (node_albumart) {
|
|
break;
|
|
}
|
|
}
|
|
if (!node_albumart) {
|
|
return false;
|
|
}
|
|
auto node_cdata {ixmlNode_getFirstChild(node_albumart)};
|
|
if (!node_cdata) {
|
|
return false;
|
|
}
|
|
auto cdata_value {ixmlNode_getNodeValue(node_cdata)};
|
|
if (!cdata_value) {
|
|
return false;
|
|
}
|
|
auto debase64_size = CalculateBase64OutputSize(strlen(cdata_value));
|
|
std::vector<std::byte> debase64_data(debase64_size);
|
|
debase64_size = DecodeBase64(debase64_data, cdata_value);
|
|
handler.OnPicture(nullptr, debase64_data);
|
|
return true;
|
|
}
|
|
|
|
bool sacd_metabase_t::init_xmldoc() {
|
|
xmldoc = ixmlLoadDocument(xmlfile.c_str());
|
|
return xmldoc != nullptr;
|
|
}
|
|
|
|
IXML_Node* sacd_metabase_t::get_node(const char* tag_name, const char* att_id) {
|
|
IXML_NodeList* list_item = nullptr;
|
|
IXML_Node* node_item_id = nullptr;
|
|
auto node_root {ixmlNodeList_item(ixmlDocument_getElementsByTagName(xmldoc, MB_TAG_ROOT), 0)};
|
|
if (node_root) {
|
|
auto list_store {ixmlNode_getChildNodes(node_root)};
|
|
if (list_store) {
|
|
for (auto i = 0u; i < ixmlNodeList_length(list_store); i++) {
|
|
auto node_store {ixmlNodeList_item(list_store, i)};
|
|
if (node_store) {
|
|
auto attr_store {ixmlNode_getAttributes(node_store)};
|
|
if (attr_store) {
|
|
IXML_Node* node_attr;
|
|
std::string attr_id;
|
|
std::string attr_type;
|
|
node_attr = ixmlNamedNodeMap_getNamedItem(attr_store, MB_ATT_ID);
|
|
if (node_attr) {
|
|
attr_id = ixmlNode_getNodeValue(node_attr);
|
|
}
|
|
node_attr = ixmlNamedNodeMap_getNamedItem(attr_store, MB_ATT_TYPE);
|
|
if (node_attr) {
|
|
attr_type = ixmlNode_getNodeValue(node_attr);
|
|
}
|
|
if (attr_id == store_id) {
|
|
list_item = ixmlNode_getChildNodes(node_store);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (list_item) {
|
|
for (auto i = 0u; i < ixmlNodeList_length(list_item); i++) {
|
|
auto node_item {ixmlNodeList_item(list_item, i)};
|
|
if (node_item) {
|
|
std::string node_name {ixmlNode_getNodeName(node_item)};
|
|
if (node_name == tag_name) {
|
|
auto attr_item {ixmlNode_getAttributes(node_item)};
|
|
if (attr_item) {
|
|
auto node_attr {ixmlNamedNodeMap_getNamedItem(attr_item, MB_ATT_ID)};
|
|
if (node_attr) {
|
|
std::string attr_value = ixmlNode_getNodeValue(node_attr);
|
|
if (attr_value == att_id) {
|
|
node_item_id = node_item;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return node_item_id;
|
|
}
|