From d4d82912da25640e256441dc8c1a7a79efdcb481 Mon Sep 17 00:00:00 2001 From: rocky Date: Mon, 24 Mar 2003 19:01:09 +0000 Subject: [PATCH] Initial revision --- .cvsignore | 15 + AUTHORS | 2 + ChangeLog | 0 Makefile.am | 30 + NEWS | 2 + README | 3 + autogen.sh | 128 +++++ configure.ac | 174 ++++++ lib/.cvsignore | 7 + lib/Makefile.am | 77 +++ lib/_cdio_bincue.c | 581 +++++++++++++++++++ lib/_cdio_bsdi.c | 441 +++++++++++++++ lib/_cdio_bsdi.h | 29 + lib/_cdio_linux.c | 725 ++++++++++++++++++++++++ lib/_cdio_nrg.c | 770 +++++++++++++++++++++++++ lib/_cdio_stdio.c | 177 ++++++ lib/_cdio_stdio.h | 40 ++ lib/_cdio_stream.c | 156 +++++ lib/_cdio_stream.h | 91 +++ lib/_cdio_sunos.c | 658 ++++++++++++++++++++++ lib/bytesex.h | 196 +++++++ lib/bytesex_asm.h | 123 ++++ lib/cdio.c | 454 +++++++++++++++ lib/cdio.h | 219 ++++++++ lib/cdio_assert.h | 55 ++ lib/cdio_private.h | 158 ++++++ lib/cdio_types.h | 217 +++++++ lib/ds.c | 249 ++++++++ lib/ds.h | 73 +++ lib/logging.c | 129 +++++ lib/logging.h | 64 +++ lib/sector.c | 79 +++ lib/sector.h | 127 +++++ lib/util.c | 192 +++++++ lib/util.h | 109 ++++ libpopt.m4 | 68 +++ src/.cvsignore | 8 + src/Makefile.am | 34 ++ src/cdinfo-linux.c | 911 ++++++++++++++++++++++++++++++ src/cdinfo.c | 999 +++++++++++++++++++++++++++++++++ tests/.cvsignore | 6 + tests/Makefile.am | 32 ++ tests/check_common_fn | 70 +++ tests/check_cue.sh | 27 + tests/check_nrg.sh | 22 + tests/monvoisin.right | 17 + tests/svcd_ogt_test_ntsc.right | 17 + tests/vcd_demo.right | 18 + 48 files changed, 8779 insertions(+) create mode 100644 .cvsignore create mode 100644 AUTHORS create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 lib/.cvsignore create mode 100644 lib/Makefile.am create mode 100644 lib/_cdio_bincue.c create mode 100644 lib/_cdio_bsdi.c create mode 100644 lib/_cdio_bsdi.h create mode 100644 lib/_cdio_linux.c create mode 100644 lib/_cdio_nrg.c create mode 100644 lib/_cdio_stdio.c create mode 100644 lib/_cdio_stdio.h create mode 100644 lib/_cdio_stream.c create mode 100644 lib/_cdio_stream.h create mode 100644 lib/_cdio_sunos.c create mode 100644 lib/bytesex.h create mode 100644 lib/bytesex_asm.h create mode 100644 lib/cdio.c create mode 100644 lib/cdio.h create mode 100644 lib/cdio_assert.h create mode 100644 lib/cdio_private.h create mode 100644 lib/cdio_types.h create mode 100644 lib/ds.c create mode 100644 lib/ds.h create mode 100644 lib/logging.c create mode 100644 lib/logging.h create mode 100644 lib/sector.c create mode 100644 lib/sector.h create mode 100644 lib/util.c create mode 100644 lib/util.h create mode 100644 libpopt.m4 create mode 100644 src/.cvsignore create mode 100644 src/Makefile.am create mode 100644 src/cdinfo-linux.c create mode 100644 src/cdinfo.c create mode 100644 tests/.cvsignore create mode 100644 tests/Makefile.am create mode 100755 tests/check_common_fn create mode 100755 tests/check_cue.sh create mode 100755 tests/check_nrg.sh create mode 100644 tests/monvoisin.right create mode 100644 tests/svcd_ogt_test_ntsc.right create mode 100644 tests/vcd_demo.right 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 `'