commit d4d82912da25640e256441dc8c1a7a79efdcb481 Author: rocky Date: Mon Mar 24 19:01:09 2003 +0000 Initial revision diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 00000000..ff110c20 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,15 @@ +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +config.guess +config.h +config.h.in +config.log +config.status +config.sub +configure +libcdio*.tar.gz +libtool +ltmain.sh +stamp-h1 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..7d9e3ca4 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ +Herbert Valerio Riedel +Rocky Bernstein diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..9ec89c7c --- /dev/null +++ b/Makefile.am @@ -0,0 +1,30 @@ +# $Id: Makefile.am,v 1.1 2003/03/24 19:01:09 rocky Exp $ +# +# Copyright (C) 2003 Rocky Bernstein +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +## Process this file with automake to produce Makefile.in +## which configure then turns into a Makefile ... +## which make can then use to produce stuff. Isn't configuration simple? + +EXTRA_DIST = libpopt.m4 + +# SUBDIRS = doc lib src tests +SUBDIRS = lib src tests + + + + diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..72c3ccb3 --- /dev/null +++ b/NEWS @@ -0,0 +1,2 @@ +0.1 +Routines split off from VCDImager. \ No newline at end of file diff --git a/README b/README new file mode 100644 index 00000000..889b7ab2 --- /dev/null +++ b/README @@ -0,0 +1,3 @@ +This library is to encapsulate CD-ROM reading and control. +Applications wishing to be oblivious of the OS- and device-dependant +properties of a CD-ROM can use this library. diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 00000000..c2892bf3 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,128 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +srcdir=`dirname $0` +PKG_NAME="libcdio" + +DIE=0 + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`autoconf' installed to." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +(grep "^AM_PROG_LIBTOOL" $srcdir/configure.ac >/dev/null) && { + (libtool --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`libtool' installed." + echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +grep "^AM_GNU_GETTEXT" $srcdir/configure.ac >/dev/null && { + grep "sed.*POTFILES" $srcdir/configure.ac >/dev/null || \ + (gettext --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`gettext' installed." + echo "Get ftp://alpha.gnu.org/gnu/gettext-0.10.35.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`automake' installed." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + NO_AUTOMAKE=yes +} + +# if no automake, don't bother testing for aclocal +test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing \`aclocal'. The version of \`automake'" + echo "installed doesn't appear recent enough." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if test -z "$*"; then + echo "**Warning**: I am going to run \`configure' with no arguments." + echo "If you wish to pass any to it, please specify them on the" + echo \`$0\'" command line." + echo +fi + +case $CC in +xlc ) + am_opt=--include-deps;; +esac + +for coin in `find $srcdir -name configure.ac -print` +do + dr=`dirname $coin` + if test -f $dr/NO-AUTO-GEN; then + echo skipping $dr -- flagged as no auto-gen + else + echo processing $dr + macrodirs=`sed -n -e 's,AM_ACLOCAL_INCLUDE(\(.*\)),\1,gp' < $coin` + ( cd $dr + aclocalinclude="$ACLOCAL_FLAGS" + for k in $macrodirs; do + if test -d $k; then + aclocalinclude="$aclocalinclude -I $k" + ##else + ## echo "**Warning**: No such directory \`$k'. Ignored." + fi + done + if grep "^AM_GNU_GETTEXT" configure.ac >/dev/null; then + if grep "sed.*POTFILES" configure.ac >/dev/null; then + : do nothing -- we still have an old unmodified configure.ac + else + echo "Creating $dr/aclocal.m4 ..." + test -r $dr/aclocal.m4 || touch $dr/aclocal.m4 + echo "Running gettextize... Ignore non-fatal messages." + echo "no" | gettextize --force --copy + echo "Making $dr/aclocal.m4 writable ..." + test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4 + fi + fi + if grep "^AM_PROG_LIBTOOL" configure.ac >/dev/null; then + echo "Running libtoolize..." + libtoolize --force --copy + fi + echo "Running aclocal $aclocalinclude ..." + aclocal $aclocalinclude + if grep "^AM_CONFIG_HEADER" configure.ac >/dev/null; then + echo "Running autoheader..." + autoheader + fi + echo "Running automake --gnu $am_opt ..." + automake --add-missing --gnu $am_opt + echo "Running autoconf ..." + autoconf + ) + fi +done + +conf_flags="--enable-maintainer-mode" # --enable-compile-warnings #--enable-iso-c + +if test x$NOCONFIGURE = x; then + echo Running $srcdir/configure $conf_flags "$@" ... + $srcdir/configure $conf_flags "$@" \ + && echo Now type \`make\' to compile $PKG_NAME +else + echo Skipping configure process. +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..2421ebae --- /dev/null +++ b/configure.ac @@ -0,0 +1,174 @@ +AC_REVISION([$Id: configure.ac,v 1.1 2003/03/24 19:01:09 rocky Exp $])dnl +AC_INIT(lib/cdio.c) +AM_CONFIG_HEADER(config.h) +AM_INIT_AUTOMAKE(libcdio, 0.1) + +AM_SANITY_CHECK + +dnl Checks for programs. +AC_PROG_CC + +if test "x$GCC" != "xyes" +then + echo "*** non GNU CC compiler detected." + echo "*** This package has not been tested very well with non GNU compilers" + echo "*** you should try to use 'gcc' for compiling this package." +else + WARN_CFLAGS="-Wall -Wchar-subscripts -Wmissing-prototypes -Wmissing-declarations -Wunused -Wpointer-arith -Wwrite-strings -Wnested-externs -Wsign-promo -Wno-sign-compare" + + for WOPT in $WARN_CFLAGS; do + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $WOPT" + AC_MSG_CHECKING([whether $CC understands $WOPT]) + AC_TRY_COMPILE([], [], has_option=yes, has_option=no,) + CFLAGS="$SAVE_CFLAGS" + AC_MSG_RESULT($has_option) + if test $has_option = yes; then + warning_flags="$warning_flags $option" + fi + unset has_option + unset SAVE_CFLAGS + done + WARNING_FLAGS="$warning_flags" + unset warning_flags +fi + +dnl We use a perl for documentation and regression tests +AC_PATH_PROG(PERL, perl, no) +AC_SUBST(PERL)dnl + +dnl AM_PATH_LIBPOPT(, [enable_cli_fe=no; enable_xml_fe=no]) + +dnl headers + +AC_STDC_HEADERS +AC_CHECK_HEADERS(stdint.h inttypes.h stdbool.h) + +dnl compiler +AC_C_BIGENDIAN +AC_C_CONST +AC_C_INLINE + +dnl ISOC99_PRAGMA +AC_MSG_CHECKING([whether $CC supports ISOC99 _Pragma()]) +AC_TRY_COMPILE([],[_Pragma("pack(1)")], [ + ISOC99_PRAGMA=yes + AC_DEFINE(HAVE_ISOC99_PRAGMA, [], [Supports ISO _Pragma() macro]) +],ISOC99_PRAGMA=no) +AC_MSG_RESULT($ISOC99_PRAGMA) + +dnl empty_array_size +AC_MSG_CHECKING([how to create empty arrays]) + +empty_array_size="xxx" +AC_TRY_COMPILE([],[struct { int foo; int bar[]; } doo;], empty_array_size="") + +if test "x$empty_array_size" = "xxxx";then + AC_TRY_COMPILE([],[struct { int foo; int bar[0]; } doo;], empty_array_size="0") +fi + +if test "x$empty_array_size" = "xxxx" +then + AC_MSG_ERROR([compiler is unable to creaty empty arrays]) +else + AC_DEFINE_UNQUOTED(EMPTY_ARRAY_SIZE, $empty_array_size, + [what to put between the brackets for empty arrays]) + changequote(`,') + msg="[${empty_array_size}]" + changequote([,]) + AC_MSG_RESULT($msg) +fi +dnl empty_array_size + +dnl bitfield order +AC_MSG_CHECKING(bitfield ordering in structs) +AC_TRY_RUN([ +int + main() { + struct { char bit_0:1, bit_12:2, bit_345:3, bit_67:2; } +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) + __attribute__((packed)) +#endif + bf = { 1,1,1,1 }; + if (sizeof (bf) != 1) return 1; + return *((unsigned char*) &bf) != 0x4b; } +], bf_lsbf=1, AC_TRY_RUN([ +int +main() { + struct { char bit_0:1, bit_12:2, bit_345:3, bit_67:2; } +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) + __attribute__((packed)) +#endif + bf = { 1,1,1,1 }; + if (sizeof (bf) != 1) return 1; + return *((unsigned char*) &bf) != 0xa5; } +], bf_lsbf=0, AC_MSG_ERROR([unsupported bitfield ordering]))) +if test "x$bf_lsbf" = "x1"; then + AC_MSG_RESULT(LSBF) + AC_DEFINE(BITFIELD_LSBF, [], [compiler does lsbf in struct bitfields]) +else + AC_MSG_RESULT(MSBF) +fi +dnl + +dnl system +AC_CYGWIN + +LIBS="$LIBS -lm" +CFLAGS="$CFLAGS $WARN_CFLAGS" + +AM_CONDITIONAL(CYGWIN, test "x$CYGWIN" = "xyes") +AM_CONDITIONAL(BUILD_CDINFO_LINUX, test "x$enable_cli_fe" = "xyes") + +AM_PROG_LIBTOOL + +dnl Checks for header files. +AC_STDC_HEADERS + +AC_HAVE_HEADERS( stdbool.h stdlib.h stdint.h stdio.h string.h strings.h linux/version.h sys/cdio.h sys/stat.h sys/types.h ) + +LIBCDIO_CFLAGS='-I$(top_srcdir)/lib/ -I$(top_srcdir)/include/' +LIBCDIO_LIBS='$(top_builddir)/lib/libcdio.la' +AC_SUBST(LIBCDIO_CFLAGS) +AC_SUBST(LIBCDIO_LIBS) + +case $host_os in + linux*) + AC_CHECK_HEADERS(linux/version.h) + AC_CHECK_HEADERS(linux/cdrom.h, [have_linux_cdrom_h="yes"]) + if test "x$have_linux_cdrom_h" = "xyes" ; then + AC_TRY_COMPILE(,[ +#include +struct cdrom_generic_command test; +int has_timeout=sizeof(test.timeout);], + [AC_DEFINE([HAVE_LINUX_CDROM_TIMEOUT], [1], + [Define 1 if timeout is in cdrom_generic_command struct])]) + AC_DEFINE([HAVE_LINUX_CDROM], [1], + [Define 1 if you have Linux-type CD-ROM support]) + fi + ;; + bsdi*) + AC_DEFINE([HAVE_BSDI_CDROM], [1], + [Define 1 if you have BSDI-type CD-ROM support]) + ;; + sunos*|sun*|solaris*) + AC_CHECK_HEADERS(sys/cdio.h) + AC_DEFINE([HAVE_SOLARIS_CDROM], [1], + [Define 1 if you have Solaris CD-ROM support]) + ;; + *) + AC_MSG_WARN(Don't have OS CD-reading support for ${host_os}...) + AC_MSG_WARN(Will use generic support.) + ;; +esac +AC_SUBST(LINUX_CDROM_TIMEOUT) +AC_SUBST(HAVE_LINUX_CDROM) +AC_SUBST(HAVE_BSDI_CDROM) +AC_SUBST(HAVE_SOLARIS_CDROM) + +AC_OUTPUT([ \ + Makefile \ + lib/Makefile \ + src/Makefile \ + tests/Makefile \ + ]) diff --git a/lib/.cvsignore b/lib/.cvsignore new file mode 100644 index 00000000..2a6ab490 --- /dev/null +++ b/lib/.cvsignore @@ -0,0 +1,7 @@ +.deps +.libs +Makefile +Makefile.in +*.o +*.lo +*.la diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 00000000..f6346fe9 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,77 @@ +# $Id: Makefile.am,v 1.1 2003/03/24 19:01:09 rocky Exp $ +# +# Copyright (C) 2003 Rocky Bernstein +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +#################################################### +# Things to make the libcdio library +#################################################### +# +noinst_HEADERS = cdio_assert.h bytesex.h bytesex_asm.h \ + cdio_private.h ds.h + +include_HEADERS = cdio.h logging.h sector.h cdio_types.h util.h + +libcdio_sources = \ + _cdio_bincue.c \ + _cdio_bsdi.c \ + _cdio_linux.c \ + _cdio_nrg.c \ + _cdio_stdio.c \ + _cdio_stdio.h \ + _cdio_stream.c \ + _cdio_stream.h \ + _cdio_sunos.c \ + bytesex.h \ + bytesex_asm.h \ + cdio.c \ + cdio.h \ + ds.c \ + ds.h \ + cdio_types.h \ + logging.c \ + logging.h \ + sector.c \ + sector.h \ + util.c \ + util.h + + +lib_LTLIBRARIES = libcdio.la +libcdio_la_SOURCES = $(libcdio_sources) + +### +# Install header files (default=$includedir/cdio) +# +install-includeHEADERS: $(include_HEADERS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(includedir)/cdio + @list='$(include_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d= ; else d="$(srcdir)/"; fi; \ + echo " $(INSTALL_DATA) $$d$$p $(DESTDIR)$(includedir)/cdio/"; \ + $(INSTALL_DATA) $$d$$p $(DESTDIR)$(includedir)/cdio/; \ + done + + +### +# Remove them +# +uninstall-includeHEADERS: + @$(NORMAL_UNINSTALL) + list='$(include_HEADERS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(includedir)/cdio/$$p; \ + done + diff --git a/lib/_cdio_bincue.c b/lib/_cdio_bincue.c new file mode 100644 index 00000000..c98e4629 --- /dev/null +++ b/lib/_cdio_bincue.c @@ -0,0 +1,581 @@ +/* + $Id: _cdio_bincue.c,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2001 Herbert Valerio Riedel + Copyright (C) 2002,2003 Rocky Bernstein + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This code implements low-level access functions for a CD images + residing inside a disk file (*.bin) and its associated cue sheet. + (*.cue). +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: _cdio_bincue.c,v 1.1 2003/03/24 19:01:09 rocky Exp $"; + +#include +#include +#include +#include + +#include "cdio_assert.h" +#include "cdio_private.h" +#include "logging.h" +#include "sector.h" +#include "util.h" +#include "_cdio_stdio.h" + +/* reader */ + +#define DEFAULT_CDIO_DEVICE "videocd.bin" +#define DEFAULT_CDIO_CUE "videocd.cue" + +typedef struct { + int blocksize; + int track_num; /* Probably is index+1 */ + msf_t start_msf; + int start_index; + int num_indices; + int flags; /* "DCP", "4CH", "PRE" */ + track_format_t track_format; + bool track_green; + +} track_info_t; + + +typedef struct { + bool sector_2336_flag; + + CdioDataSource *bin_src; + char *source_name; + char *cue_name; + track_info_t tocent[100]; /* entry info for each track */ + track_t total_tracks; /* number of tracks in image */ + track_t first_track_num; /* track number of first track */ + bool have_cue; + bool init; +} _img_private_t; + +static bool _cdio_image_read_cue (_img_private_t *_obj); +static uint32_t _cdio_stat_size (void *user_data); + +/*! + Initialize image structures. + */ +static bool +_cdio_init (_img_private_t *_obj) +{ + if (_obj->init) + return false; + + /* Read in CUE sheet. */ + if ((_obj->cue_name != NULL)) { + _obj->have_cue == _cdio_image_read_cue(_obj); + } + + if (!_obj->have_cue ) { + /* Time to fake things... + Make one big track, track 0 and 1 are the same. + We are guessing stuff atarts a msf 00:04:00 - 2 for the 150 + sector pregap and 2 for the cue information. + */ + int blocksize = _obj->sector_2336_flag + ? M2RAW_SECTOR_SIZE : CD_RAW_SECTOR_SIZE; + _obj->total_tracks = 2; + _obj->first_track_num = 1; + _obj->tocent[0].start_msf.m = to_bcd8(0); + _obj->tocent[0].start_msf.s = to_bcd8(4); + _obj->tocent[0].start_msf.f = to_bcd8(0); + _obj->tocent[0].blocksize = blocksize; + _obj->tocent[0].track_format= TRACK_FORMAT_XA; + _obj->tocent[0].track_green = true; + + _obj->tocent[1] = _obj->tocent[0]; + } + + + if (!(_obj->bin_src = cdio_stdio_new (_obj->source_name))) { + cdio_error ("init failed"); + return false; + } + + /* Have to set init before calling _cdio_stat_size() or we will + get into infinite recursion calling passing right here. + */ + _obj->init = true; + + /* Fake out leadout track. */ + cdio_lsn_to_msf ( _cdio_stat_size( (_img_private_t *) _obj), + &_obj->tocent[_obj->total_tracks].start_msf); + + return true; + +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static uint32_t +_cdio_stat_size (void *user_data) +{ + _img_private_t *_obj = user_data; + long size; + int blocksize = _obj->sector_2336_flag + ? M2RAW_SECTOR_SIZE : CD_RAW_SECTOR_SIZE; + + _cdio_init (_obj); + + size = cdio_stream_stat (_obj->bin_src); + + if (size % blocksize) + { + cdio_warn ("image file not multiple of blocksize (%d)", + blocksize); + if (size % M2RAW_SECTOR_SIZE == 0) + cdio_warn ("this may be a 2336-type disc image"); + else if (size % CD_RAW_SECTOR_SIZE == 0) + cdio_warn ("this may be a 2352-type disc image"); + /* exit (EXIT_FAILURE); */ + } + + size /= blocksize; + + return size; +} + +#define MAXLINE 512 + +static bool +_cdio_image_read_cue (_img_private_t *_obj) +{ + FILE *fp; + char line[MAXLINE]; + + int track_num; + int min,sec,frame; + int blocksize; + int start_index; + bool seen_first_index_for_track=false; + + if ( _obj == NULL || _obj->cue_name == NULL ) return false; + + fp = fopen (_obj->cue_name, "r"); + if (fp == NULL) return false; + + _obj->total_tracks=0; + _obj->first_track_num=1; + while ((fgets(line, MAXLINE, fp)) != NULL) { + char *s=NULL; + char *p; + /*printf("Retrieved line of length %zu :\n", read); + printf("%s", line); */ + for (p=line; isspace(*p); p++) ; + if (1==sscanf(p, "FILE \"%a[^\"]", &s)) { + /* Should expand file name based on cue file basename. + free(_obj->bin_file); + _obj->bin_file = s; + */ + /* printf("Found file name %s\n", s); */ + } else if (2==sscanf(p, "TRACK %d MODE2/%d", &track_num, &blocksize)) { + _obj->tocent[_obj->total_tracks].blocksize = blocksize; + _obj->tocent[_obj->total_tracks].track_num = track_num; + _obj->tocent[_obj->total_tracks].num_indices = 0; + _obj->tocent[_obj->total_tracks].track_format= TRACK_FORMAT_XA; + _obj->tocent[_obj->total_tracks].track_green = true; + _obj->total_tracks++; + seen_first_index_for_track=false; + /*printf("Added track %d with blocksize %d\n", track_num, blocksize);*/ + + } else if (2==sscanf(p, "TRACK %d MODE1/%d", &track_num, &blocksize)) { + _obj->tocent[_obj->total_tracks].blocksize = blocksize; + _obj->tocent[_obj->total_tracks].track_num = track_num; + _obj->tocent[_obj->total_tracks].num_indices = 0; + _obj->tocent[_obj->total_tracks].track_format= TRACK_FORMAT_CDI; + _obj->tocent[_obj->total_tracks].track_green = false; + _obj->total_tracks++; + seen_first_index_for_track=false; + /*printf("Added track %d with blocksize %d\n", track_num, blocksize);*/ + + } else if (1==sscanf(p, "TRACK %d AUDIO", &track_num)) { + _obj->tocent[_obj->total_tracks].blocksize = blocksize; + _obj->tocent[_obj->total_tracks].track_num = track_num; + _obj->tocent[_obj->total_tracks].num_indices = 0; + _obj->tocent[_obj->total_tracks].track_format= TRACK_FORMAT_AUDIO; + _obj->tocent[_obj->total_tracks].track_green = false; + _obj->total_tracks++; + seen_first_index_for_track=false; + + } else if (4==sscanf(p, "INDEX %d %d:%d:%d", + &start_index, &min, &sec, &frame)) { + /* FIXME! all of this is a big hack. + If start_index == 0, then this is the "last_cue" information. + The +2 below seconds is to adjust for the 150 pregap. + */ + if (!seen_first_index_for_track && start_index != 0) { + _obj->tocent[_obj->total_tracks-1].start_index = start_index; + _obj->tocent[_obj->total_tracks-1].start_msf.m = to_bcd8 (min); + _obj->tocent[_obj->total_tracks-1].start_msf.s = to_bcd8 (sec)+2; + _obj->tocent[_obj->total_tracks-1].start_msf.f = to_bcd8 (frame); + seen_first_index_for_track=true; + } + _obj->tocent[_obj->total_tracks-1].num_indices++; + } + } + _obj->have_cue = _obj->total_tracks != 0; + + fclose (fp); + return true; +} + +/*! + Release and free resources used here. + */ +static void +_cdio_free (void *user_data) +{ + _img_private_t *_obj = user_data; + + free (_obj->source_name); + + if (_obj->bin_src) + cdio_stream_destroy (_obj->bin_src); + + free (_obj); +} + +static int +_read_mode2_sector (void *user_data, void *data, uint32_t lsn, + bool mode2_form2) +{ + _img_private_t *_obj = user_data; + char buf[CD_RAW_SECTOR_SIZE] = { 0, }; + int blocksize = _obj->sector_2336_flag + ? M2RAW_SECTOR_SIZE : CD_RAW_SECTOR_SIZE; + + _cdio_init (_obj); + + cdio_stream_seek (_obj->bin_src, lsn * blocksize); + + cdio_stream_read (_obj->bin_src, + _obj->sector_2336_flag ? (buf + 12 + 4) : buf, + blocksize, 1); + + if (mode2_form2) + memcpy (data, buf + 12 + 4, M2RAW_SECTOR_SIZE); + else + memcpy (data, buf + 12 + 4 + 8, M2F1_SECTOR_SIZE); + + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode2_sectors (void *user_data, void *data, uint32_t lsn, + bool mode2_form2, unsigned nblocks) +{ + _img_private_t *_obj = user_data; + int i; + int retval; + + for (i = 0; i < nblocks; i++) { + if (mode2_form2) { + if ( (retval = _read_mode2_sector (_obj, + ((char *)data) + (M2RAW_SECTOR_SIZE * i), + lsn + i, true)) ) + return retval; + } else { + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + if ( (retval = _read_mode2_sector (_obj, buf, lsn + i, true)) ) + return retval; + + memcpy (((char *)data) + (M2F1_SECTOR_SIZE * i), buf + 8, + M2F1_SECTOR_SIZE); + } + } + return 0; +} + +/*! + Set the device to use in I/O operations. +*/ +static int +_cdio_set_arg (void *user_data, const char key[], const char value[]) +{ + _img_private_t *_obj = user_data; + + if (!strcmp (key, "source")) + { + free (_obj->source_name); + + if (!value) + return -2; + + _obj->source_name = strdup (value); + } + else if (!strcmp (key, "sector")) + { + if (!strcmp (value, "2336")) + _obj->sector_2336_flag = true; + else if (!strcmp (value, "2352")) + _obj->sector_2336_flag = false; + else + return -2; + } + else if (!strcmp (key, "cue")) + { + free (_obj->cue_name); + + if (!value) + return -2; + + _obj->cue_name = strdup (value); + } + else + return -1; + + return 0; +} + +/*! + Eject media -- there's nothing to do here. We always return 2. + also free obj. + */ +static int +_cdio_eject_media (void *user_data) { + /* Sort of a stub here. Perhaps log a message? */ + return 2; +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +_cdio_get_arg (void *user_data, const char key[]) +{ + _img_private_t *_obj = user_data; + + if (!strcmp (key, "source")) { + return _obj->source_name; + } else if (!strcmp (key, "cue")) { + return _obj->cue_name; + } + return NULL; +} + +/*! + Return a string containing the default VCD device if none is specified. + */ +static char * +_cdio_get_default_device() +{ + return strdup(DEFAULT_CDIO_DEVICE); +} + +/*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_t +_cdio_get_first_track_num(void *user_data) +{ + _img_private_t *_obj = user_data; + + _cdio_init (_obj); + + return _obj->first_track_num; +} + +/*! + Return the number of tracks in the current medium. + If no cuesheet is available, We fake it an just say there's + one big track. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_t +_cdio_get_num_tracks(void *user_data) +{ + _img_private_t *_obj = user_data; + _cdio_init (_obj); + + return _obj->have_cue && _obj->total_tracks > 0 ? _obj->total_tracks : 1; +} + +/*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_format_t +_cdio_get_track_format(void *user_data, track_t track_num) +{ + _img_private_t *_obj = user_data; + + if (!_obj->init) _cdio_init(_obj); + + if (track_num > _obj->total_tracks || track_num == 0) + return TRACK_FORMAT_ERROR; + + return _obj->tocent[track_num-1].track_format; +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +static bool +_cdio_get_track_green(void *user_data, track_t track_num) +{ + _img_private_t *_obj = user_data; + + if (!_obj->init) _cdio_init(_obj); + + if (track_num > _obj->total_tracks || track_num == 0) + return false; + + return _obj->tocent[track_num-1].track_green; +} + +/*! + Return the starting MSF (minutes/secs/frames) for the track number + track_num in obj. Tracks numbers start at 1. + Since there are no tracks, unless the track number is is 1 we return + falure. If the track number is 1, we return 00:00:00. +*/ +static bool +_cdio_get_track_msf(void *user_data, track_t track_num, msf_t *msf) +{ + _img_private_t *_obj = user_data; + _cdio_init (_obj); + + if (NULL == msf) return false; + + if (track_num == CDIO_LEADOUT_TRACK) track_num = _obj->total_tracks+1; + + if (track_num <= _obj->total_tracks+1 && track_num != 0) { + *msf = _obj->tocent[track_num-1].start_msf; + return true; + } else + return false; +} + +CdIo * +cdio_open_bincue (const char *source_name) +{ + CdIo *ret; + _img_private_t *_data; + + cdio_funcs _funcs = { + .eject_media = _cdio_eject_media, + .free = _cdio_free, + .get_arg = _cdio_get_arg, + .get_default_device = _cdio_get_default_device, + .get_first_track_num= _cdio_get_first_track_num, + .get_num_tracks = _cdio_get_num_tracks, + .get_track_format = _cdio_get_track_format, + .get_track_green = _cdio_get_track_green, + .get_track_lba = NULL, /* This could be implemented if need be. */ + .get_track_msf = _cdio_get_track_msf, + .read_mode2_sector = _read_mode2_sector, + .read_mode2_sectors = _read_mode2_sectors, + .set_arg = _cdio_set_arg, + .stat_size = _cdio_stat_size + }; + + _data = _cdio_malloc (sizeof (_img_private_t)); + _data->init = false; + + /* FIXME: should set cue initially. */ + _data->cue_name = NULL; + + ret = cdio_new (_data, &_funcs); + if (ret == NULL) return NULL; + + _cdio_set_arg(_data, "source", (NULL == source_name) + ? DEFAULT_CDIO_DEVICE: source_name); + + return ret; +} + +CdIo * +cdio_open_cue (const char *cue_name) +{ + CdIo *ret; + _img_private_t *_data; + + cdio_funcs _funcs = { + .eject_media = _cdio_eject_media, + .free = _cdio_free, + .get_arg = _cdio_get_arg, + .get_default_device = _cdio_get_default_device, + .get_first_track_num= _cdio_get_first_track_num, + .get_num_tracks = _cdio_get_num_tracks, + .get_track_format = _cdio_get_track_format, + .get_track_green = _cdio_get_track_green, + .get_track_lba = NULL, /* This could be implemented if need be. */ + .get_track_msf = _cdio_get_track_msf, + .read_mode2_sector = _read_mode2_sector, + .read_mode2_sectors = _read_mode2_sectors, + .set_arg = _cdio_set_arg, + .stat_size = _cdio_stat_size + }; + + _data = _cdio_malloc (sizeof (_img_private_t)); + _data->init = false; + + if (cue_name != NULL) { + char *source_name=strdup(cue_name); + int i=strlen(source_name)-strlen("cue"); + + if (i>0) { + if (cue_name[i]=='c' && cue_name[i+1]=='u' && cue_name[i+2]=='e') { + source_name[i++]='b'; source_name[i++]='i'; source_name[i++]='n'; + } + else if (cue_name[i]=='C' && cue_name[i+1]=='U' && cue_name[i+2]=='E') { + source_name[i++]='B'; source_name[i++]='I'; source_name[i++]='N'; + } + } + _cdio_set_arg (_data, "cue", cue_name); + _cdio_set_arg (_data, "source", source_name); + free(source_name); + } + + ret = cdio_new (_data, &_funcs); + if (ret == NULL) return NULL; + + if (_cdio_init(_data)) + return ret; + else { + _cdio_free (_data); + return NULL; + } +} + +bool +cdio_have_bincue (void) +{ + return true; +} diff --git a/lib/_cdio_bsdi.c b/lib/_cdio_bsdi.c new file mode 100644 index 00000000..14c03aa5 --- /dev/null +++ b/lib/_cdio_bsdi.c @@ -0,0 +1,441 @@ +/* + $Id: _cdio_bsdi.c,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2001 Herbert Valerio Riedel + Copyright (C) 2003 Rocky Bernstein + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "cdio_assert.h" +#include "cdio_private.h" +#include "util.h" +#include "sector.h" +#include "logging.h" + +#include +#include +#include + +static const char _rcsid[] = "$Id: _cdio_bsdi.c,v 1.1 2003/03/24 19:01:09 rocky Exp $"; + +#if HAVE_BSDI_CDROM + +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* reader */ + +typedef struct { + int fd; + + enum { + _AM_NONE, + _AM_READ_CD, + _AM_READ_10 + } access_mode; + + char *source_name; + + bool init; +} _img_private_t; + +static int +_scsi_cmd (int fd, struct scsi_user_cdb *sucp, const char *tag) +{ + u_char *cp; + + if (ioctl (fd, SCSIRAWCDB, sucp)) + { + vcd_error ("%s: ioctl (SCSIRAWCDB): %s", tag, strerror (errno)); + return 1; + } + if (sucp->suc_sus.sus_status) + { + cp = sucp->suc_sus.sus_sense; + vcd_info("%s: cmd: %x sus_status %d ", tag, sucp->suc_cdb[0], + sucp->suc_sus.sus_status); + vcd_info(" sense: %x %x %x %x %x %x %x %x", cp[0], cp[1], cp[2], + cp[3], cp[4], cp[5], cp[6], cp[7]); + return 1; + } + return 0; +} + +/*! + Initialize CD device. + */ +static bool +_cdio_init (_img_private_t *_obj) +{ + if (_obj->init) + return; + + _obj->fd = open (_obj->source_name, O_RDONLY, 0); + _obj->access_mode = _AM_READ_CD; + + if (_obj->fd < 0) + { + cdio_error ("open (%s): %s", _obj->source_name, strerror (errno)); + return false; + } + + _obj->init = true; + _obj->toc_init = false; + return true; +} + + +/*! + Release and free resources associated with cd. + */ +static void +_cdio_free (void *user_data) +{ + _img_private_t *_obj = user_data; + + free (_obj->source_name); + + if (_obj->fd) + close (_obj->fd); + + free (_obj); +} + +static int +_set_bsize (int fd, unsigned bsize) +{ + struct + { + uint8_t reserved1; + uint8_t medium; + uint8_t reserved2; + uint8_t block_desc_length; + uint8_t density; + uint8_t number_of_blocks_hi; + uint8_t number_of_blocks_med; + uint8_t number_of_blocks_lo; + uint8_t reserved3; + uint8_t block_length_hi; + uint8_t block_length_med; + uint8_t block_length_lo; + } mh; + + struct scsi_user_cdb suc; + + memset (&mh, 0, sizeof (mh)); + memset (&suc, 0, sizeof (struct scsi_user_cdb)); + + suc.suc_cdb[0] = 0x15; + suc.suc_cdb[1] = 1 << 4; + suc.suc_cdb[4] = 12; + suc.suc_cdblen = 6;; + + suc.suc_data = (u_char *)&mh; + suc.suc_datalen = sizeof (mh); + + suc.suc_timeout = 500; + suc.suc_flags = SUC_WRITE; + + mh.block_desc_length = 0x08; + mh.block_length_hi = (bsize >> 16) & 0xff; + mh.block_length_med = (bsize >> 8) & 0xff; + mh.block_length_lo = (bsize >> 0) & 0xff; + + return _scsi_cmd (fd, &suc, "_set_bsize"); +} + +static int +_read_mode2 (int fd, void *buf, uint32_t lba, unsigned nblocks, + bool _workaround) +{ + struct scsi_user_cdb suc; + int retval = 0; + + memset (&suc, 0, sizeof (struct scsi_user_cdb)); + + suc.suc_cdb[0] = (_workaround + ? 0x28 /* CMD_READ_10 */ + : 0xbe /* CMD_READ_CD */); + + if (!_workaround) + suc.suc_cdb[1] = 0; /* sector size mode2 */ + + suc.suc_cdb[2] = (lba >> 24) & 0xff; + suc.suc_cdb[3] = (lba >> 16) & 0xff; + suc.suc_cdb[4] = (lba >> 8) & 0xff; + suc.suc_cdb[5] = (lba >> 0) & 0xff; + + suc.suc_cdb[6] = (nblocks >> 16) & 0xff; + suc.suc_cdb[7] = (nblocks >> 8) & 0xff; + suc.suc_cdb[8] = (nblocks >> 0) & 0xff; + + if (!_workaround) + suc.suc_cdb[9] = 0x58; /* 2336 mode2 mixed form */ + + suc.suc_cdblen = _workaround ? 10 : 12; + + suc.suc_data = buf; + suc.suc_datalen = 2336 * nblocks; + + suc.suc_timeout = 500; + suc.suc_flags = SUC_READ; + + if (_workaround) + { + if ((retval = _set_bsize (fd, 2336))) + goto out; + + if ((retval = _scsi_cmd(fd, &suc, "_read_mode2_workaround"))) + { + _set_bsize (fd, 2048); + goto out; + } + retval = _set_bsize (fd, 2048); + } + else + retval = _scsi_cmd(fd, &suc, "_read_mode2"); + + out: + return retval; +} + +static int +_read_mode2_sector (void *user_data, void *data, uint32_t lsn, bool form2) +{ + _img_private_t *_obj = user_data; + + _cdio_init (_obj); + + if (form2) + { + retry: + switch (_obj->access_mode) + { + case _AM_NONE: + vcd_error ("no way to read mode2"); + return 1; + break; + + case _AM_READ_CD: + case _AM_READ_10: + if (_read_mode2 (_obj->fd, data, lsn, 1, + (_obj->access_mode == _AM_READ_10))) + { + if (_obj->access_mode == _AM_READ_CD) + { + vcd_info ("READ_CD failed; switching to READ_10 mode..."); + _obj->access_mode = _AM_READ_10; + goto retry; + } + else + { + vcd_info ("READ_10 failed; no way to read mode2 left."); + _obj->access_mode = _AM_NONE; + goto retry; + } + return 1; + } + break; + } + } + else + { + char buf[M2RAW_SIZE] = { 0, }; + int retval; + + if ((retval = _read_mode2_sector (_obj, buf, lsn, true))) + return retval; + + memcpy (data, buf + 8, M2F1_SECTOR_SIZE); + } + + return 0; +} + +static const u_char scsi_cdblen[8] = {6, 10, 10, 12, 12, 12, 10, 10}; + +static uint32_t +_cdio_stat_size (void *user_data) +{ + _img_private_t *_obj = user_data; + + struct scsi_user_cdb suc; + uint8_t buf[12] = { 0, }; + + uint32_t retval; + + _cdio_init(_obj); + + memset (&suc, 0, sizeof (struct scsi_user_cdb)); + + suc.suc_cdb[0] = 0x43; /* CMD_READ_TOC_PMA_ATIP */ + suc.suc_cdb[1] = 0; /* lba; msf: 0x2 */ + suc.suc_cdb[6] = 0xaa; /* CDROM_LEADOUT */ + suc.suc_cdb[8] = 12; /* ? */ + suc.suc_cdblen = 10; + + suc.suc_data = buf; + suc.suc_datalen = sizeof (buf); + + suc.suc_timeout = 500; + suc.suc_flags = SUC_READ; + + if (_scsi_cmd(_obj->fd, &suc, "_cdio_stat_size")) + return 0; + + { + int i; + + retval = 0; + for (i = 8; i < 12; i++) + { + retval <<= 8; + retval += buf[i]; + } + } + + return retval; +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +_cdio_get_arg (void *user_data, const char key[]) +{ + _img_private_t *_obj = user_data; + + if (!strcmp (key, "source")) { + return _obj->source_name; + } else if (!strcmp (key, "access-mode")) { + switch (_obj->access_mode) { + case _AM_READ_CD: + return "READ_CD"; + case _AM_READ_10: + return "READ_10"; + case _AM_NONE: + return "no access method"; + } + } + return NULL; +} + +/*! + Return a string containing the default VCD device if none is specified. + */ +static char * +_cdio_get_default_device() +{ + return strdup(DEFAULT_CDIO_DEVICE); +} + +/*! + Set the key "arg" to "value" in source device. +*/ +static int +_cdio_set_arg (void *user_data, const char key[], const char value[]) +{ + _img_private_t *_obj = user_data; + + if (!strcmp (key, "source")) + { + if (!value) + return -2; + + free (_obj->source_name); + + _obj->source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + if (!strcmp(value, "READ_CD")) + _obj->access_mode = _AM_READ_CD; + else if (!strcmp(value, "READ_10")) + _obj->access_mode = _AM_READ_10; + else + cdio_error ("unknown access type: %s. ignored.", value); + } + else + return -1; + + return 0; +} + +#endif /* HAVE_BSDI_CDROM */ + +CdIo * +cdio_open_bsdi (const char *source_name) +{ + +#ifdef HAVE_BSDI_CDROM + _img_private_t *_data; + + cdio_funcs _funcs = { + .eject_media = _cdio_eject_media, + .free = _cdio_free, + .get_arg = _cdio_get_arg, + .get_default_device = _cdio_get_default_device, + .read_mode2_sector = _read_mode2_sector, + .set_arg = _cdio_set_arg, + .stat_size = _cdio_stat_size + }; + + _data = _cdio_malloc (sizeof (_img_private_t)); + _data->access_mode = _AM_READ_CD; + _data->init = false; + _data->fd = -1; + + _cdio_set_arg(_data, "source", (NULL == source_name) + ? DEFAULT_CDIO_DEVICE: source_name); + + ret = cdio_new (_data, &_funcs); + if (ret == NULL) return NULL; + + if (_cdio_init(_data)) + return ret; + else { + _cdio_free (_data); + return NULL; + } + +#else + return NULL; +#endif /* HAVE_BSDI_CDROM */ +} + + +bool +cdio_have_bsdi (void) +{ +#ifdef HAVE_BSDI_CDROM + return true; +#else + return false; +#endif /* HAVE_BSDI_CDROM */ +} + diff --git a/lib/_cdio_bsdi.h b/lib/_cdio_bsdi.h new file mode 100644 index 00000000..41d6f7a4 --- /dev/null +++ b/lib/_cdio_bsdi.h @@ -0,0 +1,29 @@ +/* + $Id: _cdio_bsdi.h,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2001 Herbert Valerio Riedel + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __VCD_IMAGE_BSDICD_H__ +#define __VCD_IMAGE_BSDICD_H__ + +#include + +VcdImageSource * +vcd_image_source_new_bsdicd (void); + +#endif /* __VCD_IMAGE_BSDICD_H__ */ diff --git a/lib/_cdio_linux.c b/lib/_cdio_linux.c new file mode 100644 index 00000000..16363207 --- /dev/null +++ b/lib/_cdio_linux.c @@ -0,0 +1,725 @@ +/* + $Id: _cdio_linux.c,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2001 Herbert Valerio Riedel + Copyright (C) 2002,2003 Rocky Bernstein + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file contains Linux-specific code and implements low-level + control of the CD drive. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: _cdio_linux.c,v 1.1 2003/03/24 19:01:09 rocky Exp $"; + +#include "cdio_assert.h" +#include "cdio_private.h" +#include "sector.h" +#include "util.h" + +#ifdef HAVE_LINUX_CDROM + +#if defined(HAVE_LINUX_VERSION_H) +# include +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,16) +# define __CDIO_LINUXCD_BUILD +# else +# error "You need a kernel greater than 2.2.16 to have CDROM support" +# endif +#else +# error "You need to have CDROM support" +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DEFAULT_CDIO_DEVICE "/dev/cdrom" + +#define TOTAL_TRACKS (_obj->tochdr.cdth_trk1) +#define FIRST_TRACK_NUM (_obj->tochdr.cdth_trk0) + +typedef struct { + int fd; + + int ioctls_debugged; /* for debugging */ + + enum { + _AM_NONE, + _AM_IOCTL, + _AM_READ_CD, + _AM_READ_10 + } access_mode; + + char *source_name; + + bool init; + + /* Track information */ + bool toc_init; /* if true, info below is valid. */ + struct cdrom_tochdr tochdr; + struct cdrom_tocentry tocent[100]; /* entry info for each track */ + +} _img_private_t; + +/*! + Initialize CD device. + */ +static bool +_cdio_init (_img_private_t *_obj) +{ + if (_obj->init) { + cdio_error ("init called more than once"); + return false; + } + + _obj->fd = open (_obj->source_name, O_RDONLY, 0); + + if (_obj->fd < 0) + { + cdio_error ("open (%s): %s", _obj->source_name, strerror (errno)); + return false; + } + + _obj->init = true; + _obj->toc_init = false; + return true; +} + +/*! + Release and free resources associated with cd. + */ +static void +_cdio_free (void *user_data) +{ + _img_private_t *_obj = user_data; + + if (NULL == _obj) return; + free (_obj->source_name); + + if (_obj->fd >= 0) + close (_obj->fd); + + free (_obj); +} + +static int +_set_bsize (int fd, unsigned bsize) +{ + struct cdrom_generic_command cgc; + + struct + { + uint8_t reserved1; + uint8_t medium; + uint8_t reserved2; + uint8_t block_desc_length; + uint8_t density; + uint8_t number_of_blocks_hi; + uint8_t number_of_blocks_med; + uint8_t number_of_blocks_lo; + uint8_t reserved3; + uint8_t block_length_hi; + uint8_t block_length_med; + uint8_t block_length_lo; + } mh; + + memset (&mh, 0, sizeof (mh)); + memset (&cgc, 0, sizeof (struct cdrom_generic_command)); + + cgc.cmd[0] = 0x15; + cgc.cmd[1] = 1 << 4; + cgc.cmd[4] = 12; + + cgc.buflen = sizeof (mh); + cgc.buffer = (void *) &mh; + + cgc.data_direction = CGC_DATA_WRITE; + + mh.block_desc_length = 0x08; + mh.block_length_hi = (bsize >> 16) & 0xff; + mh.block_length_med = (bsize >> 8) & 0xff; + mh.block_length_lo = (bsize >> 0) & 0xff; + + return ioctl (fd, CDROM_SEND_PACKET, &cgc); +} + +static int +__read_mode2 (int fd, void *buf, lba_t lba, unsigned nblocks, + bool _workaround) +{ + struct cdrom_generic_command cgc; + + memset (&cgc, 0, sizeof (struct cdrom_generic_command)); + + cgc.cmd[0] = _workaround ? GPCMD_READ_10 : GPCMD_READ_CD; + + cgc.cmd[2] = (lba >> 24) & 0xff; + cgc.cmd[3] = (lba >> 16) & 0xff; + cgc.cmd[4] = (lba >> 8) & 0xff; + cgc.cmd[5] = (lba >> 0) & 0xff; + + cgc.cmd[6] = (nblocks >> 16) & 0xff; + cgc.cmd[7] = (nblocks >> 8) & 0xff; + cgc.cmd[8] = (nblocks >> 0) & 0xff; + + if (!_workaround) + { + cgc.cmd[1] = 0; /* sector size mode2 */ + + cgc.cmd[9] = 0x58; /* 2336 mode2 */ + } + + cgc.buflen = 2336 * nblocks; + cgc.buffer = buf; + +#ifdef HAVE_LINUX_CDROM_TIMEOUT + cgc.timeout = 500; +#endif + cgc.data_direction = CGC_DATA_READ; + + if (_workaround) + { + int retval; + + if ((retval = _set_bsize (fd, 2336))) + return retval; + + if ((retval = ioctl (fd, CDROM_SEND_PACKET, &cgc))) + { + _set_bsize (fd, 2048); + return retval; + } + + if ((retval = _set_bsize (fd, 2048))) + return retval; + } + else + return ioctl (fd, CDROM_SEND_PACKET, &cgc); + + return 0; +} + +static int +_read_mode2 (int fd, void *buf, lba_t lba, unsigned nblocks, + bool _workaround) +{ + unsigned l = 0; + int retval = 0; + + while (nblocks > 0) + { + const unsigned nblocks2 = (nblocks > 25) ? 25 : nblocks; + void *buf2 = ((char *)buf ) + (l * 2336); + + retval |= __read_mode2 (fd, buf2, lba + l, nblocks2, _workaround); + + if (retval) + break; + + nblocks -= nblocks2; + l += nblocks2; + } + + return retval; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_read_mode2_sector (void *user_data, void *data, lsn_t lsn, + bool mode2_form2) +{ + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + struct cdrom_msf *msf = (struct cdrom_msf *) &buf; + msf_t _msf; + + _img_private_t *_obj = user_data; + + cdio_lba_to_msf (cdio_lsn_to_lba(lsn), &_msf); + msf->cdmsf_min0 = from_bcd8(_msf.m); + msf->cdmsf_sec0 = from_bcd8(_msf.s); + msf->cdmsf_frame0 = from_bcd8(_msf.f); + + if (_obj->ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (_obj->ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (_obj->ioctls_debugged < 75 + || (_obj->ioctls_debugged < (30 * 75) + && _obj->ioctls_debugged % 75 == 0) + || _obj->ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %2.2d:%2.2d:%2.2d", + msf->cdmsf_min0, msf->cdmsf_sec0, msf->cdmsf_frame0); + + _obj->ioctls_debugged++; + + retry: + switch (_obj->access_mode) + { + case _AM_NONE: + cdio_error ("no way to read mode2"); + return 1; + break; + + case _AM_IOCTL: + if (ioctl (_obj->fd, CDROMREADMODE2, &buf) == -1) + { + perror ("ioctl()"); + return 1; + /* exit (EXIT_FAILURE); */ + } + break; + + case _AM_READ_CD: + case _AM_READ_10: + if (_read_mode2 (_obj->fd, buf, lsn, 1, + (_obj->access_mode == _AM_READ_10))) + { + perror ("ioctl()"); + if (_obj->access_mode == _AM_READ_CD) + { + cdio_info ("READ_CD failed; switching to READ_10 mode..."); + _obj->access_mode = _AM_READ_10; + goto retry; + } + else + { + cdio_info ("READ_10 failed; switching to ioctl(CDROMREADMODE2) mode..."); + _obj->access_mode = _AM_IOCTL; + goto retry; + } + return 1; + } + break; + } + + if (mode2_form2) + memcpy (data, buf, M2RAW_SECTOR_SIZE); + else + memcpy (((char *)data), buf + 8, M2F1_SECTOR_SIZE); + + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode2_sectors (void *user_data, void *data, lsn_t lsn, + bool mode2_form2, unsigned nblocks) +{ + _img_private_t *_obj = user_data; + int i; + int retval; + + for (i = 0; i < nblocks; i++) { + if (mode2_form2) { + if ( (retval = _read_mode2_sector (_obj, + ((char *)data) + (M2RAW_SECTOR_SIZE * i), + lsn + i, true)) ) + return retval; + } else { + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + if ( (retval = _read_mode2_sector (_obj, buf, lsn + i, true)) ) + return retval; + + memcpy (((char *)data) + (M2F1_SECTOR_SIZE * i), buf + 8, + M2F1_SECTOR_SIZE); + } + } + return 0; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static uint32_t +_cdio_stat_size (void *user_data) +{ + _img_private_t *_obj = user_data; + + struct cdrom_tocentry tocent; + uint32_t size; + + tocent.cdte_track = CDROM_LEADOUT; + tocent.cdte_format = CDROM_LBA; + if (ioctl (_obj->fd, CDROMREADTOCENTRY, &tocent) == -1) + { + perror ("ioctl(CDROMREADTOCENTRY)"); + exit (EXIT_FAILURE); + } + + size = tocent.cdte_addr.lba; + + return size; +} + +/*! + Set the key "arg" to "value" in source device. +*/ +static int +_cdio_set_arg (void *user_data, const char key[], const char value[]) +{ + _img_private_t *_obj = user_data; + + if (!strcmp (key, "source")) + { + if (!value) + return -2; + + free (_obj->source_name); + + _obj->source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + if (!strcmp(value, "IOCTL")) + _obj->access_mode = _AM_IOCTL; + else if (!strcmp(value, "READ_CD")) + _obj->access_mode = _AM_READ_CD; + else if (!strcmp(value, "READ_10")) + _obj->access_mode = _AM_READ_10; + else + cdio_error ("unknown access type: %s. ignored.", value); + } + else + return -1; + + return 0; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return false if successful or true if an error. +*/ +static bool +_cdio_read_toc (_img_private_t *_obj) +{ + int i; + + /* read TOC header */ + if ( ioctl(_obj->fd, CDROMREADTOCHDR, &_obj->tochdr) == -1 ) { + cdio_error("%s: %s\n", + "error in ioctl CDROMREADTOCHDR", strerror(errno)); + return false; + } + + /* read individual tracks */ + for (i= FIRST_TRACK_NUM; i<=TOTAL_TRACKS; i++) { + _obj->tocent[i-1].cdte_track = i; + _obj->tocent[i-1].cdte_format = CDROM_MSF; + if ( ioctl(_obj->fd, CDROMREADTOCENTRY, &_obj->tocent[i-1]) == -1 ) { + cdio_error("%s %d: %s\n", + "error in ioctl CDROMREADTOCENTRY for track", + i, strerror(errno)); + return false; + } + /**** + struct cdrom_msf0 *msf= &_obj->tocent[i-1].cdte_addr.msf; + + fprintf (stdout, "--- track# %d (msf %2.2x:%2.2x:%2.2x)\n", + i, msf->minute, msf->second, msf->frame); + ****/ + + } + + /* read the lead-out track */ + _obj->tocent[TOTAL_TRACKS].cdte_track = CDROM_LEADOUT; + _obj->tocent[TOTAL_TRACKS].cdte_format = CDROM_MSF; + + if (ioctl(_obj->fd, CDROMREADTOCENTRY, + &_obj->tocent[TOTAL_TRACKS]) == -1 ) { + cdio_error("%s: %s\n", + "error in ioctl CDROMREADTOCENTRY for lead-out", + strerror(errno)); + return false; + } + + /* + struct cdrom_msf0 *msf= &_obj->tocent[TOTAL_TRACKS].cdte_addr.msf; + + fprintf (stdout, "--- track# %d (msf %2.2x:%2.2x:%2.2x)\n", + i, msf->minute, msf->second, msf->frame); + */ + + return true; +} + +/*! + Eject media in CD drive. If successful, as a side effect we + also free obj. + */ +static int +_cdio_eject_media (void *user_data) { + + _img_private_t *_obj = user_data; + int ret, status; + + if (_obj->fd > -1) { + if((status = ioctl(_obj->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) > 0) { + switch(status) { + case CDS_TRAY_OPEN: + if((ret = ioctl(_obj->fd, CDROMCLOSETRAY)) != 0) { + cdio_error ("CDROMCLOSETRAY failed: %s\n", strerror(errno)); + } + break; + case CDS_DISC_OK: + if((ret = ioctl(_obj->fd, CDROMEJECT)) != 0) { + cdio_error("CDROMEJECT failed: %s\n", strerror(errno)); + } + break; + } + _cdio_free((void *) _obj); + return 0; + } else { + cdio_error ("CDROM_DRIVE_STATUS failed: %s\n", strerror(errno)); + _cdio_free((void *) _obj); + return 1; + } + } + return 2; +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +_cdio_get_arg (void *user_data, const char key[]) +{ + _img_private_t *_obj = user_data; + + if (!strcmp (key, "source")) { + return _obj->source_name; + } else if (!strcmp (key, "access-mode")) { + switch (_obj->access_mode) { + case _AM_IOCTL: + return "ioctl"; + case _AM_READ_CD: + return "READ_CD"; + case _AM_READ_10: + return "READ_10"; + case _AM_NONE: + return "no access method"; + } + } + return NULL; +} + +/*! + Return a string containing the default VCD device if none is specified. + */ +static char * +_cdio_get_default_device() +{ + return strdup(DEFAULT_CDIO_DEVICE); +} + +/*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_t +_cdio_get_first_track_num(void *user_data) +{ + _img_private_t *_obj = user_data; + + if (!_obj->toc_init) _cdio_read_toc (_obj) ; + + return FIRST_TRACK_NUM; +} + +/*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_t +_cdio_get_num_tracks(void *user_data) +{ + _img_private_t *_obj = user_data; + + if (!_obj->toc_init) _cdio_read_toc (_obj) ; + + return TOTAL_TRACKS; +} + +/*! + Get format of track. +*/ +static track_format_t +_cdio_get_track_format(void *user_data, track_t track_num) +{ + _img_private_t *_obj = user_data; + + if (!_obj->toc_init) _cdio_read_toc (_obj) ; + + if (track_num > TOTAL_TRACKS || track_num == 0) + return TRACK_FORMAT_ERROR; + + /* This is pretty much copied from the "badly broken" cdrom_count_tracks + in linux/cdrom.c. + */ + if (_obj->tocent[track_num-1].cdte_ctrl & CDROM_DATA_TRACK) { + if (_obj->tocent[track_num-1].cdte_format == 0x10) + return TRACK_FORMAT_CDI; + else if (_obj->tocent[track_num-1].cdte_format == 0x20) + return TRACK_FORMAT_XA; + else + return TRACK_FORMAT_DATA; + } else + return TRACK_FORMAT_AUDIO; + +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +static bool +_cdio_get_track_green(void *user_data, track_t track_num) +{ + _img_private_t *_obj = user_data; + + if (!_obj->toc_init) _cdio_read_toc (_obj) ; + + if (track_num == CDIO_LEADOUT_TRACK) track_num = TOTAL_TRACKS+1; + + if (track_num > TOTAL_TRACKS+1 || track_num == 0) + return false; + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cdinfo for a while. + */ + return ((_obj->tocent[track_num-1].cdte_ctrl & 2) != 0); +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + track_num in obj. Track numbers start at 1. + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +static bool +_cdio_get_track_msf(void *user_data, track_t track_num, msf_t *msf) +{ + _img_private_t *_obj = user_data; + + if (NULL == msf) return false; + + if (!_obj->toc_init) _cdio_read_toc (_obj) ; + + if (track_num == CDIO_LEADOUT_TRACK) track_num = TOTAL_TRACKS+1; + + if (track_num > TOTAL_TRACKS+1 || track_num == 0) { + return false; + } else { + struct cdrom_msf0 *msf0= &_obj->tocent[track_num-1].cdte_addr.msf; + msf->m = to_bcd8(msf0->minute); + msf->s = to_bcd8(msf0->second); + msf->f = to_bcd8(msf0->frame); + return true; + } +} + +#endif /* HAVE_LINUX_CDROM */ + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_linux (const char *source_name) +{ + +#ifdef HAVE_LINUX_CDROM + CdIo *ret; + _img_private_t *_data; + + cdio_funcs _funcs = { + .eject_media = _cdio_eject_media, + .free = _cdio_free, + .get_arg = _cdio_get_arg, + .get_default_device = _cdio_get_default_device, + .get_first_track_num= _cdio_get_first_track_num, + .get_num_tracks = _cdio_get_num_tracks, + .get_track_format = _cdio_get_track_format, + .get_track_green = _cdio_get_track_green, + .get_track_lba = NULL, /* This could be implemented if need be. */ + .get_track_msf = _cdio_get_track_msf, + .read_mode2_sector = _read_mode2_sector, + .read_mode2_sectors = _read_mode2_sectors, + .set_arg = _cdio_set_arg, + .stat_size = _cdio_stat_size + }; + + _data = _cdio_malloc (sizeof (_img_private_t)); + _data->access_mode = _AM_READ_CD; + _data->init = false; + _data->fd = -1; + + _cdio_set_arg(_data, "source", (NULL == source_name) + ? DEFAULT_CDIO_DEVICE: source_name); + + ret = cdio_new (_data, &_funcs); + if (ret == NULL) return NULL; + + if (_cdio_init(_data)) + return ret; + else { + _cdio_free (_data); + return NULL; + } + +#else + return NULL; +#endif /* HAVE_LINUX_CDROM */ + +} + +bool +cdio_have_linux (void) +{ +#ifdef HAVE_LINUX_CDROM + return true; +#else + return false; +#endif /* HAVE_LINUX_CDROM */ +} + diff --git a/lib/_cdio_nrg.c b/lib/_cdio_nrg.c new file mode 100644 index 00000000..3eb86e25 --- /dev/null +++ b/lib/_cdio_nrg.c @@ -0,0 +1,770 @@ +/* + $Id: _cdio_nrg.c,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2001,2003 Herbert Valerio Riedel + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*! This code implements low-level access functions for the Nero native + CD-image format residing inside a disk file (*.nrg). +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "cdio_assert.h" +#include "bytesex.h" +#include "ds.h" +#include "cdio_private.h" +#include "logging.h" +#include "sector.h" +#include "util.h" +#include "_cdio_stdio.h" + +static const char _rcsid[] = "$Id: _cdio_nrg.c,v 1.1 2003/03/24 19:01:09 rocky Exp $"; + +/* structures used */ + +/* this ugly image format is typical for lazy win32 programmers... at + least structure were set big endian, so at reverse + engineering wasn't such a big headache... */ + +PRAGMA_BEGIN_PACKED +typedef struct { + uint32_t start GNUC_PACKED; + uint32_t length GNUC_PACKED; + uint32_t type GNUC_PACKED; /* only 0x3 seen so far... + -> MIXED_MODE2 2336 blocksize */ + uint32_t start_lsn GNUC_PACKED; /* does not include any pre-gaps! */ + uint32_t _unknown GNUC_PACKED; /* wtf is this for? -- always zero... */ +} _etnf_array_t; + +/* finally they realized that 32bit offsets are a bit outdated for IA64 *eg* */ +typedef struct { + uint64_t start GNUC_PACKED; + uint64_t length GNUC_PACKED; + uint32_t type GNUC_PACKED; + uint32_t start_lsn GNUC_PACKED; + uint64_t _unknown GNUC_PACKED; /* wtf is this for? -- always zero... */ +} _etn2_array_t; + +typedef struct { + uint8_t _unknown1 GNUC_PACKED; /* 0x41 == 'A' */ + uint8_t track GNUC_PACKED; /* binary or BCD?? */ + uint8_t index GNUC_PACKED; /* makes 0->1 transitions */ + uint8_t _unknown2 GNUC_PACKED; /* ?? */ + uint32_t lsn GNUC_PACKED; +} _cuex_array_t; + +typedef struct { + uint32_t id GNUC_PACKED; + uint32_t len GNUC_PACKED; + char data[EMPTY_ARRAY_SIZE] GNUC_PACKED; +} _chunk_t; + +PRAGMA_END_PACKED + +/* to be converted into BE */ +#define CUEX_ID 0x43554558 +#define CUES_ID 0x43554553 +#define DAOX_ID 0x44414f58 +#define DAOI_ID 0x44414f49 +#define END1_ID 0x454e4421 +#define ETN2_ID 0x45544e32 +#define ETNF_ID 0x45544e46 +#define NER5_ID 0x4e455235 +#define NERO_ID 0x4e45524f +#define SINF_ID 0x53494e46 + +/* reader */ + +#define DEFAULT_CDIO_DEVICE "image.nrg" + +typedef struct { + int blocksize; + int track_num; /* Probably is index+1 */ + msf_t start_msf; + int start_index; + int secsize; /* Number of sectors in track. Does not + include pregap before next entry. */ + int flags; /* "DCP", "4CH", "PRE" */ + track_format_t track_format; + bool track_green; +} track_info_t; + +/* + Link element of track structure as a linked list. + Possibly redundant with above track_info_t */ +typedef struct { + uint32_t start_lsn; + uint32_t secsize; /* Number of sectors in track. Does not + include pregap before next entry. */ + uint64_t img_offset; /* Bytes offset from beginning of disk image file.*/ +} _mapping_t; + + +typedef struct { + bool sector_2336_flag; + CdioDataSource *nrg_src; + char *source_name; + track_info_t tocent[100]; /* entry info for each track */ + track_t total_tracks; /* number of tracks in image */ + track_t first_track_num; /* track number of first track */ + CdioList *mapping; /* List of track information */ + uint32_t size; + bool init; +} _img_private_t; + +static bool _cdio_parse_nero_footer (_img_private_t *_obj); +static uint32_t _cdio_stat_size (void *user_data); + +/*! + Release and free resources used here. + */ +static void +_cdio_free (void *user_data) +{ + _img_private_t *_obj = user_data; + + free(_obj->source_name); + + if (_obj->nrg_src) + cdio_stream_destroy (_obj->nrg_src); + + _cdio_list_free (_obj->mapping, true); + + free (_obj); +} + +/* Updates internal track TOC, so we can later + simulate ioctl(CDROMREADTOCENTRY). + */ +static void +_register_mapping (_img_private_t *_obj, lsn_t start_lsn, uint32_t secsize, + uint64_t img_offset, uint32_t blocksize) +{ + const int track_num=_obj->total_tracks; + _mapping_t *_map = _cdio_malloc (sizeof (_mapping_t)); + + if (!_obj->mapping) + _obj->mapping = _cdio_list_new (); + + _cdio_list_append (_obj->mapping, _map); + _map->start_lsn = start_lsn; + _map->secsize = secsize; + _map->img_offset = img_offset; + + _obj->size = MAX (_obj->size, (start_lsn + secsize)); + + /* Update _obj->tocent[track_num] and track_num These structures are + in a sense redundant witht the obj->mapping list. Perhaps one + or the other can be eliminated. + */ + + cdio_lba_to_msf (cdio_lsn_to_lba(start_lsn), + &_obj->tocent[track_num].start_msf); + + _obj->tocent[track_num].track_num = track_num+1; + _obj->tocent[track_num].blocksize = blocksize; + _obj->tocent[track_num].secsize = secsize; + _obj->tocent[track_num].secsize = secsize; + + _obj->total_tracks++; + + /* FIXME: These are probably not right. Probably we need to look at + "type." But this hasn't been reverse engineered yet. + */ + _obj->tocent[track_num].track_format= TRACK_FORMAT_XA; + _obj->tocent[track_num].track_green = true; + + + /* cdio_debug ("map: %d +%d -> %ld", start_lsn, secsize, img_offset); */ +} + + +/* + Disk and track information for a Nero file are located at the end + of the file. This routine extracts that information. + */ +static bool +_cdio_parse_nero_footer (_img_private_t *_obj) +{ + long unsigned int footer_start; + long unsigned int size; + char *footer_buf = NULL; + + if (_obj->size) + return 0; + + size = cdio_stream_stat (_obj->nrg_src); + + { +PRAGMA_BEGIN_PACKED + union { + struct { + uint32_t __x GNUC_PACKED; + uint32_t ID GNUC_PACKED; + uint32_t footer_ofs GNUC_PACKED; + } v50; + struct { + uint32_t ID GNUC_PACKED; + uint64_t footer_ofs GNUC_PACKED; + } v55; + } buf; +PRAGMA_END_PACKED + + cdio_assert (sizeof (buf) == 12); + + cdio_stream_seek (_obj->nrg_src, size - sizeof (buf)); + cdio_stream_read (_obj->nrg_src, (void *) &buf, sizeof (buf), 1); + + if (buf.v50.ID == UINT32_TO_BE (0x4e45524f)) /* "NERO" */ + { + cdio_info ("detected v50 (32bit offsets) NRG magic"); + footer_start = uint32_to_be (buf.v50.footer_ofs); + } + else if (buf.v55.ID == UINT32_TO_BE (0x4e455235)) /* "NER5" */ + { + cdio_info ("detected v55 (64bit offsets) NRG magic"); + footer_start = uint64_from_be (buf.v55.footer_ofs); + } + else + { + cdio_error ("Image not recognized as either v50 or v55 type NRG"); + return -1; + } + + cdio_debug ("nrg footer start = %ld, length = %ld", + (long) footer_start, (long) (size - footer_start)); + + cdio_assert (IN ((size - footer_start), 0, 4096)); + + footer_buf = _cdio_malloc (size - footer_start); + + cdio_stream_seek (_obj->nrg_src, footer_start); + cdio_stream_read (_obj->nrg_src, footer_buf, size - footer_start, 1); + } + + { + int pos = 0; + + while (pos < size - footer_start) { + _chunk_t *chunk = (void *) (footer_buf + pos); + + bool break_out = false; + + switch (UINT32_FROM_BE (chunk->id)) { + case CUES_ID: { /* "CUES" */ + unsigned entries = UINT32_FROM_BE (chunk->len); + _cuex_array_t *_entries = (void *) chunk->data; + + cdio_assert (_obj->mapping == NULL); + + cdio_assert (sizeof (_cuex_array_t) == 8); + cdio_assert ( UINT32_FROM_BE(chunk->len) % sizeof(_cuex_array_t) + == 0 ); + + entries /= sizeof (_cuex_array_t); + + cdio_info ("CUES type image detected"); + + { + lsn_t lsn = UINT32_FROM_BE (_entries[0].lsn); + int idx; + + /*cdio_assert (lsn == 0?);*/ + + _obj->total_tracks = 0; + _obj->first_track_num = 1; + for (idx = 1; idx < entries-1; idx += 2) { + lsn_t lsn2; + + cdio_assert (_entries[idx].index == 0); + cdio_assert (_entries[idx].track == _entries[idx + 1].track); + + lsn = UINT32_FROM_BE (_entries[idx].lsn); + lsn2 = UINT32_FROM_BE (_entries[idx + 1].lsn); + + _register_mapping (_obj, lsn, lsn2 - lsn, + (lsn+CDIO_PREGAP_SECTORS) * CD_RAW_SECTOR_SIZE, + CD_RAW_SECTOR_SIZE); + } + } + break; + } + + case CUEX_ID: /* "CUEX" */ { + unsigned entries = UINT32_FROM_BE (chunk->len); + _cuex_array_t *_entries = (void *) chunk->data; + + cdio_assert (_obj->mapping == NULL); + + cdio_assert ( sizeof (_cuex_array_t) == 8 ); + cdio_assert ( UINT32_FROM_BE (chunk->len) % sizeof(_cuex_array_t) + == 0 ); + + entries /= sizeof (_cuex_array_t); + + cdio_info ("DAO type image detected"); + + { + uint32_t lsn = UINT32_FROM_BE (_entries[0].lsn); + int idx; + + cdio_assert (lsn == 0xffffff6a); + + for (idx = 2; idx < entries; idx += 2) { + lsn_t lsn2; + + cdio_assert (_entries[idx].index == 1); + cdio_assert (_entries[idx].track != _entries[idx + 1].track); + + lsn = UINT32_FROM_BE (_entries[idx].lsn); + lsn2 = UINT32_FROM_BE (_entries[idx + 1].lsn); + + _register_mapping (_obj, lsn, lsn2 - lsn, + (lsn + CDIO_PREGAP_SECTORS)*M2RAW_SECTOR_SIZE, + M2RAW_SECTOR_SIZE); + } + } + break; + } + + case DAOI_ID: /* "DAOI" */ + cdio_debug ("DAOI tag detected..."); + break; + case DAOX_ID: /* "DAOX" */ + cdio_debug ("DAOX tag detected..."); + break; + + case NER5_ID: /* "NER5" */ + cdio_error ("unexpected nrg magic ID NER5 detected"); + return -1; + break; + + case NERO_ID: /* "NER0" */ + cdio_error ("unexpected nrg magic ID NER0 detected"); + return -1; + break; + + case END1_ID: /* "END!" */ + cdio_debug ("nrg end tag detected"); + break_out = true; + break; + + case ETNF_ID: /* "ETNF" */ { + unsigned entries = UINT32_FROM_BE (chunk->len); + _etnf_array_t *_entries = (void *) chunk->data; + + cdio_assert (_obj->mapping == NULL); + + cdio_assert ( sizeof (_etnf_array_t) == 20 ); + cdio_assert ( UINT32_FROM_BE(chunk->len) % sizeof(_etnf_array_t) + == 0 ); + + entries /= sizeof (_etnf_array_t); + + cdio_info ("SAO type image (ETNF) detected"); + + _obj->sector_2336_flag = true; + + { + int idx; + for (idx = 0; idx < entries; idx++) { + uint32_t _len = UINT32_FROM_BE (_entries[idx].length); + uint32_t _start = UINT32_FROM_BE (_entries[idx].start_lsn); + uint32_t _start2 = UINT32_FROM_BE (_entries[idx].start); + + cdio_assert (UINT32_FROM_BE (_entries[idx].type) == 3); + cdio_assert (_len % M2RAW_SECTOR_SIZE == 0); + + _len /= M2RAW_SECTOR_SIZE; + + cdio_assert (_start * M2RAW_SECTOR_SIZE == _start2); + + _start += idx * CDIO_PREGAP_SECTORS; + + _register_mapping (_obj, _start, _len, _start2, M2RAW_SECTOR_SIZE); + } + } + break; + } + + case ETN2_ID: { /* "ETN2", same as above, but with 64bit stuff instead */ + unsigned entries = uint32_from_be (chunk->len); + _etn2_array_t *_entries = (void *) chunk->data; + + cdio_assert (_obj->mapping == NULL); + + cdio_assert (sizeof (_etn2_array_t) == 32); + cdio_assert (uint32_from_be (chunk->len) % sizeof (_etn2_array_t) == 0); + + entries /= sizeof (_etn2_array_t); + + cdio_info ("SAO type image (ETN2) detected"); + + _obj->sector_2336_flag = true; + + { + int idx; + for (idx = 0; idx < entries; idx++) { + uint32_t _len = uint64_from_be (_entries[idx].length); + uint32_t _start = uint32_from_be (_entries[idx].start_lsn); + uint32_t _start2 = uint64_from_be (_entries[idx].start); + + cdio_assert (uint32_from_be (_entries[idx].type) == 3); + cdio_assert (_len % M2RAW_SECTOR_SIZE == 0); + + _len /= M2RAW_SECTOR_SIZE; + + cdio_assert (_start * M2RAW_SECTOR_SIZE == _start2); + + _start += idx * CDIO_PREGAP_SECTORS; + + _register_mapping (_obj, _start, _len, _start2, M2RAW_SECTOR_SIZE); + } + } + break; + } + + case SINF_ID: { /* "SINF" */ + + uint32_t *_sessions = (void *) chunk->data; + + cdio_assert (UINT32_FROM_BE (chunk->len) == 4); + + cdio_debug ("SINF: %d sessions", UINT32_FROM_BE (*_sessions)); + } + break; + + default: + cdio_warn ("unknown tag %8.8x seen", UINT32_FROM_BE (chunk->id)); + break; + } + + if (break_out) + break; + + pos += 8; + pos += UINT32_FROM_BE (chunk->len); + } + } + + /* Fake out leadout track. */ + /* Don't use _cdio_stat_size since that will lead to recursion since + we haven't fully initialized things yet. + */ + cdio_lsn_to_msf (_obj->size, &_obj->tocent[_obj->total_tracks].start_msf); + + return 0; +} + +/*! + Initialize image structures. + */ +static bool +_cdio_init (_img_private_t *_obj) +{ + if (_obj->init) { + cdio_error ("init called more than once"); + return false; + } + + if (!(_obj->nrg_src = cdio_stdio_new (_obj->source_name))) { + cdio_error ("init failed"); + return false; + } + + _cdio_parse_nero_footer (_obj); + _obj->init = true; + return true; + +} + +static uint32_t +_cdio_stat_size (void *user_data) +{ + _img_private_t *_obj = user_data; + + return _obj->size; +} + + static int +_read_mode2_sector (void *user_data, void *data, lsn_t lsn, bool form2) +{ + _img_private_t *_obj = user_data; + char buf[CD_RAW_SECTOR_SIZE] = { 0, }; + CdioListNode *node; + + if (lsn >= _obj->size) + { + cdio_warn ("trying to read beyond image size (%d >= %u)", lsn, + _obj->size); + return -1; + } + + _CDIO_LIST_FOREACH (node, _obj->mapping) + { + _mapping_t *_map = _cdio_list_node_data (node); + + if (IN (lsn, _map->start_lsn, (_map->start_lsn + _map->secsize - 1))) + { + long img_offset = _map->img_offset; + int blocksize = _obj->sector_2336_flag + ? M2RAW_SECTOR_SIZE : CD_RAW_SECTOR_SIZE; + + img_offset += (lsn - _map->start_lsn) * blocksize; + + cdio_stream_seek (_obj->nrg_src, img_offset); + cdio_stream_read (_obj->nrg_src, + _obj->sector_2336_flag ? (buf + 12 + 4) : buf, + blocksize, 1); + + break; + } + } + + if (!node) + cdio_warn ("reading into pre gap (lsn %d)", lsn); + + if (form2) + memcpy (data, buf + 12 + 4, M2RAW_SECTOR_SIZE); + else + memcpy (data, buf + 12 + 4 + 8, M2F1_SECTOR_SIZE); + + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode2_sectors (void *user_data, void *data, uint32_t lsn, + bool mode2_form2, unsigned nblocks) +{ + _img_private_t *_obj = user_data; + int i; + int retval; + + for (i = 0; i < nblocks; i++) { + if (mode2_form2) { + if ( (retval = _read_mode2_sector (_obj, + ((char *)data) + (M2RAW_SECTOR_SIZE * i), + lsn + i, true)) ) + return retval; + } else { + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + if ( (retval = _read_mode2_sector (_obj, buf, lsn + i, true)) ) + return retval; + + memcpy (((char *)data) + (M2F1_SECTOR_SIZE * i), buf + 8, + M2F1_SECTOR_SIZE); + } + } + return 0; +} + +/*! + Set the device to use in I/O operations. +*/ +static int +_cdio_set_arg (void *user_data, const char key[], const char value[]) +{ + _img_private_t *_obj = user_data; + + if (!strcmp (key, "source")) + { + free (_obj->source_name); + + if (!value) + return -2; + + _obj->source_name = strdup (value); + } + else + return -1; + + return 0; +} + +/*! + Eject media -- there's nothing to do here. We always return 2. + also free obj. + */ +static int +_cdio_eject_media (void *user_data) { + /* Sort of a stub here. Perhaps log a message? */ + return 2; +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +_cdio_get_arg (void *user_data, const char key[]) +{ + _img_private_t *_obj = user_data; + + if (!strcmp (key, "source")) { + return _obj->source_name; + } + return NULL; +} + +/*! + Return a string containing the default VCD device if none is specified. + */ +static char * +_cdio_get_default_device() +{ + return strdup(DEFAULT_CDIO_DEVICE); +} + +/*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_t +_cdio_get_first_track_num(void *user_data) +{ + _img_private_t *_obj = user_data; + + return _obj->first_track_num; +} + +/*! + Return the number of tracks. We fake it an just say there's + one big track. +*/ +static track_t +_cdio_get_num_tracks(void *user_data) +{ + _img_private_t *_obj = user_data; + + return _obj->total_tracks; +} + +/*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_format_t +_cdio_get_track_format(void *user_data, track_t track_num) +{ + _img_private_t *_obj = user_data; + + if (track_num > _obj->total_tracks || track_num == 0) + return TRACK_FORMAT_ERROR; + + return _obj->tocent[track_num-1].track_format; +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +static bool +_cdio_get_track_green(void *user_data, track_t track_num) +{ + _img_private_t *_obj = user_data; + + if (track_num > _obj->total_tracks || track_num == 0) + return false; + + return _obj->tocent[track_num-1].track_green; +} + +/*! + Return the starting MSF (minutes/secs/frames) for the track number + track_num in obj. Tracks numbers start at 1. +*/ +static bool +_cdio_get_track_msf(void *user_data, track_t track_num, msf_t *msf) +{ + _img_private_t *_obj = user_data; + + if (NULL == msf) return 1; + + if (track_num == CDIO_LEADOUT_TRACK) track_num = _obj->total_tracks+1; + + if (track_num <= _obj->total_tracks+1 && track_num != 0) { + *msf = _obj->tocent[track_num-1].start_msf; + return true; + } else + return false; +} + +CdIo * +cdio_open_nrg (const char *source_name) +{ + CdIo *ret; + _img_private_t *_data; + + cdio_funcs _funcs = { + .eject_media = _cdio_eject_media, + .free = _cdio_free, + .get_arg = _cdio_get_arg, + .get_default_device = _cdio_get_default_device, + .get_first_track_num= _cdio_get_first_track_num, + .get_num_tracks = _cdio_get_num_tracks, + .get_track_format = _cdio_get_track_format, + .get_track_green = _cdio_get_track_green, + .get_track_lba = NULL, /* This could be implemented if need be. */ + .get_track_msf = _cdio_get_track_msf, + .read_mode2_sector = _read_mode2_sector, + .read_mode2_sectors = _read_mode2_sectors, + .set_arg = _cdio_set_arg, + .stat_size = _cdio_stat_size, + }; + + _data = _cdio_malloc (sizeof (_img_private_t)); + _data->init = false; + + + _data->total_tracks = 0; + _data->first_track_num= 1; + _data->sector_2336_flag = false; + + _cdio_set_arg(_data, "source", (NULL == source_name) + ? DEFAULT_CDIO_DEVICE: source_name); + + ret = cdio_new (_data, &_funcs); + if (ret == NULL) return NULL; + + if (_cdio_init(_data)) + return ret; + else { + _cdio_free (_data); + return NULL; + } + +} + +bool +cdio_have_nrg (void) +{ + return true; +} diff --git a/lib/_cdio_stdio.c b/lib/_cdio_stdio.c new file mode 100644 index 00000000..711ff2ee --- /dev/null +++ b/lib/_cdio_stdio.c @@ -0,0 +1,177 @@ +/* + $Id: _cdio_stdio.c,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + Copyright (C) 2003 Rocky Bernstein + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "logging.h" +#include "util.h" +#include "_cdio_stream.h" +#include "_cdio_stdio.h" + +static const char _rcsid[] = "$Id: _cdio_stdio.c,v 1.1 2003/03/24 19:01:09 rocky Exp $"; + +#define CDIO_STDIO_BUFSIZE (128*1024) + +typedef struct { + char *pathname; + FILE *fd; + char *fd_buf; + off_t st_size; /* used only for source */ +} _UserData; + +static int +_stdio_open (void *user_data) +{ + _UserData *const ud = user_data; + + if ((ud->fd = fopen (ud->pathname, "rb"))) + { + ud->fd_buf = _cdio_malloc (CDIO_STDIO_BUFSIZE); + setvbuf (ud->fd, ud->fd_buf, _IOFBF, CDIO_STDIO_BUFSIZE); + } + + return (ud->fd == NULL); +} + +static int +_stdio_close(void *user_data) +{ + _UserData *const ud = user_data; + + if (fclose (ud->fd)) + cdio_error ("fclose (): %s", strerror (errno)); + + ud->fd = NULL; + + free (ud->fd_buf); + ud->fd_buf = NULL; + + return 0; +} + +static void +_stdio_free(void *user_data) +{ + _UserData *const ud = user_data; + + if (ud->pathname) + free(ud->pathname); + + if (ud->fd) /* should be NULL anyway... */ + _stdio_close(user_data); + + free(ud); +} + +static long +_stdio_seek(void *user_data, long offset) +{ + _UserData *const ud = user_data; + + if (fseek (ud->fd, offset, SEEK_SET)) + cdio_error ("fseek (): %s", strerror (errno)); + + return offset; +} + +static long +_stdio_stat(void *user_data) +{ + const _UserData *const ud = user_data; + + return ud->st_size; +} + +static long +_stdio_read(void *user_data, void *buf, long count) +{ + _UserData *const ud = user_data; + long read; + + read = fread(buf, 1, count, ud->fd); + + if (read != count) + { /* fixme -- ferror/feof */ + if (feof (ud->fd)) + { + cdio_debug ("fread (): EOF encountered"); + clearerr (ud->fd); + } + else if (ferror (ud->fd)) + { + cdio_error ("fread (): %s", strerror (errno)); + clearerr (ud->fd); + } + else + cdio_debug ("fread (): short read and no EOF?!?"); + } + + return read; +} + +CdioDataSource* +cdio_stdio_new(const char pathname[]) +{ + CdioDataSource *new_obj = NULL; + cdio_stream_io_functions funcs = { 0, }; + _UserData *ud = NULL; + struct stat statbuf; + + if (stat (pathname, &statbuf) == -1) + { + cdio_error ("could not stat() file `%s': %s", pathname, strerror (errno)); + return NULL; + } + + ud = _cdio_malloc (sizeof (_UserData)); + + ud->pathname = strdup(pathname); + ud->st_size = statbuf.st_size; /* let's hope it doesn't change... */ + + funcs.open = _stdio_open; + funcs.seek = _stdio_seek; + funcs.stat = _stdio_stat; + funcs.read = _stdio_read; + funcs.close = _stdio_close; + funcs.free = _stdio_free; + + new_obj = cdio_stream_new(ud, &funcs); + + return new_obj; +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/_cdio_stdio.h b/lib/_cdio_stdio.h new file mode 100644 index 00000000..21ef715a --- /dev/null +++ b/lib/_cdio_stdio.h @@ -0,0 +1,40 @@ +/* + $Id: _cdio_stdio.h,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + Copyright (C) 2003 Rocky Bernstein + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef __CDIO_STDIO_H__ +#define __CDIO_STDIO_H__ + +#include "_cdio_stream.h" + +CdioDataSource* +cdio_stdio_new(const char pathname[]); + +#endif /* __CDIO_STREAM_STDIO_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/_cdio_stream.c b/lib/_cdio_stream.c new file mode 100644 index 00000000..9268fef2 --- /dev/null +++ b/lib/_cdio_stream.c @@ -0,0 +1,156 @@ +/* + $Id: _cdio_stream.c,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include "cdio_assert.h" + +/* #define STREAM_DEBUG */ + +#include "logging.h" +#include "util.h" +#include "_cdio_stream.h" + +static const char _rcsid[] = "$Id: _cdio_stream.c,v 1.1 2003/03/24 19:01:09 rocky Exp $"; + +/* + * DataSource implementations + */ + +struct _CdioDataSource { + void* user_data; + cdio_stream_io_functions op; + int is_open; + long position; +}; + +static void +_cdio_stream_open_if_necessary(CdioDataSource *obj) +{ + cdio_assert (obj != NULL); + + if (!obj->is_open) { + if (obj->op.open(obj->user_data)) + cdio_error ("could not opening input stream..."); + else { +#ifdef STREAM_DEBUG + cdio_debug ("opened source..."); +#endif + obj->is_open = 1; + obj->position = 0; + } + } +} + +long +cdio_stream_seek(CdioDataSource* obj, long offset) +{ + cdio_assert (obj != NULL); + + _cdio_stream_open_if_necessary(obj); + + if (obj->position != offset) { +#ifdef STREAM_DEBUG + cdio_warn("had to reposition DataSource from %ld to %ld!", obj->position, offset); +#endif + obj->position = offset; + return obj->op.seek(obj->user_data, offset); + } + + return 0; +} + +CdioDataSource* +cdio_stream_new(void *user_data, const cdio_stream_io_functions *funcs) +{ + CdioDataSource *new_obj; + + new_obj = _cdio_malloc (sizeof (CdioDataSource)); + + new_obj->user_data = user_data; + memcpy(&(new_obj->op), funcs, sizeof(cdio_stream_io_functions)); + + return new_obj; +} + +long +cdio_stream_read(CdioDataSource* obj, void *ptr, long size, long nmemb) +{ + long read_bytes; + + cdio_assert (obj != NULL); + + _cdio_stream_open_if_necessary(obj); + + read_bytes = obj->op.read(obj->user_data, ptr, size*nmemb); + obj->position += read_bytes; + + return read_bytes; +} + +long +cdio_stream_stat(CdioDataSource* obj) +{ + cdio_assert (obj != NULL); + + _cdio_stream_open_if_necessary(obj); + + return obj->op.stat(obj->user_data); +} + +void +cdio_stream_close(CdioDataSource* obj) +{ + cdio_assert (obj != NULL); + + if (obj->is_open) { +#ifdef STREAM_DEBUG + cdio_debug ("closed source..."); +#endif + obj->op.close(obj->user_data); + obj->is_open = 0; + obj->position = 0; + } +} + +void +cdio_stream_destroy(CdioDataSource* obj) +{ + cdio_assert (obj != NULL); + + cdio_stream_close(obj); + + obj->op.free(obj->user_data); +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/_cdio_stream.h b/lib/_cdio_stream.h new file mode 100644 index 00000000..0e0b97b9 --- /dev/null +++ b/lib/_cdio_stream.h @@ -0,0 +1,91 @@ +/* + $Id: _cdio_stream.h,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + Copyright (C) 2003 Rocky Bernstein + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef __CDIO_STREAM_H__ +#define __CDIO_STREAM_H__ + +#include "cdio_types.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* typedef'ed IO functions prototypes */ + +typedef int(*cdio_data_open_t)(void *user_data); + +typedef long(*cdio_data_read_t)(void *user_data, void *buf, long count); + +typedef long(*cdio_data_seek_t)(void *user_data, long offset); + +typedef long(*cdio_data_stat_t)(void *user_data); + +typedef int(*cdio_data_close_t)(void *user_data); + +typedef void(*cdio_data_free_t)(void *user_data); + + +/* abstract data source */ + +typedef struct _CdioDataSource CdioDataSource; + +typedef struct { + cdio_data_open_t open; + cdio_data_seek_t seek; + cdio_data_stat_t stat; + cdio_data_read_t read; + cdio_data_close_t close; + cdio_data_free_t free; +} cdio_stream_io_functions; + +CdioDataSource* +cdio_stream_new(void *user_data, const cdio_stream_io_functions *funcs); + +long +cdio_stream_read(CdioDataSource* obj, void *ptr, long size, long nmemb); + +long +cdio_stream_seek(CdioDataSource* obj, long offset); + +long +cdio_stream_stat(CdioDataSource* obj); + +void +cdio_stream_destroy(CdioDataSource* obj); + +void +cdio_stream_close(CdioDataSource* obj); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_STREAM_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/_cdio_sunos.c b/lib/_cdio_sunos.c new file mode 100644 index 00000000..7eabf48f --- /dev/null +++ b/lib/_cdio_sunos.c @@ -0,0 +1,658 @@ +/* + $Id: _cdio_sunos.c,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2001 Herbert Valerio Riedel + Copyright (C) 2002,2003 Rocky Bernstein + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "cdio_assert.h" +#include "cdio_private.h" +#include "logging.h" +#include "sector.h" +#include "util.h" + +#ifdef HAVE_SOLARIS_CDROM + +static const char _rcsid[] = "$Id: _cdio_sunos.c,v 1.1 2003/03/24 19:01:09 rocky Exp $"; + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_CDIO_H +# include /* CDIOCALLOW etc... */ +#else +#error "You need to have CDROM support" +#endif + +#include +#include +#include + +#include +#include +#include + +#define DEFAULT_CDIO_DEVICE "/vol/dev/aliases/cdrom0" + +#define TOTAL_TRACKS (_obj->tochdr.cdth_trk1) +#define FIRST_TRACK_NUM (_obj->tochdr.cdth_trk0) + +/* reader */ + +typedef struct { + int fd; + + int ioctls_debugged; /* for debugging */ + + enum { + _AM_NONE, + _AM_SUN_CTRL_ATAPI, + _AM_SUN_CTRL_SCSI +#if FINISHED + _AM_READ_CD, + _AM_READ_10 +#endif + } access_mode; + + char *source_name; + + bool init; + + /* Track information */ + bool toc_init; /* if true, info below is valid. */ + struct cdrom_tochdr tochdr; + struct cdrom_tocentry tocent[100]; /* entry info for each track */ + +} _img_private_t; + +/*! + Initialize CD device. + */ +static bool +_cdio_init (_img_private_t *_obj) +{ + + struct dk_cinfo cinfo; + + _obj->fd = open (_obj->source_name, O_RDONLY, 0); + + if (_obj->fd < 0) + { + cdio_error ("open (%s): %s", _obj->source_name, strerror (errno)); + return false; + } + + /* + * CDROMCDXA/CDROMREADMODE2 are broken on IDE/ATAPI devices. + * Try to send MMC3 SCSI commands via the uscsi interface on + * ATAPI devices. + */ + if ( ioctl(_obj->fd, DKIOCINFO, &cinfo) == 0 + && ((strcmp(cinfo.dki_cname, "ide") == 0) + || (strncmp(cinfo.dki_cname, "pci", 3) == 0)) ) { + _obj->access_mode = _AM_SUN_CTRL_ATAPI; + } else { + _obj->access_mode = _AM_SUN_CTRL_SCSI; + } + + _obj->init = true; + _obj->toc_init = false; + + return true; +} + + +/*! + Release and free resources associated with cd. + */ +static void +_cdio_free (void *user_data) +{ + _img_private_t *_obj = user_data; + + if (NULL == _obj) return; + free (_obj->source_name); + + if (_obj->fd >= 0) + close (_obj->fd); + + free (_obj); +} + +/*! + Reads a single mode2 sector from cd device into data starting from lsn. + Returns 0 if no error. + */ +static int +_read_mode2_sector (void *user_data, void *data, lsn_t lsn, + bool mode2_form2) +{ + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + struct cdrom_msf *msf = (struct cdrom_msf *) &buf; + msf_t _msf; + + _img_private_t *_obj = user_data; + + cdio_lba_to_msf (cdio_lsn_to_lba(lsn), &_msf); + msf->cdmsf_min0 = from_bcd8(_msf.m); + msf->cdmsf_sec0 = from_bcd8(_msf.s); + msf->cdmsf_frame0 = from_bcd8(_msf.f); + + if (_obj->ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (_obj->ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (_obj->ioctls_debugged < 75 + || (_obj->ioctls_debugged < (30 * 75) + && _obj->ioctls_debugged % 75 == 0) + || _obj->ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %2.2d:%2.2d:%2.2d", + msf->cdmsf_min0, msf->cdmsf_sec0, msf->cdmsf_frame0); + + _obj->ioctls_debugged++; + + switch (_obj->access_mode) + { + case _AM_NONE: + cdio_error ("No way to read CD mode2."); + return 1; + break; + + case _AM_SUN_CTRL_SCSI: + if (ioctl (_obj->fd, CDROMREADMODE2, &buf) == -1) { + perror ("ioctl(..,CDROMREADMODE2,..)"); + return 1; + /* exit (EXIT_FAILURE); */ + } + break; + + case _AM_SUN_CTRL_ATAPI: + { + struct uscsi_cmd sc; + union scsi_cdb cdb; + int blocks = 1; + int sector_type; + int sync, header_code, user_data, edc_ecc, error_field; + int sub_channel; + + sector_type = 0; /* all types */ + /*sector_type = 1;*/ /* CD-DA */ + /*sector_type = 2;*/ /* mode1 */ + /*sector_type = 3;*/ /* mode2 */ + /*sector_type = 4;*/ /* mode2/form1 */ + /*sector_type = 5;*/ /* mode2/form2 */ + sync = 0; + header_code = 2; + user_data = 1; + edc_ecc = 0; + error_field = 0; + sub_channel = 0; + + memset(&cdb, 0, sizeof(cdb)); + memset(&sc, 0, sizeof(sc)); + cdb.scc_cmd = 0xBE; + cdb.cdb_opaque[1] = (sector_type) << 2; + cdb.cdb_opaque[2] = (lsn >> 24) & 0xff; + cdb.cdb_opaque[3] = (lsn >> 16) & 0xff; + cdb.cdb_opaque[4] = (lsn >> 8) & 0xff; + cdb.cdb_opaque[5] = lsn & 0xff; + cdb.cdb_opaque[6] = (blocks >> 16) & 0xff; + cdb.cdb_opaque[7] = (blocks >> 8) & 0xff; + cdb.cdb_opaque[8] = blocks & 0xff; + cdb.cdb_opaque[9] = (sync << 7) | + (header_code << 5) | + (user_data << 4) | + (edc_ecc << 3) | + (error_field << 1); + cdb.cdb_opaque[10] = sub_channel; + + sc.uscsi_cdb = (caddr_t)&cdb; + sc.uscsi_cdblen = 12; + sc.uscsi_bufaddr = (caddr_t) buf; + sc.uscsi_buflen = M2RAW_SECTOR_SIZE; + sc.uscsi_flags = USCSI_ISOLATE | USCSI_READ; + sc.uscsi_timeout = 20; + if (ioctl(_obj->fd, USCSICMD, &sc)) { + perror("USCSICMD: READ CD"); + return 1; + } + if (sc.uscsi_status) { + cdio_error("SCSI command failed with status %d\n", + sc.uscsi_status); + return 1; + } + break; + } + } + + if (mode2_form2) + memcpy (data, buf, M2RAW_SECTOR_SIZE); + else + memcpy (((char *)data), buf + 8, M2F1_SECTOR_SIZE); + + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode2_sectors (void *user_data, void *data, uint32_t lsn, + bool mode2_form2, unsigned nblocks) +{ + _img_private_t *_obj = user_data; + int i; + int retval; + + for (i = 0; i < nblocks; i++) { + if (mode2_form2) { + if ( (retval = _read_mode2_sector (_obj, + ((char *)data) + (M2RAW_SECTOR_SIZE * i), + lsn + i, true)) ) + return retval; + } else { + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + if ( (retval = _read_mode2_sector (_obj, buf, lsn + i, true)) ) + return retval; + + memcpy (((char *)data) + (M2F1_SECTOR_SIZE * i), buf + 8, + M2F1_SECTOR_SIZE); + } + } + return 0; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static uint32_t +_cdio_stat_size (void *user_data) +{ + _img_private_t *_obj = user_data; + + struct cdrom_tocentry tocent; + uint32_t size; + + tocent.cdte_track = CDROM_LEADOUT; + tocent.cdte_format = CDROM_LBA; + if (ioctl (_obj->fd, CDROMREADTOCENTRY, &tocent) == -1) + { + perror ("ioctl(CDROMREADTOCENTRY)"); + exit (EXIT_FAILURE); + } + + size = tocent.cdte_addr.lba; + + return size; +} + +/*! + Set the key "arg" to "value" in source device. +*/ +static int +_cdio_set_arg (void *user_data, const char key[], const char value[]) +{ + _img_private_t *_obj = user_data; + + if (!strcmp (key, "source")) + { + if (!value) + return -2; + + free (_obj->source_name); + + _obj->source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + if (!strcmp(value, "ATAPI")) + _obj->access_mode = _AM_SUN_CTRL_ATAPI; + else if (!strcmp(value, "SCSI")) + _obj->access_mode = _AM_SUN_CTRL_SCSI; + else + cdio_error ("unknown access type: %s. ignored.", value); + } + else + return -1; + + return 0; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return true if successful or false if an error. +*/ +static bool +_cdio_read_toc (_img_private_t *_obj) +{ + int i; + + /* read TOC header */ + if ( ioctl(_obj->fd, CDROMREADTOCHDR, &_obj->tochdr) == -1 ) { + cdio_error("%s: %s\n", + "error in ioctl CDROMREADTOCHDR", strerror(errno)); + return false; + } + + /* read individual tracks */ + for (i=_obj->tochdr.cdth_trk0; i<=_obj->tochdr.cdth_trk1; i++) { + _obj->tocent[i-1].cdte_track = i; + _obj->tocent[i-1].cdte_format = CDROM_MSF; + if ( ioctl(_obj->fd, CDROMREADTOCENTRY, &_obj->tocent[i-1]) == -1 ) { + cdio_error("%s %d: %s\n", + "error in ioctl CDROMREADTOCENTRY for track", + i, strerror(errno)); + return false; + } + } + + /* read the lead-out track */ + _obj->tocent[_obj->tochdr.cdth_trk1].cdte_track = CDROM_LEADOUT; + _obj->tocent[_obj->tochdr.cdth_trk1].cdte_format = CDROM_MSF; + + if (ioctl(_obj->fd, CDROMREADTOCENTRY, + &_obj->tocent[_obj->tochdr.cdth_trk1]) == -1 ) { + cdio_error("%s: %s\n", + "error in ioctl CDROMREADTOCENTRY for lead-out", + strerror(errno)); + return false; + } + + return true; +} + +/*! + Eject media in CD drive. If successful, as a side effect we + also free obj. + */ +static int +_cdio_eject_media (void *user_data) { + + _img_private_t *_obj = user_data; + int ret; + + if (_obj->fd > -1) { + if ((ret = ioctl(_obj->fd, CDROMEJECT)) != 0) { + _cdio_free((void *) _obj); + cdio_error ("CDROMEJECT failed: %s\n", strerror(errno)); + return 1; + } else { + _cdio_free((void *) _obj); + return 0; + } + } + return 2; +} + + +static void * +_cdio_malloc_and_zero(size_t size) { + void *ptr; + + if( !size ) size++; + + if((ptr = malloc(size)) == NULL) { + cdio_error("malloc() failed: %s", strerror(errno)); + return NULL; + } + + memset(ptr, 0, size); + return ptr; +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +_cdio_get_arg (void *user_data, const char key[]) +{ + _img_private_t *_obj = user_data; + + if (!strcmp (key, "source")) { + return _obj->source_name; + } else if (!strcmp (key, "access-mode")) { + switch (_obj->access_mode) { + case _AM_SUN_CTRL_ATAPI: + return "ATAPI"; + case _AM_SUN_CTRL_SCSI: + return "SCSI"; + case _AM_NONE: + return "no access method"; + } + } + return NULL; +} + +/*! + Return a string containing the default VCD device if none is specified. + */ +static char * +_cdio_get_default_device(void) +{ + char *volume_device; + char *volume_name; + char *volume_action; + char *device; + struct stat stb; + + if ((volume_device = getenv("VOLUME_DEVICE")) != NULL && + (volume_name = getenv("VOLUME_NAME")) != NULL && + (volume_action = getenv("VOLUME_ACTION")) != NULL && + strcmp(volume_action, "insert") == 0) { + + device = _cdio_malloc_and_zero(strlen(volume_device) + + strlen(volume_name) + 2); + if (device == NULL) + return strdup(DEFAULT_CDIO_DEVICE); + sprintf(device, "%s/%s", volume_device, volume_name); + if (stat(device, &stb) != 0 || !S_ISCHR(stb.st_mode)) { + free(device); + return strdup(DEFAULT_CDIO_DEVICE); + } + return device; + } + return strdup(DEFAULT_CDIO_DEVICE); +} + +/*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_t +_cdio_get_first_track_num(void *user_data) +{ + _img_private_t *_obj = user_data; + + if (!_obj->toc_init) _cdio_read_toc (_obj) ; + + return FIRST_TRACK_NUM; +} + +/*! + Return the number of tracks in the current medium. +*/ +static track_t +_cdio_get_num_tracks(void *user_data) +{ + _img_private_t *_obj = user_data; + + if (!_obj->toc_init) _cdio_read_toc (_obj) ; + + return TOTAL_TRACKS; +} + +/*! + Get format of track. +*/ +static track_format_t +_cdio_get_track_format(void *user_data, track_t track_num) +{ + _img_private_t *_obj = user_data; + + if (!_obj->init) _cdio_init(_obj); + if (!_obj->toc_init) _cdio_read_toc (_obj) ; + + if (track_num > TOTAL_TRACKS || track_num == 0) + return TRACK_FORMAT_ERROR; + + /* This is pretty much copied from the "badly broken" cdrom_count_tracks + in linux/cdrom.c. + */ + if (_obj->tocent[track_num-1].cdte_ctrl & CDROM_DATA_TRACK) { + if (_obj->tocent[track_num-1].cdte_format == 0x10) + return TRACK_FORMAT_CDI; + else if (_obj->tocent[track_num-1].cdte_format == 0x20) + return TRACK_FORMAT_XA; + else + return TRACK_FORMAT_DATA; + } else + return TRACK_FORMAT_AUDIO; + +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +static bool +_cdio_get_track_green(void *user_data, track_t track_num) +{ + _img_private_t *_obj = user_data; + + if (!_obj->init) _cdio_init(_obj); + if (!_obj->toc_init) _cdio_read_toc (_obj) ; + + if (track_num == CDIO_LEADOUT_TRACK) track_num = TOTAL_TRACKS+1; + + if (track_num > TOTAL_TRACKS+1 || track_num == 0) + return false; + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cdinfo for a while. + */ + return ((_obj->tocent[track_num-1].cdte_ctrl & 2) != 0); +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + track_num in obj. Tracks numbers start at 1. + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + NULL is returned if there is no entry. +*/ +static bool +_cdio_get_track_msf(void *user_data, track_t track_num, msf_t *msf) +{ + _img_private_t *_obj = user_data; + + if (NULL == msf) return false; + + if (!_obj->init) _cdio_init(_obj); + if (!_obj->toc_init) _cdio_read_toc (_obj) ; + + if (track_num == CDIO_LEADOUT_TRACK) track_num = TOTAL_TRACKS+1; + + if (track_num > TOTAL_TRACKS+1 || track_num == 0) { + return false; + } else { + struct cdrom_tocentry *msf0 = &_obj->tocent[track_num-1]; + msf->m = to_bcd8(msf0->cdte_addr.msf.minute); + msf->s = to_bcd8(msf0->cdte_addr.msf.second); + msf->f = to_bcd8(msf0->cdte_addr.msf.frame); + return true; + } +} + +#endif /* HAVE_SOLARIS_CDROM */ + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_solaris (const char *source_name) +{ + +#ifdef HAVE_SOLARIS_CDROM + CdIo *ret; + _img_private_t *_data; + + cdio_funcs _funcs = { + .eject_media = _cdio_eject_media, + .free = _cdio_free, + .get_arg = _cdio_get_arg, + .get_default_device = _cdio_get_default_device, + .get_first_track_num= _cdio_get_first_track_num, + .get_num_tracks = _cdio_get_num_tracks, + .get_track_format = _cdio_get_track_format, + .get_track_green = _cdio_get_track_green, + .get_track_msf = _cdio_get_track_msf, + .read_mode2_sector = _read_mode2_sector, + .read_mode2_sectors = _read_mode2_sectors, + .stat_size = _cdio_stat_size, + .set_arg = _cdio_set_arg + }; + + _data = _cdio_malloc (sizeof (_img_private_t)); + _data->fd = -1; + + _cdio_set_arg(_data, "source", (NULL == source_name) + ? DEFAULT_CDIO_DEVICE: source_name); + + ret = cdio_new (_data, &_funcs); + if (ret == NULL) return NULL; + + if (_cdio_init(_data)) + return ret; + else { + _cdio_free (_data); + return NULL; + } + +#else + return NULL; +#endif /* HAVE_SOLARIS_CDROM */ + +} + +bool +cdio_have_solaris (void) +{ +#ifdef HAVE_SOLARIS_CDROM + return true; +#else + return false; +#endif /* HAVE_SOLARIS_CDROM */ +} + diff --git a/lib/bytesex.h b/lib/bytesex.h new file mode 100644 index 00000000..62dfa27c --- /dev/null +++ b/lib/bytesex.h @@ -0,0 +1,196 @@ +/* + $Id: bytesex.h,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __CDIO_BYTESEX_H__ +#define __CDIO_BYTESEX_H__ + +#include "cdio_types.h" +#include "logging.h" +#include "bytesex_asm.h" + +/* generic byteswap routines */ + +#define UINT16_SWAP_LE_BE_C(val) ((uint16_t) ( \ + (((uint16_t) (val) & (uint16_t) 0x00ffU) << 8) | \ + (((uint16_t) (val) & (uint16_t) 0xff00U) >> 8))) + +#define UINT32_SWAP_LE_BE_C(val) ((uint32_t) ( \ + (((uint32_t) (val) & (uint32_t) 0x000000ffU) << 24) | \ + (((uint32_t) (val) & (uint32_t) 0x0000ff00U) << 8) | \ + (((uint32_t) (val) & (uint32_t) 0x00ff0000U) >> 8) | \ + (((uint32_t) (val) & (uint32_t) 0xff000000U) >> 24))) + +#define UINT64_SWAP_LE_BE_C(val) ((uint64_t) ( \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0x00000000000000ff)) << 56) | \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0x000000000000ff00)) << 40) | \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0x0000000000ff0000)) << 24) | \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0x00000000ff000000)) << 8) | \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0x000000ff00000000)) >> 8) | \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0x0000ff0000000000)) >> 24) | \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0x00ff000000000000)) >> 40) | \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0xff00000000000000)) >> 56))) + +#ifndef UINT16_SWAP_LE_BE +# define UINT16_SWAP_LE_BE UINT16_SWAP_LE_BE_C +#endif + +#ifndef UINT32_SWAP_LE_BE +# define UINT32_SWAP_LE_BE UINT32_SWAP_LE_BE_C +#endif + +#ifndef UINT64_SWAP_LE_BE +# define UINT64_SWAP_LE_BE UINT64_SWAP_LE_BE_C +#endif + +inline static +uint16_t uint16_swap_le_be (const uint16_t val) +{ + return UINT16_SWAP_LE_BE (val); +} + +inline static +uint32_t uint32_swap_le_be (const uint32_t val) +{ + return UINT32_SWAP_LE_BE (val); +} + +inline static +uint64_t uint64_swap_le_be (const uint64_t val) +{ + return UINT64_SWAP_LE_BE (val); +} + +# define UINT8_TO_BE(val) ((uint8_t) (val)) +# define UINT8_TO_LE(val) ((uint8_t) (val)) +#ifdef WORDS_BIGENDIAN +# define UINT16_TO_BE(val) ((uint16_t) (val)) +# define UINT16_TO_LE(val) ((uint16_t) UINT16_SWAP_LE_BE(val)) + +# define UINT32_TO_BE(val) ((uint32_t) (val)) +# define UINT32_TO_LE(val) ((uint32_t) UINT32_SWAP_LE_BE(val)) + +# define UINT64_TO_BE(val) ((uint64_t) (val)) +# define UINT64_TO_LE(val) ((uint64_t) UINT64_SWAP_LE_BE(val)) +#else +# define UINT16_TO_BE(val) ((uint16_t) UINT16_SWAP_LE_BE(val)) +# define UINT16_TO_LE(val) ((uint16_t) (val)) + +# define UINT32_TO_BE(val) ((uint32_t) UINT32_SWAP_LE_BE(val)) +# define UINT32_TO_LE(val) ((uint32_t) (val)) + +# define UINT64_TO_BE(val) ((uint64_t) UINT64_SWAP_LE_BE(val)) +# define UINT64_TO_LE(val) ((uint64_t) (val)) +#endif + +/* symmetric conversions */ +#define UINT8_FROM_BE(val) (UINT8_TO_BE (val)) +#define UINT8_FROM_LE(val) (UINT8_TO_LE (val)) +#define UINT16_FROM_BE(val) (UINT16_TO_BE (val)) +#define UINT16_FROM_LE(val) (UINT16_TO_LE (val)) +#define UINT32_FROM_BE(val) (UINT32_TO_BE (val)) +#define UINT32_FROM_LE(val) (UINT32_TO_LE (val)) +#define UINT64_FROM_BE(val) (UINT64_TO_BE (val)) +#define UINT64_FROM_LE(val) (UINT64_TO_LE (val)) + +/* converter function template */ +#define CVT_TO_FUNC(bits) \ + static inline uint ## bits ## _t \ + uint ## bits ## _to_be (uint ## bits ## _t val) \ + { return UINT ## bits ## _TO_BE (val); } \ + static inline uint ## bits ## _t \ + uint ## bits ## _to_le (uint ## bits ## _t val) \ + { return UINT ## bits ## _TO_LE (val); } \ + +CVT_TO_FUNC(8) +CVT_TO_FUNC(16) +CVT_TO_FUNC(32) +CVT_TO_FUNC(64) + +#undef CVT_TO_FUNC + +#define uint8_from_be(val) (uint8_to_be (val)) +#define uint8_from_le(val) (uint8_to_le (val)) +#define uint16_from_be(val) (uint16_to_be (val)) +#define uint16_from_le(val) (uint16_to_le (val)) +#define uint32_from_be(val) (uint32_to_be (val)) +#define uint32_from_le(val) (uint32_to_le (val)) +#define uint64_from_be(val) (uint64_to_be (val)) +#define uint64_from_le(val) (uint64_to_le (val)) + +/* ISO9660 related stuff */ + +#define to_711(i) uint8_to_le(i) +#define from_711(i) uint8_from_le(i) + +#define to_721(i) uint16_to_le(i) +#define from_721(i) uint16_from_le(i) + +#define to_721(i) uint16_to_le(i) +#define from_721(i) uint16_from_le(i) + +#define to_722(i) uint16_to_be(i) +#define from_722(i) uint16_from_be(i) + +static inline uint32_t +to_723(uint16_t i) +{ + return uint32_swap_le_be(i) | i; +} + +static inline uint16_t +from_723 (uint32_t p) +{ + if (uint32_swap_le_be (p) != p) + cdio_warn ("from_723: broken byte order"); + + return (0xFFFF & p); +} + +#define to_731(i) uint32_to_le(i) +#define from_731(i) uint32_from_le(i) + +#define to_732(i) uint32_to_be(i) +#define from_732(i) uint32_from_be(i) + +static inline uint64_t +to_733(uint32_t i) +{ + return uint64_swap_le_be(i) | i; +} + +static inline uint32_t +from_733 (uint64_t p) +{ + if (uint64_swap_le_be (p) != p) + cdio_warn ("from_733: broken byte order"); + + return (UINT32_C(0xFFFFFFFF) & p); +} + +#endif /* __CDIO_BYTESEX_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/bytesex_asm.h b/lib/bytesex_asm.h new file mode 100644 index 00000000..a2149011 --- /dev/null +++ b/lib/bytesex_asm.h @@ -0,0 +1,123 @@ +/* + $Id: bytesex_asm.h,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2001 Sven Ottemann + 2001 Herbert Valerio Riedel + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __CDIO_BYTESEX_ASM_H__ +#define __CDIO_BYTESEX_ASM_H__ +#if !defined(DISABLE_ASM_OPTIMIZE) + +#include "cdio_types.h" + +#if defined(__powerpc__) && defined(__GNUC__) + +inline static +uint32_t uint32_swap_le_be_asm(const uint32_t a) +{ + uint32_t b; + + __asm__ ("lwbrx %0,0,%1" + :"=r"(b) + :"r"(&a), "m"(a)); + + return b; +} + +inline static +uint16_t uint16_swap_le_be_asm(const uint16_t a) +{ + uint32_t b; + + __asm__ ("lhbrx %0,0,%1" + :"=r"(b) + :"r"(&a), "m"(a)); + + return b; +} + +#define UINT16_SWAP_LE_BE uint16_swap_le_be_asm +#define UINT32_SWAP_LE_BE uint32_swap_le_be_asm + +#elif defined(__mc68000__) && defined(__STORMGCC__) + +inline static +uint32_t uint32_swap_le_be_asm(uint32_t a __asm__("d0")) +{ + /* __asm__("rolw #8,%0; swap %0; rolw #8,%0" : "=d" (val) : "0" (val)); */ + + __asm__("move.l %1,d0;rol.w #8,d0;swap d0;rol.w #8,d0;move.l d0,%0" + :"=r"(a) + :"r"(a)); + + return(a); +} + +inline static +uint16_t uint16_swap_le_be_asm(uint16_t a __asm__("d0")) +{ + __asm__("move.l %1,d0;rol.w #8,d0;move.l d0,%0" + :"=r"(a) + :"r"(a)); + + return(a); +} + +#define UINT16_SWAP_LE_BE uint16_swap_le_be_asm +#define UINT32_SWAP_LE_BE uint32_swap_le_be_asm + +#elif 0 && defined(__i386__) && defined(__GNUC__) + +inline static +uint32_t uint32_swap_le_be_asm(uint32_t a) +{ + __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */ + "rorl $16,%0\n\t" /* swap words */ + "xchgb %b0,%h0" /* swap higher bytes */ + :"=q" (a) + : "0" (a)); + + return(a); +} + +inline static +uint16_t uint16_swap_le_be_asm(uint16_t a) +{ + __asm__("xchgb %b0,%h0" /* swap bytes */ + : "=q" (a) + : "0" (a)); + + return(a); +} + +#define UINT16_SWAP_LE_BE uint16_swap_le_be_asm +#define UINT32_SWAP_LE_BE uint32_swap_le_be_asm + +#endif + +#endif /* !defined(DISABLE_ASM_OPTIMIZE) */ +#endif /* __CDIO_BYTESEX_ASM_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/cdio.c b/lib/cdio.c new file mode 100644 index 00000000..8417e42a --- /dev/null +++ b/lib/cdio.c @@ -0,0 +1,454 @@ +/* + $Id: cdio.c,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2003 Rocky Bernstein + Copyright (C) 2001 Herbert Valerio Riedel + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "cdio_assert.h" +#include "util.h" +#include "logging.h" +#include "cdio_private.h" + +static const char _rcsid[] = "$Id: cdio.c,v 1.1 2003/03/24 19:01:09 rocky Exp $"; + + +const char *track_format2str[5] = + { + "audio", "CD-i", "XA", "data", "error" + }; + +/* The below array gives of the drivers that are currently available for + on a particular host. */ + +CdIo_driver_t CdIo_driver[MAX_DRIVER] = { {0} }; + +struct _CdIo { + void *user_data; + cdio_funcs op; +}; + +/* The last valid entry of Cdio_driver. -1 means uninitialzed. -2 + means some sort of error. +*/ +int CdIo_last_driver = -1; + +static bool +cdio_have_false(void) +{ + return false; +} + + +/* The below array gives all drivers that can possibly appear. + on a particular host. */ + +CdIo_driver_t CdIo_all_drivers[MAX_DRIVER+1] = { + {DRIVER_UNKNOWN, + 0, + "Unknown", + "No driver", + &cdio_have_false, + NULL + }, + + {DRIVER_LINUX, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK, + "Linux", + "Linux ioctl and packet driver", + &cdio_have_linux, + &cdio_open_linux + }, + + {DRIVER_SOLARIS, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK|CDIO_SRC_IS_SCSI_MASK, + "Solaris", + "Solaris ATAPI and SCSI driver", + &cdio_have_solaris, + &cdio_open_solaris + }, + + {DRIVER_BSDI, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK|CDIO_SRC_IS_SCSI_MASK, + "BSDI", + "BSDI ATAPI and SCSI driver", + &cdio_have_bsdi, + cdio_open_bsdi + }, + + {DRIVER_NRG, + CDIO_SRC_IS_DISK_IMAGE_MASK, + "NRG", + "Nero NRG disk image driver", + &cdio_have_nrg, + &cdio_open_nrg + }, + + {DRIVER_BINCUE, + CDIO_SRC_IS_DISK_IMAGE_MASK, + "BIN/CUE", + "bin/cuesheet disk image driver", + &cdio_have_bincue, + &cdio_open_bincue + } + +}; + +/*! + Eject media in CD drive if there is a routine to do so. + Return 0 if success and 1 for failure, and 2 if no routine. + */ +int +cdio_eject_media (const CdIo *obj) +{ + cdio_assert (obj != NULL); + + if (obj->op.eject_media) { + return obj->op.eject_media (obj->user_data); + } else { + return 2; + } +} + +/*! + Return a string containing the default CD device if none is specified. + */ +const char * +cdio_get_arg (const CdIo *obj, const char key[]) +{ + cdio_assert (obj != NULL); + + if (obj->op.get_arg) { + return obj->op.get_arg (obj->user_data, key); + } else { + return NULL; + } +} + +/*! + Return a string containing the default CD device if none is specified. + */ +char * +cdio_get_default_device (const CdIo *obj) +{ + cdio_assert (obj != NULL); + + if (obj->op.get_default_device) { + return obj->op.get_default_device (); + } else { + return NULL; + } +} + +/*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. +*/ +track_t +cdio_get_first_track_num (const CdIo *obj) +{ + cdio_assert (obj != NULL); + + if (obj->op.get_first_track_num) { + return obj->op.get_first_track_num (obj->user_data); + } else { + return CDIO_INVALID_TRACK; + } +} + +/*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. +*/ +track_t +cdio_get_num_tracks (const CdIo *obj) +{ + cdio_assert (obj != NULL); + + if (obj->op.get_num_tracks) { + return obj->op.get_num_tracks (obj->user_data); + } else { + return CDIO_INVALID_TRACK; + } +} + +/*! + Get format of track. +*/ +track_format_t +cdio_get_track_format(const CdIo *obj, track_t track_num) +{ + cdio_assert (obj != NULL); + + if (obj->op.get_track_format) { + return obj->op.get_track_format (obj->user_data, track_num); + } else { + return TRACK_FORMAT_ERROR; + } +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +bool +cdio_get_track_green(const CdIo *obj, track_t track_num) +{ + cdio_assert (obj != NULL); + + if (obj->op.get_track_green) { + return obj->op.get_track_green (obj->user_data, track_num); + } else { + return false; + } +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + track_num in obj. Track numbers start at 1. + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +bool +cdio_get_track_msf(const CdIo *obj, track_t track_num, msf_t *msf) +{ + cdio_assert (obj != NULL); + + if (obj->op.get_track_msf) { + return obj->op.get_track_msf (obj->user_data, track_num, msf); + } else { + return false; + } +} + +bool +cdio_have_driver(driver_id_t driver_id) +{ + return (*CdIo_all_drivers[driver_id].have_driver)(); +} + + +/*! + Initialize CD Reading and control routines. Should be called first. + May be implicitly called by other routines if not called first. +*/ +bool +cdio_init(void) +{ + + CdIo_driver_t *all_dp; + CdIo_driver_t *dp = CdIo_driver; + driver_id_t driver_id; + + if (CdIo_last_driver != -1) { + cdio_warn ("Init routine called more than once."); + return false; + } + + for (driver_id=DRIVER_UNKNOWN; driver_id<=MAX_DRIVER; driver_id++) { + all_dp = &CdIo_all_drivers[driver_id]; + if ((*CdIo_all_drivers[driver_id].have_driver)()) { + *dp++ = *all_dp; + CdIo_last_driver++; + } + } + + return true; +} + +CdIo * +cdio_new (void *user_data, const cdio_funcs *funcs) +{ + CdIo *new_obj; + + new_obj = _cdio_malloc (sizeof (CdIo)); + + new_obj->user_data = user_data; + new_obj->op = *funcs; + + return new_obj; +} + +void +cdio_destroy (CdIo *obj) +{ + cdio_assert (obj != NULL); + + obj->op.free (obj->user_data); + free (obj); +} + +int +cdio_read_mode2_sectors (CdIo *obj, void *buf, lsn_t lsn, bool mode2raw, + unsigned num_sectors) +{ + char *_buf = buf; + const int blocksize = mode2raw ? M2RAW_SECTOR_SIZE : M2F1_SECTOR_SIZE; + int n, rc; + + cdio_assert (obj != NULL); + cdio_assert (buf != NULL); + cdio_assert (obj->op.read_mode2_sector != NULL + || obj->op.read_mode2_sectors != NULL); + + if (obj->op.read_mode2_sectors) + return obj->op.read_mode2_sectors (obj->user_data, buf, lsn, + mode2raw, num_sectors); + + /* fallback */ + if (obj->op.read_mode2_sector != NULL) + for (n = 0; n < num_sectors; n++) + if ((rc = cdio_read_mode2_sector (obj, &_buf[n * blocksize], + lsn + n, mode2raw))) + return rc; + + return 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +int +cdio_read_mode2_sector (CdIo *obj, void *buf, uint32_t lsn, bool mode2raw) +{ + cdio_assert (obj != NULL); + cdio_assert (buf != NULL); + cdio_assert (obj->op.read_mode2_sector != NULL + || obj->op.read_mode2_sectors != NULL); + + if (obj->op.read_mode2_sector) + return obj->op.read_mode2_sector (obj->user_data, buf, lsn, mode2raw); + + /* fallback */ + if (obj->op.read_mode2_sectors != NULL) + return cdio_read_mode2_sectors (obj, buf, lsn, mode2raw, 1); + return 1; +} + +uint32_t +cdio_stat_size (CdIo *obj) +{ + cdio_assert (obj != NULL); + + return obj->op.stat_size (obj->user_data); +} + +/*! + Set the arg "key" with "value" in the source device. +*/ +int +cdio_set_arg (CdIo *obj, const char key[], const char value[]) +{ + cdio_assert (obj != NULL); + cdio_assert (obj->op.set_arg != NULL); + cdio_assert (key != NULL); + + return obj->op.set_arg (obj->user_data, key, value); +} + +/*! Sets up to read from place specified by source_name and + driver_id This should be called before using any other routine, + except cdio_init. This will call cdio_init, if that hasn't been + done previously. to call one of the specific routines below. + + NULL is returned on error. +*/ +/* In the future we'll have more complicated code to allow selection + of an I/O routine as well as code to find an appropriate default + routine among the "registered" routines. Possibly classes too + disk-based, SCSI-based, native-based, vendor (e.g. Sony, or + Plextor) based + + For now though, we'll start more simply... +*/ +CdIo * +cdio_open (const char *source_name, driver_id_t driver_id) +{ + if (CdIo_last_driver == -1) cdio_init(); + + switch (driver_id) { + case DRIVER_UNKNOWN: + case DRIVER_DEVICE: + { + /* Scan for a driver. */ + for (driver_id=DRIVER_UNKNOWN; driver_id<=MAX_DRIVER; driver_id++) { + if ((*CdIo_all_drivers[driver_id].have_driver)()) { + CdIo *ret=(*CdIo_all_drivers[driver_id].driver_open)(source_name); + if (ret != NULL) return ret; + } + } + return NULL; + } + break; + case DRIVER_LINUX: + case DRIVER_SOLARIS: + case DRIVER_BSDI: + case DRIVER_NRG: + case DRIVER_BINCUE: + if ((*CdIo_all_drivers[driver_id].have_driver)()) { + return (*CdIo_all_drivers[driver_id].driver_open)(source_name); + } + } + + return NULL; +} + + +/* In the future we'll have more complicated code to allow selection + of an I/O routine as well as code to find an appropriate default + routine among the "registered" routines. Possibly classes too + disk-based, SCSI-based, native-based, vendor (e.g. Sony, or + Plextor) based + + For now though, we'll start more simply... +*/ +CdIo * +cdio_open_cd (const char *source_name) +{ + driver_id_t driver_id; + + if (CdIo_last_driver == -1) cdio_init(); + + /* Scan for a driver. */ + for (driver_id=DRIVER_UNKNOWN; driver_id<=MAX_DRIVER; driver_id++) { + if ((*CdIo_all_drivers[driver_id].have_driver)()) { + CdIo *ret=(*CdIo_all_drivers[driver_id].driver_open)(source_name); + if (ret != NULL) return ret; + } + } + return NULL; +} + + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/cdio.h b/lib/cdio.h new file mode 100644 index 00000000..3ed944f0 --- /dev/null +++ b/lib/cdio.h @@ -0,0 +1,219 @@ +/* + $Id: cdio.h,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2001 Herbert Valerio Riedel + Copyright (C) 2003 Rocky Bernstein + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Public CD Input and Control Interface . */ + + +#ifndef __CDIO_H__ +#define __CDIO_H__ + +#include "cdio_types.h" +#include "sector.h" + +/* Flags specifying the category of device to open or is opened. */ +#define CDIO_SRC_IS_DISK_IMAGE_MASK 0x0001 +#define CDIO_SRC_IS_DEVICE_MASK 0x0002 +#define CDIO_SRC_IS_SCSI_MASK 0x0004 +#define CDIO_SRC_IS_NATIVE_MASK 0x0008 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* opaque structure */ + typedef struct _CdIo CdIo; + + /* The below enumerations may be used to tag a specific driver + that is opened or is desired to be opened. Note that this is + different than what is available on a given host. + */ + typedef enum { + DRIVER_UNKNOWN, + DRIVER_LINUX, + DRIVER_SOLARIS, + DRIVER_BSDI, + DRIVER_NRG, + DRIVER_BINCUE, + DRIVER_DEVICE, + } driver_id_t; + + /* Make sure what's listed below is the last one above. Since we have + a bogus (but useful) 0th entry above we don't have to add one below. + */ +#define MAX_DRIVER DRIVER_BINCUE + + + typedef enum { + TRACK_FORMAT_AUDIO, /* Audio track, e.g. CD-DA */ + TRACK_FORMAT_CDI, /* CD-i. How this is different from DATA below? */ + TRACK_FORMAT_XA, /* Mode2 of some sort */ + TRACK_FORMAT_DATA, /* Mode1 of some sort */ + TRACK_FORMAT_ERROR /* Dunno what is or some other error. */ + } track_format_t; + + /* Printable tags for above enumeration. */ + extern const char *track_format2str[5]; + + /*! + Eject media in CD drive if there is a routine to do so. + Return 0 if success and 1 for failure, and 2 if no routine. + */ + int cdio_eject_media (const CdIo *obj); + + void cdio_destroy (CdIo *obj); + + /*! + Return the value associated with the key "arg". + */ + const char * cdio_get_arg (const CdIo *obj, const char key[]); + + /*! + Return a string containing the default CD device if none is specified. + */ + char * cdio_get_default_device (const CdIo *obj); + + /*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. + */ + track_t cdio_get_first_track_num(const CdIo *obj); + + /*! + Return a string containing the default CD device if none is specified. + */ + track_t cdio_get_num_tracks (const CdIo *obj); + + /*! + Get format of track. + */ + track_format_t cdio_get_track_format(const CdIo *obj, track_t track_num); + + /*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? + */ + bool cdio_get_track_green(const CdIo *obj, track_t track_num); + + /*! + Return the starting MSF (minutes/secs/frames) for track number + track_num in obj. Track numbers start at 1. + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. + */ + bool cdio_get_track_msf(const CdIo *obj, track_t track_num, msf_t *msf); + + /*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ + int cdio_read_mode2_sector (CdIo *obj, void *buf, lsn_t lsn, bool mode2raw); + + /*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ + int cdio_read_mode2_sectors (CdIo *obj, void *buf, lsn_t lsn, bool mode2raw, + unsigned int num_sectors); + + /*! + Set the arg "key" with "value" in the source device. + */ + int cdio_set_arg (CdIo *obj, const char key[], const char value[]); + + /*! + Return the size of the CD in logical block address (LBA) units. + */ + uint32_t cdio_stat_size (CdIo *obj); + + /*! + Initialize CD Reading and control routines. Should be called first. + */ + bool cdio_init(void); + + /* True if xxx driver is available. where xxx=linux, solaris, nrg, ... + */ + bool cdio_have_linux (void); + bool cdio_have_solaris (void); + bool cdio_have_nrg (void); + bool cdio_have_bincue (void); + bool cdio_have_bsdi (void); + + /* Like above but uses the enumeration instead. */ + bool cdio_have_driver (driver_id_t driver_id); + + /*! Sets up to read from place specified by source_name and + driver_id This should be called before using any other routine, + except cdio_init. This will call cdio_init, if that hasn't been + done previously. to call one of the specific routines below. + + NULL is returned on error. + */ + CdIo * cdio_open (const char *source_name, driver_id_t driver_id); + + /*! cdrao BIN/CUE CD disk-image routines. Source is the .bin file + + NULL is returned on error. + */ + CdIo * cdio_open_bincue (const char *bin_name); + + /*! cdrao CD routines. Source is the some sort of device. + + NULL is returned on error. + */ + CdIo * cdio_open_cd (const char *cue_name); + + /*! cdrao BIN/CUE CD disk-image routines. Source is the .cue file + + NULL is returned on error. + */ + CdIo * cdio_open_cue (const char *cue_name); + + /*! BSDI CD-reading routines. + NULL is returned on error. + */ + CdIo * cdio_open_bsdi (const char *source_name); + + /*! Linux CD-reading routines. + NULL is returned on error. + */ + CdIo * cdio_open_linux (const char *source_name); + + /*! Solaris CD-reading routines. + NULL is returned on error. + */ + CdIo * cdio_open_solaris (const char *soruce_name); + + /*! Nero CD disk-image routines. + NULL is returned on error. + */ + CdIo * cdio_open_nrg (const char *source_name); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_H__ */ diff --git a/lib/cdio_assert.h b/lib/cdio_assert.h new file mode 100644 index 00000000..eb5d4e31 --- /dev/null +++ b/lib/cdio_assert.h @@ -0,0 +1,55 @@ +/* + $Id: cdio_assert.h,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __CDIO_ASSERT_H__ +#define __CDIO_ASSERT_H__ + +#if defined(__GNUC__) + +#include "cdio_types.h" +#include "logging.h" + +#define cdio_assert(expr) \ + { \ + if (GNUC_UNLIKELY (!(expr))) cdio_log (CDIO_LOG_ASSERT, \ + "file %s: line %d (%s): assertion failed: (%s)", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ + } + +#define cdio_assert_not_reached() \ + { \ + cdio_log (CDIO_LOG_ASSERT, \ + "file %s: line %d (%s): should not be reached", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + } + +#else /* non GNU C */ + +#include + +#define cdio_assert(expr) \ + assert(expr) + +#define cdio_assert_not_reached() \ + assert(0) + +#endif + +#endif /* __CDIO_ASSERT_H__ */ diff --git a/lib/cdio_private.h b/lib/cdio_private.h new file mode 100644 index 00000000..4ba84617 --- /dev/null +++ b/lib/cdio_private.h @@ -0,0 +1,158 @@ +/* + $Id: cdio_private.h,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2003 Rocky Bernstein + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Internal routines for CD I/O drivers. */ + + +#ifndef __CDIO_PRIVATE_H__ +#define __CDIO_PRIVATE_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct { + + /*! + Eject media in CD drive. If successful, as a side effect we + also free obj. Return 0 if success and 1 for failure. + */ + int (*eject_media) (void *user_data); + + /*! + Release and free resources associated with cd. + */ + void (*free) (void *user_data); + + /*! + Return the value associated with the key "arg". + */ + const char * (*get_arg) (void *user_data, const char key[]); + + /*! + Return a string containing the default VCD device if none is specified. + */ + char * (*get_default_device)(void); + + /*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. + */ + track_t (*get_first_track_num) (void *user_data); + + /*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. + */ + track_t (*get_num_tracks) (void *user_data); + + /*! + Return the starting MSF (minutes/secs/frames) for track number + track_num in obj. Tracks numbers start at 1. + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + 1 is returned on error. + */ + uint32_t (*get_track_lba) (void *user_data, track_t track_num); + + /*! + Get format of track. + */ + track_format_t (*get_track_format) (void *user_data, track_t track_num); + + /*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? + */ + bool (*get_track_green) (void *user_data, track_t track_num); + + /*! + Return the starting MSF (minutes/secs/frames) for track number + track_num in obj. Tracks numbers start at 1. + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + 1 is returned on error. + */ + bool (*get_track_msf) (void *user_data, track_t track_num, msf_t *msf); + + /*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ + int (*read_mode2_sector) (void *user_data, void *buf, lsn_t lsn, + bool mode2raw); + + /*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ + int (*read_mode2_sectors) (void *user_data, void *buf, lsn_t lsn, + bool mode2raw, unsigned nblocks); + + /*! + Set the arg "key" with "value" in the source device. + */ + int (*set_arg) (void *user_data, const char key[], const char value[]); + + /*! + Return the size of the CD in logical block address (LBA) units. + */ + uint32_t (*stat_size) (void *user_data); + + } cdio_funcs; + + CdIo * cdio_new (void *user_data, const cdio_funcs *funcs); + + /* The below structure describes a specific CD Input driver */ + typedef struct + { + unsigned int flags; + driver_id_t id; + const char *name; + const char *describe; + bool (*have_driver) (void); + CdIo *(*driver_open) (const char *source_name); + } CdIo_driver_t; + + /* The below array gives of the drivers that are currently available for + on a particular host. */ + extern CdIo_driver_t CdIo_driver[MAX_DRIVER]; + + /* The last valid entry of Cdio_driver. -1 means uninitialzed. -2 + means some sort of error. + */ + extern int CdIo_last_driver; + + /* The below array gives all drivers that can possibly appear. + on a particular host. */ + extern CdIo_driver_t CdIo_all_drivers[MAX_DRIVER+1]; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_PRIVATE_H__ */ diff --git a/lib/cdio_types.h b/lib/cdio_types.h new file mode 100644 index 00000000..0154822f --- /dev/null +++ b/lib/cdio_types.h @@ -0,0 +1,217 @@ +/* + $Id: cdio_types.h,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + Copyright (C) 2002,2003 Rocky Bernstein + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __CDIO_TYPES_H__ +#define __CDIO_TYPES_H__ + +/* provide some C99 definitions */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if defined(HAVE_STDINT_H) +# include +#elif defined(HAVE_INTTYPES_H) +# include +#elif defined(__CYGWIN__) || defined(AMIGA) || defined(__linux__) +# include +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int32_t uint32_t; +typedef u_int64_t uint64_t; +#else +/* warning ISO/IEC 9899:1999 was missing and even */ +/* fixme */ +#endif /* HAVE_STDINT_H */ + +/* default HP/UX macros are broken */ +#if defined(__hpux__) +# undef UINT16_C +# undef UINT32_C +# undef UINT64_C +# undef INT64_C +#endif + +/* if it's still not defined, take a good guess... should work for + most 32bit and 64bit archs */ + +#ifndef UINT16_C +# define UINT16_C(c) c ## U +#endif + +#ifndef UINT32_C +# if defined (SIZEOF_INT) && SIZEOF_INT == 4 +# define UINT32_C(c) c ## U +# elif defined (SIZEOF_LONG) && SIZEOF_LONG == 4 +# define UINT32_C(c) c ## UL +# else +# define UINT32_C(c) c ## U +# endif +#endif + +#ifndef UINT64_C +# if defined (SIZEOF_LONG) && SIZEOF_LONG == 8 +# define UINT64_C(c) c ## UL +# elif defined (SIZEOF_INT) && SIZEOF_INT == 8 +# define UINT64_C(c) c ## U +# else +# define UINT64_C(c) c ## ULL +# endif +#endif + +#ifndef INT64_C +# if defined (SIZEOF_LONG) && SIZEOF_LONG == 8 +# define INT64_C(c) c ## L +# elif defined (SIZEOF_INT) && SIZEOF_INT == 8 +# define INT64_C(c) c +# else +# define INT64_C(c) c ## LL +# endif +#endif + +#if defined(HAVE_STDBOOL_H) +#include +#else +/* ISO/IEC 9899:1999 missing -- enabling workaround */ + +# ifndef __cplusplus +typedef enum + { + false = 0, + true = 1 + } _Bool; + +# define false false +# define true true +# define bool _Bool +# endif +#endif + +/* some GCC optimizations -- gcc 2.5+ */ + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +#define GNUC_PRINTF( format_idx, arg_idx ) \ + __attribute__((format (printf, format_idx, arg_idx))) +#define GNUC_SCANF( format_idx, arg_idx ) \ + __attribute__((format (scanf, format_idx, arg_idx))) +#define GNUC_FORMAT( arg_idx ) \ + __attribute__((format_arg (arg_idx))) +#define GNUC_NORETURN \ + __attribute__((noreturn)) +#define GNUC_CONST \ + __attribute__((const)) +#define GNUC_UNUSED \ + __attribute__((unused)) +#define GNUC_PACKED \ + __attribute__((packed)) +#else /* !__GNUC__ */ +#define GNUC_PRINTF( format_idx, arg_idx ) +#define GNUC_SCANF( format_idx, arg_idx ) +#define GNUC_FORMAT( arg_idx ) +#define GNUC_NORETURN +#define GNUC_CONST +#define GNUC_UNUSED +#define GNUC_PACKED +#endif /* !__GNUC__ */ + +#if defined(__GNUC__) +/* for GCC we try to use GNUC_PACKED */ +# define PRAGMA_BEGIN_PACKED +# define PRAGMA_END_PACKED +#elif defined(HAVE_ISOC99_PRAGMA) +/* should work with most EDG-frontend based compilers */ +# define PRAGMA_BEGIN_PACKED _Pragma("pack(1)") +# define PRAGMA_END_PACKED _Pragma("pack()") +#else /* neither gcc nor _Pragma() available... */ +/* ...so let's be naive and hope the regression testsuite is run... */ +# define PRAGMA_BEGIN_PACKED +# define PRAGMA_END_PACKED +#endif + +/* + * user directed static branch prediction gcc 2.96+ + */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 95) +# define GNUC_LIKELY(x) __builtin_expect((x),true) +# define GNUC_UNLIKELY(x) __builtin_expect((x),false) +#else +# define GNUC_LIKELY(x) (x) +# define GNUC_UNLIKELY(x) (x) +#endif + +#ifndef NULL +# define NULL ((void*) 0) +#endif + +/* our own offsetof()-like macro */ +#define __cd_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +/* In many structures on the disk a sector address is stored as a + BCD-encoded mmssff in three bytes. */ +PRAGMA_BEGIN_PACKED +typedef struct { + uint8_t m, s, f; +} GNUC_PACKED msf_t; +PRAGMA_END_PACKED + +#define msf_t_SIZEOF 3 + +/* type used for bit-fields in structs (1 <= bits <= 8) */ +#if defined(__GNUC__) +/* this is strict ISO C99 which allows only 'unsigned int', 'signed + int' and '_Bool' explicitly as bit-field type */ +typedef unsigned int bitfield_t; +#else +/* other compilers might increase alignment requirements to match the + 'unsigned int' type -- fixme: find out how unalignment accesses can + be pragma'ed on non-gcc compilers */ +typedef uint8_t bitfield_t; +#endif + +/* The type of a Logical Block Address. */ +typedef uint32_t lba_t; + +/* The type of an Logical Sector Number. */ +typedef uint32_t lsn_t; + +/* The type of an track number. */ +typedef uint8_t track_t; + +/*! + Constant for invalid track number +*/ +#define CDIO_INVALID_TRACK 0xFF + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_TYPES_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/ds.c b/lib/ds.c new file mode 100644 index 00000000..36267d93 --- /dev/null +++ b/lib/ds.c @@ -0,0 +1,249 @@ +/* + $Id: ds.c,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "ds.h" +#include "util.h" +#include "cdio_types.h" +#include "cdio_assert.h" + +static const char _rcsid[] = "$Id: ds.c,v 1.1 2003/03/24 19:01:09 rocky Exp $"; + +struct _CdioList +{ + unsigned length; + + CdioListNode *begin; + CdioListNode *end; +}; + +struct _CdioListNode +{ + CdioList *list; + + CdioListNode *next; + + void *data; +}; + +/* impl */ + +CdioList * +_cdio_list_new (void) +{ + CdioList *new_obj = _cdio_malloc (sizeof (CdioList)); + + return new_obj; +} + +void +_cdio_list_free (CdioList *list, int free_data) +{ + while (_cdio_list_length (list)) + _cdio_list_node_free (_cdio_list_begin (list), free_data); + + free (list); +} + +unsigned +_cdio_list_length (const CdioList *list) +{ + cdio_assert (list != NULL); + + return list->length; +} + +void +_cdio_list_prepend (CdioList *list, void *data) +{ + CdioListNode *new_node; + + cdio_assert (list != NULL); + + new_node = _cdio_malloc (sizeof (CdioListNode)); + + new_node->list = list; + new_node->next = list->begin; + new_node->data = data; + + list->begin = new_node; + if (list->length == 0) + list->end = new_node; + + list->length++; +} + +void +_cdio_list_append (CdioList *list, void *data) +{ + cdio_assert (list != NULL); + + if (list->length == 0) + { + _cdio_list_prepend (list, data); + } + else + { + CdioListNode *new_node = _cdio_malloc (sizeof (CdioListNode)); + + new_node->list = list; + new_node->next = NULL; + new_node->data = data; + + list->end->next = new_node; + list->end = new_node; + + list->length++; + } +} + +void +_cdio_list_foreach (CdioList *list, _cdio_list_iterfunc func, void *user_data) +{ + CdioListNode *node; + + cdio_assert (list != NULL); + cdio_assert (func != 0); + + for (node = _cdio_list_begin (list); + node != NULL; + node = _cdio_list_node_next (node)) + func (_cdio_list_node_data (node), user_data); +} + +CdioListNode * +_cdio_list_find (CdioList *list, _cdio_list_iterfunc cmp_func, void *user_data) +{ + CdioListNode *node; + + cdio_assert (list != NULL); + cdio_assert (cmp_func != 0); + + for (node = _cdio_list_begin (list); + node != NULL; + node = _cdio_list_node_next (node)) + if (cmp_func (_cdio_list_node_data (node), user_data)) + break; + + return node; +} + +CdioListNode * +_cdio_list_begin (const CdioList *list) +{ + cdio_assert (list != NULL); + + return list->begin; +} + +CdioListNode * +_cdio_list_end (CdioList *list) +{ + cdio_assert (list != NULL); + + return list->end; +} + +CdioListNode * +_cdio_list_node_next (CdioListNode *node) +{ + if (node) + return node->next; + + return NULL; +} + +void +_cdio_list_node_free (CdioListNode *node, int free_data) +{ + CdioList *list; + CdioListNode *prev_node; + + cdio_assert (node != NULL); + + list = node->list; + + cdio_assert (_cdio_list_length (list) > 0); + + if (free_data) + free (_cdio_list_node_data (node)); + + if (_cdio_list_length (list) == 1) + { + cdio_assert (list->begin == list->end); + + list->end = list->begin = NULL; + list->length = 0; + free (node); + return; + } + + cdio_assert (list->begin != list->end); + + if (list->begin == node) + { + list->begin = node->next; + free (node); + list->length--; + return; + } + + for (prev_node = list->begin; prev_node->next; prev_node = prev_node->next) + if (prev_node->next == node) + break; + + cdio_assert (prev_node->next != NULL); + + if (list->end == node) + list->end = prev_node; + + prev_node->next = node->next; + + list->length--; + + free (node); +} + +void * +_cdio_list_node_data (CdioListNode *node) +{ + if (node) + return node->data; + + return NULL; +} + +/* eof */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ + diff --git a/lib/ds.h b/lib/ds.h new file mode 100644 index 00000000..52e06d21 --- /dev/null +++ b/lib/ds.h @@ -0,0 +1,73 @@ +/* + $Id: ds.h,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __CDIO_DS_H__ +#define __CDIO_DS_H__ + +#include "cdio_types.h" + +/* opaque... */ +typedef struct _CdioList CdioList; +typedef struct _CdioListNode CdioListNode; + +typedef int (*_cdio_list_cmp_func) (void *data1, void *data2); + +typedef int (*_cdio_list_iterfunc) (void *data, void *user_data); + +/* methods */ +CdioList *_cdio_list_new (void); + +void _cdio_list_free (CdioList *list, int free_data); + +unsigned _cdio_list_length (const CdioList *list); + +void _cdio_list_prepend (CdioList *list, void *data); + +void _cdio_list_append (CdioList *list, void *data); + +void _cdio_list_foreach (CdioList *list, _cdio_list_iterfunc func, void *user_data); + +CdioListNode *_cdio_list_find (CdioList *list, _cdio_list_iterfunc cmp_func, void *user_data); + +#define _CDIO_LIST_FOREACH(node, list) \ + for (node = _cdio_list_begin (list); node; node = _cdio_list_node_next (node)) + +/* node ops */ + +CdioListNode *_cdio_list_begin (const CdioList *list); + +CdioListNode *_cdio_list_end (CdioList *list); + +CdioListNode *_cdio_list_node_next (CdioListNode *node); + +void _cdio_list_node_free (CdioListNode *node, int free_data); + +void *_cdio_list_node_data (CdioListNode *node); + +#endif /* __CDIO_DS_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ + diff --git a/lib/logging.c b/lib/logging.c new file mode 100644 index 00000000..75f563dc --- /dev/null +++ b/lib/logging.c @@ -0,0 +1,129 @@ +/* + $Id: logging.c,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include "cdio_assert.h" + +#include "logging.h" + +static const char _rcsid[] = "$Id: logging.c,v 1.1 2003/03/24 19:01:09 rocky Exp $"; + +static void +default_cdio_log_handler (cdio_log_level_t level, const char message[]) +{ + switch (level) + { + case CDIO_LOG_ERROR: + fprintf (stderr, "**ERROR: %s\n", message); + fflush (stderr); + exit (EXIT_FAILURE); + break; + case CDIO_LOG_DEBUG: + fprintf (stdout, "--DEBUG: %s\n", message); + break; + case CDIO_LOG_WARN: + fprintf (stdout, "++ WARN: %s\n", message); + break; + case CDIO_LOG_INFO: + fprintf (stdout, " INFO: %s\n", message); + break; + case CDIO_LOG_ASSERT: + fprintf (stderr, "!ASSERT: %s\n", message); + fflush (stderr); + abort (); + break; + default: + cdio_assert_not_reached (); + break; + } + + fflush (stdout); +} + +static cdio_log_handler_t _handler = default_cdio_log_handler; + +cdio_log_handler_t +cdio_log_set_handler (cdio_log_handler_t new_handler) +{ + cdio_log_handler_t old_handler = _handler; + + _handler = new_handler; + + return old_handler; +} + +static void +cdio_logv (cdio_log_level_t level, const char format[], va_list args) +{ + char buf[1024] = { 0, }; + static int in_recursion = 0; + + if (in_recursion) + cdio_assert_not_reached (); + + in_recursion = 1; + + vsnprintf(buf, sizeof(buf)-1, format, args); + + _handler(level, buf); + + in_recursion = 0; +} + +void +cdio_log (cdio_log_level_t level, const char format[], ...) +{ + va_list args; + va_start (args, format); + cdio_logv (level, format, args); + va_end (args); +} + +#define CDIO_LOG_TEMPLATE(level, LEVEL) \ +void \ +cdio_ ## level (const char format[], ...) \ +{ \ + va_list args; \ + va_start (args, format); \ + cdio_logv (CDIO_LOG_ ## LEVEL, format, args); \ + va_end (args); \ +} + +CDIO_LOG_TEMPLATE(debug, DEBUG) +CDIO_LOG_TEMPLATE(info, INFO) +CDIO_LOG_TEMPLATE(warn, WARN) +CDIO_LOG_TEMPLATE(error, ERROR) + +#undef CDIO_LOG_TEMPLATE + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/logging.h b/lib/logging.h new file mode 100644 index 00000000..a77671e5 --- /dev/null +++ b/lib/logging.h @@ -0,0 +1,64 @@ +/* + $Id: logging.h,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LOGGING_H__ +#define __LOGGING_H__ + +#include "cdio_types.h" + +typedef enum { + CDIO_LOG_DEBUG = 1, + CDIO_LOG_INFO, + CDIO_LOG_WARN, + CDIO_LOG_ERROR, + CDIO_LOG_ASSERT +} cdio_log_level_t; + +void +cdio_log (cdio_log_level_t level, const char format[], ...) GNUC_PRINTF(2, 3); + +typedef void (*cdio_log_handler_t) (cdio_log_level_t level, + const char message[]); + +cdio_log_handler_t +cdio_log_set_handler (cdio_log_handler_t new_handler); + +void +cdio_debug (const char format[], ...) GNUC_PRINTF(1,2); + +void +cdio_info (const char format[], ...) GNUC_PRINTF(1,2); + +void +cdio_warn (const char format[], ...) GNUC_PRINTF(1,2); + +void +cdio_error (const char format[], ...) GNUC_PRINTF(1,2); + +#endif /* __LOGGING_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/sector.c b/lib/sector.c new file mode 100644 index 00000000..94e352f5 --- /dev/null +++ b/lib/sector.c @@ -0,0 +1,79 @@ +/* + $Id: sector.c,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "cdio_assert.h" +#include "sector.h" +#include "util.h" + +static const char _rcsid[] = "$Id: sector.c,v 1.1 2003/03/24 19:01:09 rocky Exp $"; + +void +cdio_lba_to_msf (uint32_t lba, msf_t *msf) +{ + cdio_assert (msf != 0); + + msf->m = to_bcd8 (lba / (60 * 75)); + msf->s = to_bcd8 ((lba / 75) % 60); + msf->f = to_bcd8 (lba % 75); +} + +void +cdio_lsn_to_msf (lsn_t lsn, msf_t *msf) +{ + cdio_assert (msf != 0); + cdio_lba_to_msf(cdio_lsn_to_lba(lsn), msf); +} + +uint32_t +cdio_msf_to_lba (const msf_t *msf) +{ + uint32_t lba = 0; + + cdio_assert (msf != 0); + + lba = from_bcd8 (msf->m); + lba *= 60; + + lba += from_bcd8 (msf->s); + lba *= 75; + + lba += from_bcd8 (msf->f); + + return lba; +} + +uint32_t +cdio_msf_to_lsn (const msf_t *msf) +{ + return cdio_lba_to_lsn(cdio_msf_to_lba (msf)); +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/sector.h b/lib/sector.h new file mode 100644 index 00000000..303f2935 --- /dev/null +++ b/lib/sector.h @@ -0,0 +1,127 @@ +/* + $Id: sector.h,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + Copyright (C) 2003 Rocky Bernstein + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* + Things related to CDROM layout. Sector sizes, MSFs, LBAs, +*/ + +#ifndef _CDIO_SECTOR_H_ +#define _CDIO_SECTOR_H_ + +#include "cdio_types.h" + +#define CDIO_PREGAP_SECTORS 150 +#define CDIO_POSTGAP_SECTORS 150 + +/*! + Constant for ending or "leadout" track. +*/ +#define CDIO_LEADOUT_TRACK 0xaa + +/* + * A CD-ROM physical sector size is 2048, 2052, 2056, 2324, 2332, 2336, + * 2340, or 2352 bytes long. + +* Sector types of the standard CD-ROM data formats: + * + * format sector type user data size (bytes) + * ----------------------------------------------------------------------------- + * 1 (Red Book) CD-DA 2352 (CDDA_SECTOR_SIZE) + * 2 (Yellow Book) Mode1 Form1 2048 (M2F1_SECTOR_SIZE) + * 3 (Yellow Book) Mode1 Form2 2336 (M2RAW_SECTOR_SIZE) + * 4 (Green Book) Mode2 Form1 2048 (M2F1_SECTOR_SIZE) + * 5 (Green Book) Mode2 Form2 2328 (2324+4 spare bytes) + * + * + * The layout of the standard CD-ROM data formats: + * ----------------------------------------------------------------------------- + * - audio (red): | audio_sample_bytes | + * | 2352 | + * + * - data (yellow, mode1): | sync - head - data - EDC - zero - ECC | + * | 12 - 4 - 2048 - 4 - 8 - 276 | + * + * - data (yellow, mode2): | sync - head - data | + * | 12 - 4 - 2336 | + * + * - XA data (green, mode2 form1): | sync - head - sub - data - EDC - ECC | + * | 12 - 4 - 8 - 2048 - 4 - 276 | + * + * - XA data (green, mode2 form2): | sync - head - sub - data - Spare | + * | 12 - 4 - 8 - 2324 - 4 | + * + */ + +#define CD_RAW_SECTOR_SIZE 2352 +#define CDDA_SECTOR_SIZE CD_RAW_SECTOR_SIZE +#define M2F1_SECTOR_SIZE 2048 +#define M2F2_SECTOR_SIZE 2324 +#define M2SUB_SECTOR_SIZE 2332 +#define M2RAW_SECTOR_SIZE 2336 + +#define CD_MAX_TRACKS 99 + +#define CD_FRAMES_PER_SECOND 75 +#define CD_FRAMES_PER_MINUTE (60*(CD_FRAMES_PER_SECOND)) +#define CD_74MIN_SECTORS (UINT32_C(74)*CD_FRAMES_PER_MINUTE) +#define CD_80MIN_SECTORS (UINT32_C(80)*CD_FRAMES_PER_MINUTE) +#define CD_90MIN_SECTORS (UINT32_C(90)*CD_FRAMES_PER_MINUTE) +#define CD_MAX_SECTORS (UINT32_C(100)*CD_FRAMES_PER_MINUTE-CDIO_PREGAP_SECTORS) + +#define msf_t_SIZEOF 3 + +/* warning, returns new allocated string */ +char * +cdio_lba_to_msf_str (lba_t lba); + +static inline lba_t +cdio_lba_to_lsn (lba_t lba) +{ + return lba - CDIO_PREGAP_SECTORS; +} + +void +cdio_lba_to_msf(lba_t lba, msf_t *msf); + +static inline lba_t +cdio_lsn_to_lba (lsn_t lsn) +{ + return lsn + CDIO_PREGAP_SECTORS; +} + +void +cdio_lsn_to_msf (lsn_t lsn, msf_t *msf); + +uint32_t +cdio_msf_to_lba (const msf_t *msf); + +uint32_t +cdio_msf_to_lsn (const msf_t *msf); + +#endif /* _CDIO_SECTOR_H_ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/util.c b/lib/util.c new file mode 100644 index 00000000..ef70e5b4 --- /dev/null +++ b/lib/util.c @@ -0,0 +1,192 @@ +/* + $Id: util.c,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + Copyright (C) 2003 Rocky Bernstein + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include "cdio_assert.h" +#include "util.h" + +static const char _rcsid[] = "$Id: util.c,v 1.1 2003/03/24 19:01:09 rocky Exp $"; + +size_t +_cdio_strlenv(char **str_array) +{ + size_t n = 0; + + cdio_assert (str_array != NULL); + + while(str_array[n]) + n++; + + return n; +} + +void +_cdio_strfreev(char **strv) +{ + int n; + + cdio_assert (strv != NULL); + + for(n = 0; strv[n]; n++) + free(strv[n]); + + free(strv); +} + +char * +_cdio_strjoin (char *strv[], unsigned count, const char delim[]) +{ + size_t len; + char *new_str; + unsigned n; + + cdio_assert (strv != NULL); + cdio_assert (delim != NULL); + + len = (count-1) * strlen (delim); + + for (n = 0;n < count;n++) + len += strlen (strv[n]); + + len++; + + new_str = _cdio_malloc (len); + new_str[0] = '\0'; + + for (n = 0;n < count;n++) + { + if (n) + strcat (new_str, delim); + strcat (new_str, strv[n]); + } + + return new_str; +} + +char ** +_cdio_strsplit(const char str[], char delim) /* fixme -- non-reentrant */ +{ + int n; + char **strv = NULL; + char *_str, *p; + char _delim[2] = { 0, 0 }; + + cdio_assert (str != NULL); + + _str = strdup(str); + _delim[0] = delim; + + cdio_assert (_str != NULL); + + n = 1; + p = _str; + while(*p) + if (*(p++) == delim) + n++; + + strv = _cdio_malloc (sizeof (char *) * (n+1)); + + n = 0; + while((p = strtok(n ? NULL : _str, _delim)) != NULL) + strv[n++] = strdup(p); + + free(_str); + + return strv; +} + +void * +_cdio_malloc (size_t size) +{ + void *new_mem = malloc (size); + + cdio_assert (new_mem != NULL); + + memset (new_mem, 0, size); + + return new_mem; +} + +void * +_cdio_memdup (const void *mem, size_t count) +{ + void *new_mem = NULL; + + if (mem) + { + new_mem = _cdio_malloc (count); + memcpy (new_mem, mem, count); + } + + return new_mem; +} + +char * +_cdio_strdup_upper (const char str[]) +{ + char *new_str = NULL; + + if (str) + { + char *p; + + p = new_str = strdup (str); + + while (*p) + { + *p = toupper (*p); + p++; + } + } + + return new_str; +} + +uint8_t +to_bcd8 (uint8_t n) +{ + cdio_assert (n < 100); + + return ((n/10)<<4) | (n%10); +} + +uint8_t +from_bcd8(uint8_t p) +{ + return (0xf & p)+(10*(p >> 4)); +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/util.h b/lib/util.h new file mode 100644 index 00000000..3fdde828 --- /dev/null +++ b/lib/util.h @@ -0,0 +1,109 @@ +/* + $Id: util.h,v 1.1 2003/03/24 19:01:09 rocky Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __CDIO_UTIL_H__ +#define __CDIO_UTIL_H__ + +#include + +#undef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#undef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#undef IN +#define IN(x, low, high) ((x) >= (low) && (x) <= (high)) + +#undef CLAMP +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +static inline unsigned +_cdio_len2blocks (unsigned len, int blocksize) +{ + unsigned blocks; + + blocks = len / blocksize; + if (len % blocksize) + blocks++; + + return blocks; +} + +/* round up to next block boundary */ +static inline unsigned +_cdio_ceil2block (unsigned offset, int blocksize) +{ + return _cdio_len2blocks (offset, blocksize) * blocksize; +} + +static inline unsigned +_cdio_ofs_add (unsigned offset, unsigned length, int blocksize) +{ + if (blocksize - (offset % blocksize) < length) + offset = _cdio_ceil2block (offset, blocksize); + + offset += length; + + return offset; +} + +void * +_cdio_malloc (size_t size); + +void * +_cdio_memdup (const void *mem, size_t count); + +char * +_cdio_strdup_upper (const char str[]); + +void +_cdio_strfreev(char **strv); + +char * +_cdio_strjoin (char *strv[], unsigned count, const char delim[]); + +size_t +_cdio_strlenv(char **str_array); + +char ** +_cdio_strsplit(const char str[], char delim); + +static inline const char * +_cdio_bool_str (bool b) +{ + return b ? "yes" : "no"; +} + +/* BCD */ + +uint8_t to_bcd8(uint8_t n); +uint8_t from_bcd8(uint8_t p); + +#endif /* __CDIO_UTIL_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/libpopt.m4 b/libpopt.m4 new file mode 100644 index 00000000..f59626de --- /dev/null +++ b/libpopt.m4 @@ -0,0 +1,68 @@ +# Configure paths for libpopt, based on m4's part of gnome +# (c) 2002 Herbert Valerio Riedel + +dnl AM_PATH_LIBPOPT([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl Test for libpopt, sets LIBPOPT_{CFLAGS,LIBS} +dnl alas there's no easy way to check the version available + +AC_DEFUN(AM_PATH_LIBPOPT, [ + +AC_ARG_WITH(libpopt-prefix,[ --with-libpopt-prefix=PFX Prefix where libpopt is installed (optional)], + libpopt_prefix="$withval", libpopt_prefix="") + + if test x$libpopt_prefix != x ; then + LIBPOPT_CFLAGS="-I$libpopt_prefix/include" + LIBPOPT_LIBS="-L$libpopt_prefix/lib -lpopt" + else + LIBPOPT_CFLAGS="" + LIBPOPT_LIBS="-lpopt" + fi + + AC_MSG_CHECKING(for libpopt library) + +dnl save CFLAGS and LIBS + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $LIBPOPT_CFLAGS" + LIBS="$LIBPOPT_LIBS $LIBS" + +dnl now check whether the installed libpopt is usable + rm -f conf.glibtest + AC_TRY_RUN([ +#include + +int +main(int argc, const char *argv[]) +{ + const struct poptOption options[] = { + POPT_AUTOHELP + { NULL, 0, 0, NULL, 0 } + }; + + poptContext context = poptGetContext("popt-test", argc, argv, options, 0); + poptSetOtherOptionHelp (context, "[OPTION...] "); + poptGetNextOpt(context); + + return 0; +} +],, no_popt=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + +dnl handle test result + if test "x$no_popt" = x ; then + AC_MSG_RESULT(yes) + ifelse([$1], , :, [$1]) + else + AC_MSG_RESULT(no) + LIBPOPT_CFLAGS="" + LIBPOPT_LIBS="" + ifelse([$2], , :, [$2]) + fi + + AC_SUBST(LIBPOPT_CFLAGS) + AC_SUBST(LIBPOPT_LIBS) + +]) dnl AC_DEFUN(AM_PATH_LIBPOPT, [... + +dnl EOF diff --git a/src/.cvsignore b/src/.cvsignore new file mode 100644 index 00000000..750abe86 --- /dev/null +++ b/src/.cvsignore @@ -0,0 +1,8 @@ +.deps +.libs +Makefile +Makefile.in +*.o +cdinfo +cdinfo-linux +*.right \ No newline at end of file diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..1823c459 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,34 @@ +# $Id: Makefile.am,v 1.1 2003/03/24 19:01:09 rocky Exp $ +# +# Copyright (C) 2003 Rocky Bernstein +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +#################################################### +# Things to make the sample/test programs +#################################################### +if BUILD_CDINFO_LINUX +bin_PROGRAMS = cdinfo cdinfo-linux +cdinfo_linux_SOURCES = cdinfo-linux.c +cdinfo_linux_LDADD = $(LIBCDIO_LIBS) -lpopt +else +EXTRA_DIST = cdinfo-linux.c +bin_PROGRAMS = cdinfo +endif + +INCLUDES = -I$(top_srcdir) $(LIBCDIO_CFLAGS) + +cdinfo_SOURCES = cdinfo.c +cdinfo_LDADD = $(LIBCDIO_LIBS) -lpopt diff --git a/src/cdinfo-linux.c b/src/cdinfo-linux.c new file mode 100644 index 00000000..a9c3c2c1 --- /dev/null +++ b/src/cdinfo-linux.c @@ -0,0 +1,911 @@ +/* + $Id: cdinfo-linux.c,v 1.1 2003/03/24 19:01:10 rocky Exp $ + + Copyright (C) 2003 Rocky Bernstein + Copyright (C) 1996,1997,1998 Gerd Knorr + and Heiko Eißfeldt + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* + CD Info - prints various information about a CD, and detects the type of + the CD. + + usage: cdinfo [options] [ dev ] + +*/ +#define PROGRAM_NAME "CD Info" +#define CDINFO_VERSION "2.0" + +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +# include +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,50) +# include +# endif +#endif + +#include +#include +#include + +#include "config.h" +#include "cdio.h" +#include "logging.h" +#include "util.h" + +#ifdef ENABLE_NLS +#include +# include +# define _(String) dgettext ("cdinfo", String) +#else +/* Stubs that do something close enough. */ +# define _(String) (String) +#endif + +/* The following test is to work around the gross typo in + systems like Sony NEWS-OS Release 4.0C, whereby EXIT_FAILURE + is defined to 0, not 1. */ +#if !EXIT_FAILURE +# undef EXIT_FAILURE +# define EXIT_FAILURE 1 +#endif + +#ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +#endif + +/* Used by `main' to communicate with `parse_opt'. And global options + */ +struct arguments +{ + bool show_tracks; + bool show_ioctl; + bool show_analysis; + int debug_level; + bool silent; +} opts; + +#define DEBUG 1 +#if DEBUG +#define dbg_print(level, s, args...) \ + if (opts.debug_level >= level) \ + fprintf(stderr, "%s: "s, __func__ , ##args) +#else +#define dbg_print(level, s, args...) +#endif + +#define err_exit(fmt, args...) \ + fprintf(stderr, "%s: "fmt, program_name, ##args); \ + myexit(EXIT_FAILURE) + +/* +Subject: -65- How can I read an IRIX (EFS) CD-ROM on a machine which + doesn't use EFS? +Date: 18 Jun 1995 00:00:01 EST + + You want 'efslook', at + ftp://viz.tamu.edu/pub/sgi/software/efslook.tar.gz. + +and +! Robert E. Seastrom 's software (with source +! code) for using an SGI CD-ROM on a Macintosh is at +! ftp://bifrost.seastrom.com/pub/mac/CDROM-Jumpstart.sit151.hqx. + +*/ + +#define FS_NO_DATA 0 /* audio only */ +#define FS_HIGH_SIERRA 1 +#define FS_ISO_9660 2 +#define FS_INTERACTIVE 3 +#define FS_HFS 4 +#define FS_UFS 5 +#define FS_EXT2 6 +#define FS_ISO_HFS 7 /* both hfs & isofs filesystem */ +#define FS_ISO_9660_INTERACTIVE 8 /* both CD-RTOS and isofs filesystem */ +#define FS_3DO 9 +#define FS_UNKNOWN 15 +#define FS_MASK 15 + +#define XA 16 +#define MULTISESSION 32 +#define PHOTO_CD 64 +#define HIDDEN_TRACK 128 +#define CDTV 256 +#define BOOTABLE 512 +#define VIDEOCDI 1024 +#define ROCKRIDGE 2048 +#define JOLIET 4096 + +/* Some interesting sector numbers. */ +#define ISO_SUPERBLOCK_SECTOR 16 +#define UFS_SUPERBLOCK_SECTOR 4 +#define BOOT_SECTOR 17 +#define VCD_INFO_SECTOR 150 + +#if 0 +#define STRONG "\033[1m" +#define NORMAL "\033[0m" +#else +#define STRONG "__________________________________\n" +#define NORMAL "" +#endif + +typedef struct signature +{ + unsigned int buf_num; + unsigned int offset; + const char *sig_str; + const char *description; +} signature_t; + +#define IS_ISOFS 0 +#define IS_CD_I 1 +#define IS_CDTV 2 +#define IS_CD_RTOS 3 +#define IS_HS 4 +#define IS_BRIDGE 5 +#define IS_XA 6 +#define IS_PHOTO_CD 7 +#define IS_EXT2 8 +#define IS_UFS 9 +#define IS_BOOTABLE 10 +#define IS_VIDEO_CD 11 + +static signature_t sigs[] = + { + /* Buff off look for description */ + {0, 1, "CD001", "ISO 9660"}, + {0, 1, "CD-I", "CD-I"}, + {0, 8, "CDTV", "CDTV"}, + {0, 8, "CD-RTOS", "CD-RTOS"}, + {0, 9, "CDROM", "HIGH SIERRA"}, + {0, 16, "CD-BRIDGE", "BRIDGE"}, + {0, 1024, "CD-XA001", "XA"}, + {1, 64, "PPPPHHHHOOOOTTTTOOOO____CCCCDDDD", "PHOTO CD"}, + {1, 0x438, "\x53\xef", "EXT2 FS"}, + {2, 1372, "\x54\x19\x01\x0", "UFS"}, + {3, 7, "EL TORITO", "BOOTABLE"}, + {4, 0, "VIDEO_CD", "VIDEO CD"}, + { 0 } + }; + +int filehandle; /* Handle of /dev/>cdrom< */ +int rc; /* return code */ +int i,j; /* index */ +int isofs_size = 0; /* size of session */ +int start_track; /* first sector of track */ +int ms_offset; /* multisession offset found by track-walking */ +int data_start; /* start of data area */ +int joliet_level = 0; + +char buffer[6][CDDA_SECTOR_SIZE]; /* for CD-Data */ + +CdIo *img; +track_t num_tracks; +track_t first_track_num; + +struct cdrom_tocentry *toc[CDIO_LEADOUT_TRACK+1]; /* TOC-entries */ +struct cdrom_mcn mcn; +struct cdrom_multisession ms; +struct cdrom_subchnl sub; +int first_data = -1; /* # of first data track */ +int num_data = 0; /* # of data tracks */ +int first_audio = -1; /* # of first audio track */ +int num_audio = 0; /* # of audio tracks */ + + +char *devname = NULL; +char *program_name; + +const char *argp_program_version = PROGRAM_NAME CDINFO_VERSION; +const char *argp_program_bug_address = "rocky@panix.com"; + +/* Program documentation. */ +const char doc[] = + PROGRAM_NAME " -- Get information about a Compact Disk or CD image."; + +/* A description of the arguments we accept. */ +const char args_doc[] = "[DEVICE or DISK-IMAGE]"; + +static struct argp_option options[] = +{ + {"debug", 'd', "LEVEL", 0, "Set debugging to LEVEL"}, + {"quiet", 'q', 0, 0, "Don't produce any output" }, + {"silent", 's', 0, OPTION_ALIAS }, + {"notracks", 'T', 0, 0, "Don't show track information"}, + {"noanalyze",'A', 0, 0, "Don't filesystem analysis"}, + {"noioctl", 'I', 0, 0, "Don't show ioctl() information"}, + { 0 } +}; + +/* Parse a single option. */ +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + /* Get the INPUT argument from `argp_parse', which we + know is a pointer to our arguments structure. */ + struct arguments *arguments = state->input; + + switch (key) + { + case 'q': case 's': + arguments->silent = 1; + break; + case 'd': + /* Default debug level is 1. */ + arguments->debug_level = arg ? atol(arg): 1; + break; + case 'I': + arguments->show_ioctl = false; + break; + case 'T': + arguments->show_tracks = false; + break; + case 'A': + arguments->show_analysis = false; + break; + case ARGP_KEY_ARG: + /* Let the next case parse it. */ + return ARGP_ERR_UNKNOWN; + break; + + case ARGP_KEY_ARGS: + { + /* Check that only one device given. If so, handle it. */ + unsigned int num_remaining_args = state->argc - state->next; + char **remaining_args = state->argv + state->next; + if (num_remaining_args > 1) { + argp_usage (state); + } + if (0 == strncmp(remaining_args[0],"/dev/",5)) + devname = remaining_args[0]; + else { + devname=malloc(6+strlen(remaining_args[0])); + sprintf(devname,"/dev/%s", remaining_args[0]); + } + } + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static void +print_version (void) +{ + printf( _("CD Info %s | (c) 2003 Gerd Knorr, Heiko Eißfeldt & R. Bernstein\n\ +This is free software; see the source for copying conditions.\n\ +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n\ +PARTICULAR PURPOSE.\n\ +"), + CDINFO_VERSION); +} + +/* ------------------------------------------------------------------------ */ +/* some ISO 9660 fiddling */ + +static int +read_block(int superblock, uint32_t offset, uint8_t bufnum, bool is_green) +{ + memset(buffer[bufnum],0,M2F1_SECTOR_SIZE); + + dbg_print(2, "about to read sector %u\n", offset+superblock); + if (cdio_read_mode2_sector(img, buffer[bufnum], + offset+superblock, !is_green)) + return -1; + + + /* For now compare with what we get the old way.... */ + if (0 > lseek(filehandle,M2F1_SECTOR_SIZE*(offset+superblock),SEEK_SET)) + return -1; + + memset(buffer[5],0,M2F1_SECTOR_SIZE); + if (0 > read(filehandle,buffer[5],M2F1_SECTOR_SIZE)) + return -1; + + if (memcmp(buffer[bufnum], buffer[5], M2F1_SECTOR_SIZE) != 0) { + dbg_print(0, + "libcdio conversion problem in reading super, buf %d\n", + bufnum); + } + + return 0; +} + +static bool +is_it(int num) +{ + signature_t *sigp; + + /* TODO: check that num < largest sig. */ + sigp = &sigs[num]; + + int len = strlen(sigp->sig_str); + return 0 == memcmp(&buffer[sigp->buf_num][sigp->offset], + sigp->sig_str, len); +} + +static int +is_hfs(void) +{ + return (0 == memcmp(&buffer[1][512],"PM",2)) || + (0 == memcmp(&buffer[1][512],"TS",2)) || + (0 == memcmp(&buffer[1][1024], "BD",2)); +} + +static int +is_3do(void) +{ + return (0 == memcmp(&buffer[1][0],"\x01\x5a\x5a\x5a\x5a\x5a\x01", 7)) && + (0 == memcmp(&buffer[1][40], "CD-ROM", 6)); +} + +static int is_joliet(void) +{ + return 2 == buffer[3][0] && buffer[3][88] == 0x25 && buffer[3][89] == 0x2f; +} + +/* ISO 9660 volume space in M2F1_SECTOR_SIZE byte units */ +static int +get_size(void) +{ + return ((buffer[0][80] & 0xff) | + ((buffer[0][81] & 0xff) << 8) | + ((buffer[0][82] & 0xff) << 16) | + ((buffer[0][83] & 0xff) << 24)); +} + +static int +get_joliet_level( void ) +{ + switch (buffer[3][90]) { + case 0x40: return 1; + case 0x43: return 2; + case 0x45: return 3; + } + return 0; +} + +#define is_it_dbg(sig) \ + if (is_it(sig)) printf("%s, ", sigs[sig].description) + +static int +guess_filesystem(int start_session, bool is_green) +{ + int ret = 0; + + if (read_block(ISO_SUPERBLOCK_SECTOR, start_session, 0, is_green) < 0) + return FS_UNKNOWN; + + if (opts.debug_level > 0) { + /* buffer is defined */ + is_it_dbg(IS_CD_I); + is_it_dbg(IS_CD_RTOS); + is_it_dbg(IS_ISOFS); + is_it_dbg(IS_HS); + is_it_dbg(IS_BRIDGE); + is_it_dbg(IS_XA); + is_it_dbg(IS_CDTV); + puts(""); + } + + /* filesystem */ + if (is_it(IS_CD_I) && is_it(IS_CD_RTOS) + && !is_it(IS_BRIDGE) && !is_it(IS_XA)) { + return FS_INTERACTIVE; + } else { /* read sector 0 ONLY, when NO greenbook CD-I !!!! */ + + if (read_block(0, start_session, 1, true) < 0) + return ret; + + if (opts.debug_level > 0) { + /* buffer[1] is defined */ + is_it_dbg(IS_PHOTO_CD); + if (is_hfs()) printf("HFS, "); + is_it_dbg(IS_EXT2); + if (is_3do()) printf("3DO, "); + puts(""); + } + + if (is_it(IS_HS)) + ret |= FS_HIGH_SIERRA; + else if (is_it(IS_ISOFS)) { + if (is_it(IS_CD_RTOS) && is_it(IS_BRIDGE)) + ret = FS_ISO_9660_INTERACTIVE; + else if (is_hfs()) + ret = FS_ISO_HFS; + else + ret = FS_ISO_9660; + isofs_size = get_size(); + +#if 0 + if (is_rockridge()) + ret |= ROCKRIDGE; +#endif + + if (read_block(BOOT_SECTOR, start_session, 3, true) < 0) + return ret; + + if (opts.debug_level > 0) { + + /* buffer[3] is defined */ + if (is_joliet()) printf("JOLIET, "); + puts(""); + is_it_dbg(IS_BOOTABLE); + puts(""); + } + + if (is_joliet()) { + joliet_level = get_joliet_level(); + ret |= JOLIET; + } + if (is_it(IS_BOOTABLE)) + ret |= BOOTABLE; + + if (is_it(IS_BRIDGE) && is_it(IS_XA) && is_it(IS_ISOFS) + && is_it(IS_CD_RTOS) && + !is_it(IS_PHOTO_CD)) { + + if (read_block(VCD_INFO_SECTOR, start_session, 4, true) < 0) + return ret; + + if (opts.debug_level > 0) { + /* buffer[4] is defined */ + is_it_dbg(IS_VIDEO_CD); + puts(""); + } + + if (is_it(IS_VIDEO_CD)) ret |= VIDEOCDI; + } + } + else if (is_hfs()) ret |= FS_HFS; + else if (is_it(IS_EXT2)) ret |= FS_EXT2; + else if (is_3do()) ret |= FS_3DO; + else { + + if (read_block(UFS_SUPERBLOCK_SECTOR, start_session, 2, true) < 0) + return ret; + + if (opts.debug_level > 0) { + /* buffer[2] is defined */ + is_it_dbg(IS_UFS); + puts(""); + } + + if (is_it(IS_UFS)) + ret |= FS_UFS; + else + ret |= FS_UNKNOWN; + } + } + + /* other checks */ + if (is_it(IS_XA)) ret |= XA; + if (is_it(IS_PHOTO_CD)) ret |= PHOTO_CD; + if (is_it(IS_CDTV)) ret |= CDTV; + return ret; +} + +static void +myexit(int rc) +{ + close(filehandle); + cdio_destroy(img); + exit(rc); +} + + +/* ------------------------------------------------------------------------ */ +/* CDDB */ + +/* + Returns the sum of the decimal digits in a number. Eg. 1955 = 20 +*/ +static int +cddb_dec_digit_sum(int n) +{ + int ret=0; + + for (;;) { + ret += n%10; + n = n/10; + if (!n) + return ret; + } +} + +/* Return the number of seconds (discarding frame portion) of an MSF */ +static inline unsigned int +msf_seconds(msf_t *msf) +{ + return from_bcd8(msf->m)*60 + from_bcd8(msf->s); +} + +/* + Compute the CDDB disk ID for an Audio disk. This is a funny checksum + consisting of the concatenation of 3 things: + the sum of the decimal digits of sizes of all tracks, + the total length of the disk, and + the number of tracks. +*/ +static unsigned long +cddb_discid() +{ + int i,t,n=0; + msf_t start_msf; + msf_t msf; + + for (i = 1; i <= num_tracks; i++) { + cdio_get_track_msf(img, i, &msf); + n += cddb_dec_digit_sum(msf_seconds(&msf)); + } + + cdio_get_track_msf(img, 1, &start_msf); + cdio_get_track_msf(img, CDIO_LEADOUT_TRACK, &msf); + + t = msf_seconds(&msf) - msf_seconds(&start_msf); + + return ((n % 0xff) << 24 | t << 8 | num_tracks); +} + + +/* CDIO logging routines */ + +static cdio_log_handler_t gl_default_log_handler = NULL; + +static void +_log_handler (log_level_t level, const char message[]) +{ + if (level == LOG_DEBUG && opts.debug_level < 2) + return; + + if (level == LOG_INFO && opts.debug_level < 1) + return; + + if (level == LOG_WARN && opts.silent) + return; + + gl_default_log_handler (level, message); +} + +static void +print_analysis(int fs, int num_audio) +{ + int need_lf; + + switch(fs & FS_MASK) { + case FS_NO_DATA: + if (num_audio > 0) + printf("Audio CD, CDDB disc ID is %08lx\n", cddb_discid()); + break; + case FS_ISO_9660: + printf("CD-ROM with ISO 9660 filesystem"); + if (fs & JOLIET) + printf(" and joliet extension level %d", joliet_level); + if (fs & ROCKRIDGE) + printf(" and rockridge extensions"); + printf("\n"); + break; + case FS_ISO_9660_INTERACTIVE: + printf("CD-ROM with CD-RTOS and ISO 9660 filesystem\n"); + break; + case FS_HIGH_SIERRA: + printf("CD-ROM with High Sierra filesystem\n"); + break; + case FS_INTERACTIVE: + printf("CD-Interactive%s\n", num_audio > 0 ? "/Ready" : ""); + break; + case FS_HFS: + printf("CD-ROM with Macintosh HFS\n"); + break; + case FS_ISO_HFS: + printf("CD-ROM with both Macintosh HFS and ISO 9660 filesystem\n"); + break; + case FS_UFS: + printf("CD-ROM with Unix UFS\n"); + break; + case FS_EXT2: + printf("CD-ROM with Linux second extended filesystem\n"); + break; + case FS_3DO: + printf("CD-ROM with Panasonic 3DO filesystem\n"); + break; + case FS_UNKNOWN: + printf("CD-ROM with unknown filesystem\n"); + break; + } + switch(fs & FS_MASK) { + case FS_ISO_9660: + case FS_ISO_9660_INTERACTIVE: + case FS_ISO_HFS: + printf("ISO 9660: %i blocks, label `%.32s'\n", + isofs_size, buffer[0]+40); + break; + } + need_lf = 0; + if (first_data == 1 && num_audio > 0) + need_lf += printf("mixed mode CD "); + if (fs & XA) + need_lf += printf("XA sectors "); + if (fs & MULTISESSION) + need_lf += printf("Multisession, offset = %i ",ms_offset); + if (fs & HIDDEN_TRACK) + need_lf += printf("Hidden Track "); + if (fs & PHOTO_CD) + need_lf += printf("%sPhoto CD ", num_audio > 0 ? " Portfolio " : ""); + if (fs & CDTV) + need_lf += printf("Commodore CDTV "); + if (first_data > 1) + need_lf += printf("CD-Plus/Extra "); + if (fs & BOOTABLE) + need_lf += printf("bootable CD "); + if (fs & VIDEOCDI && num_audio == 0) + need_lf += printf("Video CD "); + if (need_lf) puts(""); +} + + +/* ------------------------------------------------------------------------ */ + +/* Our argp parser. */ +static struct argp argp = { options, parse_opt, args_doc, doc }; + +int +main(int argc, char *argv[]) +{ + + + int fs=0; + gl_default_log_handler = cdio_log_set_handler (_log_handler); + + program_name = strrchr(argv[0],'/'); + program_name = program_name ? program_name+1 : argv[0]; + + /* Default option values. */ + opts.silent = false; + opts.debug_level = 0; + opts.show_tracks = true; + opts.show_ioctl = true; + opts.show_analysis = true; + + /* Parse our arguments; every option seen by `parse_opt' will + be reflected in `arguments'. */ + argp_parse (&argp, argc, argv, 0, 0, &opts); + + print_version(); + + img = cdio_new_cd (); + if (devname==NULL) { + devname=strdup(cdio_get_default_device(img)); + } + cdio_set_arg (img, "device", devname); + + /* open device */ + filehandle = open(devname,O_RDONLY); + if (filehandle == -1) { + err_exit("%s: %s\n", devname, strerror(errno)); + } + + first_track_num = cdio_get_first_track_num(img); + num_tracks = cdio_get_num_tracks(img); + + if (opts.show_tracks) { + printf(STRONG "Track List (%i - %i)\n" NORMAL, + first_track_num, num_tracks); + + printf(" nr: MSF LSN Ctrl Adr Type\n"); + } + + /* Read and possibly print track information. */ + for (i = first_track_num; i <= CDIO_LEADOUT_TRACK; i++) { + msf_t msf; + + toc[i] = malloc(sizeof(struct cdrom_tocentry)); + if (toc[i] == NULL) { + err_exit("out of memory"); + } + memset(toc[i],0,sizeof(struct cdrom_tocentry)); + toc[i]->cdte_track = i; + toc[i]->cdte_format = CDROM_MSF; + if (ioctl(filehandle,CDROMREADTOCENTRY,toc[i])) { + err_exit("read TOC entry ioctl failed for track %i, give up\n", i); + } + + if (!cdio_get_track_msf(img, i, &msf)) { + err_exit("cdio_track_msf for track %i failed, I give up.\n", i); + } + + if (opts.show_tracks) { + printf("%3d: %2.2x:%2.2x:%2.2x (%06d) 0x%x 0x%x %s%s\n", + (int) i, + msf.m, msf.s, msf.f, + cdio_msf_to_lsn(&msf), + (int)toc[i]->cdte_ctrl, + (int)toc[i]->cdte_adr, + track_format2str[cdio_get_track_format(img, i)], + CDIO_LEADOUT_TRACK == i ? " (leadout)" : ""); + } + + if (i == CDIO_LEADOUT_TRACK) + break; + if (TRACK_FORMAT_DATA == cdio_get_track_format(img, i)) { + num_data++; + if (-1 == first_data) + first_data = i; + } else { + num_audio++; + if (-1 == first_audio) + first_audio = i; + } + /* skip to leadout */ + if (i == num_tracks) + i = CDIO_LEADOUT_TRACK-1; + } + + if (opts.show_ioctl) { + printf(STRONG "What ioctl's report...\n" NORMAL); + +#ifdef CDROM_GET_MCN + /* get mcn */ + printf("Get MCN : "); fflush(stdout); + if (ioctl(filehandle,CDROM_GET_MCN, &mcn)) + printf("FAILED\n"); + else + printf("%s\n",mcn.medium_catalog_number); +#endif + +#ifdef CDROM_DISC_STATUS + /* get disk status */ + printf("disc status : "); fflush(stdout); + switch (ioctl(filehandle,CDROM_DISC_STATUS,0)) { + case CDS_NO_INFO: printf("no info\n"); break; + case CDS_NO_DISC: printf("no disc\n"); break; + case CDS_AUDIO: printf("audio\n"); break; + case CDS_DATA_1: printf("data mode 1\n"); break; + case CDS_DATA_2: printf("data mode 2\n"); break; + case CDS_XA_2_1: printf("XA mode 1\n"); break; + case CDS_XA_2_2: printf("XA mode 2\n"); break; + default: printf("unknown (failed?)\n"); + } +#endif + +#ifdef CDROMMULTISESSION + /* get multisession */ + printf("multisession: "); fflush(stdout); + ms.addr_format = CDROM_LBA; + if (ioctl(filehandle,CDROMMULTISESSION,&ms)) + printf("FAILED\n"); + else + printf("%d%s\n",ms.addr.lba,ms.xa_flag?" XA":""); +#endif + +#ifdef CDROMSUBCHNL + /* get audio status from subchnl */ + printf("audio status: "); fflush(stdout); + sub.cdsc_format = CDROM_MSF; + if (ioctl(filehandle,CDROMSUBCHNL,&sub)) + printf("FAILED\n"); + else { + switch (sub.cdsc_audiostatus) { + case CDROM_AUDIO_INVALID: printf("invalid\n"); break; + case CDROM_AUDIO_PLAY: printf("playing"); break; + case CDROM_AUDIO_PAUSED: printf("paused"); break; + case CDROM_AUDIO_COMPLETED: printf("completed\n"); break; + case CDROM_AUDIO_ERROR: printf("error\n"); break; + case CDROM_AUDIO_NO_STATUS: printf("no status\n"); break; + default: printf("Oops: unknown\n"); + } + if (sub.cdsc_audiostatus == CDROM_AUDIO_PLAY || + sub.cdsc_audiostatus == CDROM_AUDIO_PAUSED) { + printf(" at: %02d:%02d abs / %02d:%02d track %d\n", + sub.cdsc_absaddr.msf.minute, + sub.cdsc_absaddr.msf.second, + sub.cdsc_reladdr.msf.minute, + sub.cdsc_reladdr.msf.second, + sub.cdsc_trk); + } + } +#endif + } + + if (opts.show_analysis) { + printf(STRONG "try to find out what sort of CD this is\n" NORMAL); + + /* try to find out what sort of CD we have */ + if (0 == num_data) { + /* no data track, may be a "real" audio CD or hidden track CD */ + + msf_t msf; + cdio_get_track_msf(img, 1, &msf); + start_track = cdio_msf_to_lsn(&msf); + + /* CD-I/Ready says start_track <= 30*75 then CDDA */ + if (start_track > 100 /* 100 is just a guess */) { + fs = guess_filesystem(0, false); + if ((fs & FS_MASK) != FS_UNKNOWN) + fs |= HIDDEN_TRACK; + else { + fs &= ~FS_MASK; /* del filesystem info */ + printf("Oops: %i unused sectors at start, but hidden track check failed.\n",start_track); + } + } + print_analysis(fs, num_audio); + } else { + /* we have data track(s) */ + + for (j = 2, i = first_data; i <= num_tracks; i++) { + msf_t msf; + track_format_t track_format = cdio_get_track_format(img, i); + + cdio_get_track_msf(img, i, &msf); + + switch ( track_format ) { + case TRACK_FORMAT_AUDIO: + case TRACK_FORMAT_ERROR: + break; + case TRACK_FORMAT_CDI: + case TRACK_FORMAT_XA: + case TRACK_FORMAT_DATA: + ; + } + + start_track = (i == 1) ? 0 : cdio_msf_to_lsn(&msf); + + /* save the start of the data area */ + if (i == first_data) + data_start = start_track; + + /* skip tracks which belong to the current walked session */ + if (start_track < data_start + isofs_size) + continue; + + fs = guess_filesystem(start_track, cdio_get_track_green(img, i)); + + if (i > 1) { + /* track is beyond last session -> new session found */ + ms_offset = start_track; + printf("session #%d starts at track %2i, LSN: %6i," + " ISO 9660 blocks: %6i\n", + j++, i, start_track, isofs_size); + printf("ISO 9660: %i blocks, label `%.32s'\n", + isofs_size, buffer[0]+40); + fs |= MULTISESSION; + } else { + print_analysis(fs, num_audio); + } + + if (!(((fs & FS_MASK) == FS_ISO_9660 || + (fs & FS_MASK) == FS_ISO_HFS || + /* (fs & FS_MASK) == FS_ISO_9660_INTERACTIVE) && (fs & XA))) */ + (fs & FS_MASK) == FS_ISO_9660_INTERACTIVE))) + break; /* no method for non-iso9660 multisessions */ + } + } + } + + + myexit(EXIT_SUCCESS); + /* Not reached:*/ + return(EXIT_SUCCESS); +} diff --git a/src/cdinfo.c b/src/cdinfo.c new file mode 100644 index 00000000..57b7a420 --- /dev/null +++ b/src/cdinfo.c @@ -0,0 +1,999 @@ +/* + $Id: cdinfo.c,v 1.1 2003/03/24 19:01:10 rocky Exp $ + + Copyright (C) 2003 Rocky Bernstein + Copyright (C) 1996,1997,1998 Gerd Knorr + and Heiko Eißfeldt + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* + CD Info - prints various information about a CD, and detects the type of + the CD. + + usage: cdinfo [options] [ dev ] + +*/ +#define PROGRAM_NAME "CD Info" +#define CDINFO_VERSION "2.0" + +#include +#include +#include +#include + +#include +/* Accomodate to older popt that doesn't support the "optional" flag */ +#ifndef POPT_ARGFLAG_OPTIONAL +#define POPT_ARGFLAG_OPTIONAL 0 +#endif + +#include +#include +#ifdef __linux__ +# include +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,50) +# include +# endif +#endif + +#include +#include + +#include "config.h" +#include "cdio.h" +#include "logging.h" +#include "util.h" + +#ifdef ENABLE_NLS +#include +# include +# define _(String) dgettext ("cdinfo", String) +#else +/* Stubs that do something close enough. */ +# define _(String) (String) +#endif + +/* The following test is to work around the gross typo in + systems like Sony NEWS-OS Release 4.0C, whereby EXIT_FAILURE + is defined to 0, not 1. */ +#if !EXIT_FAILURE +# undef EXIT_FAILURE +# define EXIT_FAILURE 1 +#endif + +#ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +#endif + +#define DEBUG 1 +#if DEBUG +#define dbg_print(level, s, args...) \ + if (opts.debug_level >= level) \ + fprintf(stderr, "%s: "s, __func__ , ##args) +#else +#define dbg_print(level, s, args...) +#endif + +#define err_exit(fmt, args...) \ + fprintf(stderr, "%s: "fmt, program_name, ##args); \ + myexit(EXIT_FAILURE) + +/* +Subject: -65- How can I read an IRIX (EFS) CD-ROM on a machine which + doesn't use EFS? +Date: 18 Jun 1995 00:00:01 EST + + You want 'efslook', at + ftp://viz.tamu.edu/pub/sgi/software/efslook.tar.gz. + +and +! Robert E. Seastrom 's software (with source +! code) for using an SGI CD-ROM on a Macintosh is at +! ftp://bifrost.seastrom.com/pub/mac/CDROM-Jumpstart.sit151.hqx. + +*/ + +#define FS_NO_DATA 0 /* audio only */ +#define FS_HIGH_SIERRA 1 +#define FS_ISO_9660 2 +#define FS_INTERACTIVE 3 +#define FS_HFS 4 +#define FS_UFS 5 +#define FS_EXT2 6 +#define FS_ISO_HFS 7 /* both hfs & isofs filesystem */ +#define FS_ISO_9660_INTERACTIVE 8 /* both CD-RTOS and isofs filesystem */ +#define FS_3DO 9 +#define FS_UNKNOWN 15 +#define FS_MASK 15 + +#define XA 16 +#define MULTISESSION 32 +#define PHOTO_CD 64 +#define HIDDEN_TRACK 128 +#define CDTV 256 +#define BOOTABLE 512 +#define VIDEOCDI 1024 +#define ROCKRIDGE 2048 +#define JOLIET 4096 + +/* Some interesting sector numbers. */ +#define ISO_SUPERBLOCK_SECTOR 16 +#define UFS_SUPERBLOCK_SECTOR 4 +#define BOOT_SECTOR 17 +#define VCD_INFO_SECTOR 150 + +#if 0 +#define STRONG "\033[1m" +#define NORMAL "\033[0m" +#else +#define STRONG "__________________________________\n" +#define NORMAL "" +#endif + +typedef struct signature +{ + unsigned int buf_num; + unsigned int offset; + const char *sig_str; + const char *description; +} signature_t; + +#define IS_ISOFS 0 +#define IS_CD_I 1 +#define IS_CDTV 2 +#define IS_CD_RTOS 3 +#define IS_HS 4 +#define IS_BRIDGE 5 +#define IS_XA 6 +#define IS_PHOTO_CD 7 +#define IS_EXT2 8 +#define IS_UFS 9 +#define IS_BOOTABLE 10 +#define IS_VIDEO_CD 11 + +static signature_t sigs[] = + { + /* Buff off look for description */ + {0, 1, "CD001", "ISO 9660"}, + {0, 1, "CD-I", "CD-I"}, + {0, 8, "CDTV", "CDTV"}, + {0, 8, "CD-RTOS", "CD-RTOS"}, + {0, 9, "CDROM", "HIGH SIERRA"}, + {0, 16, "CD-BRIDGE", "BRIDGE"}, + {0, 1024, "CD-XA001", "XA"}, + {1, 64, "PPPPHHHHOOOOTTTTOOOO____CCCCDDDD", "PHOTO CD"}, + {1, 0x438, "\x53\xef", "EXT2 FS"}, + {2, 1372, "\x54\x19\x01\x0", "UFS"}, + {3, 7, "EL TORITO", "BOOTABLE"}, + {4, 0, "VIDEO_CD", "VIDEO CD"}, + { 0 } + }; + +int rc; /* return code */ +int i,j; /* index */ +int isofs_size = 0; /* size of session */ +int start_track; /* first sector of track */ +int ms_offset; /* multisession offset found by track-walking */ +int data_start; /* start of data area */ +int joliet_level = 0; + +char buffer[6][CDDA_SECTOR_SIZE]; /* for CD-Data */ + +CdIo *img; +track_t num_tracks; +track_t first_track_num; + +#if CDIO_IOCTL_FINISHED +struct cdrom_mcn mcn; +struct cdrom_multisession ms; +struct cdrom_subchnl sub; +#endif + +int first_data = -1; /* # of first data track */ +int num_data = 0; /* # of data tracks */ +int first_audio = -1; /* # of first audio track */ +int num_audio = 0; /* # of audio tracks */ + + +char *source_name = NULL; +char *program_name; + +const char *argp_program_version = PROGRAM_NAME CDINFO_VERSION; +const char *argp_program_bug_address = "rocky@panix.com"; + +typedef enum +{ + IMAGE_AUTO, + IMAGE_DEVICE, + IMAGE_BIN, + IMAGE_CUE, + IMAGE_NRG, + IMAGE_UNKNOWN +} source_image_t; + +/* Used by `main' to communicate with `parse_opt'. And global options + */ +struct arguments +{ + bool no_tracks; + bool no_ioctl; + bool no_analysis; + int debug_level; + bool silent; + source_image_t source_image; +} opts; + +/* Program documentation. */ +const char doc[] = + PROGRAM_NAME " -- Get information about a Compact Disk or CD image."; + +/* A description of the arguments we accept. */ +const char args_doc[] = "[DEVICE or DISK-IMAGE]"; + + +/* Configuration option codes */ +enum { + + OP_SOURCE_UNDEF, + OP_SOURCE_AUTO, + OP_SOURCE_BIN, + OP_SOURCE_CUE, + OP_SOURCE_NRG , + OP_SOURCE_DEVICE, + + /* These are the remaining configuration options */ + OP_VERSION, OP_NOIOCTL, OP_NOTRACKS, OP_QUIET, + OP_NOANALYZE + +}; + +char *temp_str; + +struct poptOption optionsTable[] = { + {"debug", 'd', POPT_ARG_INT, &opts.debug_level, 0, + "Set debugging to LEVEL"}, + + {"quiet", 'q', POPT_ARG_NONE, &opts.silent, 0, + "Don't produce warning output" }, + + {"notracks", 'T', POPT_ARG_NONE, &opts.no_tracks, 0, + "Don't show track information"}, + + {"noanalyze",'A', POPT_ARG_NONE, &opts.no_analysis, 0, + "Don't filesystem analysis"}, + + {"noioctl", 'I', POPT_ARG_NONE, &opts.no_ioctl, 0, + "Don't show ioctl() information"}, + + {"bin-file", 'b', POPT_ARG_STRING|POPT_ARGFLAG_OPTIONAL, &source_name, + OP_SOURCE_BIN, "set \"bin\" CD-ROM disk image file as source", "FILE"}, + + {"cue-file", 'c', POPT_ARG_STRING|POPT_ARGFLAG_OPTIONAL, &source_name, + OP_SOURCE_CUE, "set \"cue\" CD-ROM disk image file as source", "FILE"}, + + {"input", 'i', POPT_ARG_STRING|POPT_ARGFLAG_OPTIONAL, &source_name, + OP_SOURCE_AUTO, + "set source and determine if \"bin\" image or device", "FILE"}, + + {"cdrom-device", 'C', POPT_ARG_STRING|POPT_ARGFLAG_OPTIONAL, &source_name, + OP_SOURCE_DEVICE, + "set CD-ROM device as source", "DEVICE"}, + + {"nrg-file", 'N', POPT_ARG_STRING|POPT_ARGFLAG_OPTIONAL, &source_name, + OP_SOURCE_NRG, "set Nero CD-ROM disk image file as source", "FILE"}, + + {"quiet", 'q', POPT_ARG_NONE, &opts.silent, 0, + "show only critical messages"}, + + {"version", 'V', POPT_ARG_NONE, NULL, OP_VERSION, + "display version and copyright information and exit"}, + POPT_AUTOHELP {NULL, 0, 0, NULL, 0} +}; + +#define DEV_PREFIX "/dev/" +static char * +fillout_device_name(const char *device_name) +{ + unsigned int prefix_len=strlen(DEV_PREFIX); + if (0 == strncmp(device_name, DEV_PREFIX, prefix_len)) + return strdup(device_name); + else { + char *full_device_name=malloc(strlen(device_name)+prefix_len); + sprintf(full_device_name, DEV_PREFIX "%s", device_name); + return full_device_name; + } +} + + +/* Parse a single option. */ +static bool +parse_options (poptContext optCon) +{ + int opt; + + while ((opt = poptGetNextOpt (optCon)) != -1) { + switch (opt) { + + case OP_SOURCE_AUTO: + case OP_SOURCE_BIN: + case OP_SOURCE_CUE: + case OP_SOURCE_NRG: + case OP_SOURCE_DEVICE: + if (opts.source_image != IMAGE_UNKNOWN) { + fprintf(stderr, + "%s: another source type option given before.\n", + program_name); + fprintf(stderr, "%s: give only one source type option.\n", + program_name); + break; + } + switch (opt) { + case OP_SOURCE_BIN: + opts.source_image = IMAGE_BIN; + break; + case OP_SOURCE_CUE: + opts.source_image = IMAGE_CUE; + break; + case OP_SOURCE_NRG: + opts.source_image = IMAGE_NRG; + break; + case OP_SOURCE_AUTO: + opts.source_image = IMAGE_AUTO; + break; + case OP_SOURCE_DEVICE: + opts.source_image = IMAGE_DEVICE; + source_name = fillout_device_name(source_name); + break; + } + break; + + default: + return false; + } + } + { + const char *remaining_arg = poptGetArg(optCon); + if ( remaining_arg != NULL) { + if (opts.source_image != IMAGE_UNKNOWN) { + fprintf (stderr, + "%s: Source specified in option %s and as %s\n", + program_name, source_name, remaining_arg); + exit (EXIT_FAILURE); + } + + if (opts.source_image == OP_SOURCE_DEVICE) + source_name = fillout_device_name(remaining_arg); + + if ( (poptGetArgs(optCon)) != NULL) { + fprintf (stderr, + "%s: Source specified in previously %s and %s\n", + program_name, source_name, remaining_arg); + exit (EXIT_FAILURE); + + } + } + } + + return true; +} + +static void +print_version (void) +{ + printf( _("CD Info %s | (c) 2003 Gerd Knorr, Heiko Eißfeldt & R. Bernstein\n\ +This is free software; see the source for copying conditions.\n\ +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n\ +PARTICULAR PURPOSE.\n\ +"), + CDINFO_VERSION); +} + +/* ------------------------------------------------------------------------ */ +/* some ISO 9660 fiddling */ + +static int +read_block(int superblock, uint32_t offset, uint8_t bufnum, bool is_green) +{ + memset(buffer[bufnum],0,M2F1_SECTOR_SIZE); + + dbg_print(2, "about to read sector %u\n", offset+superblock); + if (cdio_read_mode2_sector(img, buffer[bufnum], + offset+superblock, !is_green)) + return -1; + + return 0; +} + +static bool +is_it(int num) +{ + signature_t *sigp; + + /* TODO: check that num < largest sig. */ + sigp = &sigs[num]; + + int len = strlen(sigp->sig_str); + return 0 == memcmp(&buffer[sigp->buf_num][sigp->offset], + sigp->sig_str, len); +} + +static int +is_hfs(void) +{ + return (0 == memcmp(&buffer[1][512],"PM",2)) || + (0 == memcmp(&buffer[1][512],"TS",2)) || + (0 == memcmp(&buffer[1][1024], "BD",2)); +} + +static int +is_3do(void) +{ + return (0 == memcmp(&buffer[1][0],"\x01\x5a\x5a\x5a\x5a\x5a\x01", 7)) && + (0 == memcmp(&buffer[1][40], "CD-ROM", 6)); +} + +static int is_joliet(void) +{ + return 2 == buffer[3][0] && buffer[3][88] == 0x25 && buffer[3][89] == 0x2f; +} + +/* ISO 9660 volume space in M2F1_SECTOR_SIZE byte units */ +static int +get_size(void) +{ + return ((buffer[0][80] & 0xff) | + ((buffer[0][81] & 0xff) << 8) | + ((buffer[0][82] & 0xff) << 16) | + ((buffer[0][83] & 0xff) << 24)); +} + +static int +get_joliet_level( void ) +{ + switch (buffer[3][90]) { + case 0x40: return 1; + case 0x43: return 2; + case 0x45: return 3; + } + return 0; +} + +#define is_it_dbg(sig) \ + if (is_it(sig)) printf("%s, ", sigs[sig].description) + +static int +guess_filesystem(int start_session, bool is_green) +{ + int ret = 0; + + if (read_block(ISO_SUPERBLOCK_SECTOR, start_session, 0, is_green) < 0) + return FS_UNKNOWN; + + if (opts.debug_level > 0) { + /* buffer is defined */ + is_it_dbg(IS_CD_I); + is_it_dbg(IS_CD_RTOS); + is_it_dbg(IS_ISOFS); + is_it_dbg(IS_HS); + is_it_dbg(IS_BRIDGE); + is_it_dbg(IS_XA); + is_it_dbg(IS_CDTV); + puts(""); + } + + /* filesystem */ + if (is_it(IS_CD_I) && is_it(IS_CD_RTOS) + && !is_it(IS_BRIDGE) && !is_it(IS_XA)) { + return FS_INTERACTIVE; + } else { /* read sector 0 ONLY, when NO greenbook CD-I !!!! */ + + if (read_block(0, start_session, 1, true) < 0) + return ret; + + if (opts.debug_level > 0) { + /* buffer[1] is defined */ + is_it_dbg(IS_PHOTO_CD); + if (is_hfs()) printf("HFS, "); + is_it_dbg(IS_EXT2); + if (is_3do()) printf("3DO, "); + puts(""); + } + + if (is_it(IS_HS)) + ret |= FS_HIGH_SIERRA; + else if (is_it(IS_ISOFS)) { + if (is_it(IS_CD_RTOS) && is_it(IS_BRIDGE)) + ret = FS_ISO_9660_INTERACTIVE; + else if (is_hfs()) + ret = FS_ISO_HFS; + else + ret = FS_ISO_9660; + isofs_size = get_size(); + +#if 0 + if (is_rockridge()) + ret |= ROCKRIDGE; +#endif + + if (read_block(BOOT_SECTOR, start_session, 3, true) < 0) + return ret; + + if (opts.debug_level > 0) { + + /* buffer[3] is defined */ + if (is_joliet()) printf("JOLIET, "); + puts(""); + is_it_dbg(IS_BOOTABLE); + puts(""); + } + + if (is_joliet()) { + joliet_level = get_joliet_level(); + ret |= JOLIET; + } + if (is_it(IS_BOOTABLE)) + ret |= BOOTABLE; + + if (is_it(IS_BRIDGE) && is_it(IS_XA) && is_it(IS_ISOFS) + && is_it(IS_CD_RTOS) && + !is_it(IS_PHOTO_CD)) { + + if (read_block(VCD_INFO_SECTOR, start_session, 4, true) < 0) + return ret; + + if (opts.debug_level > 0) { + /* buffer[4] is defined */ + is_it_dbg(IS_VIDEO_CD); + puts(""); + } + + if (is_it(IS_VIDEO_CD)) ret |= VIDEOCDI; + } + } + else if (is_hfs()) ret |= FS_HFS; + else if (is_it(IS_EXT2)) ret |= FS_EXT2; + else if (is_3do()) ret |= FS_3DO; + else { + + if (read_block(UFS_SUPERBLOCK_SECTOR, start_session, 2, true) < 0) + return ret; + + if (opts.debug_level > 0) { + /* buffer[2] is defined */ + is_it_dbg(IS_UFS); + puts(""); + } + + if (is_it(IS_UFS)) + ret |= FS_UFS; + else + ret |= FS_UNKNOWN; + } + } + + /* other checks */ + if (is_it(IS_XA)) ret |= XA; + if (is_it(IS_PHOTO_CD)) ret |= PHOTO_CD; + if (is_it(IS_CDTV)) ret |= CDTV; + return ret; +} + +static void +myexit(int rc) +{ + cdio_destroy(img); + exit(rc); +} + + +/* ------------------------------------------------------------------------ */ +/* CDDB */ + +/* + Returns the sum of the decimal digits in a number. Eg. 1955 = 20 +*/ +static int +cddb_dec_digit_sum(int n) +{ + int ret=0; + + for (;;) { + ret += n%10; + n = n/10; + if (!n) + return ret; + } +} + +/* Return the number of seconds (discarding frame portion) of an MSF */ +static inline unsigned int +msf_seconds(msf_t *msf) +{ + return from_bcd8(msf->m)*60 + from_bcd8(msf->s); +} + +/* + Compute the CDDB disk ID for an Audio disk. This is a funny checksum + consisting of the concatenation of 3 things: + the sum of the decimal digits of sizes of all tracks, + the total length of the disk, and + the number of tracks. +*/ +static unsigned long +cddb_discid() +{ + int i,t,n=0; + msf_t start_msf; + msf_t msf; + + for (i = 1; i <= num_tracks; i++) { + cdio_get_track_msf(img, i, &msf); + n += cddb_dec_digit_sum(msf_seconds(&msf)); + } + + cdio_get_track_msf(img, 1, &start_msf); + cdio_get_track_msf(img, CDIO_LEADOUT_TRACK, &msf); + + t = msf_seconds(&msf) - msf_seconds(&start_msf); + + return ((n % 0xff) << 24 | t << 8 | num_tracks); +} + + +/* CDIO logging routines */ + +static cdio_log_handler_t gl_default_log_handler = NULL; + +static void +_log_handler (cdio_log_level_t level, const char message[]) +{ + if (level == CDIO_LOG_DEBUG && opts.debug_level < 2) + return; + + if (level == CDIO_LOG_INFO && opts.debug_level < 1) + return; + + if (level == CDIO_LOG_WARN && opts.silent) + return; + + gl_default_log_handler (level, message); +} + +static void +print_analysis(int fs, int num_audio) +{ + int need_lf; + + switch(fs & FS_MASK) { + case FS_NO_DATA: + if (num_audio > 0) + printf("Audio CD, CDDB disc ID is %08lx\n", cddb_discid()); + break; + case FS_ISO_9660: + printf("CD-ROM with ISO 9660 filesystem"); + if (fs & JOLIET) + printf(" and joliet extension level %d", joliet_level); + if (fs & ROCKRIDGE) + printf(" and rockridge extensions"); + printf("\n"); + break; + case FS_ISO_9660_INTERACTIVE: + printf("CD-ROM with CD-RTOS and ISO 9660 filesystem\n"); + break; + case FS_HIGH_SIERRA: + printf("CD-ROM with High Sierra filesystem\n"); + break; + case FS_INTERACTIVE: + printf("CD-Interactive%s\n", num_audio > 0 ? "/Ready" : ""); + break; + case FS_HFS: + printf("CD-ROM with Macintosh HFS\n"); + break; + case FS_ISO_HFS: + printf("CD-ROM with both Macintosh HFS and ISO 9660 filesystem\n"); + break; + case FS_UFS: + printf("CD-ROM with Unix UFS\n"); + break; + case FS_EXT2: + printf("CD-ROM with Linux second extended filesystem\n"); + break; + case FS_3DO: + printf("CD-ROM with Panasonic 3DO filesystem\n"); + break; + case FS_UNKNOWN: + printf("CD-ROM with unknown filesystem\n"); + break; + } + switch(fs & FS_MASK) { + case FS_ISO_9660: + case FS_ISO_9660_INTERACTIVE: + case FS_ISO_HFS: + printf("ISO 9660: %i blocks, label `%.32s'\n", + isofs_size, buffer[0]+40); + break; + } + need_lf = 0; + if (first_data == 1 && num_audio > 0) + need_lf += printf("mixed mode CD "); + if (fs & XA) + need_lf += printf("XA sectors "); + if (fs & MULTISESSION) + need_lf += printf("Multisession, offset = %i ",ms_offset); + if (fs & HIDDEN_TRACK) + need_lf += printf("Hidden Track "); + if (fs & PHOTO_CD) + need_lf += printf("%sPhoto CD ", num_audio > 0 ? " Portfolio " : ""); + if (fs & CDTV) + need_lf += printf("Commodore CDTV "); + if (first_data > 1) + need_lf += printf("CD-Plus/Extra "); + if (fs & BOOTABLE) + need_lf += printf("bootable CD "); + if (fs & VIDEOCDI && num_audio == 0) + need_lf += printf("Video CD "); + if (need_lf) puts(""); +} + + +/* ------------------------------------------------------------------------ */ + +int +main(int argc, const char *argv[]) +{ + + int fs=0; + poptContext optCon = poptGetContext (NULL, argc, argv, optionsTable, 0); + + gl_default_log_handler = cdio_log_set_handler (_log_handler); + + program_name = strrchr(argv[0],'/'); + program_name = program_name ? program_name+1 : strdup(argv[0]); + + /* Default option values. */ + opts.silent = false; + opts.debug_level = 0; + opts.no_tracks = false; + opts.no_ioctl = false; + opts.no_analysis = false; + opts.source_image = IMAGE_UNKNOWN; + + + /* Parse our arguments; every option seen by `parse_opt' will + be reflected in `arguments'. */ + parse_options(optCon); + + print_version(); + + switch (opts.source_image) { + case IMAGE_UNKNOWN: + case IMAGE_AUTO: + img = cdio_open (source_name, DRIVER_UNKNOWN); + break; + case IMAGE_DEVICE: + img = cdio_open (source_name, DRIVER_DEVICE); + break; + case IMAGE_BIN: + img = cdio_open (source_name, DRIVER_BINCUE); + break; + case IMAGE_CUE: + img = cdio_open_cue(source_name); + break; + case IMAGE_NRG: + img = cdio_open (source_name, DRIVER_NRG); + break; + } + + if (source_name==NULL) { + source_name=strdup(cdio_get_arg(img, "source")); + } + + first_track_num = cdio_get_first_track_num(img); + num_tracks = cdio_get_num_tracks(img); + + if (!opts.no_tracks) { + printf(STRONG "CD-ROM Track List (%i - %i)\n" NORMAL, + first_track_num, num_tracks); + + printf(" #: MSF LSN Type\n"); + } + + /* Read and possibly print track information. */ + for (i = first_track_num; i <= CDIO_LEADOUT_TRACK; i++) { + msf_t msf; + + if (!cdio_get_track_msf(img, i, &msf)) { + err_exit("cdio_track_msf for track %i failed, I give up.\n", i); + } + + if (i == CDIO_LEADOUT_TRACK) { + if (!opts.no_tracks) + printf("%3d: %2.2x:%2.2x:%2.2x %06d leadout\n", + (int) i, + msf.m, msf.s, msf.f, + cdio_msf_to_lsn(&msf)); + break; + } else if (!opts.no_tracks) { + printf("%3d: %2.2x:%2.2x:%2.2x %06d %s\n", + (int) i, + msf.m, msf.s, msf.f, + cdio_msf_to_lsn(&msf), + track_format2str[cdio_get_track_format(img, i)]); + + } + + if (TRACK_FORMAT_AUDIO == cdio_get_track_format(img, i)) { + num_audio++; + if (-1 == first_audio) + first_audio = i; + } else { + num_data++; + if (-1 == first_data) + first_data = i; + } + /* skip to leadout? */ + if (i == num_tracks) i = CDIO_LEADOUT_TRACK-1; + } + +#if CDIO_IOCTL_FINISHED + if (!opts.no_ioctl) { + printf(STRONG "What ioctl's report...\n" NORMAL); + +#ifdef CDROM_GET_MCN + /* get mcn */ + printf("Get MCN : "); fflush(stdout); + if (ioctl(filehandle,CDROM_GET_MCN, &mcn)) + printf("FAILED\n"); + else + printf("%s\n",mcn.medium_catalog_number); +#endif + +#ifdef CDROM_DISC_STATUS + /* get disk status */ + printf("disc status : "); fflush(stdout); + switch (ioctl(filehandle,CDROM_DISC_STATUS,0)) { + case CDS_NO_INFO: printf("no info\n"); break; + case CDS_NO_DISC: printf("no disc\n"); break; + case CDS_AUDIO: printf("audio\n"); break; + case CDS_DATA_1: printf("data mode 1\n"); break; + case CDS_DATA_2: printf("data mode 2\n"); break; + case CDS_XA_2_1: printf("XA mode 1\n"); break; + case CDS_XA_2_2: printf("XA mode 2\n"); break; + default: printf("unknown (failed?)\n"); + } +#endif + +#ifdef CDROMMULTISESSION + /* get multisession */ + printf("multisession: "); fflush(stdout); + ms.addr_format = CDROM_LBA; + if (ioctl(filehandle,CDROMMULTISESSION,&ms)) + printf("FAILED\n"); + else + printf("%d%s\n",ms.addr.lba,ms.xa_flag?" XA":""); +#endif + +#ifdef CDROMSUBCHNL + /* get audio status from subchnl */ + printf("audio status: "); fflush(stdout); + sub.cdsc_format = CDROM_MSF; + if (ioctl(filehandle,CDROMSUBCHNL,&sub)) + printf("FAILED\n"); + else { + switch (sub.cdsc_audiostatus) { + case CDROM_AUDIO_INVALID: printf("invalid\n"); break; + case CDROM_AUDIO_PLAY: printf("playing"); break; + case CDROM_AUDIO_PAUSED: printf("paused"); break; + case CDROM_AUDIO_COMPLETED: printf("completed\n"); break; + case CDROM_AUDIO_ERROR: printf("error\n"); break; + case CDROM_AUDIO_NO_STATUS: printf("no status\n"); break; + default: printf("Oops: unknown\n"); + } + if (sub.cdsc_audiostatus == CDROM_AUDIO_PLAY || + sub.cdsc_audiostatus == CDROM_AUDIO_PAUSED) { + printf(" at: %02d:%02d abs / %02d:%02d track %d\n", + sub.cdsc_absaddr.msf.minute, + sub.cdsc_absaddr.msf.second, + sub.cdsc_reladdr.msf.minute, + sub.cdsc_reladdr.msf.second, + sub.cdsc_trk); + } + } +#endif /* CDROMSUBCHNL */ + } +#endif /*CDIO_IOCTL_FINISHED*/ + + if (!opts.no_analysis) { + printf(STRONG "try to find out what sort of CD this is\n" NORMAL); + + /* try to find out what sort of CD we have */ + if (0 == num_data) { + /* no data track, may be a "real" audio CD or hidden track CD */ + + msf_t msf; + cdio_get_track_msf(img, 1, &msf); + start_track = cdio_msf_to_lsn(&msf); + + /* CD-I/Ready says start_track <= 30*75 then CDDA */ + if (start_track > 100 /* 100 is just a guess */) { + fs = guess_filesystem(0, false); + if ((fs & FS_MASK) != FS_UNKNOWN) + fs |= HIDDEN_TRACK; + else { + fs &= ~FS_MASK; /* del filesystem info */ + printf("Oops: %i unused sectors at start, but hidden track check failed.\n",start_track); + } + } + print_analysis(fs, num_audio); + } else { + /* we have data track(s) */ + + for (j = 2, i = first_data; i <= num_tracks; i++) { + msf_t msf; + track_format_t track_format = cdio_get_track_format(img, i); + + cdio_get_track_msf(img, i, &msf); + + switch ( track_format ) { + case TRACK_FORMAT_AUDIO: + case TRACK_FORMAT_ERROR: + break; + case TRACK_FORMAT_CDI: + case TRACK_FORMAT_XA: + case TRACK_FORMAT_DATA: + ; + } + + start_track = (i == 1) ? 0 : cdio_msf_to_lsn(&msf); + + /* save the start of the data area */ + if (i == first_data) + data_start = start_track; + + /* skip tracks which belong to the current walked session */ + if (start_track < data_start + isofs_size) + continue; + + fs = guess_filesystem(start_track, cdio_get_track_green(img, i)); + + if (i > 1) { + /* track is beyond last session -> new session found */ + ms_offset = start_track; + printf("session #%d starts at track %2i, LSN: %6i," + " ISO 9660 blocks: %6i\n", + j++, i, start_track, isofs_size); + printf("ISO 9660: %i blocks, label `%.32s'\n", + isofs_size, buffer[0]+40); + fs |= MULTISESSION; + } else { + print_analysis(fs, num_audio); + } + + if (!(((fs & FS_MASK) == FS_ISO_9660 || + (fs & FS_MASK) == FS_ISO_HFS || + /* (fs & FS_MASK) == FS_ISO_9660_INTERACTIVE) && (fs & XA))) */ + (fs & FS_MASK) == FS_ISO_9660_INTERACTIVE))) + break; /* no method for non-iso9660 multisessions */ + } + } + } + + + myexit(EXIT_SUCCESS); + /* Not reached:*/ + return(EXIT_SUCCESS); +} diff --git a/tests/.cvsignore b/tests/.cvsignore new file mode 100644 index 00000000..6c2d8db4 --- /dev/null +++ b/tests/.cvsignore @@ -0,0 +1,6 @@ +Makefile +Makefile.in +*.dump +*.cue +*.bin +*.nrg diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 00000000..451bba54 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,32 @@ +# $Id: Makefile.am,v 1.1 2003/03/24 19:01:10 rocky Exp $ +# +# Copyright (C) 2003 Rocky Bernstein +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +#################################################### +# Things to regression testing +#################################################### +# +check_SCRIPTS = check_nrg.sh check_cue.sh + +check_DATA = monvoisin.right svcd_ogt_test_ntsc.right vcd_demo.right + +EXTRA_DIST = $(check_SCRIPTS) $(check_DATA) \ + check_common_fn + +TESTS = $(check_SCRIPTS) + +MOSTLYCLEANFILES = core.* *.dump diff --git a/tests/check_common_fn b/tests/check_common_fn new file mode 100755 index 00000000..caf209de --- /dev/null +++ b/tests/check_common_fn @@ -0,0 +1,70 @@ +# $Id: check_common_fn,v 1.1 2003/03/24 19:01:10 rocky Exp $ + +have_cmp() { + if cmp /dev/null /dev/null > /dev/null 2>&1; then + : + else + + return 1; + fi + + if cmp /dev/zero /dev/null > /dev/null 2>&1; then + return 1; + fi + + return 0; +} + +check_result() { + RC=$1 + shift + msg=$* + if test $RC -ne 0 ; then + if test $RC -ne 77 ; then + echo "$0: $msg failed." + exit $RC + else + echo "$0: $msg skipped." + fi + else + echo "$0: $msg ok." + fi +} + +test_cdinfo() { + + opts="$1" + outfile="$2" + rightfile="$3" + + CDINFO="../src/cdinfo" + + if [ ! -x ${CDINFO} ]; then + echo "$0: No cdinfo" + return 1 + fi + + if ${CDINFO} ${opts} >${outfile} 2>&1 ; then + if have_cmp; then + if cmp ${outfile} ${rightfile} ; then + rm -f $outfile + return 0 + else + return 3 + fi + else + echo "$0: No cmp(1) found - cannot test cdinfo" + rm -f $outfile + return 77 + fi + else + echo "$0: ${CDINFO} ${opts} failed" + return 2 + fi + +} + +#;;; Local Variables: *** +#;;; mode:shell-script *** +#;;; eval: (sh-set-shell "bash") *** +#;;; End: *** diff --git a/tests/check_cue.sh b/tests/check_cue.sh new file mode 100755 index 00000000..bcd99d5c --- /dev/null +++ b/tests/check_cue.sh @@ -0,0 +1,27 @@ +#!/bin/sh +#$Id: check_cue.sh,v 1.1 2003/03/24 19:01:10 rocky Exp $ + +if test -z $srcdir ; then + srcdir=`pwd` +fi + +. ${srcdir}/check_common_fn + +BASE=`basename $0 .sh` + +test_cdinfo "--cue-file ${srcdir}/svcd_ogt_test_ntsc.cue" \ + svcd_ogt_test_ntsc.dump ${srcdir}/svcd_ogt_test_ntsc.right +RC=$? +check_result $RC 'cdinfo CUE test 1' + +test_cdinfo "-c ${srcdir}/vcd_demo.cue" \ + vcd_demo.dump ${srcdir}/vcd_demo.right +RC=$? +check_result $RC 'cdinfo CUE test 2' + +exit $RC + +#;;; Local Variables: *** +#;;; mode:shell-script *** +#;;; eval: (sh-set-shell "bash") *** +#;;; End: *** diff --git a/tests/check_nrg.sh b/tests/check_nrg.sh new file mode 100755 index 00000000..d4c60164 --- /dev/null +++ b/tests/check_nrg.sh @@ -0,0 +1,22 @@ +#!/bin/sh +#$Id: check_nrg.sh,v 1.1 2003/03/24 19:01:10 rocky Exp $ + +if test -z $srcdir ; then + srcdir=`pwd` +fi + +. ${srcdir}/check_common_fn + +BASE=`basename $0 .sh` + +test_cdinfo "--nrg-file ${srcdir}/monvoisin.nrg" \ + monvoisin.dump ${srcdir}/monvoisin.right +RC=$? +check_result $RC 'cdinfo NRG test 1' + +exit $RC + +#;;; Local Variables: *** +#;;; mode:shell-script *** +#;;; eval: (sh-set-shell "bash") *** +#;;; End: *** diff --git a/tests/monvoisin.right b/tests/monvoisin.right new file mode 100644 index 00000000..7d474c30 --- /dev/null +++ b/tests/monvoisin.right @@ -0,0 +1,17 @@ +CD Info 2.0 | (c) 2003 Gerd Knorr, Heiko Eißfeldt & R. Bernstein +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ +CD-ROM Track List (1 - 2) + #: MSF LSN Type + 1: 00:02:00 000000 XA + 2: 00:18:51 001251 XA +170: 00:39:71 002846 leadout +__________________________________ +try to find out what sort of CD this is +CD-ROM with CD-RTOS and ISO 9660 filesystem +ISO 9660: 1101 blocks, label `MONVOISIN ' +XA sectors Video CD +session #2 starts at track 2, LSN: 1251, ISO 9660 blocks: 1101 +ISO 9660: 1101 blocks, label `' diff --git a/tests/svcd_ogt_test_ntsc.right b/tests/svcd_ogt_test_ntsc.right new file mode 100644 index 00000000..bcb18dcb --- /dev/null +++ b/tests/svcd_ogt_test_ntsc.right @@ -0,0 +1,17 @@ +CD Info 2.0 | (c) 2003 Gerd Knorr, Heiko Eißfeldt & R. Bernstein +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ +CD-ROM Track List (1 - 2) + #: MSF LSN Type + 1: 00:02:00 000000 XA + 2: 00:09:01 000526 XA +170: 00:56:56 004106 leadout +__________________________________ +try to find out what sort of CD this is +CD-ROM with CD-RTOS and ISO 9660 filesystem +ISO 9660: 376 blocks, label `SVCD_OGT_TEST_NTSC ' +XA sectors +session #2 starts at track 2, LSN: 526, ISO 9660 blocks: 376 +ISO 9660: 376 blocks, label `DD3#3"C"$’@' diff --git a/tests/vcd_demo.right b/tests/vcd_demo.right new file mode 100644 index 00000000..240e8c27 --- /dev/null +++ b/tests/vcd_demo.right @@ -0,0 +1,18 @@ +CD Info 2.0 | (c) 2003 Gerd Knorr, Heiko Eißfeldt & R. Bernstein +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ +CD-ROM Track List (1 - 3) + #: MSF LSN Type + 1: 00:02:00 000000 XA + 2: 00:17:57 001182 XA + 3: 00:24:71 001721 XA +170: 00:30:10 002110 leadout +__________________________________ +try to find out what sort of CD this is +CD-ROM with CD-RTOS and ISO 9660 filesystem +ISO 9660: 1032 blocks, label `V0469 ' +XA sectors Video CD +session #2 starts at track 2, LSN: 1182, ISO 9660 blocks: 1032 +ISO 9660: 1032 blocks, label `'