From ad3325348e9f97d4c489595cec468f7d0c5e588d Mon Sep 17 00:00:00 2001
From: Leon merten Lohse <leon@green-side.de>
Date: Sun, 20 Nov 2011 10:14:50 +0100
Subject: [PATCH 1/1] CD-Text restructuring

---
 example/cdtext.c             |   35 +++--
 include/cdio++/cdtext.hpp    |   12 +-
 include/cdio++/device.hpp    |    8 -
 include/cdio++/disc.hpp      |   10 ++
 include/cdio++/track.hpp     |   11 --
 include/cdio/cdio.h          |    3 +-
 include/cdio/cdtext.h        |   58 ++++++++-
 include/cdio/disc.h          |   20 +++-
 include/cdio/track.h         |   13 --
 lib/driver/FreeBSD/freebsd.c |    2 +-
 lib/driver/MSWindows/win32.c |    2 +-
 lib/driver/_cdio_generic.c   |   75 +++++-------
 lib/driver/aix.c             |    7 +-
 lib/driver/bsdi.c            |    4 +-
 lib/driver/cdio.c            |   19 ---
 lib/driver/cdio_private.h    |   16 ++-
 lib/driver/cdtext.c          |  294 ++++++++++++++++++++++++++----------------
 lib/driver/cdtext_private.h  |   65 ++--------
 lib/driver/disc.c            |   39 ++++++
 lib/driver/generic.h         |   11 +-
 lib/driver/gnu_linux.c       |    2 +-
 lib/driver/image.h           |    1 -
 lib/driver/image/bincue.c    |   75 ++++++++---
 lib/driver/image/cdrdao.c    |   30 ++---
 lib/driver/image/nrg.c       |   19 ++-
 lib/driver/image_common.c    |   17 ++-
 lib/driver/image_common.h    |    5 +
 lib/driver/libcdio.sym       |    2 +
 lib/driver/mmc/mmc.c         |   34 +++--
 lib/driver/mmc/mmc_private.h |    6 +-
 lib/driver/netbsd.c          |    2 +
 lib/driver/os2.c             |    4 +-
 lib/driver/osx.c             |   19 +---
 lib/driver/solaris.c         |    2 +-
 src/cd-info.c                |   18 ++-
 src/cdda-player.c            |   24 ++--
 test/cdtext.right            |   49 +++++++
 test/check_cue.sh.in         |   27 +++-
 test/check_fuzzyiso.sh       |    2 +-
 test/data/cdtext.bin         |  Bin 0 -> 661500 bytes
 test/data/cdtext.cdt         |  Bin 0 -> 811 bytes
 test/data/cdtext.cue         |    8 +
 test/driver/nrg.c.in         |    4 +-
 43 files changed, 636 insertions(+), 418 deletions(-)
 create mode 100644 test/cdtext.right
 create mode 100644 test/data/cdtext.bin
 create mode 100644 test/data/cdtext.cdt
 create mode 100644 test/data/cdtext.cue

diff --git a/example/cdtext.c b/example/cdtext.c
index 1d50f89..7e99ad0 100644
--- a/example/cdtext.c
+++ b/example/cdtext.c
@@ -31,34 +31,39 @@
 
 
 static void 
-print_cdtext_track_info(CdIo_t *p_cdio, track_t i_track, const char *psz_msg) {
-  const cdtext_t *cdtext = cdio_get_cdtext(p_cdio, i_track);
-  if (NULL != cdtext) {
-    cdtext_field_t i;
-    
-    printf("%s\n", psz_msg);
-    
-    for (i=0; i < MAX_CDTEXT_FIELDS; i++) {
-      if (cdtext->field[i]) {
-	printf("\t%s: %s\n", cdtext_field2str(i), cdtext->field[i]);
-      }
+print_cdtext_track_info(cdtext_t *cdtext, track_t i_track, const char *psz_msg) {
+  cdtext_field_t i;
+  
+  printf("%s\n", psz_msg);
+  
+  for (i=0; i < MAX_CDTEXT_FIELDS; i++) {
+    if (cdtext_get_const(i, i_track, cdtext)) {
+      printf("\t%s: %s\n", cdtext_field2str(i), cdtext_get_const(i, i_track, cdtext));
     }
   }
-  
+
 }
     
 static void 
 print_disc_info(CdIo_t *p_cdio, track_t i_tracks, track_t i_first_track) {
   track_t i_last_track = i_first_track+i_tracks;
   discmode_t cd_discmode = cdio_get_discmode(p_cdio);
+  cdtext_t *cdtext = cdio_get_cdtext(p_cdio);
 
   printf("%s\n", discmode2str[cd_discmode]);
-  
-  print_cdtext_track_info(p_cdio, 0, "\nCD-Text for Disc:");
+
+  if (NULL == cdtext)
+  {
+    printf("\nNo CD-Text found on Disc.");
+    return;
+  }
+
+  printf("Encoding: %s; Language: %s\n", cdtext->encoding, cdtext->language);
+  print_cdtext_track_info(cdtext, 0, "\nCD-Text for Disc:");
   for ( ; i_first_track < i_last_track; i_first_track++ ) {
     char psz_msg[50];
     snprintf(psz_msg, sizeof(psz_msg), "CD-Text for Track %d:", i_first_track);
-    print_cdtext_track_info(p_cdio, i_first_track, psz_msg);
+    print_cdtext_track_info(cdtext, i_first_track, psz_msg);
   }
 }
 
diff --git a/include/cdio++/cdtext.hpp b/include/cdio++/cdtext.hpp
index 2aeb4b4..2154257 100644
--- a/include/cdio++/cdtext.hpp
+++ b/include/cdio++/cdtext.hpp
@@ -37,9 +37,9 @@ const char *field2str (cdtext_field_t i)
   have to be freed.
   
 */
-char *get (cdtext_field_t key) 
+char *get (cdtext_field_t key, track_t i_track) 
 {
-  return cdtext_get (key, p_cdtext);
+  return cdtext_get (key, i_track, p_cdtext);
 }
 
 /*! returns the C cdtext_t pointer associated with this object. */
@@ -58,9 +58,9 @@ cdtext_t *get ()
   cdtext object.
 
 */
-const char *getConst (cdtext_field_t key) 
+const char *getConst (cdtext_field_t key, track_t i_track) 
 {
-  return cdtext_get_const (key, p_cdtext);
+  return cdtext_get_const (key, i_track, p_cdtext);
 }
 
 /*!
@@ -75,9 +75,9 @@ cdtext_field_t isKeyword (const char *key)
 /*! 
   sets cdtext's keyword entry to field 
 */
-void set (cdtext_field_t key, const char *value) 
+void set (cdtext_field_t key, track_t i_track, const char *value) 
 {
-  cdtext_set (key, value, p_cdtext);
+  cdtext_set (key, i_track, value, p_cdtext);
 }
 
 
diff --git a/include/cdio++/device.hpp b/include/cdio++/device.hpp
index cbb75ca..324b560 100644
--- a/include/cdio++/device.hpp
+++ b/include/cdio++/device.hpp
@@ -82,14 +82,6 @@ CdIo_t *getCdIo()
   return p_cdio;
 }
 
-/*!  
-  Return an opaque CdIo_t pointer for the given track object.
-*/
-cdtext_t *getCdtext(track_t i_track)
-{
-  return cdio_get_cdtext (p_cdio, i_track);
-}
-
 /*!
   Get the CD device name for the object.
   
diff --git a/include/cdio++/disc.hpp b/include/cdio++/disc.hpp
index 6322387..b6c8292 100644
--- a/include/cdio++/disc.hpp
+++ b/include/cdio++/disc.hpp
@@ -171,4 +171,14 @@ bool isDiscmodeDvd (discmode_t discmode)
   return cdio_is_discmode_dvd (discmode) ;
 }
 
+/*! 
+ *   Get CD-Text information for a CdIo_t object.
+ *     
+ *       @return the CD-Text object or NULL if obj is NULL
+ *         or CD-Text information does not exist.
+ *         */
+cdtext_t *getCdtext () 
+{
+  return cdio_get_cdtext (p_cdio);
+}
 
diff --git a/include/cdio++/track.hpp b/include/cdio++/track.hpp
index 3e7942f..cf7b2b4 100644
--- a/include/cdio++/track.hpp
+++ b/include/cdio++/track.hpp
@@ -30,17 +30,6 @@ CdIo_t *getCdIo()
   return p_cdio;
 }
 
-/*! 
-  Get CD-Text information for a CdIo_t object.
-  
-  @return the CD-Text object or NULL if obj is NULL
-  or CD-Text information does not exist.
-*/
-cdtext_t *getCdtext () 
-{
-  return cdio_get_cdtext (p_cdio, i_track);
-}
-
 /*! Return number of channels in track: 2 or 4; -2 if not
   implemented or -1 for error.
   Not meaningful if track is not an audio track.
diff --git a/include/cdio/cdio.h b/include/cdio/cdio.h
index 5d8755f..16ca913 100644
--- a/include/cdio/cdio.h
+++ b/include/cdio/cdio.h
@@ -52,8 +52,7 @@ extern "C" {
   /** This is an opaque structure for the CD object. */
   typedef struct _CdIo CdIo_t; 
 
-  /** This is an opaque structure for the CD-Text object. */
-  typedef struct cdtext cdtext_t;
+  typedef struct cdtext_s cdtext_t;
 
 #ifdef __cplusplus
 }
diff --git a/include/cdio/cdtext.h b/include/cdio/cdtext.h
index daac733..43ace90 100644
--- a/include/cdio/cdtext.h
+++ b/include/cdio/cdtext.h
@@ -37,6 +37,9 @@ extern "C" {
 
 #define MAX_CDTEXT_FIELDS 13
 #define MIN_CDTEXT_FIELD  0
+#define MAX_CDTEXT_DATA_LENGTH 5000
+#define MAX_CDTEXT_GENRE_CODE 28
+
   
   /*! \brief structure for holding CD-Text information
 
@@ -45,6 +48,15 @@ extern "C" {
   struct cdtext {
     char *field[MAX_CDTEXT_FIELDS];
   };
+  typedef struct cdtext cdtext_track_t;
+
+  struct cdtext_s {
+    cdtext_track_t track[100];  /* cdtext for track 1..99. 0 represents cd-text of disc */
+    uint16_t genre_code;        /* genre code */
+    uint8_t  block;
+    char encoding[16];          /* encoding of character strings */
+    char language[3];           /* ISO 639-1 (2 letter) language code */
+  };
   
   /*! \brief A list of all of the CD-Text fields. Because
     the interval has no gaps, we can use ++ to iterate over fields.
@@ -68,12 +80,51 @@ extern "C" {
 
   /*! Return string representation of the enum values above */
   const char *cdtext_field2str (cdtext_field_t i);
+
+  /*! CD-Text genre codes */
+  typedef enum {
+    CDIO_CDTEXT_GENRE_UNUSED         =  0,        /**< not used  */
+    CDIO_CDTEXT_GENRE_UNDEFINED      =  1,        /**< not defined */
+    CDIO_CDTEXT_GENRE_ADULT_CONTEMP  =  2,        /**< Adult Contemporary       */
+    CDIO_CDTEXT_GENRE_ALT_ROCK       =  3,        /**< Alternative Rock         */
+    CDIO_CDTEXT_GENRE_CHILDRENS      =  4,        /**< Childrens Music          */
+    CDIO_CDTEXT_GENRE_CLASSIC        =  5,        /**< Classical                */
+    CDIO_CDTEXT_GENRE_CHRIST_CONTEMP =  6,        /**< Contemporary Christian   */
+    CDIO_CDTEXT_GENRE_COUNTRY        =  7,        /**< Country                  */
+    CDIO_CDTEXT_GENRE_DANCE          =  8,        /**< Dance                    */
+    CDIO_CDTEXT_GENRE_EASY_LISTENING =  9,        /**< Easy Listening           */
+    CDIO_CDTEXT_GENRE_EROTIC         = 10,        /**< Erotic                   */
+    CDIO_CDTEXT_GENRE_FOLK           = 11,        /**< Folk                     */
+    CDIO_CDTEXT_GENRE_GOSPEL         = 12,        /**< Gospel                   */
+    CDIO_CDTEXT_GENRE_HIPHOP         = 13,        /**< Hip Hop                  */
+    CDIO_CDTEXT_GENRE_JAZZ           = 14,        /**< Jazz                     */
+    CDIO_CDTEXT_GENRE_LATIN          = 15,        /**< Latin                    */
+    CDIO_CDTEXT_GENRE_MUSICAL        = 16,        /**< Musical                  */
+    CDIO_CDTEXT_GENRE_NEWAGE         = 17,        /**< New Age                  */
+    CDIO_CDTEXT_GENRE_OPERA          = 18,        /**< Opera                    */
+    CDIO_CDTEXT_GENRE_OPERETTA       = 19,        /**< Operetta                 */
+    CDIO_CDTEXT_GENRE_POP            = 20,        /**< Pop Music                */
+    CDIO_CDTEXT_GENRE_RAP            = 21,        /**< RAP                      */
+    CDIO_CDTEXT_GENRE_REGGAE         = 22,        /**< Reggae                   */
+    CDIO_CDTEXT_GENRE_ROCK           = 23,        /**< Rock Music               */
+    CDIO_CDTEXT_GENRE_RYTHMANDBLUES  = 24,        /**< Rhythm & Blues           */
+    CDIO_CDTEXT_GENRE_SOUNDEFFECTS   = 25,        /**< Sound Effects            */
+    CDIO_CDTEXT_GENRE_SOUNDTRACK     = 26,        /**< Soundtrack               */
+    CDIO_CDTEXT_GENRE_SPOKEN_WORD    = 27,        /**< Spoken Word              */
+    CDIO_CDTEXT_GENRE_WORLD_MUSIC    = 28         /**< World Music              */
+  } cdtext_genre_t;
+  
+  /*! Return string representation of the given genre code */
+  const char *cdtext_genre2str (cdtext_genre_t i);
   
   /*! Initialize a new cdtext structure.
     When the structure is no longer needed, release the 
     resources using cdtext_delete.
   */
   void cdtext_init (cdtext_t *cdtext);
+
+  /*! Parse raw CD-Text data into cdtext structure */ 
+  bool cdtext_data_init(cdtext_t *cdtext, uint8_t *wdata);
   
   /*! Free memory assocated with cdtext*/
   void cdtext_destroy (cdtext_t *cdtext);
@@ -86,7 +137,8 @@ extern "C" {
     @see cdio_get_const to retrieve a constant string that doesn't
     have to be freed.
   */
-  char *cdtext_get (cdtext_field_t key, const cdtext_t *cdtext);
+  char *cdtext_get (cdtext_field_t key, track_t track, const cdtext_t *cdtext);
+
 
   /*! returns a const string associated with the given field.  NULL is
     returned if key is CDTEXT_INVALID or the field is not set.
@@ -97,7 +149,7 @@ extern "C" {
     @see cdio_get to retrieve an allocated string that persists past
     the cdtext object.
   */
-  const char *cdtext_get_const (cdtext_field_t key, const cdtext_t *cdtext);
+  const char *cdtext_get_const (cdtext_field_t key, track_t track, const cdtext_t *cdtext);
   
   /*!
     returns enum of keyword if key is a CD-Text keyword, 
@@ -108,7 +160,7 @@ extern "C" {
   /*! 
     sets cdtext's keyword entry to field 
   */
-  void cdtext_set (cdtext_field_t key, const char *value, cdtext_t *cdtext);
+  void cdtext_set (cdtext_field_t key, track_t track, const char *value, cdtext_t *cdtext);
   
 #ifdef __cplusplus
 }
diff --git a/include/cdio/disc.h b/include/cdio/disc.h
index 82373c6..638c032 100644
--- a/include/cdio/disc.h
+++ b/include/cdio/disc.h
@@ -56,7 +56,25 @@ extern "C" {
   } discmode_t;
 
   extern const char *discmode2str[];
-  
+  /**
+    Get binary CD-Text information for a CdIo_t object.
+
+    @param p_cdio the CD object that may contain CD-Text information.
+    @return malloc'd pointer to raw CD-Text data as stored on the disc 
+    or NULL if p_cdio is NULL or CD-Text information does not exist.
+    Return value should be freed when done with it and not NULL.
+  */
+  uint8_t * cdio_get_cdtext_raw (CdIo_t *p_cdio);
+
+  /** 
+    Get CD-Text information for a CdIo_t object.
+ 
+    @param p_cdio the CD object that may contain CD-Text information.
+    @return the CD-Text object or NULL if p_cdio is NULL
+    or CD-Text information does not exist.
+  */
+  cdtext_t *cdio_get_cdtext (CdIo_t *p_cdio);
+
   /** 
     Get disc mode - the kind of CD (CD-DA, CD-ROM mode 1, CD-MIXED, etc.
     that we've got. The notion of "CD" is extended a little to include
diff --git a/include/cdio/track.h b/include/cdio/track.h
index 9deb942..8eccfdd 100644
--- a/include/cdio/track.h
+++ b/include/cdio/track.h
@@ -98,19 +98,6 @@ extern "C" {
     MODE2_RAW			/**< 2352 byte block length */
   } trackmode_t;
   
-  /*! 
-    Get CD-Text information for a CdIo_t object.
-
-    @param p_cdio the CD object that may contain CD-Text information.
-    @param i_track track for which we are requesting CD-Text information.
-    @return the CD-Text object or NULL if obj is NULL
-    or CD-Text information does not exist.
-
-    If i_track is 0 or CDIO_CDROM_LEADOUT_TRACK the track returned
-    is the information assocated with the CD. 
-  */
-  cdtext_t *cdio_get_cdtext (CdIo_t *p_cdio, track_t i_track);
-
   /*!
     Get the number of the first track. 
 
diff --git a/lib/driver/FreeBSD/freebsd.c b/lib/driver/FreeBSD/freebsd.c
index e6ae2f5..9cb1a2c 100644
--- a/lib/driver/FreeBSD/freebsd.c
+++ b/lib/driver/FreeBSD/freebsd.c
@@ -1111,6 +1111,7 @@ cdio_open_am_freebsd (const char *psz_orig_source_name,
     .get_arg                = get_arg_freebsd,
     .get_blocksize          = get_blocksize_mmc,
     .get_cdtext             = get_cdtext_generic,
+    .get_cdtext_raw         = read_cdtext_generic,
     .get_default_device     = cdio_get_default_device_freebsd,
     .get_devices            = cdio_get_devices_freebsd,
     .get_disc_last_lsn      = get_disc_last_lsn_freebsd,
@@ -1145,7 +1146,6 @@ cdio_open_am_freebsd (const char *psz_orig_source_name,
   _data->gen.init           = false;
   _data->gen.fd             = -1;
   _data->gen.toc_init       = false;
-  _data->gen.b_cdtext_init  = false;
   _data->gen.b_cdtext_error = false;
 
   if (NULL == psz_orig_source_name) {
diff --git a/lib/driver/MSWindows/win32.c b/lib/driver/MSWindows/win32.c
index 6e2cb85..d2c40fb 100644
--- a/lib/driver/MSWindows/win32.c
+++ b/lib/driver/MSWindows/win32.c
@@ -306,7 +306,6 @@ init_win32 (void *p_user_data)
   
   p_env->gen.init           = true;
   p_env->gen.toc_init       = false;
-  p_env->gen.b_cdtext_init  = false;
   p_env->gen.b_cdtext_error = false;
   p_env->gen.fd             = open (p_env->gen.source_name, O_RDONLY, 0);
 
@@ -962,6 +961,7 @@ cdio_open_am_win32 (const char *psz_orig_source, const char *psz_access_mode)
   _funcs.free                   = free_win32;
   _funcs.get_arg                = get_arg_win32;
   _funcs.get_cdtext             = get_cdtext_generic;
+  _funcs.get_cdtext_raw         = read_cdtext_generic;
   _funcs.get_default_device     = cdio_get_default_device_win32;
   _funcs.get_devices            = cdio_get_devices_win32;
   _funcs.get_disc_last_lsn      = get_disc_last_lsn_win32;
diff --git a/lib/driver/_cdio_generic.c b/lib/driver/_cdio_generic.c
index c7e811d..7ef1443 100644
--- a/lib/driver/_cdio_generic.c
+++ b/lib/driver/_cdio_generic.c
@@ -88,15 +88,14 @@ void
 cdio_generic_free (void *p_user_data)
 {
   generic_img_private_t *p_env = p_user_data;
-  track_t i_track;
 
   if (NULL == p_env) return;
   if (p_env->source_name) free (p_env->source_name);
 
-  if (p_env->b_cdtext_init) {
-    for (i_track=0; i_track < p_env->i_tracks; i_track++) {
-      cdtext_destroy(&(p_env->cdtext_track[i_track]));
-    }
+  if (NULL != p_env->cdtext) {
+      cdtext_destroy(p_env->cdtext);
+      free(p_env->cdtext);
+      p_env->cdtext = NULL;
   }
 
   if (p_env->fd >= 0)
@@ -130,7 +129,7 @@ cdio_generic_init (void *user_data, int open_flags)
 
   p_env->init = true;
   p_env->toc_init = false;
-  p_env->b_cdtext_init  = false;
+  p_env->cdtext  = NULL;
   p_env->b_cdtext_error = false;
   p_env->i_joliet_level = 0;  /* Assume no Joliet extensions initally */
   return true;
@@ -262,27 +261,31 @@ cdio_add_device_list(char **device_list[], const char *drive,
   not exist, or we don't know how to get this implemented.
 */
 cdtext_t *
-get_cdtext_generic (void *p_user_data, track_t i_track)
+get_cdtext_generic (void *p_user_data)
 {
   generic_img_private_t *p_env = p_user_data;
+  uint8_t *p_cdtext_data = NULL;
 
   if (!p_env) return NULL;
-  if (!p_env->toc_init) 
-    p_env->cdio->op.read_toc (p_user_data);
 
-  if ( (0 != i_track 
-        && i_track >= p_env->i_tracks+p_env->i_first_track ) )
-    return NULL;
-
-  if (!p_env->b_cdtext_init)
-    init_cdtext_generic(p_env);
-  if (!p_env->b_cdtext_init) return NULL;
+  if (NULL == p_env->cdtext) {
+    p_cdtext_data = read_cdtext_generic (p_env);
+    if (NULL != p_cdtext_data) {
+      p_env->cdtext = malloc (sizeof(cdtext_t));
+      cdtext_init(p_env->cdtext);
+
+      if(!cdtext_data_init (p_env->cdtext, p_cdtext_data)) {
+        p_env->b_cdtext_error = true;
+        cdtext_destroy (p_env->cdtext);
+        free(p_env->cdtext);
+        p_env->cdtext = NULL;
+      }
 
-  if (0 == i_track) 
-    return &(p_env->cdtext);
-  else 
-    return &(p_env->cdtext_track[i_track-p_env->i_first_track]);
+      free(p_cdtext_data);
+    }
+  }
 
+  return p_env->cdtext;
 }
 
 /*! 
@@ -422,36 +425,20 @@ get_num_tracks_generic(void *p_user_data)
   return p_env->toc_init ? p_env->i_tracks : CDIO_INVALID_TRACK;
 }
 
-void
-set_cdtext_field_generic(void *p_user_data, track_t i_track, 
-                       track_t i_first_track,
-                       cdtext_field_t e_field, const char *psz_value)
-{
-  char **pp_field;
-  generic_img_private_t *p_env = p_user_data;
-  
-  if( i_track == 0 )
-    pp_field = &(p_env->cdtext.field[e_field]);
-  
-  else
-    pp_field = &(p_env->cdtext_track[i_track-i_first_track].field[e_field]);
-
-  if (*pp_field) free(*pp_field);
-  *pp_field = (psz_value) ? strdup(psz_value) : NULL;
-}
 
 /*!
   Read CD-Text information for a CdIo_t object .
   
-  return true on success, false on error or CD-Text information does
-  not exist.
+  return pointer to raw cdtext on success,
+  NULL on failure
+  free when done and not NULL
 */
-bool
-init_cdtext_generic (generic_img_private_t *p_env)
+uint8_t *
+read_cdtext_generic(void *p_env)
 {
-  return mmc_init_cdtext_private( p_env,
-                                  p_env->cdio->op.run_mmc_cmd, 
-                                  set_cdtext_field_generic
+  generic_img_private_t *p_user_data = p_env;
+  return mmc_read_cdtext_private (p_user_data,
+                                  p_user_data->cdio->op.run_mmc_cmd
                                   );
 }
 
diff --git a/lib/driver/aix.c b/lib/driver/aix.c
index 774ae0e..4f4ec2d 100644
--- a/lib/driver/aix.c
+++ b/lib/driver/aix.c
@@ -110,7 +110,6 @@ typedef struct {
   lsn_t          start_lsn;
   uchar          Control : 4;
   uchar          Format;
-  cdtext_t       cdtext;	         /* CD-TEXT */
 } track_info_t;
 
 typedef struct {
@@ -172,8 +171,6 @@ init_aix (_img_private_t *p_env)
 
   p_env->gen.init = true;
   p_env->gen.toc_init = false;
-  p_env->gen.b_cdtext_init  = false;
-  p_env->gen.b_cdtext_error = false;
   p_env->gen.i_joliet_level = 0;  /* Assume no Joliet extensions initally */
   p_env->access_mode = _AM_CTRL_SCSI;    
 
@@ -946,6 +943,7 @@ cdio_open_am_aix (const char *psz_orig_source, const char *access_mode)
   _funcs.free               = cdio_generic_free;
   _funcs.get_arg            = get_arg_aix;
   _funcs.get_cdtext         = get_cdtext_generic;
+  _funcs.get_cdtext_raw     = read_cdtext_generic;
   _funcs.get_default_device = cdio_get_default_device_aix;
   _funcs.get_devices        = cdio_get_devices_aix;
   _funcs.get_disc_last_lsn  = get_disc_last_lsn_aix;
@@ -973,11 +971,10 @@ cdio_open_am_aix (const char *psz_orig_source, const char *access_mode)
   _data                 = calloc (1, sizeof (_img_private_t));
 
   _data->access_mode    = _AM_CTRL_SCSI;
+  _data->gen.b_cdtext_error = false;
   _data->gen.init       = false;
   _data->gen.fd         = -1;
   _data->gen.toc_init   = false;
-  _data->gen.b_cdtext_init  = false;
-  _data->gen.b_cdtext_error = false;
 
   if (NULL == psz_orig_source) {
     psz_source = cdio_get_default_device_aix();
diff --git a/lib/driver/bsdi.c b/lib/driver/bsdi.c
index d5424eb..4c0a6cc 100644
--- a/lib/driver/bsdi.c
+++ b/lib/driver/bsdi.c
@@ -995,6 +995,7 @@ cdio_open_bsdi (const char *psz_orig_source)
     .free                  = cdio_generic_free,
     .get_arg               = _get_arg_bsdi,
     .get_cdtext            = get_cdtext_generic,
+    .get_cdtext_raw        = read_cdtext_generic,
     .get_default_device    = cdio_get_default_device_bsdi,
     .get_devices           = cdio_get_devices_bsdi,
     .get_drive_cap         = get_drive_cap_mmc,
@@ -1027,11 +1028,10 @@ cdio_open_bsdi (const char *psz_orig_source)
 
   _data                     = calloc (1, sizeof (_img_private_t));
   _data->access_mode        = _AM_IOCTL;
+  _data->gen.b_cdtext_error = false;
   _data->gen.init           = false;
   _data->gen.fd             = -1;
   _data->gen.toc_init       = false;
-  _data->gen.b_cdtext_init  = false;
-  _data->gen.b_cdtext_error = false;
 
   if (NULL == psz_orig_source) {
     psz_source=cdio_get_default_device_bsdi();
diff --git a/lib/driver/cdio.c b/lib/driver/cdio.c
index 02a9364..03d1c41 100644
--- a/lib/driver/cdio.c
+++ b/lib/driver/cdio.c
@@ -53,25 +53,6 @@ cdio_get_arg (const CdIo *obj, const char key[])
   }
 }
 
-/*! 
-  Get cdtext information for a CdIo object .
-  
-  @param obj the CD object that may contain CD-TEXT information.
-  @return the CD-TEXT object or NULL if obj is NULL
-  or CD-TEXT information does not exist.
-*/
-cdtext_t *
-cdio_get_cdtext (CdIo *obj, track_t i_track)
-{
-  if (obj == NULL) return NULL;
-  
-  if (obj->op.get_cdtext) {
-    return obj->op.get_cdtext (obj->env, i_track);
-  } else {
-    return NULL;
-  }
-}
-
 CdIo_t *
 cdio_new (generic_img_private_t *p_env, cdio_funcs_t *p_funcs)
 {
diff --git a/lib/driver/cdio_private.h b/lib/driver/cdio_private.h
index 2b390dc..e75d873 100644
--- a/lib/driver/cdio_private.h
+++ b/lib/driver/cdio_private.h
@@ -150,11 +150,19 @@ extern "C" {
       @param obj the CD object that may contain CD-TEXT information.
       @return the CD-TEXT object or NULL if obj is NULL
       or CD-TEXT information does not exist.
-    
-      If i_track is 0 or CDIO_CDROM_LEADOUT_TRACK the track returned
-      is the information assocated with the CD. 
     */
-    cdtext_t * (*get_cdtext) ( void *p_env, track_t i_track );
+    cdtext_t * (*get_cdtext) ( void *p_env );
+
+    /*!
+      Get raw cdtext information as on the disc for a CdIo object
+
+      @param obj the CD object that may contain CD-TEXT information.
+      @return pointer to the raw CD-TEXT data or NULL if obj is NULL
+      or no CD-TEXT information present on the disc.
+
+      free when done and not NULL.
+    */
+    uint8_t * (*get_cdtext_raw) ( void *p_env );
     
     /*!
       Return an array of device names. if CdIo is NULL (we haven't
diff --git a/lib/driver/cdtext.c b/lib/driver/cdtext.c
index d1b389e..f8e9a55 100644
--- a/lib/driver/cdtext.c
+++ b/lib/driver/cdtext.c
@@ -37,26 +37,26 @@
 #include <strings.h>
 #endif
 
-#include <stdio.h>
+#define _CDTEXT_DBCC
 
 /*! Note: the order and number items (except CDTEXT_INVALID) should
   match the cdtext_field_t enumeration. */
 static const char cdtext_keywords[][16] = 
-  {
-    "ARRANGER",
-    "COMPOSER",
-    "DISC_ID",
-    "GENRE",
-    "MESSAGE",
-    "ISRC",
-    "PERFORMER",
-    "SIZE_INFO",
-    "SONGWRITER",
-    "TITLE",
-    "TOC_INFO",
-    "TOC_INFO2",
-    "UPC_EAN",
-  };
+{
+  "ARRANGER",
+  "COMPOSER",
+  "DISC_ID",
+  "GENRE",
+  "MESSAGE",
+  "ISRC",
+  "PERFORMER",
+  "SIZE_INFO",
+  "SONGWRITER",
+  "TITLE",
+  "TOC_INFO",
+  "TOC_INFO2",
+  "UPC_EAN",
+};
 
 static const char cdtext_genre[][30] =
 {
@@ -88,7 +88,7 @@ static const char cdtext_genre[][30] =
   "Sound Effects",
   "Spoken Word",
   "World Music"
-} ;
+};
 
 /*! Return string representation of the enum values above */
 const char *
@@ -100,19 +100,31 @@ cdtext_field2str (cdtext_field_t i)
     return cdtext_keywords[i];
 }
 
+/*! Return string representation of the given genre oode */
+const char *
+cdtext_genre2str (cdtext_genre_t i)
+{
+  if (i >= MAX_CDTEXT_GENRE_CODE)
+    return "Invalid genre code";
+  else
+    return cdtext_genre[i];
+}
+
 /*! Free memory assocated with cdtext*/
 void 
 cdtext_destroy (cdtext_t *p_cdtext)
 {
   cdtext_field_t i;
+  track_t i2;
 
   if (!p_cdtext) return; 
-  for (i=0; i < MAX_CDTEXT_FIELDS; i++) {
-    if (p_cdtext->field[i]) {
-      free(p_cdtext->field[i]);
-      p_cdtext->field[i] = NULL;
+  for (i2=0; i2<=99; i2++) {
+    for (i=0; i < MAX_CDTEXT_FIELDS; i++) {
+      if (p_cdtext->track[i2].field[i]) {
+        free(p_cdtext->track[i2].field[i]);
+        p_cdtext->track[i2].field[i] = NULL;
+      }
     }
-    
   }
 }
 
@@ -121,18 +133,25 @@ cdtext_destroy (cdtext_t *p_cdtext)
   if key is CDTEXT_INVALID or the field is not set.
  */
 char *
-cdtext_get (cdtext_field_t key, const cdtext_t *p_cdtext)
+cdtext_get (cdtext_field_t key, track_t track, const cdtext_t *p_cdtext)
 {
-  if ((key == CDTEXT_INVALID) || !p_cdtext 
-      || (!p_cdtext->field[key])) return NULL;
-  return strdup(p_cdtext->field[key]);
+  const char *ret = cdtext_get_const(key, track, p_cdtext);
+  if (NULL == ret)
+    return NULL;
+  else
+    return strdup(ret);
 }
 
 const char *
-cdtext_get_const (cdtext_field_t key, const cdtext_t *p_cdtext)
+cdtext_get_const (cdtext_field_t key, track_t track, const cdtext_t *p_cdtext)
 {
-  if (key == CDTEXT_INVALID) return NULL;
-  return p_cdtext->field[key];
+  if (CDTEXT_INVALID == key
+      || NULL == p_cdtext
+      || 0 > track
+      || 99 < track)
+    return NULL;
+
+  return p_cdtext->track[track].field[key];
 }
 
 
@@ -144,28 +163,26 @@ void
 cdtext_init (cdtext_t *p_cdtext)
 {
   cdtext_field_t i;
+  track_t i2;
 
-  for (i=0; i < MAX_CDTEXT_FIELDS; i++) {
-    p_cdtext->field[i] = NULL;
+  for (i2=0; i2<=99; i2++) {
+    for (i=0; i < MAX_CDTEXT_FIELDS; i++) {
+      p_cdtext->track[i2].field[i] = NULL;
+    }
   }
+
+  p_cdtext->genre_code = CDIO_CDTEXT_GENRE_UNUSED;
+  p_cdtext->language[0] = '\0';
+  p_cdtext->encoding[0] = '\0';
+  p_cdtext->block       = 0;
 }
 
 /*!
-  returns 0 if field is a CD-TEXT keyword, returns non-zero otherwise 
+  returns associated cdtext_field_t if field is a CD-TEXT keyword, returns non-zero otherwise 
 */
 cdtext_field_t
 cdtext_is_keyword (const char *key)
 {
-#if 0  
-  char *item;
-  
-  item = bsearch(key, 
-                 cdtext_keywords, 12,
-                 sizeof (char *), 
-                 (int (*)(const void *, const void *))
-                 strcmp);
-  return (NULL != item) ? 0 : 1;
-#else 
   unsigned int i;
   
   for (i = 0; i < 13 ; i++)
@@ -173,31 +190,29 @@ cdtext_is_keyword (const char *key)
       return i;
     }
   return CDTEXT_INVALID;
-#endif
 }
 
 /*! sets cdtext's keyword entry to field.
  */
 void 
-cdtext_set (cdtext_field_t key, const char *p_value, cdtext_t *p_cdtext)
+cdtext_set (cdtext_field_t key, track_t track, const char *p_value, cdtext_t *p_cdtext)
 {
-  if (NULL == p_value || key == CDTEXT_INVALID) return;
-  
-  if (p_cdtext->field[key]) free (p_cdtext->field[key]);
-  p_cdtext->field[key] = strdup (p_value);
+  if (NULL == p_value || key == CDTEXT_INVALID || 0 > track || 99 < track) return;
+
+  if (p_cdtext->track[track].field[key]) 
+    free (p_cdtext->track[track].field[key]);
+  p_cdtext->track[track].field[key] = strdup (p_value);
   
 }
 
 #define SET_CDTEXT_FIELD(FIELD) \
-  (*set_cdtext_field_fn)(p_user_data, i_track, i_first_track, FIELD, buffer);
+  cdtext_set(FIELD, i_track, buffer, p_cdtext);
 
 /* 
   parse all CD-TEXT data retrieved.
 */       
 bool
-cdtext_data_init(void *p_user_data, track_t i_first_track, 
-                 unsigned char *wdata, int i_data,
-                 set_cdtext_field_fn_t set_cdtext_field_fn) 
+cdtext_data_init(cdtext_t *p_cdtext, uint8_t *wdata) 
 {
   CDText_data_t *p_data;
   int           i = -1;
@@ -206,14 +221,13 @@ cdtext_data_init(void *p_user_data, track_t i_first_track,
   int           idx;
   int           i_track;
   bool          b_ret = false;
-  char          block = 0;
-  char    encoding[16];
+  int           block = 0;
+  uint16_t      i_data = CDTEXT_GET_LEN16(wdata) - 2;
   CDText_blocksize_t p_blocksize;
   
   memset( buffer, 0x00, sizeof(buffer) );
   idx = 0;
   
-  bzero(encoding,16);
   bzero(&p_blocksize, sizeof(CDText_blocksize_t));
 
   p_data = (CDText_data_t *) (&wdata[4]);
@@ -221,26 +235,40 @@ cdtext_data_init(void *p_user_data, track_t i_first_track,
   for( ; i_data > 0; 
        i_data -= sizeof(CDText_data_t), p_data++ ) {
     
-#if TESTED
+#ifndef _CDTEXT_DBCC
     if ( p_data->bDBC ) {
       cdio_warn("Double-byte characters not supported");
       return false;
     }
 #endif
-    
+
     if ( p_data->seq != ++i || p_data->block != block ) break;
 
     /* only handle character packs */
-    if ( ((p_data->type >= 0x80) && (p_data->type <= 0x86)) || 
-        (p_data->type == 0x8E)) {
-
+    if ( ((p_data->type >= 0x80) && (p_data->type <= 0x87)) || 
+        (p_data->type == 0x8E)) 
+    {
       i_track = p_data->i_track;
 
-      for( j=0; j < CDIO_CDTEXT_MAX_TEXT_DATA; (p_data->bDBC ? j+=2 : j++) ) {
+      j = 0;
+      if (CDIO_CDTEXT_GENRE == p_data->type) {
+        j = 2;
+        if (CDIO_CDTEXT_GENRE_UNUSED == p_cdtext->genre_code) {
+          p_cdtext->genre_code = CDTEXT_GET_LEN16(p_data->text);
+        }
+      }
+
+#ifdef _CDTEXT_DBCC
+      for( ; j < CDIO_CDTEXT_MAX_TEXT_DATA; (p_data->bDBC ? j+=2 : j++) ) {
         if( p_data->text[j] == 0x00 && (!p_data->bDBC || p_data->text[j+1] == 0x00)) {
+          if((buffer[0] != 0x00) && (!p_data->bDBC || buffer[1] != 0x00)) {
+#else
+      for( ; j < CDIO_CDTEXT_MAX_TEXT_DATA; j++) {
+        if( p_data->text[j] == 0x00) {
+          if(buffer[0] != 0x00) {
+#endif
           
           /* omit empty strings */
-          if((buffer[0] != 0x00) && (!p_data->bDBC || buffer[1] != 0x00)) {
             switch( p_data->type) {
             case CDIO_CDTEXT_TITLE: 
               SET_CDTEXT_FIELD(CDTEXT_TITLE);
@@ -273,6 +301,9 @@ cdtext_data_init(void *p_user_data, track_t i_first_track,
                 SET_CDTEXT_FIELD(CDTEXT_ISRC);
               }
               break;
+            case CDIO_CDTEXT_GENRE:
+              SET_CDTEXT_FIELD(CDTEXT_GENRE);
+              break;
             }
             
             b_ret = true;
@@ -281,82 +312,125 @@ cdtext_data_init(void *p_user_data, track_t i_first_track,
           }
         } else {
           buffer[idx++] = p_data->text[j];
+#ifdef _CDTEXT_DBCC
           if(p_data->bDBC)
              buffer[idx++] = p_data->text[j+1];
+#endif
         }
 
         buffer[idx] = 0x00;
+#ifdef _CDTEXT_DBCC
         if(p_data->bDBC)
            buffer[idx+1] = 0x00;
+#endif
       }
     } else {
       /* not a character pack */
-      if (p_data->type == CDIO_CDTEXT_GENRE) {
-        i_track = p_data->i_track;
-        /* seems like it is a uint_16 in the first 2 bytes */
-        if((p_data->text[0] << 8) + p_data->text[1] != CDIO_CDTEXT_GENRE_UNUSED) {
-          sprintf(buffer,"%s",cdtext_genre[(p_data->text[0] << 8) + p_data->text[1]]);
-          SET_CDTEXT_FIELD(CDTEXT_GENRE);
-    }
-#ifdef _DEBUG_CDTEXT
-        printf("GENRE information present: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
-	       p_data->text[0],p_data->text[1],p_data->text[2],p_data->text[3],
-	       p_data->text[4],p_data->text[5],p_data->text[6],p_data->text[7], 
-	       p_data->text[8],p_data->text[9],p_data->text[10],p_data->text[11]);
-#endif
-      }
 
       if(p_data->type == CDIO_CDTEXT_TOC) {
-#ifdef _DEBUG_CDTEXT
-        printf("TOC information present: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
-          p_data->text[0],p_data->text[1],p_data->text[2],p_data->text[3],
-          p_data->text[4],p_data->text[5],p_data->text[6],p_data->text[7], 
-          p_data->text[8],p_data->text[9],p_data->text[10],p_data->text[11]);
-#endif
+        /* no idea what this is */
       }
 
       if (p_data->type == CDIO_CDTEXT_TOC2) {
-#ifdef _DEBUG_CDTEXT
-        printf("TOC2 information present: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
-          p_data->text[0],p_data->text[1],p_data->text[2],p_data->text[3],
-          p_data->text[4],p_data->text[5],p_data->text[6],p_data->text[7], 
-          p_data->text[8],p_data->text[9],p_data->text[10],p_data->text[11]);
-#endif
+        /* no idea what this is */
       }
 
       /* i got this info from cdrtools' cdda2wav; all the tests i ran so far were successful */
       if (p_data->type == CDIO_CDTEXT_BLOCKSIZE) {
-	/* i_track is the pack element number in this case */
+	      /* i_track is the pack element number in this case */
         switch(p_data->i_track){
-	case 0:
-	  memcpy((char*)&p_blocksize,p_data->text,0x0c);
-	  break;
-	case 1:
-	  memcpy(((char*)&p_blocksize)+0x0c,p_data->text,0x0c);
-	  break;
-	case 2:
-	  memcpy(((char*)&p_blocksize)+0x18,p_data->text,0x0c);
-	  break;
+	        case 0:
+	          memcpy((char*)&p_blocksize,p_data->text,0x0c);
+	          break;
+	        case 1:
+	          memcpy(((char*)&p_blocksize)+0x0c,p_data->text,0x0c);
+	          break;
+	        case 2:
+	          memcpy(((char*)&p_blocksize)+0x18,p_data->text,0x0c);
+	          break;
         }
       }
     }
   }
   
-  if (p_blocksize.packcount[15] >= 3) {
-    /* BLOCKSIZE packs present */
+  if (p_blocksize.i_packs[15] >= 3) {
+    /* if there were more than 3 BLOCKSIZE packs */
     switch (p_blocksize.charcode){
-    case CDIO_CDTEXT_CHARCODE_ISO_8859_1:
-      sprintf(encoding,"ISO-8859-1");
-      break;
-    case CDIO_CDTEXT_CHARCODE_ASCII:
-      sprintf(encoding,"ASCII");
-      break;
-    case CDIO_CDTEXT_CHARCODE_KANJI:
-      sprintf(encoding,"Shift-JIS");
-      break;
+      case CDIO_CDTEXT_CHARCODE_ISO_8859_1:
+        strcpy(p_cdtext->encoding, "ISO-8859-1");
+        break;
+      case CDIO_CDTEXT_CHARCODE_ASCII:
+        strcpy(p_cdtext->encoding, "ASCII");
+        break;
+      case CDIO_CDTEXT_CHARCODE_KANJI:
+        strcpy(p_cdtext->encoding, "Shift-JIS");
+        break;
+    }
+ 
+    /* set ISO 639-1 code */
+    switch (p_blocksize.langcode[block]) {
+      case CDIO_CDTEXT_LANG_CZECH        :
+        strcpy(p_cdtext->language, "cs");
+        break;
+      case CDIO_CDTEXT_LANG_DANISH       :
+        strcpy(p_cdtext->language, "da");
+        break;
+      case CDIO_CDTEXT_LANG_GERMAN       :
+        strcpy(p_cdtext->language, "de");
+        break;
+      case CDIO_CDTEXT_LANG_ENGLISH      :
+        strcpy(p_cdtext->language, "en");
+        break;
+      case CDIO_CDTEXT_LANG_SPANISH      :
+        strcpy(p_cdtext->language, "es");
+        break;
+      case CDIO_CDTEXT_LANG_FRENCH       :
+        strcpy(p_cdtext->language, "fr");
+        break;
+      case CDIO_CDTEXT_LANG_ITALIAN      :
+        strcpy(p_cdtext->language, "it");
+        break;
+      case CDIO_CDTEXT_LANG_HUNGARIAN    :
+        strcpy(p_cdtext->language, "hu");
+        break;
+      case CDIO_CDTEXT_LANG_DUTCH        :
+        strcpy(p_cdtext->language, "nl");
+        break;
+      case CDIO_CDTEXT_LANG_NORWEGIAN    :
+        strcpy(p_cdtext->language, "no");
+        break;
+      case CDIO_CDTEXT_LANG_POLISH       :
+        strcpy(p_cdtext->language, "pl");
+        break;
+      case CDIO_CDTEXT_LANG_PORTUGUESE   :
+        strcpy(p_cdtext->language, "pt");
+        break;
+      case CDIO_CDTEXT_LANG_SLOVENE      :
+        strcpy(p_cdtext->language, "sl");
+        break;
+      case CDIO_CDTEXT_LANG_FINNISH      :
+        strcpy(p_cdtext->language, "fi");
+        break;
+      case CDIO_CDTEXT_LANG_SWEDISH      :
+        strcpy(p_cdtext->language, "sv");
+        break;
+      case CDIO_CDTEXT_LANG_RUSSIAN      :
+        strcpy(p_cdtext->language, "ru");
+        break;
+      case CDIO_CDTEXT_LANG_KOREAN       :
+        strcpy(p_cdtext->language, "ko");
+        break;
+      case CDIO_CDTEXT_LANG_JAPANESE     :
+        strcpy(p_cdtext->language, "ja");
+        break;
+      case CDIO_CDTEXT_LANG_GREEK        :
+        strcpy(p_cdtext->language, "el");
+        break;
+      case CDIO_CDTEXT_LANG_CHINESE      :
+        strcpy(p_cdtext->language, "zh");
+        break;
     }
     
   }
-  
   return b_ret;
 }
diff --git a/lib/driver/cdtext_private.h b/lib/driver/cdtext_private.h
index 02f338e..739373d 100644
--- a/lib/driver/cdtext_private.h
+++ b/lib/driver/cdtext_private.h
@@ -21,6 +21,8 @@
 #include <cdio/cdio.h>
 #include <cdio/cdtext.h>
 
+#define CDTEXT_GET_LEN16(p) (p[0]<<8) + p[1]
+
 /*! An enumeration for some of the CDIO_CDTEXT_* #defines below. This isn't
     really an enumeration one would really use in a program it is here
     to be helpful in debuggers where wants just to refer to the
@@ -45,41 +47,6 @@ extern const enum cdtext_enum1_s {
 
 
 /*!
- * CD-Text Genre Codes
- */
-extern const enum cdtext_genre_enum_s {
-  CDIO_CDTEXT_GENRE_UNUSED         =  0,        /**< not used  */
-  CDIO_CDTEXT_GENRE_UNDEFINED      =  1,        /**< not defined */
-  CDIO_CDTEXT_GENRE_ADULT_CONTEMP  =  2,        /**< Adult Contemporary       */
-  CDIO_CDTEXT_GENRE_ALT_ROCK       =  3,        /**< Alternative Rock         */
-  CDIO_CDTEXT_GENRE_CHILDRENS      =  4,        /**< Childrens Music          */
-  CDIO_CDTEXT_GENRE_CLASSIC        =  5,        /**< Classical                */
-  CDIO_CDTEXT_GENRE_CHRIST_CONTEMP =  6,        /**< Contemporary Christian   */
-  CDIO_CDTEXT_GENRE_COUNTRY        =  7,        /**< Country                  */
-  CDIO_CDTEXT_GENRE_DANCE          =  8,        /**< Dance                    */
-  CDIO_CDTEXT_GENRE_EASY_LISTENING =  9,        /**< Easy Listening           */
-  CDIO_CDTEXT_GENRE_EROTIC         = 10,        /**< Erotic                   */
-  CDIO_CDTEXT_GENRE_FOLK           = 11,        /**< Folk                     */
-  CDIO_CDTEXT_GENRE_GOSPEL         = 12,        /**< Gospel                   */
-  CDIO_CDTEXT_GENRE_HIPHOP         = 13,        /**< Hip Hop                  */
-  CDIO_CDTEXT_GENRE_JAZZ           = 14,        /**< Jazz                     */
-  CDIO_CDTEXT_GENRE_LATIN          = 15,        /**< Latin                    */
-  CDIO_CDTEXT_GENRE_MUSICAL        = 16,        /**< Musical                  */
-  CDIO_CDTEXT_GENRE_NEWAGE         = 17,        /**< New Age                  */
-  CDIO_CDTEXT_GENRE_OPERA          = 18,        /**< Opera                    */
-  CDIO_CDTEXT_GENRE_OPERETTA       = 19,        /**< Operetta                 */
-  CDIO_CDTEXT_GENRE_POP            = 20,        /**< Pop Music                */
-  CDIO_CDTEXT_GENRE_RAP            = 21,        /**< RAP                      */
-  CDIO_CDTEXT_GENRE_REGGAE         = 22,        /**< Reggae                   */
-  CDIO_CDTEXT_GENRE_ROCK           = 23,        /**< Rock Music               */
-  CDIO_CDTEXT_GENRE_RYTHMANDBLUES  = 24,        /**< Rhythm & Blues           */
-  CDIO_CDTEXT_GENRE_SOUNDEFFECTS   = 25,        /**< Sound Effects            */
-  CDIO_CDTEXT_GENRE_SOUNDTRACK     = 26,        /**< Soundtrack               */
-  CDIO_CDTEXT_GENRE_SPOKEN_WORD    = 27,        /**< Spoken Word              */
-  CDIO_CDTEXT_GENRE_WORLD_MUSIC    = 28         /**< World Music              */
-} cdtext_genre_enum;
-
-/*!
  * CD-Text character codes 
  */
 extern const enum cdtext_charcode_enum_s {
@@ -192,35 +159,29 @@ PRAGMA_END_PACKED
 /*
  * content of BLOCKSIZE packs
  */
-typedef struct CDText_blocksize
+ 
+PRAGMA_BEGIN_PACKED
+
+struct CDText_blocksize
 {
   uint8_t charcode;     /* character code */
-  uint8_t i_firstTrack; /* first track number */
-  uint8_t i_lastTrack;  /* last track number */
+  uint8_t i_first_track; /* first track number */
+  uint8_t i_last_track;  /* last track number */
   uint8_t copyright;    /* cd-text information copyright byte */
-  uint8_t packcount[16];/* number of packs of each type 
+  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 */
-} CDText_blocksize_t;
-
+} GNUC_PACKED;
 
-typedef struct CDText_data CDText_data_t;
+PRAGMA_END_PACKED;
 
-typedef void (*set_cdtext_field_fn_t) (void *user_data, track_t i_track,
-                                       track_t i_first_track,
-                                       cdtext_field_t field, 
-                                       const char *buffer);
 
-/* 
-   Internal routine to parse all CD-TEXT data retrieved.
-*/       
-bool cdtext_data_init(void *user_data, track_t i_first_track, 
-                      unsigned char *wdata, int i_data,
-                      set_cdtext_field_fn_t set_cdtext_field_fn);
+typedef struct CDText_blocksize CDText_blocksize_t;
+typedef struct CDText_data CDText_data_t;
 
 
 #endif /* __CDIO_CDTEXT_PRIVATE_H__ */
diff --git a/lib/driver/disc.c b/lib/driver/disc.c
index fc2934f..3c66ea9 100644
--- a/lib/driver/disc.c
+++ b/lib/driver/disc.c
@@ -49,6 +49,45 @@ const char *discmode2str[] = {
   "CD-i" 
 };
 
+/*! 
+  Get cdtext information for a CdIo object .
+  
+  @param obj the CD object that may contain CD-TEXT information.
+  @return the CD-TEXT object or NULL if obj is NULL
+  or CD-TEXT information does not exist.
+*/
+cdtext_t *
+cdio_get_cdtext (CdIo *obj)
+{
+  if (obj == NULL) return NULL;
+  
+  if (NULL != obj->op.get_cdtext) {
+    return obj->op.get_cdtext (obj->env);
+  } else {
+    return NULL;
+  }
+}
+
+/*! 
+  Get binary cdtext information for a CdIo object .
+  
+  @param obj the CD object that may contain CD-TEXT information.
+  @return pointer to allocated memory area holding the raw CD-TEXT
+  or NULL if obj is NULL or CD-TEXT does not exist.
+  free() when done.
+*/
+uint8_t *
+cdio_get_cdtext_raw (CdIo *obj)
+{
+  if (obj == NULL) return NULL;
+
+  if (NULL != obj->op.get_cdtext_raw) {
+    return obj->op.get_cdtext_raw (obj->env);
+  } else {
+    return NULL;
+  }
+}
+
 /*!
   Get the size of the CD in logical block address (LBA) units.
   
diff --git a/lib/driver/generic.h b/lib/driver/generic.h
index 182cbd4..e0dd261 100644
--- a/lib/driver/generic.h
+++ b/lib/driver/generic.h
@@ -43,7 +43,6 @@ extern "C" {
     char *source_name;      /**< Name used in open. */
     bool  init;             /**< True if structure has been initialized */
     bool  toc_init;         /**< True if TOC read in */
-    bool  b_cdtext_init;    /**< True if CD-Text read in */
     bool  b_cdtext_error;   /**< True if trouble reading CD-Text */
     
     int   ioctls_debugged;  /**< for debugging */
@@ -62,8 +61,7 @@ extern "C" {
     iso9660_pvd_t pvd;      
     iso9660_svd_t svd;      
     CdIo_t   *cdio;         /**< a way to call general cdio routines. */
-    cdtext_t  cdtext;       /**< CD-Text for disc. */
-    cdtext_t  cdtext_track[CDIO_CD_MAX_TRACKS+1]; /**< CD-TEXT for each track*/
+    cdtext_t *cdtext;       /**< CD-Text for disc. */
     track_flags_t track_flags[CDIO_CD_MAX_TRACKS+1];
 
     /* Memorized sense reply of the most recent SCSI command.
@@ -177,7 +175,7 @@ extern "C" {
     @return the CD-TEXT object or NULL if obj is NULL
     or CD-TEXT information does not exist.
   */
-  cdtext_t *get_cdtext_generic (void *p_user_data, track_t i_track);
+  cdtext_t *get_cdtext_generic (void *p_user_data);
 
   /*!
     Return the number of of the first track. 
@@ -220,16 +218,13 @@ extern "C" {
   track_flag_t get_track_preemphasis_generic(const void *p_user_data, 
 					     track_t i_track);
   
-  void set_cdtext_field_generic(void *user_data, track_t i_track, 
-				track_t i_first_track,
-				cdtext_field_t e_field, const char *psz_value);
   /*!
     Read cdtext information for a CdIo object .
   
     return true on success, false on error or CD-Text information does
     not exist.
   */
-  bool init_cdtext_generic (generic_img_private_t *p_env);
+  uint8_t * read_cdtext_generic (void *p_env);
   
   void set_track_flags(track_flags_t *p_track_flag, uint8_t flag);
   
diff --git a/lib/driver/gnu_linux.c b/lib/driver/gnu_linux.c
index 60f44fa..d8b8853 100644
--- a/lib/driver/gnu_linux.c
+++ b/lib/driver/gnu_linux.c
@@ -1685,6 +1685,7 @@ cdio_open_am_linux (const char *psz_orig_source, const char *access_mode)
     .get_arg               = get_arg_linux,
     .get_blocksize         = get_blocksize_mmc,
     .get_cdtext            = get_cdtext_generic,
+    .get_cdtext_raw        = read_cdtext_generic,
     .get_default_device    = cdio_get_default_device_linux,
     .get_devices           = cdio_get_devices_linux,
     .get_disc_last_lsn     = get_disc_last_lsn_linux,
@@ -1736,7 +1737,6 @@ cdio_open_am_linux (const char *psz_orig_source, const char *access_mode)
   _data->gen.init       = false;
   _data->gen.toc_init   = false;
   _data->gen.fd         = -1;
-  _data->gen.b_cdtext_init  = false;
   _data->gen.b_cdtext_error = false;
 
   if (NULL == psz_orig_source) {
diff --git a/lib/driver/image.h b/lib/driver/image.h
index 0a88f55..91c1eb2 100644
--- a/lib/driver/image.h
+++ b/lib/driver/image.h
@@ -63,7 +63,6 @@ typedef struct {
 				*/
   track_format_t track_format;
   bool           track_green;
-  cdtext_t       cdtext;	/**< CD-TEXT */
 
   trackmode_t    mode;
   uint16_t       datasize;      /**< How much is in the portion we return 
diff --git a/lib/driver/image/bincue.c b/lib/driver/image/bincue.c
index 2ddd85d..d465c48 100644
--- a/lib/driver/image/bincue.c
+++ b/lib/driver/image/bincue.c
@@ -87,8 +87,6 @@ _init_bincue (_img_private_t *p_env)
   p_env->psz_mcn       = NULL;
   p_env->disc_mode     = CDIO_DISC_MODE_NO_INFO;
 
-  cdtext_init (&(p_env->gen.cdtext));
-
   lead_lsn = get_disc_last_lsn_bincue( (_img_private_t *) p_env);
 
   if (-1 == lead_lsn) return false;
@@ -268,8 +266,6 @@ parse_cuefile (_img_private_t *cd, const char *psz_cue_name)
   if (cd) {
     cd->gen.i_tracks=0;
     cd->gen.i_first_track=1;
-    cd->gen.b_cdtext_init  = true;
-    cd->gen.b_cdtext_error = false;
     cd->psz_mcn=NULL;
   }
   
@@ -326,6 +322,52 @@ parse_cuefile (_img_private_t *cd, const char *psz_cue_name)
 	} else {
 	  goto not_in_global_section;
 	}
+
+  /* CDTEXTFILE "<filename>" */
+  } else if (0 == strcmp ("CDTEXTFILE", psz_keyword)) {
+    if(NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) {
+      if (cd) {
+        uint8_t cdt_data[MAX_CDTEXT_DATA_LENGTH+4];
+        int size;
+        int i;
+        CdioDataSource_t *source;
+
+        if(NULL == (source = cdio_stdio_new(psz_field))) {
+          cdio_log (log_level, "%s line %d: can't open file `%s' for reading", psz_cue_name, i_line, psz_field);
+          goto err_exit;
+        }
+        size = cdio_stream_read(source, cdt_data, MAX_CDTEXT_DATA_LENGTH, 1);
+
+        if (size < 4) {
+          cdio_log (log_level, "%s line %d: file `%s' is too small to contain CD-TEXT", psz_cue_name, i_line, psz_field);
+          goto err_exit;
+        }
+
+        /* prepend data length if not yet present */
+        if (cdt_data[0] >= 0x80) {
+          for(i=size-1; i >=0; i--)
+              cdt_data[i+4] = cdt_data[i];
+
+          cdt_data[0] = (size+2) << 8;
+          cdt_data[1] = (size+2) & 0xff;
+          cdt_data[2] = 0x00;
+          cdt_data[3] = 0x00;
+        }
+
+        /* init cdtext */
+        if (NULL == cd->gen.cdtext) {
+          cd->gen.cdtext = malloc(sizeof(cdtext_t));
+          cdtext_init (cd->gen.cdtext);
+        }
+
+        if(!cdtext_data_init(cd->gen.cdtext, cdt_data))
+          cdio_log (log_level, "%s line %d: failed to parse CD-TEXT file `%s'", psz_cue_name, i_line, psz_field);
+
+        cdio_stdio_destroy (source);
+      }
+    } else {
+      goto format_error;
+    }
 	
 	/* FILE "<filename>" <BINARY|WAVE|other?> */
       } else if (0 == strcmp ("FILE", psz_keyword)) {
@@ -357,7 +399,6 @@ parse_cuefile (_img_private_t *cd, const char *psz_cue_name)
 	    this_track->track_num   = cd->gen.i_tracks;
 	    this_track->num_indices = 0;
 	    b_first_index_for_track = false;
-	    cdtext_init (&(cd->gen.cdtext_track[cd->gen.i_tracks]));
 	    cd->gen.i_tracks++;
 	  }
 	  i++;
@@ -728,18 +769,15 @@ parse_cuefile (_img_private_t *cd, const char *psz_cue_name)
 	/* CD-Text */
       } else if ( CDTEXT_INVALID != 
 		  (cdtext_key = cdtext_is_keyword (psz_keyword)) ) {
-	if (-1 == i) {
-	  if (cd) {
-	    cdtext_set (cdtext_key, 
-			strtok (NULL, "\"\t\n\r"), 
-			&(cd->gen.cdtext));
-	  }
-	} else {
-	  if (cd) {
-	    cdtext_set (cdtext_key, strtok (NULL, "\"\t\n\r"), 
-			&(cd->gen.cdtext_track[i]));
-	  }
-	}
+        if (cd) {
+          if (NULL == cd->gen.cdtext) {
+            cd->gen.cdtext = malloc(sizeof(cdtext_t));
+            cdtext_init (cd->gen.cdtext);
+          }
+          cdtext_set (cdtext_key, 
+                      (-1 == i ? 0 : cd->gen.i_first_track + i + 1),
+                      strtok (NULL, "\"\t\n\r"), cd->gen.cdtext);
+        } 
 	
 	/* unrecognized line */
       } else {
@@ -1138,7 +1176,8 @@ cdio_open_cue (const char *psz_cue_name)
   _funcs.eject_media           = _eject_media_image;
   _funcs.free                  = _free_image;
   _funcs.get_arg               = _get_arg_image;
-  _funcs.get_cdtext            = get_cdtext_generic;
+  _funcs.get_cdtext            = _get_cdtext_image;
+  _funcs.get_cdtext_raw        = NULL;
   _funcs.get_devices           = cdio_get_devices_bincue;
   _funcs.get_default_device    = cdio_get_default_device_bincue;
   _funcs.get_disc_last_lsn     = get_disc_last_lsn_bincue;
diff --git a/lib/driver/image/cdrdao.c b/lib/driver/image/cdrdao.c
index 9b9979e..47dc78e 100644
--- a/lib/driver/image/cdrdao.c
+++ b/lib/driver/image/cdrdao.c
@@ -108,8 +108,6 @@ _init_cdrdao (_img_private_t *env)
   env->psz_mcn           = NULL;
   env->disc_mode         = CDIO_DISC_MODE_NO_INFO;
 
-  cdtext_init (&(env->gen.cdtext));
-
   /* Read in TOC sheet. */
   if ( !parse_tocfile(env, env->psz_cue_name) ) return false;
   
@@ -292,7 +290,6 @@ parse_tocfile (_img_private_t *cd, const char *psz_cue_name)
   }
 
   if (cd) {
-    cd->gen.b_cdtext_init  = true;
     cd->gen.b_cdtext_error = false;
   }
 
@@ -375,7 +372,6 @@ parse_tocfile (_img_private_t *cd, const char *psz_cue_name)
 	/* TRACK <track-mode> [<sub-channel-mode>] */
       } else if (0 == strcmp ("TRACK", psz_keyword)) {
 	i++;
-	if (NULL != cd) cdtext_init (&(cd->gen.cdtext_track[i]));
 	if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
 	  if (0 == strcmp ("AUDIO", psz_field)) {
 	    if (NULL != cd) {
@@ -911,19 +907,16 @@ parse_tocfile (_img_private_t *cd, const char *psz_cue_name)
 	if (i_cdtext_nest > 0) i_cdtext_nest--;
       } else if ( CDTEXT_INVALID != 
 		  (cdtext_key = cdtext_is_keyword (psz_keyword)) ) {
-	if (-1 == i) {
-	  if (NULL != cd) {
-	    cdtext_set (cdtext_key, 
-			strtok (NULL, "\"\t\n\r"), 
-			&(cd->gen.cdtext));
-	  }
-	} else {
-	  if (NULL != cd) {
-	    cdtext_set (cdtext_key, 
-			strtok (NULL, "\"\t\n\r"), 
-			&(cd->gen.cdtext_track[i]));
-	  }
-	}
+        if (NULL != cd) {
+          if (NULL == cd->gen.cdtext) {
+            cd->gen.cdtext = malloc(sizeof(cdtext_t));
+            cdtext_init (cd->gen.cdtext);
+          }
+          cdtext_set (cdtext_key, 
+                      (-1 == i ? 0 : cd->gen.i_first_track + i + 1),
+                      strtok (NULL, "\"\t\n\r"),
+                      cd->gen.cdtext);
+        }
 
 	/* unrecognized line */
       } else {
@@ -1261,7 +1254,8 @@ cdio_open_cdrdao (const char *psz_cue_name)
   _funcs.eject_media           = _eject_media_image;
   _funcs.free                  = _free_image;
   _funcs.get_arg               = _get_arg_image;
-  _funcs.get_cdtext            = get_cdtext_generic;
+  _funcs.get_cdtext            = _get_cdtext_image;
+  _funcs.get_cdtext_raw        = NULL;
   _funcs.get_devices           = cdio_get_devices_cdrdao;
   _funcs.get_default_device    = cdio_get_default_device_cdrdao;
   _funcs.get_disc_last_lsn     = get_disc_last_lsn_cdrdao;
diff --git a/lib/driver/image/nrg.c b/lib/driver/image/nrg.c
index 1573bda..bdd6af4 100644
--- a/lib/driver/image/nrg.c
+++ b/lib/driver/image/nrg.c
@@ -43,6 +43,7 @@
 #include "cdio_assert.h"
 #include "_cdio_stdio.h"
 #include "nrg.h"
+#include "cdtext_private.h"
 
 static const char _rcsid[] = "$Id: nrg.c,v 1.31 2008/04/21 18:30:22 karl Exp $";
 
@@ -431,7 +432,6 @@ parse_nrg (_img_private_t *p_env, const char *psz_nrg_name,
 	  }
 	  if (0 == disc_mode) {
 	    for (i=0; i<p_env->gen.i_tracks; i++) {
-	      cdtext_init (&(p_env->gen.cdtext_track[i]));
 	      p_env->tocent[i].track_format= track_format;
 	      p_env->tocent[i].datastart   = 0;
 	      p_env->tocent[i].track_green = false;
@@ -446,7 +446,6 @@ parse_nrg (_img_private_t *p_env, const char *psz_nrg_name,
 	    }
 	  } else if (2 == disc_mode) {
 	    for (i=0; i<p_env->gen.i_tracks; i++) {
-	      cdtext_init (&(p_env->gen.cdtext_track[i]));
 	      p_env->tocent[i].track_green = true;
 	      p_env->tocent[i].track_format= track_format;
 	      p_env->tocent[i].datasize    = CDIO_CD_FRAMESIZE;
@@ -749,7 +748,15 @@ parse_nrg (_img_private_t *p_env, const char *psz_nrg_name,
         uint8_t *wdata = (uint8_t *) chunk->data;
         int len = UINT32_FROM_BE (chunk->len);
         cdio_assert (len % sizeof (CDText_data_t) == 0);
-        cdtext_data_init (&p_env->gen, p_env->gen.i_first_track, &wdata[-4], len, set_cdtext_field_generic);
+        p_env->gen.cdtext = malloc(sizeof(cdtext_t));
+        cdtext_init (p_env->gen.cdtext);
+        if(!cdtext_data_init (p_env->gen.cdtext, &wdata[-4]))
+        {
+          cdtext_destroy(p_env->gen.cdtext);
+          free(p_env->gen.cdtext);
+          p_env->gen.cdtext = NULL;
+        }
+
         break;
       }
 
@@ -777,7 +784,6 @@ parse_nrg (_img_private_t *p_env, const char *psz_nrg_name,
   p_env->tocent[p_env->gen.i_tracks-1].sec_count = 
     cdio_lsn_to_lba(p_env->size - p_env->tocent[p_env->gen.i_tracks-1].start_lba);
 
-  p_env->gen.b_cdtext_init  = true;
   p_env->gen.b_cdtext_error = false;
   p_env->gen.toc_init       = true;
   free(footer_buf);
@@ -804,8 +810,6 @@ _init_nrg (_img_private_t *p_env)
   p_env->psz_mcn       = NULL;
   p_env->disc_mode     = CDIO_DISC_MODE_NO_INFO;
 
-  cdtext_init (&(p_env->gen.cdtext));
-
   if ( !parse_nrg (p_env, p_env->gen.source_name, CDIO_LOG_WARN) ) {
     cdio_warn ("image file %s is not a Nero image", 
 	       p_env->gen.source_name);
@@ -1276,7 +1280,8 @@ cdio_open_nrg (const char *psz_source)
   _funcs.eject_media           = _eject_media_nrg;
   _funcs.free                  = _free_nrg;
   _funcs.get_arg               = _get_arg_image;
-  _funcs.get_cdtext            = get_cdtext_generic;
+  _funcs.get_cdtext            = _get_cdtext_image;
+  _funcs.get_cdtext_raw        = NULL;
   _funcs.get_devices           = cdio_get_devices_nrg;
   _funcs.get_default_device    = cdio_get_default_device_nrg;
   _funcs.get_disc_last_lsn     = get_disc_last_lsn_nrg;
diff --git a/lib/driver/image_common.c b/lib/driver/image_common.c
index 8b91c3f..da1c9ba 100644
--- a/lib/driver/image_common.c
+++ b/lib/driver/image_common.c
@@ -63,14 +63,13 @@ _free_image (void *p_user_data)
     track_info_t *p_tocent = &(p_env->tocent[i_track]);
     free_if_notnull(p_tocent->filename);
     free_if_notnull(p_tocent->isrc);
-    cdtext_destroy(&(p_tocent->cdtext));
     if (p_tocent->data_source) cdio_stdio_destroy(p_tocent->data_source);
   }
 
   free_if_notnull(p_env->psz_mcn);
   free_if_notnull(p_env->psz_cue_name);
   free_if_notnull(p_env->psz_access_mode);
-  cdtext_destroy(&(p_env->gen.cdtext));
+  cdtext_destroy(p_env->gen.cdtext);
   cdio_generic_stdio_free(p_env);
   free(p_env);
 }
@@ -95,6 +94,20 @@ _get_arg_image (void *user_data, const char key[])
   return NULL;
 }
 
+/*!
+  Return CD-Text object
+ */
+cdtext_t *
+_get_cdtext_image (void *user_data)
+{
+  generic_img_private_t *p_env = user_data;
+
+  if(!p_env)
+    return NULL;
+
+  return p_env->cdtext;
+}
+
 /*! 
   Get disc type associated with cd_obj.
 */
diff --git a/lib/driver/image_common.h b/lib/driver/image_common.h
index f794981..c83a06e 100644
--- a/lib/driver/image_common.h
+++ b/lib/driver/image_common.h
@@ -78,6 +78,11 @@ int _eject_media_image(void *p_user_data);
 */
 const char * _get_arg_image (void *user_data, const char key[]);
 
+/*!
+  Return CD-Text object or NULL
+ */
+cdtext_t * _get_cdtext_image(void *p_user_data);
+
 /*! 
   Get disc type associated with cd_obj.
 */
diff --git a/lib/driver/libcdio.sym b/lib/driver/libcdio.sym
index d936b61..c2e0fdc 100644
--- a/lib/driver/libcdio.sym
+++ b/lib/driver/libcdio.sym
@@ -42,6 +42,7 @@ cdio_free_device_list
 cdio_from_bcd8
 cdio_get_arg
 cdio_get_cdtext
+cdio_get_cdtext_raw
 cdio_get_default_device
 cdio_get_default_device_bincue
 cdio_get_default_device_bsdi
@@ -179,6 +180,7 @@ cdio_version_string
 cdio_warn
 cdtext_destroy
 cdtext_field2str
+cdtext_genre2str
 cdtext_get
 cdtext_get_const
 cdtext_init
diff --git a/lib/driver/mmc/mmc.c b/lib/driver/mmc/mmc.c
index d591571..fab246d 100644
--- a/lib/driver/mmc/mmc.c
+++ b/lib/driver/mmc/mmc.c
@@ -324,16 +324,15 @@ mmc_get_mcn_private ( void *p_env,
   return true on success, false on error or CD-Text information does
   not exist.
 */
-bool
-mmc_init_cdtext_private ( void *p_user_data, 
-                          const mmc_run_cmd_fn_t run_mmc_cmd,
-                          set_cdtext_field_fn_t set_cdtext_field_fn 
+uint8_t *
+mmc_read_cdtext_private ( void *p_user_data, 
+                          const mmc_run_cmd_fn_t run_mmc_cmd
                           )
 {
 
   generic_img_private_t *p_env = p_user_data;
   mmc_cdb_t  cdb = {{0, }};
-  unsigned char   wdata[5000] = { 0, };
+  unsigned char * wdata;
   int             i_status, i_errno;
 
   if ( ! p_env || ! run_mmc_cmd || p_env->b_cdtext_error )
@@ -351,38 +350,45 @@ mmc_init_cdtext_private ( void *p_user_data,
 
   errno = 0;
 
+  wdata = calloc(MAX_CDTEXT_DATA_LENGTH, sizeof(unsigned char));
+
   /* We may need to give CD-Text a little more time to complete. */
   /* First off, just try and read the size */
   i_status = run_mmc_cmd (p_env, mmc_read_timeout_ms,
                           mmc_get_cmd_len(cdb.field[0]), 
                           &cdb, SCSI_MMC_DATA_READ, 
-                          4, &wdata);
+                          4, wdata);
 
   if (i_status != 0) {
     cdio_info ("CD-Text read failed for header: %s\n", strerror(errno));  
-	i_errno = errno;
+	  i_errno = errno;
     p_env->b_cdtext_error = true;
-    return false;
+    free(wdata);
+    return NULL;
   } else {
     /* Now read the CD-Text data */
     int	i_cdtext = CDIO_MMC_GET_LEN16(wdata);
 
-    if (i_cdtext > sizeof(wdata)) i_cdtext = sizeof(wdata);
+    if (i_cdtext+2 > 5000)
+      i_cdtext = MAX_CDTEXT_DATA_LENGTH-2;
+    else
+      wdata = realloc(wdata,i_cdtext+2);
+    /* the 2 bytes holding the size are not included in i_cdtext */
+
     
     CDIO_MMC_SET_READ_LENGTH16(cdb.field, i_cdtext);
     i_status = run_mmc_cmd (p_env, mmc_read_timeout_ms,
                             mmc_get_cmd_len(cdb.field[0]), 
                             &cdb, SCSI_MMC_DATA_READ, 
-                            i_cdtext, &wdata);
+                            i_cdtext, wdata);
     if (i_status != 0) {
       cdio_info ("CD-Text read for text failed: %s\n", strerror(errno));  
       i_errno = errno;
       p_env->b_cdtext_error = true;
-      return false;
+      free(wdata);
+      return NULL;
     }
-    p_env->b_cdtext_init = true;
-    return cdtext_data_init(p_env, p_env->i_first_track, wdata, i_cdtext-2,
-			    set_cdtext_field_fn);
+    return wdata;
   }
 }
 
diff --git a/lib/driver/mmc/mmc_private.h b/lib/driver/mmc/mmc_private.h
index dea2b30..66414e7 100644
--- a/lib/driver/mmc/mmc_private.h
+++ b/lib/driver/mmc/mmc_private.h
@@ -16,7 +16,6 @@
 */
 
 #include <cdio/mmc.h>
-#include "cdtext_private.h"
 
 /*! Convert milliseconds to seconds taking the ceiling value, i.e.
     1002 milliseconds gets rounded to 2 seconds.
@@ -131,9 +130,8 @@ char *mmc_get_mcn_private ( void *p_env,
 			    mmc_run_cmd_fn_t run_mmc_cmd
 			    );
 
-bool mmc_init_cdtext_private ( void *p_user_data, 
-			       mmc_run_cmd_fn_t run_mmc_cmd,
-			       set_cdtext_field_fn_t set_cdtext_field_fn
+uint8_t * mmc_read_cdtext_private ( void *p_user_data, 
+			       mmc_run_cmd_fn_t run_mmc_cmd
 			       );
 
 /*!
diff --git a/lib/driver/netbsd.c b/lib/driver/netbsd.c
index 7c33a1f..dfee81b 100644
--- a/lib/driver/netbsd.c
+++ b/lib/driver/netbsd.c
@@ -558,6 +558,7 @@ static cdio_funcs_t _funcs = {
   .get_arg               = get_arg_netbsd,
   .get_blocksize         = get_blocksize_mmc,
   .get_cdtext            = get_cdtext_generic,
+  .get_cdtext_raw        = read_cdtext_generic,
   .get_default_device    = cdio_get_default_device_netbsd,
   .get_devices           = cdio_get_devices_netbsd,
   .get_disc_last_lsn     = get_disc_last_lsn_netbsd,
@@ -601,6 +602,7 @@ cdio_open_netbsd(const char *source_name)
 	_img_private_t *_data;
 
 	_data = calloc(1, sizeof(_img_private_t));
+  _data->gen.b_cdtext_error = false;
 	_data->gen.init = false;
 	_data->gen.fd = -1;
 	_data->toc_valid = false;
diff --git a/lib/driver/os2.c b/lib/driver/os2.c
index fa543e9..c63231c 100644
--- a/lib/driver/os2.c
+++ b/lib/driver/os2.c
@@ -650,8 +650,6 @@ init_os2 (void *p_user_data)
 
   p_env->gen.init           = true;
   p_env->gen.toc_init       = false;
-  p_env->gen.b_cdtext_init  = false;
-  p_env->gen.b_cdtext_error = false;
   p_env->gen.fd             = p_env->h_cd;
 
   return true;
@@ -1447,6 +1445,7 @@ cdio_open_os2 (const char *psz_orig_source)
   _funcs.get_blocksize          = get_blocksize_os2;
 #endif
   _funcs.get_cdtext             = get_cdtext_generic;
+  _funcs.get_cdtext_raw         = read_cdtext_generic;
   _funcs.get_default_device     = cdio_get_default_device_os2;
   _funcs.get_devices            = cdio_get_devices_os2;
   _funcs.get_disc_last_lsn      = get_disc_last_lsn_os2;
@@ -1487,6 +1486,7 @@ cdio_open_os2 (const char *psz_orig_source)
 
   _data                 = calloc(1, sizeof (_img_private_t));
   _data->access_mode    = _AM_OS2;
+  _data->gen.b_cdtext_error = false;
   _data->gen.init       = false;
   _data->gen.fd         = -1;
 
diff --git a/lib/driver/osx.c b/lib/driver/osx.c
index f0ad9ca..fe7c499 100644
--- a/lib/driver/osx.c
+++ b/lib/driver/osx.c
@@ -856,20 +856,6 @@ get_hwinfo_osx ( const CdIo_t *p_cdio, /*out*/ cdio_hwinfo_t *hw_info)
 }
 #endif
 
-/*
-  Get cdtext information in p_user_data for track i_track. 
-  For disc information i_track is 0.
-  
-  Return the CD-TEXT or NULL if obj is NULL,
-  CD-TEXT information does not exist, or (as is the case here)
-  we don't know how to get this implemented.
-*/
-static cdtext_t *
-get_cdtext_osx (void *p_user_data, track_t i_track) 
-{
-  return NULL;
-}
-
 static void 
 _free_osx (void *p_user_data) {
   _img_private_t *p_env = p_user_data;
@@ -1893,7 +1879,8 @@ cdio_open_osx (const char *psz_orig_source)
     .eject_media           = _eject_media_osx,
     .free                  = _free_osx,
     .get_arg               = _get_arg_osx,
-    .get_cdtext            = get_cdtext_osx,
+    .get_cdtext            = NULL,
+    .get_cdtext_raw        = NULL,
     .get_default_device    = cdio_get_default_device_osx,
     .get_devices           = cdio_get_devices_osx,
     .get_disc_last_lsn     = get_disc_last_lsn_osx,
@@ -1930,8 +1917,6 @@ cdio_open_osx (const char *psz_orig_source)
   _data->gen.init           = false;
   _data->gen.fd             = -1;
   _data->gen.toc_init       = false;
-  _data->gen.b_cdtext_init  = false;
-  _data->gen.b_cdtext_error = false;
 
   if (NULL == psz_orig_source) {
     psz_source=cdio_get_default_device_osx();
diff --git a/lib/driver/solaris.c b/lib/driver/solaris.c
index 963730c..300e17d 100644
--- a/lib/driver/solaris.c
+++ b/lib/driver/solaris.c
@@ -1380,6 +1380,7 @@ cdio_open_am_solaris (const char *psz_orig_source, const char *access_mode)
   _funcs.get_blocksize          = get_blocksize_solaris;
 #endif
   _funcs.get_cdtext             = get_cdtext_generic;
+  _funcs.get_cdtext_raw         = read_cdtext_generic;
   _funcs.get_default_device     = cdio_get_default_device_solaris;
   _funcs.get_devices            = cdio_get_devices_solaris;
   _funcs.get_disc_last_lsn      = get_disc_last_lsn_solaris;
@@ -1422,7 +1423,6 @@ cdio_open_am_solaris (const char *psz_orig_source, const char *access_mode)
   _data->gen.init       = false;
   _data->gen.fd         = -1;
   _data->gen.toc_init   = false;
-  _data->gen.b_cdtext_init  = false;
   _data->gen.b_cdtext_error = false;
 
   if (NULL == psz_orig_source) {
diff --git a/src/cd-info.c b/src/cd-info.c
index 20572f9..b81bc5d 100644
--- a/src/cd-info.c
+++ b/src/cd-info.c
@@ -409,8 +409,7 @@ _cddb_log_handler (cddb_log_level_t level, const char message[])
 #endif
 
 static void 
-print_cdtext_track_info(CdIo_t *p_cdio, track_t i_track, const char *psz_msg) {
-   cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio, i_track);
+print_cdtext_track_info(cdtext_t *p_cdtext, track_t i_track, const char *psz_msg) {
 
   if (NULL != p_cdtext) {
     cdtext_field_t i;
@@ -418,23 +417,28 @@ print_cdtext_track_info(CdIo_t *p_cdio, track_t i_track, const char *psz_msg) {
     printf("%s\n", psz_msg);
     
     for (i=0; i < MAX_CDTEXT_FIELDS; i++) {
-      if (p_cdtext->field[i]) {
-        printf("\t%s: %s\n", cdtext_field2str(i), p_cdtext->field[i]);
+      if (cdtext_get_const(i, i_track, p_cdtext)) {
+        printf("\t%s: %s\n", cdtext_field2str(i), cdtext_get_const(i, i_track, p_cdtext));
       }
     }
   }
-  cdtext_destroy(p_cdtext);
 }
     
 static void 
 print_cdtext_info(CdIo_t *p_cdio, track_t i_tracks, track_t i_first_track) {
   track_t i_last_track = i_first_track+i_tracks;
+  cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio);
+
+  if(NULL == p_cdtext) {
+    printf("No CD-TEXT on Disc.\n");
+    return;
+  }
   
-  print_cdtext_track_info(p_cdio, 0, "\nCD-TEXT for Disc:");
+  print_cdtext_track_info(p_cdtext, 0, "\nCD-TEXT for Disc:");
   for ( ; i_first_track < i_last_track; i_first_track++ ) {
     char msg[50];
     sprintf(msg, "CD-TEXT for Track %2d:", i_first_track);
-    print_cdtext_track_info(p_cdio, i_first_track, msg);
+    print_cdtext_track_info(p_cdtext, i_first_track, msg);
   }
 }
 
diff --git a/src/cdda-player.c b/src/cdda-player.c
index b400df3..88cf5a7 100644
--- a/src/cdda-player.c
+++ b/src/cdda-player.c
@@ -537,23 +537,22 @@ get_cddb_disc_info(CdIo_t *p_cdio)
   return;
 }
 
-#define add_cdtext_disc_info(format_str, info_field, FIELD) \
-  if (p_cdtext->field[FIELD] && !strlen(info_field)) {      \
-    snprintf(info_field, sizeof(info_field), format_str,    \
-             p_cdtext->field[FIELD]);                       \
-    b_cdtext_ ## info_field = true;                         \
+#define add_cdtext_disc_info(format_str, info_field, FIELD)     \
+  if (cdtext_get_const(FIELD, 0, p_cdtext) && !strlen(info_field)) { \
+    snprintf(info_field, sizeof(info_field), format_str,        \
+             cdtext_get_const(FIELD, 0, p_cdtext));                  \
+    b_cdtext_ ## info_field = true;                             \
   }
 
 static void 
 get_cdtext_disc_info(CdIo_t *p_cdio) 
 {
-  cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio, 0);
+  cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio);
 
   if (p_cdtext) {
     add_cdtext_disc_info("%s", title, CDTEXT_TITLE);
     add_cdtext_disc_info("%s", artist, CDTEXT_PERFORMER);
     add_cdtext_disc_info("%s", genre, CDTEXT_GENRE);
-    cdtext_destroy(p_cdtext);
   }
 }
 
@@ -835,10 +834,10 @@ get_cddb_track_info(track_t i_track)
 }
 
 #define add_cdtext_track_info(format_str, info_field, FIELD) \
-  if (p_cdtext->field[FIELD]) {                              \
-    snprintf(cd_info[i_track].info_field,                    \
-             sizeof(cd_info[i_track].info_field),            \
-             format_str, p_cdtext->field[FIELD]);            \
+  if (cdtext_get_const(FIELD, i_track, p_cdtext)) {                        \
+    snprintf(cd_info[i_track].info_field,                             \
+             sizeof(cd_info[i_track].info_field),                     \
+             format_str, cdtext_get_const(FIELD, i_track, p_cdtext));      \
     cd_info[i_track].b_cdtext = true; \
   }
 
@@ -847,12 +846,11 @@ static void
 get_cdtext_track_info(track_t i_track)
 {
 
-  cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio, i_track);
+  cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio);
 
   if (NULL != p_cdtext) {
     add_cdtext_track_info("%s", title, CDTEXT_TITLE);
     add_cdtext_track_info("%s", artist, CDTEXT_PERFORMER);
-    cdtext_destroy(p_cdtext);
   }
 }
 
diff --git a/test/cdtext.right b/test/cdtext.right
new file mode 100644
index 0000000..2cdc6e3
--- /dev/null
+++ b/test/cdtext.right
@@ -0,0 +1,49 @@
+__________________________________
+
+Disc mode is listed as: CD-DA
+CD-ROM Track List (1 - 3)
+  #: MSF       LSN    Type   Green? Copy? Channels Premphasis?
+  1: 00:02:00  000000 audio  false  no    2        no
+  2: 00:04:00  000150 audio  false  no    2        no
+  3: 00:06:00  000300 audio  false  no    2        no
+170: 00:05:56  000281 leadout (645 KB raw, 645 KB formatted)
+Media Catalog Number (MCN): not available
+Last CD Session LSN: not supported by drive/driver
+audio status: not implemented
+__________________________________
+CD Analysis Report
+
+CD-TEXT for Disc:
+	ARRANGER: The Gnu Community
+	COMPOSER: Another Gnu
+	DISC_ID: 0815 42
+	GENRE: Animal Soundz
+	MESSAGE: Gnu lives!
+	PERFORMER: A Gnu
+	SONGWRITER: A Gnu, too
+	TITLE: The Voice of the Gnu
+	UPC_EAN: 0123456789876
+CD-TEXT for Track  1:
+	ARRANGER: His Brother
+	COMPOSER: His Brother
+	MESSAGE: Free the Gnu!
+	ISRC: DE-GNU-11-00001
+	PERFORMER: Gnu2
+	SONGWRITER: Gnu2
+	TITLE: I Will Survive
+CD-TEXT for Track  2:
+	ARRANGER: The Guy With the Microphone
+	COMPOSER: The Alpha Gnu
+	MESSAGE: Isn't it beatiful?
+	ISRC: DE-GNU-11-00002
+	PERFORMER: Those Particular Gnus
+	SONGWRITER: None in Particular
+	TITLE: A Forest Full of Gnus
+CD-TEXT for Track  3:
+	ARRANGER: An Even Less Famous Gnu
+	COMPOSER: A Not So Famous Gnu
+	MESSAGE: Every Gnu Is Legal!
+	ISRC: DE-GNU-11-00003
+	PERFORMER: Whatwasitsname?
+	SONGWRITER: A Famous Gnu
+	TITLE: Gnu Opera
diff --git a/test/check_cue.sh.in b/test/check_cue.sh.in
index b0aad8a..700d455 100644
--- a/test/check_cue.sh.in
+++ b/test/check_cue.sh.in
@@ -45,9 +45,26 @@ test_cdinfo "$opts" ${fname}.dump ${srcdir}/${fname}.right
 RC=$?
 check_result $RC "cd-info TOC test $testnum" "${CD_INFO} $opts"
 
+fname=cdtext
+testnum="CD-Text binary parser"
+if test -f ${srcdir}/data/${fname}.bin ; then
+  if test -f ${srcdir}/data/${fname}.cdt ; then
+    opts="--quiet --no-device-info --cue-file ${srcdir}/data/${fname}.cue --no-cddb"
+    cp ${srcdir}/data/${fname}.cdt ${srcdir}/${fname}.cdt
+    test_cdinfo "$opts" ${fname}.dump ${srcdir}/${fname}.right
+    rm ${srcdir}/${fname}.cdt
+    RC=$?
+    check_result $RC "cd-info CD-Text CUE test $testnum" "${CD_INFO} $opts"
+  else
+    echo "Don't see CD-Text file ${srcdir}/data/${fname}.cdt. Test $testnum skipped."
+  fi
+else
+  echo "Don't see binary file ${srcdir}/data/${fname}.bin. Test $testnum skipped."
+fi
+
 fname=isofs-m1
 testnum='ISO 9660 mode1 CUE'
-if test -f  ${srcdir}/${fname}.bin ; then
+if test -f  ${srcdir}/data/${fname}.bin ; then
   if test -n "@HAVE_ROCK@"; then 
     opts="-q --no-device-info --no-disc-mode --cue-file ${srcdir}/data/${fname}.cue --iso9660"
     test_cdinfo "$opts" ${fname}.dump ${srcdir}/${fname}.right
@@ -83,7 +100,7 @@ else
   right=${srcdir}/${fname}_vcdinfo.right
 fi
 testnum='Video CD'
-if test -f ${srcdir}/${fname}.bin ; then
+if test -f ${srcdir}/data/${fname}.bin ; then
   opts="-q --no-device-info --no-disc-mode -c ${srcdir}/data/${fname}.cue --iso9660"
   test_cdinfo "$opts" ${fname}.dump $right
   RC=$?
@@ -100,15 +117,15 @@ if test -f ${srcdir}/${fname}.bin ; then
     RC=$?
     check_result $RC "cd-info TOC test $testnum" "${CD_INFO} $opts"
   else 
-    echo "Don't see TOC file ${srcdir}/${fname}.toc. Test $testnum skipped."
+    echo "Don't see TOC file ${srcdir}/data/${fname}.toc. Test $testnum skipped."
   fi
 else 
-  echo "Don't see CUE file ${srcdir}/${fname}.cue. Test $testnum skipped."
+  echo "Don't see BIN file ${srcdir}/data/${fname}.bin. Test $testnum skipped."
 fi
 
 fname=svcd_ogt_test_ntsc
 testnum='Super Video CD'
-if test -f ${srcdir}/${fname}.bin ; then
+if test -f ${srcdir}/data/${fname}.bin ; then
   opts="-q --no-device-info --no-disc-mode --cue-file ${srcdir}/data/${fname}.cue $vcd_opt --iso9660"
   test_cdinfo "$opts" ${fname}.dump ${srcdir}/${fname}.right
   RC=$?
diff --git a/test/check_fuzzyiso.sh b/test/check_fuzzyiso.sh
index 251409e..0eb71d9 100755
--- a/test/check_fuzzyiso.sh
+++ b/test/check_fuzzyiso.sh
@@ -22,7 +22,7 @@ fi
 cd $srcdir; src_dir=`pwd`
 for file in $src_dir/data/*.bin $src_dir/data/*.iso $src_dir/data/*.nrg ; do 
   case "$file" in
-  $src_dir/data/p1.nrg | $src_dir/data/p1.bin | $src_dir/data/cdda.bin | $src_dir/data/cdda-mcn.nrg | $src_dir/data/svcdgs.nrg )
+  $src_dir/data/p1.nrg | $src_dir/data/p1.bin | $src_dir/data/cdda.bin | $src_dir/data/cdda-mcn.nrg | $src_dir/data/svcdgs.nrg | $src_dir/data/cdtext.bin )
     good_rc=1
     ;;
   *) 
diff --git a/test/data/cdtext.bin b/test/data/cdtext.bin
new file mode 100644
index 0000000000000000000000000000000000000000..a9b29db24db1d5316dffca0ea86e361f20c5ee5e
GIT binary patch
diff --git a/test/data/cdtext.cdt b/test/data/cdtext.cdt
new file mode 100644
index 0000000000000000000000000000000000000000..7989ec8dd4187606139d037eec94d7e42b3c3630
GIT binary patch
diff --git a/test/data/cdtext.cue b/test/data/cdtext.cue
new file mode 100644
index 0000000..fcd9f6a
--- /dev/null
+++ b/test/data/cdtext.cue
@@ -0,0 +1,8 @@
+CDTEXTFILE "cdtext.cdt"
+FILE "cdtext.bin" BINARY
+  TRACK 01 AUDIO
+    INDEX 01 00:00:00
+  TRACK 02 AUDIO
+    INDEX 01 00:02:00
+  TRACK 03 AUDIO
+    INDEX 01 00:04:00
diff --git a/test/driver/nrg.c.in b/test/driver/nrg.c.in
index 877a8a6..18ee3da 100644
--- a/test/driver/nrg.c.in
+++ b/test/driver/nrg.c.in
@@ -75,10 +75,10 @@ main(int argc, const char *argv[])
   }
   {
     unsigned int i;
-    cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio, 0);
+    cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio);
     if (!p_cdtext) return(1);
     for (i=0; i<NUM_FIELDS; i++) {
-      const char *psz_field = p_cdtext->field[cdtext_fields[i]];
+      const char *psz_field = cdtext_get_const(cdtext_fields[i], 0, p_cdtext);
       if (!psz_field)
 	return(2);
       if (0 != strncmp(psz_field, cdtext_check[i], strlen(cdtext_check[i]))) {
-- 
1.7.3.4

