Create CD-Text library - first round. (Second round will remove from lib/driver)
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -16,9 +16,9 @@
|
||||
/depcomp
|
||||
/install-sh
|
||||
/libcdio++.pc
|
||||
/libcdio-*.sig
|
||||
/libcdio-*.tar.bz2
|
||||
/libcdio-*.tar.gz
|
||||
/libcdio-*.sig
|
||||
/libcdio.pc
|
||||
/libcdio_cdda.pc
|
||||
/libiso9660++.pc
|
||||
|
||||
@@ -643,11 +643,13 @@ AC_CONFIG_FILES([
|
||||
doc/Makefile \
|
||||
lib/Makefile \
|
||||
lib/cdio++/Makefile \
|
||||
lib/cdtext/Makefile \
|
||||
lib/driver/Makefile \
|
||||
lib/iso9660/Makefile \
|
||||
lib/udf/Makefile \
|
||||
libcdio.pc \
|
||||
libcdio++.pc \
|
||||
libcdtext.pc \
|
||||
libiso9660.pc \
|
||||
libiso9660++.pc \
|
||||
libudf.pc \
|
||||
|
||||
@@ -21,4 +21,4 @@ if ENABLE_CXX_BINDINGS
|
||||
cxxdirs = cdio++
|
||||
endif
|
||||
|
||||
SUBDIRS = driver iso9660 udf $(cxxdirs)
|
||||
SUBDIRS = driver cdtext iso9660 udf $(cxxdirs)
|
||||
|
||||
9
lib/cdtext/.gitignore
vendored
Normal file
9
lib/cdtext/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/*.lo
|
||||
/*.o
|
||||
/*~
|
||||
/.deps
|
||||
/.libs
|
||||
/Makefile
|
||||
/Makefile.in
|
||||
/libcdtext.la
|
||||
/libcdtext.la.ver
|
||||
139
lib/cdtext/Makefile.am
Normal file
139
lib/cdtext/Makefile.am
Normal file
@@ -0,0 +1,139 @@
|
||||
# Copyright (C) 2012
|
||||
# Rocky Bernstein <rocky@gnu.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
########################################################
|
||||
# Things to make the libcdtext library
|
||||
########################################################
|
||||
#
|
||||
# From libtool documentation amended with guidance from N. Boullis:
|
||||
#
|
||||
# 1. Start with version information of `0:0:0' for each libtool library.
|
||||
#
|
||||
# 2. It is probably not a good idea to update the version information
|
||||
# several times between public releases, but rather once per public
|
||||
# release. (This seems to be more an aesthetic consideration than
|
||||
# a hard technical one.)
|
||||
#
|
||||
# 3. If the library source code has changed at all since the last
|
||||
# update, then increment REVISION (`C:R:A' becomes `C:R+1:A').
|
||||
#
|
||||
# 4. If any interfaces have been added, removed, or changed since the
|
||||
# last update, increment CURRENT, and set REVISION to 0.
|
||||
#
|
||||
# 5. If any interfaces have been added since the last public release,
|
||||
# then increment AGE.
|
||||
#
|
||||
# 6. If any interfaces have been removed or changed since the last
|
||||
# public release, then set AGE to 0. A changed interface means an
|
||||
# incompatibility with previous versions.
|
||||
|
||||
libcdtext_la_CURRENT = 1
|
||||
libcdtext_la_REVISION = 0
|
||||
libcdtext_la_AGE = 0
|
||||
|
||||
EXTRA_DIST = cdtext.sym
|
||||
|
||||
noinst_HEADERS = cdtext_private.h
|
||||
|
||||
lib_LTLIBRARIES = libcdtext.la
|
||||
|
||||
libcdtext_la_SOURCES = \
|
||||
cdtext.c \
|
||||
iso9660_private.h
|
||||
|
||||
libcdtext_la_LIBADD = @LIBCDIO_LIBS@
|
||||
libcdtext_la_ldflags = -version-info $(libcdtext_la_CURRENT):$(libcdtext_la_REVISION):$(libcdtext_la_AGE) @LT_NO_UNDEFINED@
|
||||
libcdtext_la_dependencies = $(top_builddir)/lib/driver/libcdio.la
|
||||
|
||||
INCLUDES = $(LIBCDIO_CFLAGS)
|
||||
|
||||
########################################################
|
||||
# Things to version the symbols in the libraries
|
||||
########################################################
|
||||
|
||||
# An explanation of the versioning problem from Nicolas Boullis and
|
||||
# the versioned symbol solution he uses below...
|
||||
#
|
||||
# Currently, libvcdinfo uses the cdio_open function from libcdio.
|
||||
# Let's imagine a program foobar that uses both the vcdinfo_open
|
||||
# function from libvcdinfo and the cdio_open function from libcdio.
|
||||
|
||||
# Currently, libcdio has SONAME libcdio.so.0, libvcdinfo has SONAME
|
||||
# libvcdinfo.so.0 and requires libcdio.so.0, and foobar requires both
|
||||
# libvcdinfo.so.0 and libcdio.so.0. Everything looks fine.
|
||||
#
|
||||
# Now, for some reason, you decide to change the cdio_open function.
|
||||
# That's your right, but you have to bump the CURRENT version and (if I
|
||||
# understand it correctly, athough this is not that clear in libtool's
|
||||
# documentation) set the AGE to 0. Anyway, this bumps the SONAME, which is
|
||||
# sane since the interface changes incompatibly.
|
||||
|
||||
# Now, you have a new libcdio with SONAME libcdio.so.1. But libvcdinfo and
|
||||
# foobar still require libcdio.so.0. Everything is still fine.
|
||||
|
||||
# Now, after some minor changes, the author of foobar recompiles foobar.
|
||||
# Then, foobar now requires libvcdinfo.so.0 and libcdio.so.1. And foobar
|
||||
# now segfaults...
|
||||
|
||||
# What is happening? When you run foobar, if brings both libvcdinfo.so.0
|
||||
# and libcdio.so.1, but libvcdinfo.so.0 also brings libcdio.so.0. So you
|
||||
# have both libcdio.so.0 and libcdio.so.1 that bring their symbols to the
|
||||
# global namespace. Hence, you have to incompatible versions of the
|
||||
# cdio_open function in the name space. When foobar calls cdio_open, it
|
||||
# may choose the wrong function, and segfaults...
|
||||
|
||||
# With versioned symbols, the cdio_open function from libcdio.so.0 may be
|
||||
# known as (something that looks like) cdio_open@@CDIO_0. An the cdio_open
|
||||
# function from libcdio.so.1 as cdio_open@@CDIO_1. Both versions of
|
||||
# libcdio would still be brought in by the most recent foobar, but foobar
|
||||
# (and libvcdinfo) know which versioned function to use and then use the
|
||||
# good one.
|
||||
|
||||
|
||||
# This is some simple versioning where every symbol is versioned with
|
||||
# something that looks like the SONAME of the library. More complex (and
|
||||
# better) versioning is possible; it is for example what is used by glibc.
|
||||
# But good complex versioning is something that requires much more
|
||||
# work...
|
||||
|
||||
|
||||
# The below is a impliments symbol versioning. First of all, I
|
||||
# compute MAJOR as CURENT - AGE; that is what is used within libtool
|
||||
# (at least on GNU/Linux systems) for the number in the SONAME. The
|
||||
# nm command gives the list of symbols known in each of the object
|
||||
# files that will be part of the shared library. And the sed command
|
||||
# extracts from this list those symbols that will be shared. (This sed
|
||||
# command comes from libtool.)
|
||||
|
||||
libcdtext_la_MAJOR = $(shell expr $(libcdtext_la_CURRENT) - $(libcdtext_la_AGE))
|
||||
if BUILD_VERSIONED_LIBS
|
||||
libcdtext_la_LDFLAGS = $(libcdtext_la_ldflags) -Wl,--version-script=libcdtext.la.ver
|
||||
libcdtext_la_DEPENDENCIES = $(libcdio9660_la_dependencies) libcdtext.la.ver
|
||||
|
||||
libcdtext.la.ver: $(libcdtext_la_OBJECTS) $(srcdir)/libcdtext.sym
|
||||
@echo 'CDTEXT_$(libcdtext_la_MAJOR) {' > $@
|
||||
@objs=`for obj in $(libcdtext_la_OBJECTS); do sed -ne "s/^pic_object='\(.*\)'$$/\1/p" $$obj; done`; \
|
||||
if test -n "$$objs" ; then \
|
||||
nm $${objs} | sed -n -e 's/^.*[ ][ABCDGIRSTW][ABCDGIRSTW]*[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$$/\1/p' | sort -u | { first=true; while read symbol; do if grep -q "^$${symbol}\$$" $(srcdir)/libcdtext.sym; then if test $$first = true; then echo " global:"; first=false; fi; echo " $${symbol};"; fi; done; } >> $@; \
|
||||
nm $${objs} | sed -n -e 's/^.*[ ][ABCDGIRSTW][ABCDGIRSTW]*[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$$/\1/p' | sort -u | { first=true; while read symbol; do if grep -q "^$${symbol}\$$" $(srcdir)/libcdtext.sym; then :; else if test $$first = true; then echo " local:"; first=false; fi; echo " $${symbol};"; fi; done; } >> $@; \
|
||||
fi
|
||||
@echo '};' >> $@
|
||||
|
||||
MOSTLYCLEANFILES = libcdtext.la.ver
|
||||
else
|
||||
libcdtext_la_LDFLAGS = $(libcdtext_la_ldflags)
|
||||
libcdtext_la_DEPENDENCIES = $(libcdio9660_la_dependencies)
|
||||
endif
|
||||
741
lib/cdtext/cdtext.c
Normal file
741
lib/cdtext/cdtext.c
Normal file
@@ -0,0 +1,741 @@
|
||||
/*
|
||||
Copyright (C) 2004, 2005, 2008, 2011, 2012
|
||||
Rocky Bernstein <rocky@gnu.org>
|
||||
|
||||
toc reading routine adapted from cuetools
|
||||
Copyright (C) 2003 Svend Sanjay Sorensen <ssorensen@fastmail.fm>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# define __CDIO_CONFIG_H__ 1
|
||||
#endif
|
||||
|
||||
#include <cdio/cdtext.h>
|
||||
#include <cdio/logging.h>
|
||||
#include "cdtext_private.h"
|
||||
#include <cdio/utf8.h>
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#define _CDTEXT_DBCC
|
||||
#define MAX_CDTEXT_GENRE_CODE 28
|
||||
#define MAX_CDTEXT_LANGUAGE_CODE 127
|
||||
|
||||
#ifndef CDIO_CD_MAX_TRACKS
|
||||
# define CDIO_CD_MAX_TRACKS 99 /* Largest CD track number */
|
||||
#endif
|
||||
|
||||
const char *cdtext_field[MAX_CDTEXT_FIELDS] =
|
||||
{
|
||||
"TITLE",
|
||||
"PERFORMER",
|
||||
"SONGWRITER",
|
||||
"COMPOSER",
|
||||
"MESSAGE",
|
||||
"ARRANGER",
|
||||
"ISRC",
|
||||
"UPC_EAN",
|
||||
"GENRE",
|
||||
"DISC_ID",
|
||||
};
|
||||
|
||||
const char *cdtext_genre[MAX_CDTEXT_GENRE_CODE] =
|
||||
{
|
||||
"Not Used",
|
||||
"Not Defined",
|
||||
"Adult Contemporary",
|
||||
"Alternative Rock",
|
||||
"Childrens Music",
|
||||
"Classical",
|
||||
"Contemporary Christian",
|
||||
"Country",
|
||||
"Dance",
|
||||
"Easy Listening",
|
||||
"Erotic",
|
||||
"Folk",
|
||||
"Gospel",
|
||||
"Hip Hop",
|
||||
"Jazz",
|
||||
"Latin",
|
||||
"Musical",
|
||||
"New Age",
|
||||
"Opera",
|
||||
"Operetta",
|
||||
"Pop Music",
|
||||
"Rap",
|
||||
"Reggae",
|
||||
"Rock Music",
|
||||
"Rhythm & Blues",
|
||||
"Sound Effects",
|
||||
"Spoken Word",
|
||||
"World Music"
|
||||
};
|
||||
|
||||
const char *cdtext_language[MAX_CDTEXT_LANGUAGE_CODE] =
|
||||
{
|
||||
"Unknown",
|
||||
"Albanian",
|
||||
"Breton",
|
||||
"Catalan",
|
||||
"Croatian",
|
||||
"Welsh",
|
||||
"Czech",
|
||||
"Danish",
|
||||
"German",
|
||||
"English",
|
||||
"Spanish",
|
||||
"Esperanto",
|
||||
"Estonian",
|
||||
"Basque",
|
||||
"Faroese",
|
||||
"French",
|
||||
"Frisian",
|
||||
"Irish",
|
||||
"Gaelic",
|
||||
"Galician",
|
||||
"Icelandic",
|
||||
"Italian",
|
||||
"Lappish",
|
||||
"Latin",
|
||||
"Latvian",
|
||||
"Luxembourgian",
|
||||
"Lithuanian",
|
||||
"Hungarian",
|
||||
"Maltese",
|
||||
"Dutch",
|
||||
"Norwegian",
|
||||
"Occitan",
|
||||
"Polish",
|
||||
"Portuguese",
|
||||
"Romanian",
|
||||
"Romansh",
|
||||
"Serbian",
|
||||
"Slovak",
|
||||
"Slovenian",
|
||||
"Finnish",
|
||||
"Swedish",
|
||||
"Turkish",
|
||||
"Flemish",
|
||||
"Wallon",
|
||||
"Zulu",
|
||||
"Vietnamese",
|
||||
"Uzbek",
|
||||
"Urdu",
|
||||
"Ukrainian",
|
||||
"Thai",
|
||||
"Telugu",
|
||||
"Tatar",
|
||||
"Tamil",
|
||||
"Tadzhik",
|
||||
"Swahili",
|
||||
"SrananTongo",
|
||||
"Somali",
|
||||
"Sinhalese",
|
||||
"Shona",
|
||||
"Serbo-croat",
|
||||
"Ruthenian",
|
||||
"Russian",
|
||||
"Russian",
|
||||
"Quechua",
|
||||
"Pushtu",
|
||||
"Punjabi",
|
||||
"Persian",
|
||||
"Papamiento",
|
||||
"Oriya",
|
||||
"Nepali",
|
||||
"Ndebele",
|
||||
"Marathi",
|
||||
"Moldavian",
|
||||
"Malaysian",
|
||||
"Malagasay",
|
||||
"Macedonian",
|
||||
"Laotian",
|
||||
"Korean",
|
||||
"Khmer",
|
||||
"Kazakh",
|
||||
"Kannada",
|
||||
"Japanese",
|
||||
"Indonesian",
|
||||
"Hindi",
|
||||
"Hebrew",
|
||||
"Hausa",
|
||||
"Gurani",
|
||||
"Gujurati",
|
||||
"Greek",
|
||||
"Georgian",
|
||||
"Fulani",
|
||||
"Dari",
|
||||
"Churash",
|
||||
"Chinese",
|
||||
"Burmese",
|
||||
"Bulgarian",
|
||||
"Bengali",
|
||||
"Bielorussian",
|
||||
"Bambora",
|
||||
"Azerbaijani",
|
||||
"Assamese",
|
||||
"Armenian",
|
||||
"Arabic",
|
||||
"Amharic"
|
||||
};
|
||||
|
||||
/*!
|
||||
Return string representation of given field type.
|
||||
*/
|
||||
const char *
|
||||
cdtext_field2str(cdtext_field_t i)
|
||||
{
|
||||
if (i >= MAX_CDTEXT_FIELDS)
|
||||
return "INVALID";
|
||||
else
|
||||
return cdtext_field[i];
|
||||
}
|
||||
|
||||
/*!
|
||||
Return string representation of the given genre code.
|
||||
*/
|
||||
const char *
|
||||
cdtext_genre2str(cdtext_genre_t i)
|
||||
{
|
||||
if (i >= MAX_CDTEXT_GENRE_CODE)
|
||||
return "INVALID";
|
||||
else
|
||||
return cdtext_genre[i];
|
||||
}
|
||||
|
||||
/*!
|
||||
Return string representation of the given language code.
|
||||
*/
|
||||
const char *
|
||||
cdtext_lang2str(cdtext_lang_t i)
|
||||
{
|
||||
if (i >= MAX_CDTEXT_LANGUAGE_CODE)
|
||||
return "INVALID";
|
||||
else
|
||||
return cdtext_language[i];
|
||||
}
|
||||
|
||||
/*!
|
||||
Free memory associated with the given cdtext_t object.
|
||||
|
||||
@param p_cdtext the CD-TEXT object
|
||||
*/
|
||||
void
|
||||
cdtext_destroy(cdtext_t *p_cdtext)
|
||||
{
|
||||
cdtext_field_t k;
|
||||
track_t j;
|
||||
int i;
|
||||
|
||||
if (!p_cdtext) return;
|
||||
for (i=0; i<CDTEXT_NUM_BLOCKS_MAX; i++) {
|
||||
for (j=0; j<CDTEXT_NUM_TRACKS_MAX; j++) {
|
||||
for (k=0; k < MAX_CDTEXT_FIELDS; k++) {
|
||||
if (p_cdtext->block[i].track[j].field[k]) {
|
||||
free(p_cdtext->block[i].track[j].field[k]);
|
||||
p_cdtext->block[i].track[j].field[k] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a copy of the return value of cdtext_get_const or NULL.
|
||||
|
||||
Should be freed when done.
|
||||
@see cdtext_get_const
|
||||
*/
|
||||
char *
|
||||
cdtext_get(const cdtext_t *p_cdtext, cdtext_field_t field, track_t track)
|
||||
{
|
||||
const char *ret = cdtext_get_const(p_cdtext, field, track);
|
||||
if (NULL == ret)
|
||||
return NULL;
|
||||
else
|
||||
return strdup(ret);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns value of the given field.
|
||||
|
||||
NULL is returned if key is CDTEXT_INVALID or the field is not set.
|
||||
Strings are encoded in UTF-8.
|
||||
|
||||
@param p_cdtext the CD-TEXT object
|
||||
@param field type of the field to return
|
||||
@param track specifies the track, 0 stands for disc
|
||||
*/
|
||||
const char *
|
||||
cdtext_get_const(const cdtext_t *p_cdtext, cdtext_field_t field, track_t track)
|
||||
{
|
||||
if (CDTEXT_FIELD_INVALID == field
|
||||
|| NULL == p_cdtext
|
||||
|| 0 > track
|
||||
|| CDIO_CD_MAX_TRACKS < track)
|
||||
return NULL;
|
||||
|
||||
return p_cdtext->block[p_cdtext->block_i].track[track].field[field];
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Returns the currently active language.
|
||||
|
||||
@param p_cdtext the CD-TEXT object
|
||||
*/
|
||||
cdtext_lang_t
|
||||
cdtext_get_language(const cdtext_t *p_cdtext)
|
||||
{
|
||||
if (NULL == p_cdtext)
|
||||
return CDTEXT_LANGUAGE_UNKNOWN;
|
||||
return p_cdtext->block[p_cdtext->block_i].language_code;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a list of available languages or NULL.
|
||||
|
||||
Internally the list is stored in a static array.
|
||||
|
||||
@param p_cdtext the CD-TEXT object
|
||||
*/
|
||||
cdtext_lang_t
|
||||
*cdtext_list_languages(const cdtext_t *p_cdtext)
|
||||
{
|
||||
static cdtext_lang_t avail[CDTEXT_NUM_BLOCKS_MAX];
|
||||
int i, j=0;
|
||||
|
||||
if (NULL == p_cdtext)
|
||||
return NULL;
|
||||
|
||||
for (i=0; i<CDTEXT_NUM_BLOCKS_MAX; i++)
|
||||
{
|
||||
avail[i] = CDTEXT_LANGUAGE_UNKNOWN;
|
||||
if (CDTEXT_LANGUAGE_UNKNOWN != p_cdtext->block[i].language_code)
|
||||
avail[j++] = p_cdtext->block[i].language_code;
|
||||
}
|
||||
|
||||
return avail;
|
||||
}
|
||||
|
||||
/*!
|
||||
Try to select the given language.
|
||||
Select default language if specified is not available or invalid and
|
||||
return false.
|
||||
|
||||
@param p_cdtext the CD-TEXT object
|
||||
@param language string representation of the language
|
||||
|
||||
@return true on success, false if language is not available
|
||||
*/
|
||||
bool
|
||||
cdtext_select_language(cdtext_t *p_cdtext, const char *language)
|
||||
{
|
||||
cdtext_lang_t lang_id;
|
||||
lang_id = cdtext_is_language(language);
|
||||
|
||||
if(NULL == p_cdtext)
|
||||
return false;
|
||||
|
||||
if (CDTEXT_LANGUAGE_UNKNOWN != lang_id)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<CDTEXT_NUM_BLOCKS_MAX; i++) {
|
||||
if (lang_id == p_cdtext->block[i].language_code) {
|
||||
p_cdtext->block_i = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p_cdtext->block_i = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
Initialize a new cdtext structure.
|
||||
|
||||
When the structure is no longer needed, release the
|
||||
resources using cdtext_delete.
|
||||
|
||||
*/
|
||||
cdtext_t
|
||||
*cdtext_init(void)
|
||||
{
|
||||
cdtext_field_t k;
|
||||
track_t j;
|
||||
int i;
|
||||
cdtext_t *p_cdtext;
|
||||
|
||||
p_cdtext = (cdtext_t *) malloc(sizeof(struct cdtext_s));
|
||||
|
||||
for (i=0; i<CDTEXT_NUM_BLOCKS_MAX; i++) {
|
||||
for (j=0; j<CDTEXT_NUM_TRACKS_MAX; j++) {
|
||||
for (k=0; k < MAX_CDTEXT_FIELDS; k++) {
|
||||
p_cdtext->block[i].track[j].field[k] = NULL;
|
||||
}
|
||||
}
|
||||
p_cdtext->block[i].genre_code = CDTEXT_GENRE_UNUSED;
|
||||
p_cdtext->block[i].language_code = CDTEXT_LANGUAGE_UNKNOWN;
|
||||
}
|
||||
|
||||
p_cdtext->block_i = 0;
|
||||
|
||||
return p_cdtext;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns associated cdtext_field_t if field is a CD-TEXT keyword.
|
||||
|
||||
Internal function.
|
||||
|
||||
@param key key to test
|
||||
|
||||
@return CDTEXT_INVALID if the given keyword is invalid
|
||||
*/
|
||||
cdtext_field_t
|
||||
cdtext_is_field (const char *key)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_CDTEXT_FIELDS ; i++)
|
||||
if (0 == strcmp(cdtext_field[i], key)) {
|
||||
return i;
|
||||
}
|
||||
return CDTEXT_FIELD_INVALID;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns associated cdtext_lang_t if argument is a supported language.
|
||||
|
||||
Internal function.
|
||||
|
||||
@param lang language to test
|
||||
|
||||
@return CDTEXT_LANGUAGE_UNKNOWN if language is not supported
|
||||
*/
|
||||
cdtext_lang_t
|
||||
cdtext_is_language(const char *lang)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_CDTEXT_LANGUAGE_CODE; i++)
|
||||
if (0 == strcmp(cdtext_language[i], lang)) {
|
||||
return i;
|
||||
}
|
||||
return CDTEXT_LANGUAGE_UNKNOWN;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the given field at the given track to the given value.
|
||||
|
||||
Recodes to UTF-8 if charset is not NULL.
|
||||
|
||||
@param p_cdtext the CD-TEXT object
|
||||
@param key field to set
|
||||
@param value value to set
|
||||
@param track track to work on
|
||||
@param charset charset to convert from
|
||||
*/
|
||||
void
|
||||
cdtext_set(cdtext_t *p_cdtext, cdtext_field_t key, const uint8_t *value,
|
||||
track_t track, const char *charset)
|
||||
{
|
||||
if (NULL == value || key == CDTEXT_FIELD_INVALID || 0 > track
|
||||
|| CDIO_CD_MAX_TRACKS < track)
|
||||
return;
|
||||
|
||||
/* free old memory */
|
||||
if (p_cdtext->block[p_cdtext->block_i].track[track].field[key])
|
||||
free(p_cdtext->block[p_cdtext->block_i].track[track].field[key]);
|
||||
|
||||
/* recode to UTF-8 */
|
||||
if (NULL != charset) {
|
||||
cdio_utf8_t *utf8_str = NULL;
|
||||
cdio_charset_to_utf8((const char*) value, strlen((const char*)value),
|
||||
&utf8_str, charset);
|
||||
p_cdtext->block[p_cdtext->block_i].track[track].field[key] = (char *)utf8_str;
|
||||
} else
|
||||
p_cdtext->block[p_cdtext->block_i].track[track].field[key] = strdup((const char *)value);
|
||||
}
|
||||
|
||||
/*!
|
||||
Read a binary CD-TEXT and fill a cdtext struct.
|
||||
|
||||
@param p_cdtext the CD-TEXT object
|
||||
@param wdata the data
|
||||
@param i_data size of wdata
|
||||
|
||||
@returns 0 on success, non-zero on failure
|
||||
*/
|
||||
int
|
||||
cdtext_data_init(cdtext_t *p_cdtext, uint8_t *wdata, size_t i_data)
|
||||
{
|
||||
uint8_t *p_data;
|
||||
int j;
|
||||
uint8_t buffer[256];
|
||||
int i_buf = 0;
|
||||
int i_block;
|
||||
int i_seq = 0;
|
||||
int i;
|
||||
cdtext_blocksize_t blocksize;
|
||||
char *charset = NULL;
|
||||
|
||||
memset( buffer, 0, sizeof(buffer) );
|
||||
|
||||
p_data = wdata;
|
||||
if (i_data < CDTEXT_LEN_PACK || 0 != i_data % CDTEXT_LEN_PACK) {
|
||||
cdio_warn("CD-Text size is not multiple of pack size");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
for(i=0; i < i_data; i++)
|
||||
printf("%0x%c", wdata[i], ((i+1) % 18 == 0 ? '\n' : ' '));
|
||||
#endif
|
||||
|
||||
|
||||
/* Iterate over blocks */
|
||||
i_block = -1;
|
||||
while(i_data > 0) {
|
||||
cdtext_pack_t pack;
|
||||
cdtext_read_pack(&pack, p_data);
|
||||
|
||||
if (i_block != pack.block || i_seq != pack.seq) {
|
||||
cdtext_pack_t tpack;
|
||||
i_block = pack.block;
|
||||
if (i_block >= CDTEXT_NUM_BLOCKS_MAX) {
|
||||
cdio_warn("CD-TEXT: Invalid blocknumber %d.\n", i_block);
|
||||
return -1;
|
||||
}
|
||||
p_cdtext->block_i = i_block;
|
||||
i_seq = 0;
|
||||
memset( &blocksize, 0, CDTEXT_LEN_BLOCKSIZE);
|
||||
|
||||
/* first read block size information for sanity checks and encoding */
|
||||
for(i=0; i <= i_data-CDTEXT_LEN_PACK; i+=CDTEXT_LEN_PACK) {
|
||||
|
||||
if (p_data[i+0] == CDTEXT_PACK_BLOCKSIZE) {
|
||||
cdtext_read_pack(&tpack, p_data+i);
|
||||
switch (tpack.i_track) {
|
||||
case 0:
|
||||
blocksize.charcode = tpack.text[0];
|
||||
blocksize.i_first_track = tpack.text[1];
|
||||
blocksize.i_last_track = tpack.text[2];
|
||||
blocksize.copyright = tpack.text[3];
|
||||
blocksize.i_packs[0] = tpack.text[4];
|
||||
blocksize.i_packs[1] = tpack.text[5];
|
||||
blocksize.i_packs[2] = tpack.text[6];
|
||||
blocksize.i_packs[3] = tpack.text[7];
|
||||
blocksize.i_packs[4] = tpack.text[8];
|
||||
blocksize.i_packs[5] = tpack.text[9];
|
||||
blocksize.i_packs[6] = tpack.text[10];
|
||||
blocksize.i_packs[7] = tpack.text[11];
|
||||
break;
|
||||
case 1:
|
||||
blocksize.i_packs[8] = tpack.text[0];
|
||||
blocksize.i_packs[9] = tpack.text[1];
|
||||
blocksize.i_packs[10] = tpack.text[2];
|
||||
blocksize.i_packs[11] = tpack.text[3];
|
||||
blocksize.i_packs[12] = tpack.text[4];
|
||||
blocksize.i_packs[13] = tpack.text[5];
|
||||
blocksize.i_packs[14] = tpack.text[6];
|
||||
blocksize.i_packs[15] = tpack.text[7];
|
||||
blocksize.lastseq[0] = tpack.text[8];
|
||||
blocksize.lastseq[1] = tpack.text[9];
|
||||
blocksize.lastseq[2] = tpack.text[10];
|
||||
blocksize.lastseq[3] = tpack.text[11];
|
||||
break;
|
||||
case 2:
|
||||
blocksize.lastseq[4] = tpack.text[0];
|
||||
blocksize.lastseq[5] = tpack.text[1];
|
||||
blocksize.lastseq[6] = tpack.text[2];
|
||||
blocksize.lastseq[7] = tpack.text[3];
|
||||
blocksize.langcode[0] = tpack.text[4];
|
||||
blocksize.langcode[1] = tpack.text[5];
|
||||
blocksize.langcode[2] = tpack.text[6];
|
||||
blocksize.langcode[3] = tpack.text[7];
|
||||
blocksize.langcode[4] = tpack.text[8];
|
||||
blocksize.langcode[5] = tpack.text[9];
|
||||
blocksize.langcode[6] = tpack.text[10];
|
||||
blocksize.langcode[7] = tpack.text[11];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(blocksize.i_packs[15] == 3) {
|
||||
/* if there were 3 BLOCKSIZE packs */
|
||||
/* set copyright */
|
||||
p_cdtext->block[i_block].copyright = (0x03 == (blocksize.copyright & 0x03));
|
||||
|
||||
/* set Language */
|
||||
if(blocksize.langcode[i_block] <= 0x7f)
|
||||
p_cdtext->block[i_block].language_code = blocksize.langcode[i_block];
|
||||
|
||||
/* determine encoding */
|
||||
switch (blocksize.charcode){
|
||||
case CDTEXT_CHARCODE_ISO_8859_1:
|
||||
/* default */
|
||||
charset = (char *) "ISO-8859-1";
|
||||
break;
|
||||
case CDTEXT_CHARCODE_ASCII:
|
||||
charset = (char *) "ASCII";
|
||||
break;
|
||||
case CDTEXT_CHARCODE_SHIFT_JIS:
|
||||
charset = (char *) "SHIFT_JIS";
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
cdio_warn("CD-TEXT: No blocksize information available for block %d.\n", i_block);
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cdtext_read_pack(&pack, p_data);
|
||||
|
||||
#ifndef _CDTEXT_DBCC
|
||||
if ( pack.db_chars ) {
|
||||
cdio_warn("CD-TEXT: Double-byte characters not supported");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* read text packs first */
|
||||
j = 0;
|
||||
switch (pack.type) {
|
||||
case CDTEXT_PACK_GENRE:
|
||||
/* If pack.text starts with an unprintable character, it is likely to be the genre_code.
|
||||
* While the specification requires the first GENRE pack to start with the 2 byte genre code,
|
||||
* it is not specific about the following ones. */
|
||||
if (pack.text[0] <= 31) {
|
||||
j = 2;
|
||||
if (CDTEXT_GENRE_UNUSED == p_cdtext->block[i_block].genre_code)
|
||||
p_cdtext->block[i_block].genre_code = CDTEXT_GET_LEN16(pack.text);
|
||||
}
|
||||
case CDTEXT_PACK_TITLE:
|
||||
case CDTEXT_PACK_PERFORMER:
|
||||
case CDTEXT_PACK_SONGWRITER:
|
||||
case CDTEXT_PACK_COMPOSER:
|
||||
case CDTEXT_PACK_ARRANGER:
|
||||
case CDTEXT_PACK_MESSAGE:
|
||||
case CDTEXT_PACK_DISCID:
|
||||
case CDTEXT_PACK_UPC:
|
||||
while (j < CDTEXT_LEN_TEXTDATA) {
|
||||
/* not terminated */
|
||||
if (pack.text[j] != 0 || (pack.db_chars && pack.text[j+1] != 0)) {
|
||||
buffer[i_buf++] = pack.text[j];
|
||||
if(pack.db_chars)
|
||||
buffer[i_buf++] = pack.text[j+1];
|
||||
} else if(i_buf > 1) {
|
||||
buffer[i_buf++] = 0;
|
||||
if(pack.db_chars)
|
||||
buffer[i_buf++] = 0;
|
||||
|
||||
switch (pack.type) {
|
||||
case CDTEXT_PACK_TITLE:
|
||||
cdtext_set(p_cdtext, CDTEXT_FIELD_TITLE, buffer, pack.i_track, charset);
|
||||
break;
|
||||
case CDTEXT_PACK_PERFORMER:
|
||||
cdtext_set(p_cdtext, CDTEXT_FIELD_PERFORMER, buffer, pack.i_track, charset);
|
||||
break;
|
||||
case CDTEXT_PACK_SONGWRITER:
|
||||
cdtext_set(p_cdtext, CDTEXT_FIELD_SONGWRITER, buffer, pack.i_track, charset);
|
||||
break;
|
||||
case CDTEXT_PACK_COMPOSER:
|
||||
cdtext_set(p_cdtext, CDTEXT_FIELD_COMPOSER, buffer, pack.i_track, charset);
|
||||
break;
|
||||
case CDTEXT_PACK_ARRANGER:
|
||||
cdtext_set(p_cdtext, CDTEXT_FIELD_ARRANGER, buffer, pack.i_track, charset);
|
||||
break;
|
||||
case CDTEXT_PACK_MESSAGE:
|
||||
cdtext_set(p_cdtext, CDTEXT_FIELD_MESSAGE, buffer, pack.i_track, charset);
|
||||
break;
|
||||
case CDTEXT_PACK_DISCID:
|
||||
if (pack.i_track == 0)
|
||||
cdtext_set(p_cdtext, CDTEXT_FIELD_DISCID, buffer, pack.i_track, NULL);
|
||||
break;
|
||||
case CDTEXT_PACK_GENRE:
|
||||
cdtext_set(p_cdtext, CDTEXT_FIELD_GENRE, buffer, pack.i_track, "ASCII");
|
||||
break;
|
||||
case CDTEXT_PACK_UPC:
|
||||
if (pack.i_track == 0)
|
||||
cdtext_set(p_cdtext, CDTEXT_FIELD_UPC_EAN, buffer, pack.i_track, "ASCII");
|
||||
else
|
||||
cdtext_set(p_cdtext, CDTEXT_FIELD_ISRC, buffer, pack.i_track, "ISO-8859-1");
|
||||
break;
|
||||
}
|
||||
i_buf = 0;
|
||||
|
||||
}
|
||||
if (pack.db_chars)
|
||||
j+=2;
|
||||
else
|
||||
j+=1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* This would be the right place to parse TOC and TOC2 fields. */
|
||||
|
||||
i_seq++;
|
||||
i_data-=CDTEXT_LEN_PACK;
|
||||
p_data+=CDTEXT_LEN_PACK;
|
||||
} /* end of while loop */
|
||||
|
||||
p_cdtext->block_i = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Fills cdtext_pack_t with information read from p_data
|
||||
|
||||
@param p_pack out
|
||||
@param p_data in
|
||||
*/
|
||||
int
|
||||
cdtext_read_pack(cdtext_pack_t *p_pack, const uint8_t *p_data) {
|
||||
p_pack->type = p_data[0];
|
||||
p_pack->i_track = p_data[1];
|
||||
p_pack->seq = p_data[2];
|
||||
p_pack->char_pos = p_data[3] & 0x0F;
|
||||
p_pack->block = (p_data[3] >> 4) & 0x07;
|
||||
p_pack->db_chars = (p_data[3] >> 7) & 0x01;
|
||||
p_pack->text[0] = p_data[4];
|
||||
p_pack->text[1] = p_data[5];
|
||||
p_pack->text[2] = p_data[6];
|
||||
p_pack->text[3] = p_data[7];
|
||||
p_pack->text[4] = p_data[8];
|
||||
p_pack->text[5] = p_data[9];
|
||||
p_pack->text[6] = p_data[10];
|
||||
p_pack->text[7] = p_data[11];
|
||||
p_pack->text[8] = p_data[12];
|
||||
p_pack->text[9] = p_data[13];
|
||||
p_pack->text[10] = p_data[14];
|
||||
p_pack->text[11] = p_data[15];
|
||||
p_pack->crc[0] = p_data[16];
|
||||
p_pack->crc[1] = p_data[17];
|
||||
|
||||
return 0;
|
||||
}
|
||||
148
lib/cdtext/cdtext_private.h
Normal file
148
lib/cdtext/cdtext_private.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright (C) 2004, 2005, 2008, 2011, 2012
|
||||
Rocky Bernstein <rocky@gnu.org>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __CDIO_CDTEXT_PRIVATE_H__
|
||||
#define __CDIO_CDTEXT_PRIVATE_H__
|
||||
|
||||
#include <cdio/types.h>
|
||||
|
||||
#define CDTEXT_GET_LEN16(p) (p[0]<<8) + p[1]
|
||||
|
||||
|
||||
enum {
|
||||
CDTEXT_LEN_BINARY_MAX = 9216,
|
||||
CDTEXT_LEN_TEXTDATA = 12,
|
||||
CDTEXT_LEN_PACK = 18,
|
||||
CDTEXT_LEN_BLOCKSIZE = 36,
|
||||
CDTEXT_NUM_BLOCKS_MAX = 8,
|
||||
CDTEXT_NUM_TRACKS_MAX = 100,
|
||||
CDTEXT_NUM_BLOCKPACKS_MAX = 255
|
||||
} cdtext_format_enum;
|
||||
|
||||
/**
|
||||
* From table J.2 - Pack Type Indicator Definitions from
|
||||
* Working Draft NCITS XXX T10/1364-D Revision 10G. November 12, 2001.
|
||||
*/
|
||||
enum {
|
||||
CDTEXT_PACK_TITLE = 0x80,
|
||||
CDTEXT_PACK_PERFORMER = 0x81,
|
||||
CDTEXT_PACK_SONGWRITER = 0x82,
|
||||
CDTEXT_PACK_COMPOSER = 0x83,
|
||||
CDTEXT_PACK_ARRANGER = 0x84,
|
||||
CDTEXT_PACK_MESSAGE = 0x85,
|
||||
CDTEXT_PACK_DISCID = 0x86,
|
||||
CDTEXT_PACK_GENRE = 0x87,
|
||||
CDTEXT_PACK_TOC = 0x88,
|
||||
CDTEXT_PACK_TOC2 = 0x89,
|
||||
CDTEXT_PACK_UPC = 0x8E,
|
||||
CDTEXT_PACK_BLOCKSIZE = 0x8F
|
||||
} cdtext_packtype_enum;
|
||||
|
||||
|
||||
/** CD-Text character encodings */
|
||||
enum cdtext_charcode_enum_s {
|
||||
CDTEXT_CHARCODE_ISO_8859_1 = 0x00, /**< ISO-8859-1 (8 bit), Latin-1 */
|
||||
CDTEXT_CHARCODE_ASCII = 0x01, /**< ASCII (7 bit) */
|
||||
CDTEXT_CHARCODE_SHIFT_JIS = 0x80 /**< Shift_JIS (double byte), JIS X 0208 Appendix 1 */
|
||||
///* The following were proposed but never implemented anywhere.
|
||||
// * They are mentioned for completeness here
|
||||
// * CDTEXT_CHARCODE_KOREAN = 0x81, /**< Korean */
|
||||
// * CDTEXT_CHARCODE_CHINESE = 0x82, /**< Mandarin Chinese */
|
||||
// * CDTEXT_CHARCODE_UNDEFINED = 0xFF, /**< everything else */
|
||||
// */
|
||||
} cdtext_charcode_enum;
|
||||
|
||||
/** Structure of CD-TEXT data Packs */
|
||||
struct cdtext_pack_s
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t i_track;
|
||||
uint8_t seq;
|
||||
uint8_t char_pos; /* character position */
|
||||
uint8_t block; /* block number 0..7 */
|
||||
uint8_t db_chars; /* double byte character */
|
||||
uint8_t text[CDTEXT_LEN_TEXTDATA];
|
||||
uint8_t crc[2];
|
||||
};
|
||||
|
||||
|
||||
/** Structure of of block size information packs */
|
||||
struct cdtext_blocksize_s
|
||||
{
|
||||
uint8_t charcode; /* character code */
|
||||
uint8_t i_first_track; /* first track number */
|
||||
uint8_t i_last_track; /* last track number */
|
||||
uint8_t copyright; /* 3 CD-TEXT is copyrighted, 0 no copyright on CD-TEXT */
|
||||
uint8_t i_packs[16]; /* number of packs of each type
|
||||
* 0 TITLE; 1 PERFORMER; 2 SONGWRITER; 3 COMPOSER;
|
||||
* 4 ARRANGER; 5 MESSAGE; 6 DISCID; 7 GENRE;
|
||||
* 8 TOC; 9 TOC2; 10-12 RESERVED; 13 CLOSED;
|
||||
* 14 UPC_ISRC; 15 BLOCKSIZE */
|
||||
uint8_t lastseq[8]; /* last sequence for block 0..7 */
|
||||
uint8_t langcode[8]; /* language code for block 0..7 */
|
||||
};
|
||||
|
||||
typedef struct cdtext_pack_s cdtext_pack_t;
|
||||
typedef struct cdtext_blocksize_s cdtext_blocksize_t;
|
||||
|
||||
/*! Structure for CD-TEXT of a track. */
|
||||
struct cdtext_track_s {
|
||||
char *field[MAX_CDTEXT_FIELDS];
|
||||
};
|
||||
|
||||
/*! Structure for CD-TEXT of a block. */
|
||||
struct cdtext_block_s {
|
||||
struct cdtext_track_s track[CDTEXT_NUM_TRACKS_MAX]; /**< 0: disc; 1..99: tracks */
|
||||
cdtext_genre_t genre_code; /**< genre code of the disc */
|
||||
cdtext_lang_t language_code; /**< language of this block */
|
||||
bool copyright; /**< CD-TEXT copyright */
|
||||
};
|
||||
|
||||
/*! Structure for CD-TEXT of a disc.
|
||||
|
||||
@see cdtext_init, cdtext_destroy, cdtext_get, and cdtext_set.
|
||||
*/
|
||||
struct cdtext_s {
|
||||
struct cdtext_block_s block[CDTEXT_NUM_BLOCKS_MAX]; /**< CD-TEXT for block 0..7 */
|
||||
uint8_t block_i; /**< index of active block */
|
||||
};
|
||||
|
||||
int cdtext_read_pack (cdtext_pack_t *pack, const uint8_t *data);
|
||||
|
||||
/*!
|
||||
returns enum of field if key is a CD-Text keyword,
|
||||
returns CDTEXT_FIELD_INVALID otherwise.
|
||||
*/
|
||||
cdtext_field_t cdtext_is_field (const char *field);
|
||||
|
||||
/*!
|
||||
returns enum of language if lang is a valid language,
|
||||
returns CDTEXT_LANGUAGE_UNKNOWN otherwise.
|
||||
*/
|
||||
cdtext_lang_t cdtext_is_language (const char *lang);
|
||||
|
||||
|
||||
#endif /* __CDIO_CDTEXT_PRIVATE_H__ */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "gnu"
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
13
lib/cdtext/libcdtext.sym
Normal file
13
lib/cdtext/libcdtext.sym
Normal file
@@ -0,0 +1,13 @@
|
||||
cdio_get_cdtext
|
||||
cdio_get_cdtext_raw
|
||||
cdtext_destroy
|
||||
cdtext_field2str
|
||||
cdtext_genre2str
|
||||
cdtext_lang2str
|
||||
cdtext_get
|
||||
cdtext_get_const
|
||||
cdtext_get_language
|
||||
cdtext_init
|
||||
cdtext_list_languages
|
||||
cdtext_set
|
||||
cdtext_select_language
|
||||
12
libcdtext.pc.in
Normal file
12
libcdtext.pc.in
Normal file
@@ -0,0 +1,12 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
libiconv=@LTLIBICONV@
|
||||
|
||||
Name: libcdtext
|
||||
Description: CD-Text library of libcdio
|
||||
Version: @PACKAGE_VERSION@
|
||||
Requires: libcdio
|
||||
Libs: -L${libdir} -lcdtext @LTLIBICONV@ -lcdio
|
||||
Cflags: -I${includedir}
|
||||
Reference in New Issue
Block a user