diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b85fe943 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +/*~ +/COPYING +/Makefile +/Makefile.in +/aclocal.m4 +/autom4te.cache +/compile +/config.guess +/config.h +/config.h.in +/config.log +/config.rpath +/config.status +/config.sub +/configure +/depcomp +/install-sh +/libcdio++.pc +/libcdio.pc +/libcdio_cdda.pc +/libcdio_paranoia.pc +/libiso9660++.pc +/libiso9660.pc +/libtool +/libudf.pc +/ltmain.sh +/missing +/stamp-h1 diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..d3c5b40a --- /dev/null +++ b/INSTALL @@ -0,0 +1,237 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006, 2007 Free Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 6. Often, you can also type `make uninstall' to remove the installed + files again. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/MSVC/README b/MSVC/README new file mode 100644 index 00000000..42884a96 --- /dev/null +++ b/MSVC/README @@ -0,0 +1,4 @@ +Files in this directory are for compiling with Microsoft Visual C. +They are courtesy of John Oseman (mog). + +$Id: README,v 1.1 2004/10/30 14:18:17 rocky Exp $ diff --git a/MSVC/cd-info.vcproj b/MSVC/cd-info.vcproj new file mode 100644 index 00000000..e3581358 --- /dev/null +++ b/MSVC/cd-info.vcproj @@ -0,0 +1,365 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MSVC/config.h b/MSVC/config.h new file mode 100644 index 00000000..5a06eb08 --- /dev/null +++ b/MSVC/config.h @@ -0,0 +1,188 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* compiler does lsbf in struct bitfields */ +#undef BITFIELD_LSBF + +/* Define 1 if you are compiling using cygwin */ +#undef CYGWIN + +/* what to put between the brackets for empty arrays */ +#define EMPTY_ARRAY_SIZE + +/* Define 1 if you have BSDI-type CD-ROM support */ +#undef HAVE_BSDI_CDROM + +/* Define to 1 if you have the `bzero' function. */ +#undef HAVE_BZERO + +/* Define this if you have libcddb installed */ +#undef HAVE_CDDB + +/* Define to 1 if you have the header file. */ +#undef HAVE_COREFOUNDATION_CFBASE_H + +/* Define 1 if you have Darwin OS X-type CD-ROM support */ +#undef HAVE_DARWIN_CDROM + +/* Define if time.h defines extern long timezone and int daylight vars. */ +#undef HAVE_DAYLIGHT + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_DVD_H + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define 1 if you have FreeBSD CD-ROM support */ +#undef HAVE_FREEBSD_CDROM + +/* Define to 1 if you have the header file. */ +#undef HAVE_GLOB_H + +/* Define if you have the iconv() function. */ +#undef HAVE_ICONV + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_IOKIT_IOKITLIB_H + +/* Supports ISO _Pragma() macro */ +#undef HAVE_ISOC99_PRAGMA + +/* Define 1 if you want ISO-9660 Joliet extension support. You must have also + libiconv installed to get Joliet extension support. */ +#undef HAVE_JOLIET + +/* Define if you have and nl_langinfo(CODESET). */ +#undef HAVE_LANGINFO_CODESET + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +#undef HAVE_LIBNSL + +/* Define to 1 if you have the `socket' library (-lsocket). */ +#undef HAVE_LIBSOCKET + +/* Define 1 if you have Linux-type CD-ROM support */ +#undef HAVE_LINUX_CDROM + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_CDROM_H + +/* Define 1 if timeout is in cdrom_generic_command struct */ +#undef HAVE_LINUX_CDROM_TIMEOUT + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_VERSION_H + +/* Define to 1 if you have the `memcpy' function. */ +#define HAVE_MEMCPY 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define 1 if you have Solaris CD-ROM support */ +#undef HAVE_SOLARIS_CDROM + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDBOOL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_CDIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if struct tm has the tm_gmtoff member. */ +#undef HAVE_TM_GMTOFF + +/* Define if time.h defines extern extern char *tzname[2] variable */ +#undef HAVE_TZNAME + +/* Define to 1 if you have the `tzset' function. */ +#undef HAVE_TZSET + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define this if you have libvcdinfo installed */ +#undef HAVE_VCDINFO + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define 1 if you have MinGW CD-ROM support */ +#define HAVE_WIN32_CDROM 1 + +/* Define as const if the declaration of iconv() needs const. */ +#undef ICONV_CONST + +/* Define 1 if you are compiling using MinGW */ +#undef MINGW32 + +/* Name of package */ +#define PACKAGE "libcdio" + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libcdio" + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#define PACKAGE_VERSION 1 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "1" + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#define inline + diff --git a/MSVC/libcdio.sln b/MSVC/libcdio.sln new file mode 100644 index 00000000..fe8a64af --- /dev/null +++ b/MSVC/libcdio.sln @@ -0,0 +1,39 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcdio", "libcdio.vcproj", "{E465056A-C6F3-45EE-B791-CAF8E0CE629D}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cd-info", "cd-info.vcproj", "{8E55CFDB-5E38-4A07-84F8-36939C825735}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + XBOX_Debug = XBOX_Debug + XBOX_Release = XBOX_Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {E465056A-C6F3-45EE-B791-CAF8E0CE629D}.Debug.ActiveCfg = Debug|Win32 + {E465056A-C6F3-45EE-B791-CAF8E0CE629D}.Debug.Build.0 = Debug|Win32 + {E465056A-C6F3-45EE-B791-CAF8E0CE629D}.Release.ActiveCfg = Release|Win32 + {E465056A-C6F3-45EE-B791-CAF8E0CE629D}.Release.Build.0 = Release|Win32 + {E465056A-C6F3-45EE-B791-CAF8E0CE629D}.XBOX_Debug.ActiveCfg = XBOX_Debug|Xbox + {E465056A-C6F3-45EE-B791-CAF8E0CE629D}.XBOX_Debug.Build.0 = XBOX_Debug|Xbox + {E465056A-C6F3-45EE-B791-CAF8E0CE629D}.XBOX_Release.ActiveCfg = XBOX_Release|Xbox + {E465056A-C6F3-45EE-B791-CAF8E0CE629D}.XBOX_Release.Build.0 = XBOX_Release|Xbox + {8E55CFDB-5E38-4A07-84F8-36939C825735}.Debug.ActiveCfg = Debug|Win32 + {8E55CFDB-5E38-4A07-84F8-36939C825735}.Debug.Build.0 = Debug|Win32 + {8E55CFDB-5E38-4A07-84F8-36939C825735}.Release.ActiveCfg = Release|Win32 + {8E55CFDB-5E38-4A07-84F8-36939C825735}.Release.Build.0 = Release|Win32 + {8E55CFDB-5E38-4A07-84F8-36939C825735}.XBOX_Debug.ActiveCfg = XBOX_Debug|Xbox + {8E55CFDB-5E38-4A07-84F8-36939C825735}.XBOX_Debug.Build.0 = XBOX_Debug|Xbox + {8E55CFDB-5E38-4A07-84F8-36939C825735}.XBOX_Release.ActiveCfg = XBOX_Release|Xbox + {8E55CFDB-5E38-4A07-84F8-36939C825735}.XBOX_Release.Build.0 = XBOX_Release|Xbox + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/MSVC/libcdio.vcproj b/MSVC/libcdio.vcproj new file mode 100644 index 00000000..43f0cfbd --- /dev/null +++ b/MSVC/libcdio.vcproj @@ -0,0 +1,547 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..cb555edd --- /dev/null +++ b/Makefile.am @@ -0,0 +1,137 @@ +# $Id: Makefile.am,v 1.50 2008/10/25 13:56:15 rocky Exp $ +# +# Copyright (C) 2003, 2004, 2006, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +## 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? + +AUTOMAKE_OPTIONS = dist-bzip2 + +EXTRA_DIST = \ + MSVC/README MSVC/cd-info.vcproj \ + MSVC/config.h \ + MSVC/libcdio.sln \ + MSVC/libcdio.vcproj \ + README.libcdio \ + THANKS \ + example/README \ + libcdio.pc.in \ + libcdio++.pc.in \ + libcdio_cdda.pc.in \ + libiso9660.pc.in \ + libiso9660++.pc.in \ + libudf.pc.in \ + package/libcdio.spec.in + +SUBDIRS = doc include lib src test example + +if BUILD_CD_PARANOIA +paranoiapcs = libcdio_paranoia.pc libcdio_cdda.pc +endif + +# pkg-config(1) related rules +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libcdio.pc \ + libiso9660.pc \ + libudf.pc \ + $(paranoiapcs) + +if ENABLE_CPP +pkgconfig_DATA += \ + libcdio++.pc \ + libiso9660++.pc +endif + + +$(pkgconfig_DATA): config.status + +# List of additional files for expanded regression tests +DISTFILES_REGRESSION = tests/monvoisin.nrg tests/monvoisin.right \ + tests/svcdgs.nrg tests/svcdgs.nrg \ + tests/svcd_ogt_test_ntsc.bin \ + tests/svcd_ogt_test_ntsc.cue \ + tests/svcd_ogt_test_ntsc.right \ + tests/vcd_demo.bin tests/vcd_demo.cue \ + tests/vcd_demo.right + +REGRESSION_VERSION = 1.1 + +distdir_regression = ../$(PACKAGE)-$(REGRESSION_VERSION)-tests + +test: check + +doxygen: + -( cd ${top_srcdir}/doc/doxygen && /bin/sh ${srcdir}/run_doxygen ) + +dist-regression: distdir-regression + cd $(distdir) && $(AMTAR) chof - tests | GZIP=$(GZIP_ENV) gzip -c >$(distdir_regression).tar.gz + $(am__remove_distdir) + +distdir-regression: $(DISTFILES_REGRESSION) + $(am__remove_distdir) + mkdir $(distdir) + @list='$(DISTFILES_REGRESSION)'; for file in $$list; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkinstalldirs) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir_regressoin)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r $(distdir) + +check_nrg.sh: $(top_builddir)/config.status check_nrg.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + chmod +x config_nrg.sh +check_cue.sh: $(top_builddir)/config.status check_cue.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + chmod +x config_cue.sh + +check_iso.sh: $(top_builddir)/config.status check_iso.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + chmod +x config_iso.sh + +make-executable: + +# cvs2cl +MAINTAINERCLEANFILES = ChangeLog + +if MAINTAINER_MODE + +.PHONY: ChangeLog +ChangeLog: + ( cd $(srcdir); $(CVS2CL) -W 450 --header cvs2cl_header --utc -w -I ChangeLog --usermap cvs2cl_usermap -P --stdout ) > ChangeLog + +ACLOCAL_AMFLAGS=-I m4 + +endif diff --git a/README b/README new file mode 100644 index 00000000..a066a3bd --- /dev/null +++ b/README @@ -0,0 +1,61 @@ +See README.libcdio for installation instructions. + +The libcdio package contains a library for CD-ROM and CD image +access. Applications wishing to be oblivious of the OS- and +device-dependent properties of a CD-ROM or of the specific details of +various CD-image formats may benefit from using this library. + +A library for working with ISO-9660 filesystems libiso9660 is +included. A generic interface for issuing MMC (multimedia commands) is +also part of the libcdio library. + +Also included is a library for working with ISO-9660 filesystems as is +also the CD-DA error/jitter correction library from cdparanoia +(http://www.xiph.org/paranoia). + +Some support for disk image types like CDRWin's BIN/CUE format, +cdrdao's TOC format, and Nero's NRG format are available. Therefore, +applications that use this library also have the ability to read disc +images as though they were CD's. + +The library is written in C, however there are OO C++, Perl, Python +and Ruby wrappers to interface to the library. However C++ is the only +one that is bundled with this package, and the interfaces provide only +a subset of the full features of the library. + +Also included in the libcdio package are a number of utility programs: + +* cd-info - displays CD information: number of tracks, CD-format and + if possible basic information about the format. If libcddb + (http://libcddb.sourceforge.net) is available, the cd-info + program will display CDDB matches on CD-DA discs. And if a + new enough version of libvcdinfo is available (from the + vcdimager project), then cd-info shows basic VCD information. + +* cd-read - performs low-level block reading of a CD or CD image, + +* iso-info - displays ISO-9660 information from an ISO-9660 image, + +* iso-read - extracting files from an ISO-9660 image, a version of the + CD-DA extraction tool cdparanoia which corrects for + CD-ROM jitter, and a simple curses-based CD player, + cdda-player using the analog CD-ROM output. + +* cd-paranoia - port of cdparanoia (CD-DA jitter and error correction) + using libcdio back-end CD-reading. + +At present, there is no support for writing CD's, nor is there any +support for reading or writing DVDs. For some of these, there are +other libraries (e.g. libdi, libscg, or libdvdread) may be helpful. + +Some of the projects using libcdio are the Video CD authoring and +ripping tools VCDImager (http://vcdimager.org), a navigation-capable +Video CD plugin and CD-DA plugins for the media players xine +(http://xinehq.de), videolan's vlc (http://videolan.org), media +players mplayerxp (http://mplayerxp.sourceforge.net/) and gmerlin +(http://gmerlin.sourceforge.net), kiso, a KDE GUI for creating, +extracting and editing ISO-9600 images (http://kiso.sourceforge.net), +and a Samba vfs module that allows exporting a CD without mounting it +(http://ontologistics.net/OpenSource/Samba/index.php). + +$Id: README,v 1.13 2006/03/28 14:48:44 rocky Exp $ diff --git a/README.libcdio b/README.libcdio new file mode 100644 index 00000000..8e166401 --- /dev/null +++ b/README.libcdio @@ -0,0 +1,104 @@ +If you check out the source from CVS run + ./autogen.sh then follow as below, except you don't need to run + ./configure. + +To compile the source: + + ./configure MAKE=gmake + make + make check + make install # may have to do this as root + + +If you have problems linking libcdio or libiso9660, see the BSD +section. You might also try the option --without-versioned-libs. However +this option does help with the situtation described below so it is +preferred all other things being equal. + +If you are debugging libcdio, the libtool and the dynamic libraries +can make things harder. I suggest setting CFLAGS to include +'-fno-inline -g' and using --disable-shared on configure. + + +VCD dependency: +--------------- +One thing that confuses people is the "dependency" on libvcdinfo from +vcdimager, while vcdimager has a dependency on libcdio. This libcdio +dependency on vcdimager is an optional (i.e. not mandatory) dependency, +while the vcdimager dependency right now is mandatory. libvcdinfo is +used only by the utility program cd-info. If you want cd-info to use +the VCD reporting portion and you don't already have vcdimager +installed, build and install libcdio, then vcdimager, then configure +libcdio again and it should find libvcdinfo. + +People who make packages might consider making two packages, a libcdio +package with just the libraries (and no dependency on libvcdinfo) and +a libcdio-utils which contains cd-info and iso-info, cd-read, +iso-read. Should you want cd-info with VCD support then you'd add a +dependency in that package to libvcdinfo. + +Another thing one can do is "make install" inside the library, or run +"configure --without-vcd-info --without-cddb" (since libcddb also has +an optional dependency on libcdio). + +Microsoft Windows +------- + +The building under Microsoft Windows the thing to do is to install +cygwin (http://www.cygwin.com). It has been reported that MinGW +(http://www.mingw.org/) also works, but it is possible you may +encounter more problems there. + +Folks may have used Microsoft compilers (e.g. Visual C), but you may +find you need to make your own "project" files. Don't undertake this +unless you are willing to spend time hacking. xboxmediacenter team +folks I believe go this route, so you may be able to use their project +files as a starting point. + +XBOX +------- + +Consult the xboxmediacenter team (www.xboxmediacenter.de) + + +BSD +--- + +Unless you use --without-versioned-libs (not recommended), you need to +use GNU make which usually can be found under the name "gmake". + +If you use another make you are likely to get problems linking libcdio +and libiso9660. + + +Solaris +------- + +You may need to use --without-versioned-libs if you get a problem +building libcdio or libiso9660. + +If you get a message like: + libcdio.so: attempted multiple inclusion of file + +because you have enable vcd-info and it is installed, then the only +way I know how to get around is to use configure with --disable-shared. + +OS Support +--------------- + +Support for Operating Systems's is really based on the desire, ability +and willingness of others to help out. I use GNU/Linux so that +probably works best. Before a release I'll test on servers I have +available. I also announce a pending release on libcdio-devel@gnu.org +and ask others to test out. + +Steve Schultz has done a great job making BSDI CD support look like +GNU/Linux and usually he let's me know where I've blown things on BSDI +and Darwin. Usage on Darwin has been picking up although Darwin is in +a world of its own so support for that (e.g. issuing MMC commnads) +seems to lag behind. + +Of late FreeBSD folks have been pretty good about testing new releases +and reporting problems. + +$Id: README.libcdio,v 1.12 2008/03/22 22:51:30 rocky Exp $ diff --git a/THANKS b/THANKS new file mode 100644 index 00000000..3d96c569 --- /dev/null +++ b/THANKS @@ -0,0 +1,64 @@ +Burkhard Plaum + some GNU/Linux and CD-Text patches + +Carlo Bramini + fixes for Mingw+MSYS and DLL support + +Diego 'Flameeyes' Pettenò + patches to FreeBSD and making Gentoo-friendly + +Frantisek Dvorak : + bug reports and miscellaneous fixes + +Heiner + FreeBSD CAM support and FreeBSD debugging & testing + +Ian MacIntosh + Sun-related things. + +Justin B. Ruggles + SCSI MMC discmode determination via Read TOC. + +Justin F. Hallett + Fink packaging and matters OSX + +Kris Verbeeck : + CDDB library support from libcddb http://libcddb.sourceforge.net + Gentoo ebuild-file + +Manfred Tremmel : + RPM spec file and inclusion of libcdio into + http://packman.links2linux.de/ + +Michael Kukat , + for the hints in extractnrg.pl + +Nicolas Boullis + Build issues, library symbol versioning, Debian packaging and issues + +Patrick Guimond + CD-Extra audio data boundaries + +Peter Hartley + Cross-compiling to mingw32 + +Peter J. Creath + removal of libpopt, paranoia documentation, some bug fixes to + cd-* programs and the paranoia lib + +Steven M. Schultz + All things BSDI and the use of a really fabulous Darwin G5 box. + +Svend S. Sorensen + cdrdao TOC-reading and CDRWin CUE parsing code based on cuetools + http://cuetools.sourceforge.net/ + +xboxmediacenter team (www.xboxmediacenter.de) + X-Box detection and XDF filesystem things + +Daniel Schwarz + log-summary option in cd-paranoia. + +Robert William Fuller + get_track_pregap_lba, get_track_pregap_lsn. Section on "CD-DA pregap" + in libcdio manual. diff --git a/TODO b/TODO new file mode 100644 index 00000000..bc289328 --- /dev/null +++ b/TODO @@ -0,0 +1,95 @@ +It isn't look hard to find a gap in libcdio or libiso9660 or think of +something you'd like added. Here are some of the many known problems +and feature requests. + +* UDF support. + +* API overhaul. hvr has expressed interest but it's unlikely he'll ever + have the time to do. It could be done in conjunction with a wrappers + for C++, Perl, Python, ... The idea is that those interfaces would not + show the ugliness of the current C interface. For example instead of + read_mode2, read_mode1, read_audio, there might be a read(mode, ...). + + - Address static loglevel variable (nboullis at debian.org) + +* All of the API should be finished on all OS's (or the API adjusted). + +* Fix the current gaps: + + - SCSI-MMC on OSX, + - CD-Text support working more often? wide character support + (Burkhard Plaum has indicated he might do) + - more accurate drive capabilities + - wxwindows interface to cd-drive + - more accurate CD track classification (Form 1/2, Mode 1/2) + - complete the image readers, e.g. "silence" and ability to use + more than one file in cdrdao. + - multi-session CDs + +* mmc_read_cd often doesn't work when request reading a large number of blocks. + +* Is paranoia correct? Get a better handle on it. Ensure more of the + drive and OS-specific features that work on GNU/Linux work + elsewhere. Regression tests over more kinds of failures. + +* Exclusive access of CD-ROM versus non-exclusive? + +* Adjusting operations based on known models. Via MMC, We often have + the ability to find out what drive is in use. That could be used (as + it was in cdparanoia) to customize the method used for various + operations. Alternatively it could be read from a configuration + file, but right now there's no internal structure for holding all of + the capabilities. + +* Combine iso-read and iso-info into an "iso-tar" for listing or + extracting files". Ideally something matching the relevant command + set of "tar" would be nice, as that is widely used and probably + fairly complete in thing that might be desired for listing/extracting. + +* Add something to show what kind of CD media is in a drive. Ideally: CD + (purchased), CD Write Once, CD Read/Write, but what is there is + probably something like Cyanine, PhthaloCyanine, Metallized Azo, + Advanced PhthaloCyanine, Formazan. See + http://www.cdmediaworld.com/hardware/cdrom/cd_dye.shtml or + http://www.cd-info.com/CDIC/History/Commentary/Parker/stcroix.html + + The discmode type can be used to classify DVD media and it also + classifies for CD track formats (in addition to CD *content* + classification). Note there is nothing for DVD content; see the below + list of things which probably won't get added. + + Given the mismatch between DVD and CD meanings in discmode, the + discmode type probably needs to be redone. + +* Write a real cue parser and TOC parser using bison. + A pcct grammar is given in cdrdao's trackdb TocParser.g and CueParser.g + The parsing is pretty much done, need to fold in semantic routines and + improve error reporting. + +* conversion tools. Assuming parser done, it should be simple to use + write simple conversion routines: + - CD images's to iso9660 .iso's + - TOC <=> CUE + +* Test more disc image types in regression testing, like CD-I. + +* Convert to use glib, removing ds.h + (Revise vcdimager too) + +* Delete and rename files in an iso9660 image (mephisto75 at web.de) + +* Some things where libcdio will probably not expand in: + + - DVD things, especially DVD-ROM (use libdvdread) + - writing applications (use cdrdao or cdrtools) + - more proprietary undocumented image format (unless someone else is willing + to do the work). But the fuzzy ISO 9660 detection may help here. + + +If there is something you really want done on the above list or have +something else you want done, it will go a lot faster if you attempt +to undertake doing it. Patches are always welcome (and CVS write +access is available for those who have demonstrated reasonable ability +through contributions.) + +$Id: TODO,v 1.11 2005/07/11 11:51:07 rocky Exp $ diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 00000000..5158e8eb --- /dev/null +++ b/autogen.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. +# Additional options go to configure. + +echo "Rebuilding ./configure with autoreconf..." +autoreconf -f -i +if [ $? -ne 0 ]; then + echo "autoreconf failed" + exit $? +fi + +./configure --enable-maintainer-mode "$@" diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..ea73489c --- /dev/null +++ b/configure.ac @@ -0,0 +1,688 @@ +dnl Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 +dnl Rocky Bernstein +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2, or (at your option) +dnl any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +dnl 02110-1301 USA. + +define(RELEASE_NUM, 82cvs) +define(CDIO_VERSION_STR, 0.$1) + +AC_PREREQ(2.52) +AC_REVISION([$Id: configure.ac,v 1.238 2008/11/27 21:09:51 rocky Exp $])dnl +AC_INIT(libcdio, CDIO_VERSION_STR(RELEASE_NUM)) +AC_CONFIG_SRCDIR(src/cd-info.c) + +dnl For multilanguage manual pages (e.g. japanese cdparanoia) we use +dnl $make_p and according to Steve Schultz this is a make_p is a 1.8 +dnl feature. + +AM_INIT_AUTOMAKE([1.7]) + +AC_CANONICAL_HOST +AM_CONFIG_HEADER(config.h) +AC_CONFIG_MACRO_DIR([m4]) + +LIBCDIO_VERSION_NUM=`echo RELEASE_NUM | cut -d . -f 1 | tr -d a-z` +AC_SUBST(LIBCDIO_VERSION_NUM) + +AM_MISSING_PROG(HELP2MAN, help2man, $missing_dir) +AM_MISSING_PROG(CVS2CL, cvs2cl, $missing_dir) +AM_MAINTAINER_MODE + +AM_SANITY_CHECK + +AC_ARG_WITH(cd-drive, +[ --without-cd-drive don't build program cd-drive (default with)], +enable_cd_drive="${withval}", enable_cd_drive=yes) + +AC_ARG_WITH(cd-info, +[ --without-cd-info don't build program cd-info (default with)], +enable_cd_info="${withval}", enable_cd_info=yes) + +AC_ARG_WITH(cd-paranoia, +[ --without-cd-paranoia don't build program cd-paranoia and paranoia libraries (default with)], +enable_cd_paranoia="${withval}", enable_cd_paranoia=yes) + +AC_ARG_WITH(cdda-player, +[ --without-cdda-player don't build program cdda-player (default with)], +enable_cdda_player="${withval}", enable_cdda_player=yes) + +AC_ARG_WITH(cd-paranoia-name, +[ --with-cd-paranoia-name name to use as the cd-paranoia program name (default cd-paranoia)], +cd_paranoia_name="${withval}", cd_paranoia_name="cd-paranoia") +CDPARANOIA_NAME="$cd_paranoia_name" +AC_SUBST(CDPARANOIA_NAME) + +AC_ARG_WITH(cd-read, +[ --without-cd-read don't build program cd-read (default with)], +enable_cd_read="${withval}", enable_cd_read=yes) + +AC_ARG_WITH(iso-info, +[ --without-iso-info don't build program iso-info (default with)], +enable_iso_info="${withval}", enable_iso_info=yes) + +AC_ARG_WITH(iso-read, +[ --without-iso-read don't build program iso-read (default with)], +enable_iso_read="${withval}", enable_iso_read=yes) + +AC_ARG_WITH(versioned-libs, +[ --without-versioned-libs build versioned library symbols (default enabled if you have GNU ld)], +enable_versioned_libs="${withval}", enable_versioned_libs=yes) + +AC_ARG_ENABLE([cxx], + AC_HELP_STRING([--disable-cxx], [Disable C++ bindings (default enabled)])) +AM_CONDITIONAL([ENABLE_CXX_BINDINGS], [test "x$enable_cxx" != "xno"]) + +AC_ARG_ENABLE(cpp-progs, + [ --enable-cpp-progs make C++ example programs (default enabled)]) +AM_CONDITIONAL(ENABLE_CPP, test x"$enable_cpp_progs" = "xyes") + +AC_ARG_ENABLE(example-progs, + AC_HELP_STRING([--disable-example-progs], [Don't build libcdio sample programs])) + +AM_CONDITIONAL(BUILD_EXAMPLES, test "x$enable_example_progs" != "xno") + +dnl We use C +AC_PROG_CC +AM_PROG_CC_C_O +dnl We also use C++ in example programs and for CXX bindings +AC_PROG_CXX + +dnl Checks for programs. +AC_AIX + +cd_drivers='cdrdao, BIN/CUE, NRG' + +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 -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 + +# We use Perl for documentation and regression tests +AC_PATH_PROG(PERL, perl, false) +AC_SUBST(PERL) +AM_CONDITIONAL(HAVE_PERL, test "$PERL" != "false") + +# We use a diff in regression testing +AC_PATH_PROG(DIFF, diff, no) +DIFF_OPTS= + +if test "$DIFF" = no ; then + AC_PATH_PROG(DIFF, cmp, no) +else + # Try for GNU diff options. + # MSDOG output uses \r\n rather than \n in tests + for diff_opt in -w --unified ; do + if $DIFF $diff_opt ./configure ./configure > /dev/null 2>&1; then + AC_MSG_RESULT([adding $diff_opt to diff in regression tests]) + DIFF_OPTS="$DIFF_OPTS $diff_opt" + fi + done +fi +AC_SUBST(DIFF) +AC_SUBST(DIFF_OPTS) + +dnl check for large file support +AC_SYS_LARGEFILE + +dnl we need to define _FILE_OFFSET_BITS or _LARGE_FILES on the compiler command +dnl line because otherwise the system headers risk being included before +dnl problems +if test "x$ac_cv_sys_largefiles" = "xyes"; then + if test "x$ac_cv_sys_file_offset_bits" = "x64"; then + LIBCDIO_LARGEFILE_FLAGS="-D_FILE_OFFSET_BITS=64 -D_LARGE_FILES" + else + LIBCDIO_LARGEFILE_FLAGS="-D_LARGE_FILES" + fi + dnl AC_FUNC_FSEEKO sets HAVE_FSEEKO and $ac_cv_sys_largefile_source + AC_FUNC_FSEEKO + if test "$ac_cv_sys_largefile_source" != no; then + LIBCDIO_LARGEFILE_FLAGS="$LIBDDIO_LARGEFILE_FLAGS -D_LARGEFILE_SOURCE=$ac_cv_sys_largefile_source" + fi + CPPFLAGS="$CPPFLAGS $LIBCDIO_LARGEFILE_FLAGS" +fi + +# We use cmp and cdparanoia in cd-paranoia regression testing +AC_PATH_PROG(CMP, cmp, no) +AC_SUBST(CMP) + +AC_PATH_PROG(OLD_CDPARANOIA, cdparanoia, no) +AC_SUBST(OLD_CDPARANOIA) + +AC_DEFINE(LIBCDIO_CONFIG_H, 1, + [Is set when libcdio's config.h has been included. Applications wishing to sue their own config.h values (such as set by the application's configure script can define this before including any of libcdio's headers.]) + +dnl headers + +AC_HEADER_STDC +AC_CHECK_HEADERS(errno.h fcntl.h glob.h limits.h pwd.h) +AC_CHECK_HEADERS(stdarg.h stdbool.h stdio.h sys/cdio.h sys/param.h) +AC_CHECK_HEADERS(sys/time.h sys/timeb.h) +AC_CHECK_HEADERS(ncurses.h curses.h, break, [enable_cdda_player='no']) + +dnl FreeBSD 4 has getopt in unistd.h. So we include that before +dnl getopt.h +AC_CHECK_HEADERS(unistd.h getopt.h) + +AC_SUBST(SBPCD_H) +AC_SUBST(UCDROM_H) +AC_SUBST(TYPESIZES) + +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) + +## +## Check for S_ISSOCK() and S_ISLNK() macros +## +AC_MSG_CHECKING(for S_ISLNK() macro) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#ifdef HAVE_SYS_STAT_H +# include +#endif +],[return S_ISLNK(0);])], +[ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_S_ISLNK, [], [Define this defines S_ISLNK()]) ], +[ AC_MSG_RESULT(no) ]) + +AC_MSG_CHECKING([for S_ISSOCK() macro]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#ifdef HAVE_SYS_STAT_H +# include +#endif +],[return S_ISSOCK(0);])], +[ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_S_ISSOCK, [], [Define this defines S_ISSOCK()]) ], +[ AC_MSG_RESULT(no) ]) + +AC_MSG_CHECKING([for struct timespec]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#ifdef HAVE_SYS_TIME_H +#include +#endif +],[struct timespec ts;])], +[ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_STRUCT_TIMESPEC, [], [Define this if you have struct timespec]) ], +[ AC_MSG_RESULT(no) ]) + +dnl empty_array_size +AC_MSG_CHECKING([how to create empty arrays]) + +empty_array_size="xxxx" +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 Enable the creation of shared libraries under Win32. +AC_LIBTOOL_WIN32_DLL + +dnl AM_PROG_LIBTOOL tests whether we have GNU ld +dnl this must come before checking --with-versioned-libs +dnl which requires GNU ld. +AM_PROG_LIBTOOL + +dnl system + +# FIXME: +# I believe some OS's require -lm, but I don't recall for what function +# When we find it, put it in below instead of "cos". +AC_CHECK_LIB(m, cos, [LIBS="$LIBS -lm"; COS_LIB="-lm"]) +CFLAGS="$CFLAGS $WARN_CFLAGS" +AC_SUBST(COS_LIB) + +# Do we have GNU ld? If we don't, we can't build versioned symbols. +if test "$with_gnu_ld" != yes; then + AC_MSG_WARN([I don't see GNU ld. I'm going to assume --without-versioned-libs]) + enable_versioned_libs='no' +fi +# We also need GNU make to build versioned symbols. +if test "x$enable_versioned_libs" = "xyes" ; then + if test -n "$MAKE" ; then + $MAKE --version 2>/dev/null >/dev/null + if test "$?" -ne 0 ; then + AC_MSG_ERROR(Either set MAKE variable to GNU make or use + --without-versioned-libs option) + fi + else + make --version 2>/dev/null >/dev/null + if test "$?" -ne 0 ; then + AC_MSG_ERROR(Either set MAKE variable to GNU make or use + --without-versioned-libs option) + fi + fi +fi + +AM_CONDITIONAL(CYGWIN, test "x$CYGWIN" = "xyes") +AM_CONDITIONAL(BUILD_CD_DRIVE, test "x$enable_cd_drive" = "xyes") +AM_CONDITIONAL(BUILD_CDINFO, test "x$enable_cd_info" = "xyes") +AM_CONDITIONAL(BUILD_CD_READ, test "x$enable_cd_read" = "xyes") +AM_CONDITIONAL(BUILD_CD_PARANOIA, test "x$enable_cd_paranoia" = "xyes") +AM_CONDITIONAL(BUILD_ISO_INFO, test "x$enable_iso_info" = "xyes") +AM_CONDITIONAL(BUILD_ISO_READ, test "x$enable_iso_read" = "xyes") +AM_CONDITIONAL(BUILD_CDINFO_LINUX, test "x$enable_cd_info_linux" = "xyes") +AM_CONDITIONAL(BUILD_CDIOTEST, test "x$enable_cdiotest" = "xyes") +AM_CONDITIONAL(BUILD_VERSIONED_LIBS, test "x$enable_versioned_libs" = "xyes") +AM_CONDITIONAL(DISABLE_CPP, test "x$disable_cpp" = "xyes") + +dnl Checks for header files. + +LIBCDIO_CDDA_LIBS='$(top_builddir)/lib/cdda_interface/libcdio_cdda.la' +LIBCDIO_CFLAGS='-I$(top_srcdir)/lib/driver -I$(top_builddir)/include -I$(top_srcdir)/include/' +LIBCDIO_LIBS='$(top_builddir)/lib/driver/libcdio.la' +LIBCDIO_DEPS="$LIBCDIO_LIBS" +LIBCDIOPP_LIBS='$(top_builddir)/lib/cdio++/libcdio++.la' +LIBISO9660PP_LIBS='$(top_builddir)/lib/cdio++/libiso9660++.la' +LIBCDIO_PARANOIA_LIBS='$(top_builddir)/lib/paranoia/libcdio_paranoia.la' +LIBISO9660_CFLAGS='-I$(top_srcdir)/lib/iso9660/' +LIBISO9660_LIBS='$(top_builddir)/lib/iso9660/libiso9660.la' +LIBUDF_CFLAGS='-I$(top_srcdir)/lib/udf/' +LIBUDF_LIBS='$(top_builddir)/lib/udf/libudf.la' +AC_SUBST(LIBCDIO_CDDA_LIBS) +AC_SUBST(LIBCDIO_CFLAGS) +AC_SUBST(LIBISO9660_CFLAGS) +AC_SUBST(LIBISO9660PP_LIBS) +AC_SUBST(LIBCDIO_LIBS) +AC_SUBST(LIBCDIOPP_LIBS) +AC_SUBST(LIBCDIO_DEPS) +AC_SUBST(LIBCDIO_PARANOIA_LIBS) +AC_SUBST(LIBISO9660_LIBS) +AC_SUBST(LIBUDF_LIBS) + +dnl Libtool flag for strict linkage +LT_NO_UNDEFINED= + +case $host_os in + aix*) + ## Don't use AIX driver until starts to really work + ## cd_drivers="${cd_drivers}, AIX" + ## AC_DEFINE([HAVE_AIX_CDROM], [1], + ## [Define 1 if you have AIX CD-ROM support]) + ;; + darwin6*|darwin7*|darwin8*|darwin9*) + AC_CHECK_HEADERS(IOKit/IOKitLib.h CoreFoundation/CFBase.h, + [have_iokit_h="yes"]) + if test "x$have_iokit_h" = "xyes" ; then + AC_DEFINE([HAVE_DARWIN_CDROM], [1], + [Define 1 if you have Darwin OS X-type CD-ROM support]) + DARWIN_PKG_LIB_HACK="-Wl,-framework,CoreFoundation -Wl,-framework,IOKit" + + dnl Prior to Mac OS X 10.4 (Tiger), DiskArbitration was a private framework. + dnl It's now public, and it's needed to do cd/dvd unmount/eject. + AC_MSG_CHECKING([for DiskArbitration framework]) + ac_save_LIBS="$LIBS" LIBS="$LIBS -framework CoreFoundation -framework DiskArbitration" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[]])], + [have_diskarbitration_framework=yes], [have_diskarbitration_framework=no]) + LIBS="$ac_save_LIBS" + AC_MSG_RESULT([$have_diskarbitration_framework]) + if test x"$have_diskarbitration_framework" = x"yes"; then + AC_DEFINE([HAVE_DISKARBITRATION], 1, [Define to 1 if you have the Apple DiskArbitration framework]) + DARWIN_PKG_LIB_HACK="$DARWIN_PKG_LIB_HACK -Wl,-framework,DiskArbitration" + fi + + AC_SUBST(DARWIN_PKG_LIB_HACK) + LIBCDIO_LIBS="$LIBCDIO_LIBS $DARWIN_PKG_LIB_HACK" + cd_drivers="${cd_drivers}, Darwin" + fi + ;; + linux*|uclinux) + AC_CHECK_HEADERS(linux/version.h linux/major.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]) + cd_drivers="${cd_drivers}, GNU/Linux" + fi + ;; + bsdi*) + AC_CHECK_HEADERS(dvd.h, [have_bsdi_dvd_h="yes"]) + if test "x$have_bsdi_dvd_h" = "xyes" ; then + AC_DEFINE([HAVE_BSDI_CDROM], [1], + [Define 1 if you have BSDI-type CD-ROM support]) + LIBS="$LIBS -ldvd -lcdrom" + LIBCDIO_LIBS="$LIBCDIO_LIBS -lcdrom" + cd_drivers="${cd_drivers}, BSDI" + fi + ;; + sunos*|sun*|solaris*) + AC_DEFINE([HAVE_SOLARIS_CDROM], [1], + [Define 1 if you have Solaris CD-ROM support]) + cd_drivers="${cd_drivers}, Solaris" + ;; + cygwin*) + AC_DEFINE([CYGWIN], [1], + [Define 1 if you are compiling using cygwin]) + AC_DEFINE([HAVE_WIN32_CDROM], [1], + [Define 1 if you have MinGW CD-ROM support]) + LIBS="$LIBS -lwinmm" + cd_drivers="${cd_drivers}, MinGW" + AC_DEFINE([NEED_TIMEZONEVAR], [1], + [Define 1 if you need timezone defined to get timzone + defined as a variable. In cygwin it is a function too]) + ;; + mingw*) + AC_CHECK_HEADERS(windows.h) + AC_DEFINE([MINGW32], [1], + [Define 1 if you are compiling using MinGW]) + AC_DEFINE([HAVE_WIN32_CDROM], [1], + [Define 1 if you have MinGW CD-ROM support]) + LIBS="$LIBS -lwinmm -mwindows" + LT_NO_UNDEFINED="-no-undefined" + cd_drivers="${cd_drivers}, MinGW " + ;; + freebsd4.*|freebsd5.*|freebsd6*|freebsd7*|dragonfly*|kfreebsd*) + AC_DEFINE([HAVE_FREEBSD_CDROM], [1], + [Define 1 if you have FreeBSD CD-ROM support]) + LIBS="$LIBS -lcam" + cd_drivers="${cd_drivers}, FreeBSD " + ;; + netbsd*) + AC_DEFINE([HAVE_NETBSD_CDROM], [1], + [Define 1 if you have NetBSD CD-ROM support]) + # LIBS="$LIBS -lcam" + cd_drivers="${cd_drivers}, NetBSD " + ;; + *) + AC_MSG_WARN([Don't have OS CD-reading support for ${host_os}...]) + AC_MSG_WARN([Will use generic support.]) + ;; +esac + +AC_SUBST(LT_NO_UNDEFINED) + +AC_MSG_CHECKING(extern long timezone variable) +AC_LINK_IFELSE([ +#ifdef NEED_TIMEZONEVAR +#define timezonevar 1 +#endif + +#include +extern long timezone; +int main(int argc, char **argv) { + long test_timezone = timezone; + return 0; +} +], [AC_MSG_RESULT(yes); + AC_DEFINE([HAVE_TIMEZONE_VAR], 1, + [Define if you have an extern long timenzone variable.])], + [AC_MSG_RESULT(no)]) +dnl + +AC_SUBST(LINUX_CDROM_TIMEOUT) +AC_SUBST(DARWIN_PKG_LIB_HACK) +AC_SUBST(HAVE_BSDI_CDROM) +AC_SUBST(HAVE_DARWIN_CDROM) +AC_SUBST(HAVE_FREEBSD_CDROM) +AC_SUBST(HAVE_LINUX_CDROM) +AC_SUBST(HAVE_SOLARIS_CDROM) +AC_SUBST(HAVE_WIN32_CDROM) + +LIBCDIO_SOURCE_PATH="`pwd`" +AC_DEFINE_UNQUOTED(LIBCDIO_SOURCE_PATH, "$LIBCDIO_SOURCE_PATH", + [Full path to libcdio top_sourcedir.]) +AC_SUBST(LIBCDIO_SOURCE_PATH) + +AC_CHECK_FUNCS( [bzero drand48 ftruncate geteuid getgid \ + getuid getpwuid gettimeofday lstat memcpy memset \ + rand seteuid setegid snprintf setenv unsetenv tzset \ + sleep vsnprintf readlink gmtime_r localtime_r] ) + +# check for timegm() support +AC_CHECK_FUNC(timegm, AC_DEFINE(HAVE_TIMEGM,1, + [Define to 1 if timegm is available])) + +AC_CHECK_MEMBER([struct tm.tm_gmtoff], + [AC_DEFINE(HAVE_TM_GMTOFF, 1, + [Define if struct tm has the tm_gmtoff member.])], + , + [#include ]) + +if test $ac_cv_member_struct_tm_tm_gmtoff = yes ; then + AC_MSG_CHECKING([whether time.h defines daylight and timezone variables]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include +],[return (timezone != 0) + daylight;])], + [AC_DEFINE(HAVE_DAYLIGHT, 1, + [Define if time.h defines extern long timezone and int daylight vars.]) + has_daylight=yes + ],[has_daylight=no]) + AC_MSG_RESULT($has_daylight) + + AC_MSG_CHECKING([whether time.h defines tzname variable]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include +],[return (tzname != NULL);])], + [AC_DEFINE(HAVE_TZNAME, 1, + [Define if time.h defines extern extern char *tzname[2] variable]) + has_tzname=yes + ],[has_tzname=no]) + AC_MSG_RESULT($has_tzname) +fi + +AC_ARG_ENABLE(joliet, + AS_HELP_STRING([--disable-joliet], [don't include Joliet extension support (default enabled)]), + [enable_joliet=$enableval], + [enable_joliet=yes]) +if test "${enable_joliet}" != "no" ; then + AM_ICONV + AM_LANGINFO_CODESET + if test "$am_cv_func_iconv" = yes ; then + AC_DEFINE(HAVE_JOLIET, [1], + [Define 1 if you want ISO-9660 Joliet extension support. + You must have also libiconv installed to get Joliet extension support.]) + HAVE_JOLIET=1 + else + AC_MSG_ERROR([You must have iconv installed.]) + fi +fi +AC_SUBST(HAVE_JOLIET) + +if test x$enable_rock = x; then + enable_rock=yes + AC_ARG_ENABLE(rock, + [ --enable-rock include Rock-Ridge extension support (default enabled)], + enable_rock=yes, enable_rock=no) +fi + +if test "${enable_rock}" != "no" ; then + AC_DEFINE(HAVE_ROCK, [1], + [Define 1 if you want ISO-9660 Rock-Ridge extension support.]) + HAVE_ROCK=1 +fi +AC_SUBST(HAVE_ROCK) +AM_CONDITIONAL(ENABLE_ROCK, test x"$enable_rock" = "xyes") + +if test x$enable_cddb = x; then + enable_cddb=yes + AC_ARG_ENABLE(cddb, + [ --enable-cddb include CDDB lookups in cd_info (default enabled)], + enable_cddb=yes, enable_cddb=no) +fi +if test x$enable_cddb != "no" ; then + PKG_CHECK_MODULES(CDDB, libcddb >= 1.0.1, [ + HAVE_CDDB=yes + AC_DEFINE(HAVE_CDDB, [], [Define this if you have libcddb installed]) + ], + [AC_MSG_WARN([new enough libcddb not found. CDDB access disabled. Get libcddb from http://libcddb.sourceforge.net]) + HAVE_CDDB=no]) + AC_CHECK_LIB(socket, connect) + AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, gethostbyname)) +fi + + +AC_SUBST(CDDB_LIBS) + +AC_DEFINE(HAVE_KEYPAD, [], [Define this if your libcurses has keypad]) +if test x$enable_cdda_player = xyes; then + AC_CHECK_LIB(ncurses, mvprintw, + [LIBCURSES=ncurses; CDDA_PLAYER_LIBS="$CDDA_PLAYER_LIBS -lncurses"], + AC_CHECK_LIB(curses, mvprintw, + [LIBCURSES=curses; CDDA_PLAYER_LIBS="$CDDA_PLAYER_LIBS -lcurses"], + [AC_MSG_WARN([Will not build cdda-player - did not find libcurses or libncurses]) + enable_cdda_player=no])) + if test x$enable_cdda_player = xyes; then + AC_CHECK_LIB($LIBCURSES, keypad, [HAVE_KEYPAD=yes]) + fi +fi + +AM_CONDITIONAL(BUILD_CDDA_PLAYER, test "x$enable_cdda_player" = "xyes") +AC_SUBST(CDDA_PLAYER_LIBS) + +if test x$enable_vcd_info = x; then +AC_ARG_ENABLE(vcd_info, + [ --enable-vcd-info include Video CD Info from libvcd], + enable_vcd_info=yes, + enable_vcd_info=no) +fi +if test x$enable_vcd_info = xyes; then + PKG_CHECK_MODULES(VCDINFO, libvcdinfo >= 0.7.21, + [AC_DEFINE([HAVE_VCDINFO],1, + [Define this if you have libvcdinfo installed])], + [AC_MSG_WARN(a new enough libvcdinfo not found. +VCD info display in cd-info disabled. +libvcdinfo is part of vcdimager. Get it from http://vcdimager.org) + enable_vcd_info=no]) +fi + +AC_SUBST(VCDINFO_LIBS) +AC_SUBST(VCDINFO_CFLAGS) + +dnl +dnl Newest automake workaround - needed for multi-language manual pages +dnl +AC_SUBST(mkdir_p) + +AC_CONFIG_COMMANDS([checks], + [chmod +x test/check_cue.sh; chmod +x test/check_nrg.sh + ]) + +dnl +dnl Output configuration files +dnl +## AC_CONFIG_FILES([ po/Makefile.in\ +AC_CONFIG_FILES([ + Makefile \ + example/C++/Makefile \ + example/C++/OO/Makefile \ + example/Makefile \ + include/Makefile \ + include/cdio/Makefile \ + include/cdio++/Makefile \ + include/cdio/version.h \ + doc/doxygen/Doxyfile \ + doc/Makefile \ + lib/Makefile \ + lib/cdda_interface/Makefile \ + lib/cdio++/Makefile \ + lib/driver/Makefile \ + lib/iso9660/Makefile \ + lib/paranoia/Makefile \ + lib/udf/Makefile \ + libcdio.pc \ + libcdio++.pc \ + libcdio_cdda.pc \ + libcdio_paranoia.pc \ + libiso9660.pc \ + libiso9660++.pc \ + libudf.pc \ + package/libcdio.spec \ + src/cd-paranoia/Makefile \ + src/cd-paranoia/usage.txt \ + src/cd-paranoia/doc/Makefile \ + src/cd-paranoia/doc/en/cd-paranoia.1 \ + src/cd-paranoia/doc/en/Makefile \ + src/cd-paranoia/doc/ja/cd-paranoia.1 \ + src/cd-paranoia/doc/ja/Makefile \ + src/Makefile \ + test/testbincue.c \ + test/testisocd2.c \ + test/testpregap.c \ + test/testnrg.c \ + test/check_common_fn \ + test/Makefile \ + ]) + +# AC_CONFIG_FILES([po/Makefile]) +AC_CONFIG_FILES([test/check_cue.sh], [chmod +x test/check_cue.sh]) +AC_CONFIG_FILES([test/check_iso.sh], [chmod +x test/check_iso.sh]) +AC_CONFIG_FILES([test/check_nrg.sh], [chmod +x test/check_nrg.sh]) +AC_CONFIG_FILES([test/check_paranoia.sh], [chmod +x test/check_paranoia.sh]) + +AC_CONFIG_COMMANDS([default],[[ +# Make sure scripts are executable +# This is a big hack, but I don't know any other way around it. +cat > include/cdio/cdio_config.h << EOF +/** \file cdio_config.h + * \brief configuration-time settings useful in compilation; a run-time + version of config.h +*/ +EOF +head -n 233 config.h >> include/cdio/cdio_config.h +]],[[]]) + +AC_OUTPUT + +echo "Using CD-ROM drivers: $cd_drivers" +echo "Building cd-paranoia: $(test "x$enable_cd_paranoia" = "xyes" && echo yes || echo no)" +echo "Building cd-info : $(test "x$enable_cd_info" = "xyes" && echo yes || echo no)" +echo "Building cd-read : $(test "x$enable_cd_read" = "xyes" && echo yes || echo no)" +echo "Building cdda-player: $(test "x$enable_cdda_player" = "xyes" && echo yes || echo no)" +echo "Building iso-info : $(test "x$enable_iso_info" = "xyes" && echo yes || echo no)" +echo "Building iso-read : $(test "x$enable_iso_read" = "xyes" && echo yes || echo no)" diff --git a/cvs2cl_header b/cvs2cl_header new file mode 100644 index 00000000..93cdd64a --- /dev/null +++ b/cvs2cl_header @@ -0,0 +1,4 @@ +## +## autogenerated ChangeLog -- don't edit +## + diff --git a/cvs2cl_usermap b/cvs2cl_usermap new file mode 100644 index 00000000..1afc0ae1 --- /dev/null +++ b/cvs2cl_usermap @@ -0,0 +1,7 @@ +edsdead:Robert William Fuller +flameeyes:Diego 'Flameeyes' Pettenò +hvr:Herbert Valerio Riedel +karl:Karl Berry +nboullis:Nicolas Boullis +rocky:Rocky Bernstein +thesin:Justin F. Hallett diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 00000000..a07183bf --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,7 @@ +/Makefile +/Makefile.in +/libcdio.info +/mdate-sh +/stamp-vti +/texinfo.tex +/version.texi diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 00000000..f8e72e86 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,46 @@ +# $Id: Makefile.am,v 1.9 2008/04/17 17:39:47 karl Exp $ +# +# Copyright (C) 2003, 2004, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +EXTRA_DIST = doxygen/Doxyfile.in doxygen/run_doxygen +info_TEXINFOS = libcdio.texi +libcdio_TEXINFOS = fdl.texi glossary.texi + +reference: + -( cd ${top_srcdir} && $(MAKE) doxygen ) + +pdf: libcdio.pdf + +txt: libcdio.txt + +ps: libcdio.ps + +html: libcdio.html + +%.ps.gz: %.ps + gzip -9c $< > $@ + +.texi.pdf: + texi2pdf $< + +.texi.html: + texi2html $< + +.texi.txt: + makeinfo --no-headers $< > $@ + +all-formats: pdf dvi txt ps html + +MOSTLYCLEANFILES = libcdio.html libcdio.pdf libcdio.ps.gz diff --git a/doc/doxygen/.gitignore b/doc/doxygen/.gitignore new file mode 100644 index 00000000..eeb3b95d --- /dev/null +++ b/doc/doxygen/.gitignore @@ -0,0 +1 @@ +/Doxyfile diff --git a/doc/doxygen/Doxyfile.in b/doc/doxygen/Doxyfile.in new file mode 100644 index 00000000..868137ee --- /dev/null +++ b/doc/doxygen/Doxyfile.in @@ -0,0 +1,1302 @@ +# Doxyfile 1.5.3 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file that +# follow. The default is UTF-8 which is also the encoding used for all text before +# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into +# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of +# possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = @PACKAGE@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @VERSION@ + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, +# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, +# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, +# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class " \ + "The $name widget " \ + "The $name file " \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = @LIBCDIO_SOURCE_PATH@ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be extracted +# and appear in the documentation as a namespace called 'anonymous_namespace{file}', +# where file will be replaced with the base name of the file that contains the anonymous +# namespace. By default anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text " + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ../../include/cdio/ + +# This tag can be used to specify the character encoding of the source files that +# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default +# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. +# See http://www.gnu.org/software/libiconv for the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the output. +# The symbol name can be a fully qualified name, a word, or if the wildcard * is used, +# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = ../../example + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH +# then you must also enable this option. If you don't then doxygen will produce +# a warning and turn it on anyway + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = letter + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = libcdio.xml + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to +# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to +# specify the directory where the mscgen tool resides. If left empty the tool is assumed to +# be found in the default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will +# generate a caller dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the number +# of direct children of the root node in a graph is already larger than +# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/doc/doxygen/run_doxygen b/doc/doxygen/run_doxygen new file mode 100755 index 00000000..1765ed59 --- /dev/null +++ b/doc/doxygen/run_doxygen @@ -0,0 +1,107 @@ +#!/bin/sh +# $Id: run_doxygen,v 1.1 2003/11/09 14:11:02 rocky Exp $ + +# Runs doxygen and massages the output files. +# Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. +# +# Synopsis: run_doxygen --mode=[user|maint|man] v3srcdir v3builddir +# +# Originally hacked together by Phil Edwards + + +# We can check now that the version of doxygen is >= this variable. +DOXYVER=1.2.15 +doxygen= + +find_doxygen() { + v_required=`echo $DOXYVER | \ + awk -F. '{if(NF<3)$3=0;print ($1*100+$2)*100+$3}'` + testing_version= + # thank you goat book + set `IFS=:; X="$PATH:/usr/local/bin:/bin:/usr/bin"; echo $X` + for dir + do + # AC_EXEEXT could come in useful here + maybedoxy="$dir/doxygen" + test -f "$maybedoxy" && testing_version=`$maybedoxy --version` + if test -n "$testing_version"; then + v_found=`echo $testing_version | \ + awk -F. '{if(NF<3)$3=0;print ($1*100+$2)*100+$3}'` + if test $v_found -ge $v_required; then + doxygen="$maybedoxy" + break + fi + fi + done + if test -z "$doxygen"; then + echo run_doxygen error: Could not find Doxygen $DOXYVER in path. 1>&2 + print_usage + fi +} + +print_usage() { + cat 1>&2 <] + MODE is one of: + user Generate user-level HTML library documentation. + maint Generate maintainers' HTML documentation (lots more; + exposes non-public members, etc). + man Generate user-level man pages. + + more options when i think of them + +Note: Requires Doxygen ${DOXYVER} or later; get it at + ftp://ftp.stack.nl/pub/users/dimitri/doxygen-${DOXYVER}.src.tar.gz + +EOF + exit 1 +} + +parse_options() { + for o + do + # Blatantly ripped from autoconf, er, I mean, "gratefully standing + # on the shoulders of those giants who have gone before us." + case "$o" in + -*=*) arg=`echo "$o" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) arg= ;; + esac + + case "$o" in + --mode=*) + mode=$arg ;; + --mode | --help | -h) + print_usage ;; + *) + # this turned out to be a mess, maybe change to --srcdir=, etc + if test $srcdir = unset; then + srcdir=$o + elif test $outdir = unset; then + builddir=${o} + outdir=${o}/doc/doxygen + else + echo run_doxygen error: Too many arguments 1>&2 + exit 1 + fi + ;; + esac + done +} + + +# script begins here +mode=unset +srcdir=unset +outdir=unset +do_html=no +do_man=no +enabled_sections= +DATEtext=`date '+%Y-%m-%d'` + +parse_options $* +find_doxygen +$doxygen ./Doxyfile + +exit 0 + +# vim:ts=4:et: diff --git a/doc/fdl.texi b/doc/fdl.texi new file mode 100644 index 00000000..96ce74ea --- /dev/null +++ b/doc/fdl.texi @@ -0,0 +1,451 @@ +@c The GNU Free Documentation License. +@center Version 1.2, November 2002 + +@c This file is intended to be included within another document, +@c hence no sectioning command or @node. + +@display +Copyright @copyright{} 2000,2001,2002 Free Software Foundation, Inc. +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@enumerate 0 +@item +PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document @dfn{free} in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of ``copyleft'', which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + +@item +APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The ``Document'', below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as ``you''. You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A ``Modified Version'' of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A ``Secondary Section'' is a named appendix or a front-matter section +of the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The ``Invariant Sections'' are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The ``Cover Texts'' are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A ``Transparent'' copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not ``Transparent'' is called ``Opaque''. + +Examples of suitable formats for Transparent copies include plain +@sc{ascii} without markup, Texinfo input format, La@TeX{} input +format, @acronym{SGML} or @acronym{XML} using a publicly available +@acronym{DTD}, and standard-conforming simple @acronym{HTML}, +PostScript or @acronym{PDF} designed for human modification. Examples +of transparent image formats include @acronym{PNG}, @acronym{XCF} and +@acronym{JPG}. Opaque formats include proprietary formats that can be +read and edited only by proprietary word processors, @acronym{SGML} or +@acronym{XML} for which the @acronym{DTD} and/or processing tools are +not generally available, and the machine-generated @acronym{HTML}, +PostScript or @acronym{PDF} produced by some word processors for +output purposes only. + +The ``Title Page'' means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, ``Title Page'' means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section ``Entitled XYZ'' means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as ``Acknowledgements'', +``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' +of such a section when you modify the Document means that it remains a +section ``Entitled XYZ'' according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +@item +VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + +@item +COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + +@item +MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +@enumerate A +@item +Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions +(which should, if there were any, be listed in the History section +of the Document). You may use the same title as a previous version +if the original publisher of that version gives permission. + +@item +List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified +Version, together with at least five of the principal authors of the +Document (all of its principal authors, if it has fewer than five), +unless they release you from this requirement. + +@item +State on the Title page the name of the publisher of the +Modified Version, as the publisher. + +@item +Preserve all the copyright notices of the Document. + +@item +Add an appropriate copyright notice for your modifications +adjacent to the other copyright notices. + +@item +Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the +terms of this License, in the form shown in the Addendum below. + +@item +Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. + +@item +Include an unaltered copy of this License. + +@item +Preserve the section Entitled ``History'', Preserve its Title, and add +to it an item stating at least the title, year, new authors, and +publisher of the Modified Version as given on the Title Page. If +there is no section Entitled ``History'' in the Document, create one +stating the title, year, authors, and publisher of the Document as +given on its Title Page, then add an item describing the Modified +Version as stated in the previous sentence. + +@item +Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise +the network locations given in the Document for previous versions +it was based on. These may be placed in the ``History'' section. +You may omit a network location for a work that was published at +least four years before the Document itself, or if the original +publisher of the version it refers to gives permission. + +@item +For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve +the Title of the section, and preserve in the section all the +substance and tone of each of the contributor acknowledgements and/or +dedications given therein. + +@item +Preserve all the Invariant Sections of the Document, +unaltered in their text and in their titles. Section numbers +or the equivalent are not considered part of the section titles. + +@item +Delete any section Entitled ``Endorsements''. Such a section +may not be included in the Modified Version. + +@item +Do not retitle any existing section to be Entitled ``Endorsements'' or +to conflict in title with any Invariant Section. + +@item +Preserve any Warranty Disclaimers. +@end enumerate + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled ``Endorsements'', provided it contains +nothing but endorsements of your Modified Version by various +parties---for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + +@item +COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled ``History'' +in the various original documents, forming one section Entitled +``History''; likewise combine any sections Entitled ``Acknowledgements'', +and any sections Entitled ``Dedications''. You must delete all +sections Entitled ``Endorsements.'' + +@item +COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + +@item +AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an ``aggregate'' if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + +@item +TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled ``Acknowledgements'', +``Dedications'', or ``History'', the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + +@item +TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + +@item +FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. See +@uref{http://www.gnu.org/copyleft/}. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License ``or any later version'' applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. +@end enumerate + +@page +@heading ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +@smallexample +@group + Copyright (C) @var{year} @var{your name}. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. +@end group +@end smallexample + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the ``with@dots{}Texts.'' line with this: + +@smallexample +@group + with the Invariant Sections being @var{list their titles}, with + the Front-Cover Texts being @var{list}, and with the Back-Cover Texts + being @var{list}. +@end group +@end smallexample + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +@c Local Variables: +@c ispell-local-pdict: "ispell-dict" +@c End: + diff --git a/doc/glossary.texi b/doc/glossary.texi new file mode 100644 index 00000000..c7d2c2f5 --- /dev/null +++ b/doc/glossary.texi @@ -0,0 +1,360 @@ +See also @uref{http://www.dvdrhelp.com/glossary}. + +@table @dfn + +@item ASPI +@cindex ASPI +See @acronym{Win32 ASPI} + +@item ATA + +Advanced Technology Attachment (ATA). The same things as IDE. + +@item ATAPI + +Advanced Technology Attachment (ATA) Packet Interface. The interface +provides a mechanism for transferring and executing SCSI CDBs on IDE +CD Drives and DVD Drives. + +IDE (also called ATA) was originally designed for hard drives only, +but with help of ATAPI it is possible to connect other devices, in +particular CD-ROMS to the IDE/EIDE connections. + +The ATAPI CD-ROM drives understand a subset of MMC commands. In +particular multi-initiator commands are neither needed nor deviced for +ATAPI devices. + +@item BIN/CUE + +A CD-image format developed by Jeff Arnold for CDRWIN software on +Microsoft Windows. Many other programs subsequently support using this +format. The @code{.CUE} file is a text file which contains CD format +and track layout information, while the @code{.BIN} file holds the +actual data of each track. + +@item CD +Compact Disc + +@item CD-DA +@cindex CD-DA +Compact Disc Digital Audio, described in the ``Red Book'' or IEC 60908 +(formerly IEC 908). This commonly referred to as an audio @acronym{CD} +and what most people think of when you play a @acronym{CD} as it was +the first to use the @acronym{CD} medium. + +See @url{http://en.wikipedia.org/wiki/Red_Book_(audio_CD_standard)} + +@item CD+G +@cindex CD+G + +Compact Disc + Graphics. An extension of the CD audio format contains +a limited amount of graphics in subcode channels. This disc works in +all audio players but the graphics portion is only available in a +special CD+G or Karaoke player. + +@item CD-i +@cindex CD-i + +Compact Disc Interactive. An extension of the CD format designed +around a set-top computer that connects to a TV to provide interactive +home entertainment, including digital audio and video, video games, +and software applications. Defined by the ``Green Book'' standard. +@uref{http://www.icdia.org/}. CD-i for video and video music has +largely (if not totally) been superceded by VCDs. + +@item CD-i Bridge +@cindex CD-i Bridge + +A standard allowing CD-ROM XA discs to play on CD-i. Kodak PhotoCDs +are CD-XA Bridge discs. + +@item CD-ROM +@cindex CD-ROM + +Compact Disc Read Only Memory or ``Yellow Book'' describe in Standards +ISO/IEC 10149. The data stored on it can be either in the form of +audio, computer or video files. + +@item CD-ROM Mode 1 and Mode2 + +The Yellow Book specifies two types of tracks, Mode 1 and Mode 2. Mode +1 is used for computer data and text and has an extra error correction +layer. Mode 2 is for audio and video data and has no extra correction +layer. CD-ROM/XA An expansion of the CD-ROM Mode 2 format that allows +both computer and audio/video to be mixed in the same track. + +@item CD Text +@cindex CD Text + +CD Text is a technology developed by Sony Corporation and Philips +Electronics in 1996 that allows storing in an audio CD and its tracks +information such as artist name, title, songwriter, composer, or +arranger. Commercially available audio CDs sometimes contain CD Text +information. + +@item CD XA +@cindex CD XA + +CD-ROM EXtended Architecture. A modification to the CD-ROM +specification that defines two new types of sectors. CD-ROM XA was +developed jointly by Sony, Philips, and Microsoft, and announced in +August 1988. Its specifications were published in an extension to the +Yellow Book. CD-i, Photo CD, Video CD and CD-EXTRA have all +subsequently been based on CD-ROM XA. + +CD-XA defines another way of formatting sectors on a CD-ROM, including +headers in the sectors that describe the type (audio, video, data) and +some additional info (markers, resolution in case of a video or audio +sector, file numbers, etc). + +The data written on a CD-XA is consistent with and can be in ISO-9660 +file system format and therefore be readable by ISO-9660 file system +translators. But also a CD-I player can also read CD-XA discs even if +its own `Green Book' file system only resembles ISO 9660 and isn't +fully compatible. + +@item Command Packet +@cindex Command Packet + +The data structure that is used to issue an ATAPI command. The same +thing as a SCSI Command Descriptor Block (CDB). + +@item FSF +@cindex FSF + +Free Software Foundation, @uref{http://www.fsf.org/} + +@item GNU +@cindex GNU + +@acronym{GNU} is not @acronym{UNIX}, @uref{http://www.gnu.org/} + +@item IDE + +Integrated Drive Electronics. This is a commonly used interface for +hard disk drives and CD-ROM drives. It is less expensive than SCSI, +but offers slightly less in terms of performance. + +@item ISO +@cindex ISO + +International Standards Organization. + +@item ISO 9660 +@cindex ISO 9660 + +The ISO 9660 is an operating-system independent filesystem format for +CD-ROM media and DVD-ROMs. It was standardized in 1988 and replaced the +High Sierra standard for the logical format on CD-ROM media (ISO 9660 +and High Sierra are identical in content, but the exact format is +different). + +There are several specification levels. In Level 1, file names must be +in the 8.3 format (no more than eight characters in the name, no more +than three characters in the suffix) and in capital letters. Directory +names can be no longer than eight characters. There can be no more +than eight nested directory levels. Level 2 and 3 specifications allow +file names up to 32 characters long. + +ECMA-119 +(@uref{http://www.ecma-international.org/publications/standards/Ecma-119.htm} +is the European specification which is identical to ISO 9660. +ISO 13490 is basically ISO 9660 with multisession support. + +@item Joliet extensions +@cindex Joliet extensions + +This ISO-9660 upward-compatible standard was developed for Windows 95 +and Windows NT by Microsoft as an extension of ISO 9600 which allows +the use of Unicode characters and supports file names up to 64 +characters. + +See @uref{http://bmrc.berkeley.edu/people/chaffee/jolspec.html} for +the Joliet Specification. + +The name Joliet comes from the city in Illinois (U.S) that the +standard was defined. + +@item LBA +@cindex LBA + +Logical Block Addressing. Mapped integer numbers from CD Red Book +Addressing MSF. The starting sector is -150 and ending sector is +449849, which correlates directly to MSF: 00:00:00 to 99:59:74. +Because an LBA is a single number it is often easier to work with in +programming than an MSF. + +@item Lead in +@cindex lead in + +The area of a CD where the Table Of Contents (TOC) and CD Text are +stored. I think it is supposed to be around 4500 (1 min) or more +sectors in length. On a CDR(W) the lead-in length is variable, +because each manufacturer will have a different starting position +indicated by the ATIP start of lead-in position that is recorded in +the ATIP groove on the disk. + +@item LSN +@cindex LSN + +Logical Sector Number. Mapped integer numbers from CD Red Book +Addressing MSF. The starting sector is 0 and ending sector is 449699, +which correlates to MSF: 00:00:00 to 99:59:74. Because an LSN is a +single number it is often easier to work with in programming than an +MSF. Because it starts at 0 rather than -150 as is the case of an LBA +it can be represented as an unsigned value. + +@item MCN +@cindex MCN + +Media Catalog Number. A identification number on an audio CD. Also +called a UPC. Another identification number is ISRC. + +@item MMC +@cindex MMC (Multimedia Commands) + +MMC (Multimedia Commands). A SCSI programming specification made by +the SCSI committee T10 organization @url{http://www.t10.org/}. MMC +are raw commands for communicating with CDROM drives, CD-Rewriters, +DVD-Rewriters, etc. + +Many manufacturers have adopted this standard and it also applies to +ATAPI versions of their drives. + +@item Mixed Mode CD +@cindex Mixed Mode CD + +A Mixed Mode is a CD that contains tracks of differing CD-ROM Mode +formats. In particular the first track may contain both computer data +(Yellow Book) CD ROM data while the remaining tracks are audio or +video data. Video CD's can be Mixed Mode CDs. + +@item Multisession +@cindex Multisession + +A way of writing to a CD that allows more data to be +added to readable discs at a later time. + +@item Nero NRG format file +@cindex Nero NRG, CD-Image format + +A proprietary CD image file format use by a popular program for +Microsoft Windows, Ahead Nero. The specification of this format is +not to our knowlege published. + +@item Rock Ridge Extensions +@cindex Rock Ridge extensions + +An extension to the ISO-9660 standard which adds POSIX information to files. + +@item SCSI +@cindex SCSI + +Small Computer System Interface. A set of ANSI standard electronic +interfaces (originally developed at Apple Computer) that allow +personal computers to communicate with peripheral hardware such as +CD-ROM drives, disk drives, printers, etc. + +@item SCSI CDB +@cindex SCSI CDB + +SCSI Command Descriptor Block. The data structure that is used to +issue a SCSI command. + +@item SCSI Pass Through Interface. +@cindex SCSI Pass Through Interface. + +Yet another way of issuing MMC commands for accessing a CD-ROM. As +with MMC or ASPI, the CD-ROM doesn't necessarily have to be a +SCSI-attached drive. See also @acronym{MMC} and @acronym{ASPI}. + +@item Session + +A fully readable complete recording that contains one or more tracks +of computer data or audio on a CD. + +@item SVCD +@cindex Super VCD (SVCD) + +Super @acronym{VCD} + +An improvement of Video CD 2.0 specification which includes most +notably a switch from @acronym{MPEG}-1 (constant bit rate encoding) to +@acronym{MPEG}-2 (variable bit rate encoding) for the video stream. + +Also added was higher video-stream resolution, up to 4 overlay +graphics and text (@dfn{OGT}) sub-channels for user switchable +subtitle displaying, closed caption text, and command lists for +controlling the @acronym{SVCD} virtual machine. + +See @uref{http://www.dvdrhelp.com/svcd} + +@item TOC +@cindex TOC (CD Table of Contents) + +(Compact Disc) Table of Contents. The TOC contains the starting track +number, last track number individual track starting time, and some +track flags (copy protection, linear audio preemphasis, track format: +CDDA or data). Every CD must have at least 1 TOC, the first TOC is +always recorded at the start of the CD (lead-in area). A +multi-session CD may have several TOCs. + +@item Track +@cindex track + +A unit of data of a CD. The size of a track can vary; it can occupy +the entire contents of the CD. Most CD standards however require that +tracks have a 150 frame (or ``2 second'') lead-in gap. + +@item VCD +@cindex Video CD (VCD) + +The Video Compact Disc (@dfn{Video CD} or @dfn{VCD}) is a standardized +digital video storage format. It is based on the commonly available +Compact Disc technology, which allows for low-cost video authoring. +Video CD's can be played in most @acronym{DVD} standalone player, +dedicated VCD players and finally, modern Personal Computers with +multimedia support. + +A Video CD is made up of @acronym{CD-ROM XA} sectors, +i.e. @acronym{CD-ROM} mode 2 form 1 & 2 sectors. Non-@acronym{MPEG} data +is stored in mode 2 form 1 sectors with a user data area of 2048 byte, +which have a similiar L2 error correction and detection +(@acronym{ECC}/@acronym{EDC}) to @acronym{CD-ROM} mode 1 sectors. While +realtime @acronym{MPEG} streams is stored in @acronym{CD-ROM} mode 2 +form 2 sectors, which by have no L2 @acronym{ECC}, yield a ~14% greater +user data area consisting of 2324 bytes@footnote{actually raw mode 2 +sectors have a 2336 byte user data area, but parts of it are used for +error codes and headers when using the mode 2 form 1 or form 2 +configurations.} + +@uref{http://www.dvdrhelp.com/vcd} + +@item Win32 ASPI +@cindex ASPI + +The ASPI interface specification was developed by Adaptec for +sending commands to a SCSI host adapter (such as those controlling CD +and DVD drives) and used on Window 9x/NT and later. Emulation for +ATAPI drives was added so that the same sets of commands worked those +even though the drives might not be SCSI nor might there even be a +SCSI controller attached. + +However in Windows NT/2K/XP, Microsoft provides their Win32 ioctl +interface, and has take steps to make using ASPI more inaccessible +(e.g. requiring adminstrative access to use ASPI). + +See also @acronym{MMC} + +@item Win32 ioctl driver + +Ioctl (Input Output ConTroLs). A Win32 function, implemented in all +Microsoft Windows. It is used for sending commands to devices using +defined codes and structures. + +@item XA +@cindex XA + +See @acronym{CD-ROM XA} + +@end table diff --git a/doc/libcdio.texi b/doc/libcdio.texi new file mode 100644 index 00000000..8f83414f --- /dev/null +++ b/doc/libcdio.texi @@ -0,0 +1,2421 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename libcdio.info +@include version.texi +@settitle GNU @code{libcdio}: Compact Disc Input and Control Library +@c %**end of header + +@c Karl Berry informs me that this will add straight quotes in +@c typewriter text. +@c See the "Inserting Quote Characters" node in the Texinfo manual +@set txicodequoteundirected +@set txicodequotebacktick + +@copying +This manual documents @code{libcdio}, the GNU CD Input and Control +Library. + +Copyright @copyright{} 2003, 2004, 2005, 2006, 2007, 2008 Rocky +Bernstein and Herbert Valerio Riedel. + +@quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.2 or +any later version published by the Free Software Foundation; with no +Invariant Sections, with no Front-Cover Texts, and with no Back-Cover +Texts. A copy of the license is included in the section entitled +``GNU Free Documentation License''. +@end quotation +@end copying + +@paragraphindent 0 +@exampleindent 0 + +@set libcdio @code{libcdio} +@set program @kbd{libcdio} + +@c A macro for defining terms variables. +@macro term{varname} +@c @cindex{\varname\} +@emph{\varname\} +@end macro + +@dircategory Software libraries +@direntry +* libcdio: (libcdio). GNU Compact Disc Input and Control Library. +@end direntry + +@titlepage +@title GNU @code{libcdio} +@subtitle GNU Compact Disc Input and Control Library +@subtitle for version @value{VERSION}, @value{UPDATED} +@author Rocky Bernstein et al. (@email{bug-libcdio@@gnu.org}) +@page +@vskip 0pt plus 1filll +@insertcopying +@end titlepage + +@contents + +@ifnottex +@node Top +@top GNU @value{libcdio} + +This manual documents @value{libcdio}, the GNU CD Input and Control +Library. + +@menu +* History:: How this came about +* Previous Work:: The problem and previous work +* Purpose:: What is in this package (and what's not) +* CD Formats:: A tour through the CD-specification spectrum +* CD Image Formats:: A tour through various CD-image formats +* CD Units:: The units that make up a CD +* How to use:: Okay enough babble, lemme at the library! +* Utility Programs:: Diagnostic programs that come with this library +* CD-ROM Access and Drivers:: CD-ROM access and drivers +* Internal Program Organization:: Looking under the hood + +Appendices +* ISO-9660 Character Sets:: +* Glossary:: +* GNU Free Documentation License:: + +Indices +* General Index:: Overall index +@end menu +@end ifnottex + +@node History +@chapter History + +As a result of the repressive Digital Millennium Copyright Act (DMCA) +I became aware of Video CD's (VCD's). Video CD's are not subject to +the DMCA and therefore enjoy the protection afforded by copyright but +no more. But in order for VCD's to be competitive with DVD's, good +tools (including GPL tools) are needed for authoring and playing +them. And so through VCD's I became aware of the excellent Video CD +tools by Herbert Valerio Riedel which form the @kbd{vcdimager} package. + +Although vcdimager is great for authoring, examining and extracting +parts of a Video CD, it is not a VCD player. And when I looked at the +state of Video CD handling in existing VCD players: @code{xine}, +@code{MPlayer}, and @code{vlc}, I was a bit disappointed. None handled +playback control, menu selections, or playing still frames and +segments from track 1. + +Version 0.7.12 of vcdimager was very impressive, however it lacked +exportable libraries that could be used in other projects. So with the +blessing and encouragement of Herbert Valerio Riedel, I took to +extract and create libraries from this code base. The result was two +libraries: one to extract information from a VCD which I called +libvcdinfo, and another to do the reading and control of a VCD. Well, +actually, at this point I should say that a Video CD is really just +Video put on a existing well-established Compact Disc or CD format. So +the library for this is called @value{libcdio} rather than +@kbd{libvcdio}. + +While on the topic of the name @value{libcdio}, I should also explain that +the library really doesn't handle writing or output (the final "o" in +the name). However it was felt that if I put @code{libcdi} that might be +confused with a particular CD format called CD-I. + +Later on, the ISO-9660 filesystem handling component from +@kbd{vcdimager} was extracted, expanded and made a separate +library. Next the ability to add MMC commands was added, and then +CD paranoia support. And from there, the rest is history. + +@node Previous Work +@chapter The problem and previous work + +If around the year 2002 you were to look at the code for a number of +free software CD or media players that work on several platforms such as +vlc, MPlayer, xine, or xmms to name but a few, you'd find the code to +read a CD sprinkled with conditional compilation for this or that +platform. That is there was @emph{no} OS-independent programmer +library for CD reading and control even though the technology was over +10 years old; yet there are media players which strive for OS +independence. + +One early CD player, @kbd{xmcd} by Ti Kan, was I think a bit better +than most in that it tried to @emph{encapsulate} the kinds of CD +control mechanisms (SCSI, Linux ioctl, Toshiba, etc.) in a "CD Audio +Device Interface Library" called @code{libdi}. However this library is for +Audio CD's only and I don't believe this library has been used outside +of xmcd. + +Another project, Simple DirectMedia Layer also encapsulates CD +reading. + +@quotation +SDL is a library that allows you portable low-level access to a video +framebuffer, audio output, mouse, and keyboard. With SDL, it is easy +to write portable games which run on ... +@end quotation + +Many of the media players mentioned above do in fact can make use of +the SDL library but for @emph{video} output only. Because the encapsulation +is over @emph{many} kinds of I/O (video, joysticks, mice, as well as CD's), +I believe that the level of control provided for CD a little bit +limited. (However to be fair, it may have only been intended for games +and may be suitable for that). Applications that just want the CD +reading and control portion I think will find quite a bit overhead. + +Another related project is J@"org Schilling's SCSI library. You can +use that to make a non-SCSI CD-ROM act like one that understands SCSI +MMC commands which is a neat thing to do. However it is a little weird +to have to install drivers just so you can run a particular user-level +program. Installing drivers often requires special privileges and +permissions and it is pervasive on a system. It is a little sad that +along the way to creating such a SCSI library a library similar to +@value{libcdio} wasn't created which could be used. Were that the +case, this library certainly never would have been written. + +At the OS level there is the ``A Linux CD-ROM Standard'' by David van +Leeuwen from around 1999. This defines a set of definitions and +ioctl's that mask hardware differences of various Compact Disc +hardware. It is a great idea, however this ``standard'' lacked +adoption on OS's other than GNU/Linux. Or maybe it's the case that the +standard on other OS's lacked adoption on GNU/Linux. For example on +FreeBSD there is a ``Common Access Method'' (CAM) used for all SCSI +access which seems not to be adopted in GNU/Linux.@footnote{And I'm +thankful for that since, at least for MMC commands, it is +inordinately complicated and in some places arcane.} + +Finally at the hardware level where a similar chaos exists, there has +been an attempt to do something similar with the MMC (multimedia +commands). This attempts to provide a uniform command set for CD +devices like PostScript does for printer commands.@footnote{I wrote +``attempts'' because over time the command set has changed and now +there are several different commands to do a particular function like +read a CD table of contents and some hardware understands some of the +version of the commands set but might not others} In contrast to +PostScript where there one in theory can write a PostScript program in +a uniform ASCII representation and send that to a printer, for MMC +although there are common internal structures defined, there is no +common syntax for representing the structures or an OS-independent +library or API for issuing MMC-commands which a programmer would need +to use. Instead each Operating System has its own interface. For +example Adaptec's ASPI or Microsoft's DeviceIoControl on Microsoft +Windows, or IOKit for Apple's OS/X, or FreeBSD's CAM. I've been +positively awed at how many different variations and differing levels +of complexity there are for doing basically the same thing. How easy +it is to issue an MMC command from a program varies from easy to very +difficult. And mastering the boilerplate code to issue an MMC command +on one OS really doesn't help much in figuring out how to do it on +another OS. So in @value{libcdio} we provide a common (and hopefully +simple) API to issue MMC commands. + +@node Purpose +@chapter What is in this package (and what's not) + +The library, @command{libcdio}, encapsulates CD-ROM reading and +control. Applications wishing to be oblivious of the OS- and +device-dependent properties of a CD-ROM can use this library. + +Also included is a library, @command{libiso9660}, for working with +ISO-9660 filesystems, @command{libcdio_paranoia}, and +@command{libcdio_cdda} libraries for applications which want to use +cdparanoia's error-correction and jitter detection. + +Some support for disk-image types like cdrdao's TOC, CDRWIN's BIN/CUE +and Ahead Nero's NRG format is available, so applications that use this +library also have the ability to read disc images as though they were +CDs. + +@command{libcdio} also provides a way to issue SCSI ``MultiMedia +Commands'' (MMC). MMC is supported by many hardware CD-ROM +manufacturers; and in some cases where a CD-ROM doesn't understand MMC +directly, some Operating Systems (such as GNU/Linux, Solaris, or +FreeBSD or Microsoft Windows ASPI to name a few) provide the MMC +emulation.@footnote{This concept of software emulation of a common +hardware command language is common for printers such as using +ghostscript to private postscript emulation for a non-postscript +printer.} + +The first use of the library in this package are the Video CD +authoring and ripping tools, VCDImager +(@url{http://vcdimager.org}). See +@url{http://www.gnu.org/software/libcdio/projects.html} for a list of +projects using @command{libcdio}. + +A version of the CD-DA extraction tool cdparanoia +(@url{http://www.xiph.org/paranoia} and its library which corrects +for CD-ROM jitter are part of the distribution. + +Also included in the libcdio package is a utility program +@command{cd-info} which displays CD information: number of tracks, +CD-format and if possible basic information about the format. If +libcddb (@url{http://libcddb.sourceforge.net}) is available, the +@command{cd-info} program will display CDDB matches on CD-DA +discs. And if a new enough version of libvcdinfo is available (from +the vcdimager project), then @command{cd-info} shows basic VCD +information. + +Other utility programs in the libcdio package are: + +@table @code + +@item @code{cdda-player} + +shows off @value{libcdio} audio and CD-ROM control commands. It can +play a track, eject or load media and show the the status of a CD-DA +that is might be currently played via the audio control commands. It +can be run in batch mode or has a simple curses-based interface. + +If libcddb is available or a CD has CD-Text and your CD-ROM drive +supports CD-Text, track/album information about the CD can be shown. + +@item @code{cd-drive} + +shows what drivers are available and some basic properties of +cd-drives attached to the system. (But media may have to be inserted +in order to get this info.) lists out drive capabilities + +@item cd-read +performs low-level block reading of a CD or CD image, + +@item @code{iso-info} + +displays ISO-9660 information from an ISO-9660 image. Below is some +sample output + +@smallexample +iso-info version 0.72 +Copyright (c) 2003, 2004, 2005 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. +__________________________________ +ISO 9660 image: ../test/joliet.iso +Application: K3B THE CD KREATOR VERSION 0.11.12 (C) 2003 SEBASTIAN TRUEG AND THE K3B TEAM +Preparer : K3b - Version 0.11.12 +Publisher : Rocky Bernstein +System : LINUX +Volume : K3b data project +Volume Set : K3b data project +__________________________________ +ISO-9660 Information +/: + Oct 22 2004 19:44 . + Oct 22 2004 19:44 .. + Oct 22 2004 19:44 libcdio + +/libcdio/: + Oct 22 2004 19:44 . + Oct 22 2004 19:44 .. + Mar 12 2004 02:18 COPYING + Jun 26 2004 07:01 README + Aug 12 2004 06:22 README.libcdio + Oct 22 2004 19:44 test + +/libcdio/test/: + Oct 22 2004 19:44 . + Oct 22 2004 19:44 .. + Jul 25 2004 06:52 isofs-m1.cue +@end smallexample + +@item @code{iso-read} + +extracts files from an ISO-9660 image. + +@end table + +At present, there is no support for writing CD's. Nor is there any +support for reading or writing DVDs. For some of these, there are +other libraries (e.g. @code{libdi}, @code{libscg}, or @code{libdvdread}) may be helpful. + +I'm not theoretically opposed to putting support like this into +libcdio. However at present there are already many gaps in this +package, so narrowing its scope in order to focus on these things I +think is a good idea. + +@node CD Formats +@chapter CD Formats + +Much of what I write in this section can be found elsewhere. See for +example @url{http://www.pctechguide.com/08cd-rom.htm} or +@url{http://www.pcguide.com/ref/cd/format.htm} + +We give just enough background here to cover Compact Discs and Compact +Disc formats that are handled by this library. + +The Sony and Philips Corporations invented and Compact Disc (CD) in +the early 1980s. The specifications for the layout is often referred +to by the color of the cover on the specification. + +@menu +* Red Book:: Red Book (CD-DA) CD Text, CDDB +* Yellow Book:: Yellow Book (CD-ROM Digital Data) +* Green Book:: Green Book (CD-i) +* White Book:: White Book (DV, Video CD) +@end menu + +@node Red Book +@section Red Book (CD-DA) +@cindex Red Book + +@menu +* CD Text:: CD Text and CD+G +* CDDB:: Internet CD Database (CDDB) +@end menu + +The first type of CD that was produced was the Compact Disc Digital +Audio (CD-DA) or just plain ``audio CD''. The specification, ICE 60908 +(formerly IEC 908) is commonly called the ``Red Book'', +@cite{@url{http://en.wikipedia.org/wiki/Red_Book_(audio_CD_standard)}}. Music +CD's are recorded in this format which basically allows for around 74 +minutes of audio per disc and for that information to be split up into +tracks. Tracks are broken up into "sectors" and each sector contains +up to 2,352 bytes. To play one 44.1 kHz CD-DA sampled audio second, 75 +sectors are used. + +The minute/second/frame numbering of sectors or MSF format is based on +the fact that 75 sectors are used in a second of playing of +sound. (And for almost every other CD format and application the MSF +format doesn't make that much sense). + +In @value{libcdio} when you you want to read an audio sector, you call +@code{cdio_read_audio_sector()} or @code{cdio_read_audio_sectors()}. + +@cindex subchannel +In addition the the audio data ``channel'' a provision for other +information or @term{subchannel} information) can be stored in a +sector. Other subchannels include a Media Catalog Number (also +abbreviated as MCN and sometimes a UPC), or album meta data (also +called CD-Text). Karioke graphics can also be stored in a format +called @term{CD+G}. + +@node CD Text +@subsection CD Text, CD+G +@cindex CD Text +@cindex CD+G + +CD Text is an extension to the CD-DA standard that adds the ability to +album and track meta data (titles, artist/performer names, song +titles) and and graphical (e.g. Karaoke) information. For an +alternative way to get album and track meta-data see @xref{CDDB}. + +Information is stored in such a way that it doesn't interfere with the +normal operation of any CD players or CDROM drives. There are two +different parts of the CD where the data can be stored. + +The first place the information can be recorded is in the R-W sub +codes in the lead in area of the CD giving a data capacity of about +5,000 ASCII characters (or 2,500 Kanji or Unicode characters). This +information is stored as a single block of data and is the format used +in virtually all of the CD Text CDs shipping today. The method for +reading this data from a CDROM drive is covered under the Sony +proposal to the MMC specification. The format of the data is partially +covered in the MMC specification. + +The second place the information can be recorded is in the R-W sub +codes in the program area of the CD giving a data capacity of roughly +31MB. This information is stored in a format that follows the +Interactive Text Transmission System (ITTS) which is the same data +transmission standard used by such things as Digital Audio +Broadcasting (DAB), and virtually the same as the data standard for +the MiniDisc. Traditionally the R-W sub codes have been used for text +and graphics in applications such as CD+G (CD w/graphics) or in the +case of most audio CDs, not at all. The methods for reading this data +from a CD-ROM drive is covered by the programming specs from the +individual drive manufacturers. In the case of ATAPI drives, the +SFF8020 spec covers the reading of the RW subcodes. + +Not all drives support reading the RW subcodes from the program +area. However for those that do, @value{libcdio} provides a way to get +at this information via @code{cdtext_get()} and its friends. + +@node CDDB +@subsection Internet CD Database (CDDB) +@cindex CDDB + +CDDB is an database on the Internet of of CD album/track, artist, and +genre information similar to CD Text information. Using track +information (number of tracks and length of the tracks), devices that +have access to the Internet can query for meta information and +contribute information for CD's where there is no existing +information. When storage is available (such as you'd expect for any +program using @value{libcdio}, the information is often saved for +later use when the Internet is not available; people tend request the +same information since they via programs play the same music. + +Obtaining CD meta information when none is encoded in an audio CD is +useful in media players or making ones own compilations from audio +CDs. + +There are currently two popular CDDB services on the Internet. The +original database has been renamed Gracenote and is a profit making +entity. FreeDB (@url{http://freedb.org} is an open source CD +information resource that is free for developers and the public to +use. + +As there already is an excellent library for handling CDDB libcddb +(@url{http://libcddb.sourceforge.net} we suggest using that. Our +utility program @command{cd-info} will make use it if it is available +and it's what we use in our applications that need it. + +@node Yellow Book +@section Yellow Book (CD-ROM Digital Data) +The CD-ROM specification or the ``Yellow Book'' followed a few years +later (Standards ISO/IEC 10149), and describes the extension of CD's +to store computer data, i.e. CD-ROM (Compact Disk Read Only Memory). + +The specification in the Yellow Book defines two modes: Mode 1 and +Mode 2. + +@menu +* ISO 9660:: +* Mode 1:: Mode 1 Format +* Mode 2:: Mode 2 Format +@end menu + +@node ISO 9660 +@subsection ISO 9660 +@cindex ISO 9660 + +@menu +* ISO 9660 Level 1:: +* ISO 9660 Level 2:: +* ISO 9660 Level 3:: +* Joliet Extensions:: +* Rock Ridge Extensions:: +@end menu + +The Yellow Book doesn't specify how data is to be stored on a CD-ROM. +It was feared that different companies would implement proprietary +data storage formats using this specification, resulting in +incompatible data CDs. To prevent this, representatives of major +manufacturers met at the High Sierra Hotel and Casino in Lake Tahoe, +NV, in 1985, to define a standard for storing data on CDs. This format +was nicknamed High Sierra Format. In a slightly modified form it was +later adopted as ISO the ISO 9660 standard. This standard is further +broken down into 3 "levels", the higher the level, the more +permissive. + +@node ISO 9660 Level 1 +@subsubsection ISO 9660 Level 1 +Level 1 ISO 9660 defines names in the 8+3 convention so familiar to +MS-DOS: eight characters for the filename, a period, and then three +characters for the file type, all in upper case. The allowed +characters are A-Z, 0-9, ".", and "_".Level 1 ISO 9660 requires that +files occupy a contiguous range of sectors. This allows a file to be +specified with a start block and a count. The maximum directory depth +is 8. For a table of the characters, see @xref{ISO-9660 Character +Sets}. + +@node ISO 9660 Level 2 +@subsubsection ISO 9660 Level 2 +Level 2 ISO 9660 allows far more flexibility in filenames, but isn't +usable on some systems, notably MS-DOS. + +@node ISO 9660 Level 3 +@subsubsection ISO 9660 Level 3 +Level 3 ISO-9660 allows non-contiguous files, useful if the file was +written in multiple packets with packet-writing software. + +There have been a number of extensions to the ISO 9660 CD-ROM file +format. One extension is Microsoft's Joliet specification, designed to +resolve a number of deficiencies in the original ISO 9660 Level 1 file +system, and in particular to support the long file names used in +Windows 95 and subsequent versions of Windows. + +Another extension is the Rock Ridge Interchange Protocol (RRIP), which +enables the recording of sufficient information to support POSIX File +System semantics. + +@node Joliet Extensions +@subsubsection Joliet Extensions +@cindex Joliet extensions + +Joliet extensions were an upward-compatible extension to the ISO 9660 +specification that removes the limitation initially put in to deal +with the limited filename conventions found in Microsoft DOS OS. In +particular, the Joliet specification allows for long filenames and +allows for UCS-BE (BigEndian Unicode) encoding of filenames which +include mixed case letter, accented characters spaces and various +symbols. + +The way all of this is encoded is by adding a second directory and +filesystem structure in addition to or in parallels to original ISO +9600 filesystem. The root node of the ISO 9660 filesystem is found via +the @term{Primary Volume Descriptor} or @term{PVD}. The root of the +Joliet-encode filesystem is found in a Supplementary Volume +Descriptor or @term{SVD} defined in the ISO 9660 specification. The +SVD structure is almost identical to a PVD with a couple of unused +fields getting used and with the filename encoding changed to UCS-BE. + +@node Rock Ridge Extensions +@subsubsection Rock Ridge Extensions +@cindex Rock Ridge extensions + +Using the Joliet Extension one overcome the limitedness of the +original ISO-9660 naming scheme. But another and probably better +method is to use the Rock Ridge Extension. Not only can one store a +filename as one does in a POSIX OS, but the other file attributes, +such as the various timestamps (creation, modification, access), file +attributes (user, group, file mode permissions, device type, symbolic +links) can be stored. This is much as one would do in XA attributes; +however the two are not completely interchangeable in the information +they store: XA does @emph{not} address filename limitations, and the +Rock Ridge extensions don't indicate if a sector is in Mode 1 or Mode +2 format. + +The Rock Ridge extension makes use of a hook that was defined as part +of the ISO 9660 standard. + +@node Mode 1 +@subsection Mode 1 (2048 data bytes per sector) +@cindex Mode 1 +Mode 1 is the data storage mode used by to store computer data. There +are 3 layers of error correction. A Compact Disc using only this format can +hold at most 650 MB. The data is laid out in basically the same way as +in and audio CD format, except that the 2,352 bytes of data in each +block are broken down further. 2,048 of these bytes are for ``real'' +data. The other 304 bytes are used for an additional level of error +detecting and correcting code. This is necessary because data CDs +cannot tolerate the loss of a handful of bits now and then, the way +audio CDs can. + +In @value{libcdio} when you you want to read a mode1 +sector you call the @code{cdio_read_mode1_sector()} or +@code{cdio_read_mode1_sectors()}. + +@node Mode 2 +@subsection Mode 2 (2336 data bytes per sector) +@cindex Mode 2 +Mode 2 data CDs are the same as mode 1 CDs except that the error +detecting and correcting codes are omitted. So still there are 2 +layers of error correction. A Compact Disc using only this mode can +thus hold at most 742 MB. Similar to audio CDs, the mode 2 format +provides a more flexible vehicle for storing types of data that do not +require high data integrity: for example, graphics and video can use +this format. But in contrast to the Red Book standard, different modes +can be mixed together; this is the basis for the extensions to the +original data CD standards known as CD-ROM Extended Architecture, or +CD-ROM XA. CD-ROM XA formats currently in use are CD-I Bridge +formats, Photo CD and Video CD plus Sony's Playstation. + +In @value{libcdio} when you you want to read a mode1 +sector you call the @code{cdio_read_mode2_sector()} or +@code{cdio_read_mode2_sectors()}. + +@node Green Book +@section Green Book (CD-i) +@cindex Green Book + +This was a CD-ROM format developed by Philips for CD-i (an obsolete +embedded CD-ROM application allowing limited user user interaction +with films, games and educational applications). The format is ISO +9660 compliant and introduced mode 2 form 2 addressing. It also +contains XA (Extended Architecture) attributes. + +Although some Green Book discs contain CD-i applications which can +only be played on a CD-i player, others have films or music +videos. Video CDs in Green-Book format are labeled "Digital Video on +CD." The Green Book for video is largely superseded by White book +CD-ROM which draws on this specification. + + +@node White Book +@section White Book (DV, Video CD) +@cindex Green Book + +The White Book was released by Sony, Philips, Matsushita, and JVC in +1993, defines the Video CD specification. The White Book is also known +as Digital Video (DV). + +A Video CD contains one data track recorded in CD-ROM XA Mode 2 Form +2. It is always the first track on the disc (Track 1). The ISO-9660 +file structure and a CD-i application program are recorded in this +track, as well as the Video CD Information Area which gives general +information about the Video Compact Disc. After the data track, video +is written in one or more subsequent tracks within the same +session. These tracks are also recorded in Mode 2 Form 2. + +In @value{libcdio} when you you want to read a mode2 format 2 audio +sector you call the @code{cdio_read_mode2_sector()} or +@code{cdio_read_mode2_sectors()} setting @code{b_form2} to @code{true}. + +@node CD Image Formats +@chapter CD Image Formats + +@menu +* CDRDAO TOC Format:: +* CDRWIN BIN/CUE Format:: +* NRG Format:: +@end menu + +In both the @command{cdrdao} and bin/cue formats there is one meta-file with +extensions @code{.toc} or @code{.cue} respectively and one or more +files (often with the extension @code{.bin}) which contains the +content of tracks. The format of the track data is often +interchangeable between the two formats. For example, in +@value{libcdio}'s regression tests we make use of this to reduce the +size of the test data and just provide alternate meta-data files +(@code{.toc} or @code{.cue}). + +In contrast to the first two formats, the NRG format consists of a +single file. This has the advantage of being a self-contained +unit: in the other two formats it is possible for the meta file to +refer to a file that can't be found. A disadvantage of the NRG format +is that the meta data can't be easily viewed or modified say in a text +file as it can be with the first two formats. In conjunction with this +disadvantage is another disadvantage that the format is not +documented, so how @value{libcdio} interprets an NRG image is based on +inference. It is recommended that one of the other forms be used +instead of NRG where possible. + +@node CDRDAO TOC Format +@section CDRDAO TOC Format + +This is @command{cdrdao}'s CD-image description format. Since this +program is GPL and everything about it is in the open, it is the +preferred format to use. (Alas, at present it isn't as well supported +in @value{libcdio} as the BIN/CUE format.) + +The @emph{toc}-file describes what data is written to the media in the +@acronym{CD-ROM}; it allows control over track/index positions, +pre-gaps and sub-channel information. It is a text file, so a text +editor can be used to create, view or modify it. + +The @cite{cdrdao(1) manual page}, contains more information about this +format. + +@subsection CDRDAO Grammar + +Below are the lexical tokens and grammar for a cdrdao TOC. It was +taken from the cdrdao's pacct grammar; the token and nonterminal names +are the same. + +@example +#lexclass START +#token Eof "@@" +#token "[\t\r\ ]+" +#token Comment "//~[\n@@]*" +#token "\n" +#token BeginString "\"" +#token Integer "[0-9]+" +#tokclass AudioFile @{ "AUDIOFILE" "FILE" @} + +#lexclass STRING +#token EndString "\"" +#token StringQuote "\\\"" +#token StringOctal "\\[0-9][0-9][0-9]" +#token String "\\" +#token String "[ ]+" +#token String "~[\\\n\"\t ]*" +@end example + +@example + + ::= ( "CATALOG" | )* @{ @} + ( )+ Eof + + ::= "TRACK" + @{ @} + ( "ISRC" | @{ "NO" @} "COPY" | @{ "NO" @} "PRE_EMPHASIS" + | "TWO_CHANNEL_AUDIO" | "FOUR_CHANNEL_AUDIO" )* + @{ @} + @{ "PREGAP" @} + ( | "START" @{ msf @} | "END" @{ msf @} )+ + ( "INDEX" )* + + ::= + AudioFile @{ "SWAP" @} @{ "#" @} + | "DATAFILE" @{ "#" @{ @} @} + | "FIFO" + | "SILENCE" + | "ZERO" @{ dataMode @} @{ @} + + + ::= BeginString ( String | StringQuote | StringOctal )+ + EndString + + ::= BeginString ( String | StringQuote | StringOctal )* + EndString + + ::= Integer + + ::= Integer + + ::= Integer ":" Integer ":" Integer + + ::= | + + ::= | + + ::= "AUDIO" | "MODE0" | "MODE1" | "MODE1_RAW" | "MODE2" + | "MODE2_RAW" | "MODE2_FORM1" | "MODE2_FORM2" | "MODE2_FORM_MIX" + + ::= "AUDIO" | "MODE1" | "MODE1_RAW" | "MODE2" + | "MODE2_RAW" | "MODE2_FORM1" | "MODE2_FORM2" | "MODE2_FORM_MIX" + + ::= "RW" | "RW_RAW" + + ::= "CD_DA" | "CD_ROM" | "CD_ROM_XA" | "CD_I" + + ::= "TITLE" | "PERFORMER" | "SONGWRITER" | "COMPOSER" | "ARRANGER" + | "MESSAGE" | "DISC_ID" | "GENRE" | "TOC_INFO1" | "TOC_INFO2" + | "RESERVED1" | "RESERVED2" | "RESERVED3" | "RESERVED4" | "UPC_EAN" | + "ISRC" | "SIZE_INFO" + + ::= "@{" + @{ Integer ( "," Integer )* @} + "@}" + + ::= ( | ) + + ::= "LANGUAGE" Integer "@{" ( )* "@}" + + ::= + "LANGUAGE_MAP" "@{" + ( Integer ":" ( Integer | "EN" ) )+ + "@}" + + ::= "CD_TEXT" "@{" ( )* "@}" + + ::= "CD_TEXT" "@{" @{ @} ( )* "@}" +@end example + + + +@node CDRWIN BIN/CUE Format +@section CDRWIN BIN/CUE Format +@cindex BIN/CUE, CD Image Format + +The format referred to as @emph{CDRWIN BIN/CUE Format} in this manual +is a popular CD image format used in the @acronym{PC} world. Not +unlike @command{cdrdao}'s TOC file, the @emph{cue} file describes the +track layout, i.e. how the sectors are to be placed on the CD +media. The @emph{cue} file usually contains a reference to a file +traditionally having the @file{.bin} extension in its filename, the +@emph{bin} file. This @emph{bin} file contains the sector data payload +which is to be written to the CD medium according to the description +in the @emph{cue} file. + +The following is an attempt to describe the subset of the @file{.cue} +file syntax used in @value{libcdio} and vcdimager in an EBNF-like +notation: + +@subsection BIN/CUE Grammar + +@example +@cartouche + ::= +( + ) + + ::= "0" | "1" ... "8" | "9" + ::= + + ::= ":" ":" + + ::= "FILE" + + ::= [ "\"" ] [ "\"" ] + | "\"" "\"" + + ::= "BINARY" + + ::= [ ] + [ ] * [ ] + + ::= "FLAGS" * + ::= "DCP" + + ::= "TRACK" + + ::= "PREGAP" + + ::= "INDEX" + + ::= "POSTGAP" + + ::= "AUDIO" | "MODE1/2048" | "MODE1/2352" + | "MODE2/2336" | "MODE2/2352" + + ::= "REM" * +@end cartouche +@end example + +@node NRG Format +@section NRG Format +@cindex Nero NRG, CD-Image format + +The format referred to as @emph{NRG Format} in this manual is another +popular CD image format. It is available only on Nero software +on a Microsoft Windows Operating System. It is proprietary and not +generally published, so the information we have comes from guessing +based on sample CD images. So support for this is incomplete and using +this format is not recommended. + +Unlike @command{cdrdao}'s TOC file the BIN/CUE format everything is +contained in one file. that one can edit Meta information such as the +number of tracks and track format is contained at the end of the +file. This information is not intended to be edited through a text +editor. + +@node CD Units +@chapter The units that make up a CD + +@menu +* Tracks:: Tracks +* Sectors:: Block addressing (MSF, LSN, LBA) +* Pre-gaps:: Track pre-gaps +@end menu + +@node Tracks +@section tracks --- disc subdivisions +@cindex track +@cindex gaps + +In this section we describe CD properties and terms that we make use +of in @value{libcdio}. + +A CD is formatted into a number of @term{tracks}, and a CD can hold at +most 99 such tracks. This is defined by @code{CDIO_CD_MAX_TRACKS} in +@file{cdio/sector.h}. Between some tracks CD specifications require a +``2 second'' in gap (called a @term{lead-in gap}. This is unused space +with no ``data'' similar to the space between tracks on an old +phonograph. The word ``second'' here really refers to a measure of +space and not really necessarily an amount of time. However in the +special case that the CD encodes an audio CD or CD-DA, the amount of +time to play a gap of this size will take 2 seconds. + +@cindex lead out +The beginning (or inner edge) of the CD is supposed to have a ``2 +second'' lead-in gap and there is supposed to be another ``2 second'' +@term{lead-out} gap at the end (or outer edge) of the CD. + +People have discovered that they can put useful data in the @term{lead-in} +and @term{lead-out} gaps, and their equipment can read this, violating +the standards but allowing a CD to store more data. + +In order to determine the number of tracks on a CD and where they +start, commands are used to get this table-of-contents or @term{TOC} +information. Asking about the start of the @term{lead-out track} +gives the amount of data stored on the Compact Disk. To make it easy +to specify this leadout track, special constant 0xAA (decimal 170) is +used to indicate it. This is safe since this is higher than the +largest legal track position. In @value{libcdio}, +@code{CDIO_CDROM_LEADOUT_TRACK} is defined to be this special value. + +@node Sectors +@section block addressing (MSF, LSN, LBA) +@cindex MSF +@cindex LSN +@cindex LBA +@cindex sectors +@cindex frames + +A track is broken up into a number of 2352-byte @emph{blocks} which we +sometimes call @emph{sectors} or @emph{frames}. Whereas tracks may +have a gap between them, a block or sector does not. (In +@value{libcdio} the block size constant is defined using +@code{CDIO_CD_FRAMESIZE_RAW}). + +A Compact Disc has a limit on the number of blocks or sectors. This +values is defined by constant @code{CDIO_CD_MAX_LSN} in +@file{cdio/sector.h}. + +One can addressing a block in one of three formats. The oldest format +is by it's minute/second/frame number, also referred to as @term{MSF} +and written in time-like format MM:SS:FF (e.g. 30:01:40). It is best +suited in audio (Red Book) applications. In @value{libcdio}, the type +@code{msf_t} can be used to declare variables to hold such +values. Minute, second and frame values are one byte @emph{and stored +BCD notation}.@footnote{Perhaps this is a @value{libcdio} design +flaw. It was originally done I guess because it was convenient for +VCDs.} There are @value{libcdio} conversion routines +@code{cdio_from_bcd8()} and @code{cdio_to_bcd8()} to convert the +minute, second, and frame values into or out of integers. If you want +to print a field in a BCD-encoded MSF, one can use the format +specifier @code{%x} @emph{(not @code{%d})} and things will come out +right. + +In the MSF notation, there are 75 ``frames'' in a ``second,'' and the +familiar (if awkward) 60 seconds in a minute. @emph{Frame} here is +what we called a @emph{block} above. The CD specification defines +``frame'' to be @emph{another} unit which makes up a block. Very +confusing. A frame is also sometimes called a sector, analogous to +hard-disk terminology. + +Even more confusing is using this time-like notation for an address or +for a length. Too often people confuse the MSF notation this with an +amount of time. A ``second'' (or @code{CDIO_CD_FRAMES_PER_SEC} blocks) +in this notation is only a second of playing time for something +encoded as CD-DA. It does @emph{not} necessarily represent the amount +time that it will take to play a of Video CD---usually you need more +blocks than this. Nor does it represent the amount of data used to +play a second of an MP3---usually you need fewer blocks than this. It +is also not the amount of time your CD-ROM will take to read a +``second'' of data off a Compact Disc: for example a 12x CD player +will read 12x @code{CDIO_CD_FRAMES_PER_SEC} +@code{CDIO_CD_FRAMSIZE_RAW}-byte blocks in a one second of time. + +When programming, unless one is working with a CD-DA (and even here, +only in a time-like fashion), is generally more cumbersome to use an +MSF rather than a LBA or LSN described below, since subtraction of two +MSF's has the awkwardness akin to subtraction using Roman Numerals. + +Probably the simplest way to address a block is to use its @term{LSN} +or ``logical sector number.'' This just numbers the blocks usually +from 0 on. @emph{fix me: LSNs can be negative up to the pregap size?} +The Lead-in and Lead-out gaps described above have LSNs just like any +other space on a CD. The last unit of address is a @term{LBA}. It is +the same as a LSN but the 150 blocks associated with the initial +lead-in is are not counted. So to convert a LBA into an LSN you just +add 150. Why the distinction between LBA and LSN? I don't know, +perhaps this has something to do with ``multisession'' CDs. + +@node Pre-gaps +@section track pre-gaps -- @acronym{CD-DA} discs and gaps +@cindex CD-DA +@cindex gaps +@cindex lead in +@cindex lead out +@cindex pre-gap +@cindex Q sub-channel + +Gaps are possibly one of the least understood topics in audio discs. +In the case of @acronym{CD-DA} discs, standards require a silent 2 +second gap before the first audio track and after the last audio track +(in each session.) These are respectively referred to as +@term{lead-in} and @term{lead-out} gaps. No other gaps are required. +It is important not to confuse the required @term{lead-in} and +@term{lead-out} gaps with the optional track @term{pre-gap}s. Track +@term{pre-gap}s are the gaps that may occur between audio tracks. +Typically, track @term{pre-gap}s are filled with silence so that the +listener knows that one song has ended, and the next will soon begin. +However, track @term{pre-gap}s do not have to contain silence. One +exception is an audio disc of a live performance. Because the +performer may seamlessly move from one piece of the performance to the +next, it would be unnatural for the disc to contain silence between +the two pieces. Instead, the track number updates with no +interruption in the performance. This allows the listener to either +hear the entire performance without unnatural interruptions, or to +conveniently skip to certain pieces of the performance. Finally, some +@acronym{CD-DA} discs--whose behavior will be described below--lack +track @term{pre-gap}s altogether although they must still include the +@term{lead-in} and @term{lead-out} gaps. + +In order to understand the track @term{pre-gap}s that occur between +audio tracks, it is necessary to understand how CD players display the +track number and time. Embedded in each block of audio data is +non-audio information known as the @term{Q sub-channel}. The +@term{Q sub-channel} data tells the CD player what track number and time +it should display while it is playing the block of audio data in which +the @term{Q sub-channel} data is embedded. Near the end of some +tracks, the @term{Q sub-channel} may instruct the CD player to update +the track number to the next track, and display a count down to the +next track, often starting at -2 seconds and proceeding to zero. This +is known as an audio track @term{pre-gap}. It may either contain +silence, or as previously discussed--in the case of live +performances--it may contain audio. Almost as often as not, there is +no @term{pre-gap} whatsoever. Regardless, an audio track +@term{pre-gap} is purely determined by the contents of the +@term{Q sub-channel}, which is embedded in each audio sector. This has +some interesting implications for the track forward button. + +When the track forward button is pressed on a CD player, the CD player +advances to the next track, skipping that track's @term{pre-gap}. +This is because the CD player uses the starting address of the track +from the disc's table of contents (TOC) to determine where to start +playing a track when either the track forward or track backward +buttons are pressed. So to hear a @term{pre-gap} for track 4, the +listener must either listen to track 3 first, or use the track forward +or backward buttons to go to track 4, then use the seek backward +button to back up into track 4's @term{pre-gap}, which is really part +of track 3, at least according to the TOC. Track 1 @term{pre-gap}s +are especially interesting because some commercial discs have audio +hidden before the beginning of the first track! The only way to hear +this hidden audio with a standard player is to use the seek backward +button as soon as track 1 begins playing! + +Audio track @term{pre-gap}s may be specified in a couple of different +ways in the popular cue file format. The first way of specifying a +@term{pre-gap} is to use the @command{PREGAP} command. This will +place a @term{pre-gap} containing silence before a track. The second +way of specifying a @term{pre-gap} is to give a track an +@command{INDEX 00} as well as the more normal @command{INDEX 01}. +@command{INDEX 01} will be used to specify the start of the track in +the disc's TOC, while @command{INDEX 00} will be used to specify the +start of the track's @term{pre-gap} as recorded in the @term{Q sub-channel}. +@command{INDEX 00} is ordinarily used for specifying +track @term{pre-gap}s that contain audio rather than silence. Thus, +the cue file format may be used to specify track @term{pre-gap}s with +silence or audio, depending on whether the @command{PREGAP} or +@command{INDEX 00} commands are specified. If neither type of +@term{pre-gap} is specified for a track, no @term{pre-gap} is created +for that track, which merely means the absence of @term{pre-gap} +information in the @term{Q sub-channel}, and the lack of a short count +down to the next track. + +Various @acronym{CD-DA} ripping programs take various approaches to +track @term{pre-gap}s. Some ripping programs ignore track +@term{pre-gap}s altogether, relying solely on the disc's TOC to +determine where tracks begin and end. If a disc is ripped with such a +program, then re-burned later, the resulting disc will lack track +@term{pre-gap}s, and thereby lack the playback behavior of counting +down to the next track. Other ripping programs detect track +@term{pre-gap}s and record them in the popular cue file format among +others. Such ripping programs sometimes allow the user to determine +whether track @term{pre-gap}s will be appended to the prior track or +pre-pended to the track to which they "belong". Note that if a +ripping program is ignorant of track @term{pre-gap}s, the track +@term{pre-gap}s will be appended to the prior track, because that is +where the disc's TOC puts them. Thus, there are many different ways +an application may chose to deal with track @term{pre-gap}s. +Consequently, @kbd{libcdio} does not dictate the policy a ripping +program should use in dealing with track @term{pre-gap}s. Hence, +@kbd{libcdio} provides the @code{cdio_get_track_pregap_[lba|lsn]()} +interfaces to allow the application to deal with track @term{pre-gap}s +as it sees fit. + +Note that the @code{cdio_get_track_pregap_[lba|lsn]()} interfaces +currently only provide information for CDRDAO TOC, CDRWIN BIN/CUE, and +NRG images. Getting the track @term{pre-gap}s from a CD drive is a +more complicated problem because not all CD drives support reading the +@term{Q sub-channel} @emph{directly} at @emph{high} speed, and there is no +interface to determine whether or not a drive supports this optional +feature, aside from trying to read the @term{Q sub-channel}, and +possibly incurring IO errors. However, all drives @emph{do} support reading +the @term{Q sub-channel} @emph{indirectly} while playing an audio disc by +asking the drive for the current position. Unfortunately, this occurs +at normal playback speed, and requires a certain settling time after +the disc starts playing. Thus, using this @emph{slow} interface +requires a more sophisticated algorithm, such as binary search or some +heuristic, like backing up progressively from the end of the prior +track to look for the next track's @term{pre-gap}. Note that CD +drives seek @emph{slow}ly, so it is better to simply use a drive that +can read the @term{Q sub-channel} directly at @emph{high} speed, and +avoid complicated software solutions. (Not to mention that if the +user has an older system with an analog audio cable hooked up between +their soundboard and their drive, and a ripping program uses the +@emph{slow} interface, the user will hear bits of the audio on the +disc!) Consequently, because there is no good universal solution to +the problem of reading the @term{Q sub-channel} from a drive, +@kbd{libcdio} currently leaves this problem up to the application, a +problem which is readily approachable through either @kbd{libcdio}'s +MMC interface or @kbd{libcdio}'s cdda interface. For an example of +one such application, see @url{https://gna.org/projects/cued/}. + +The preceding section on track @term{pre-gaps} and @acronym{CD-DA} was +contributed by Robert William Fuller (@email{hydrologiccycle@@gmail.com}). + +@node How to use +@chapter How to use + +The @value{libcdio} package comes with a number of small example +programs in the directory @file{example} which demonstrate different +aspects of the library and show how to use the library. The source +code to all of the examples here are contained on the package. + +Other sources for examples would be the larger utility programs +@command{cd-drive}, @command{cd-info}, @command{cd-read}, +@command{iso-info}, and @command{iso-read} which are all in the +@file{src} directory of the @value{libcdio} package. See also +@xref{Utility Programs}. + +@menu +* Example 1:: list out tracks and LSNs +* Example 2:: list drivers available and default CD device +* Example 3:: figure out what kind of CD (image) we've got +* Example 4:: use libiso9660 to extract a file from an ISO-9660 image +* Example 5:: list CD-Text and CD disc mode info +* Example 6:: run a MMC INQUIRY command +* Example 7:: using the CD Paranoia library for CD-DA reading +* All sample programs:: list of all programs in the example directory +@end menu + +@node Example 1 +@section Example 1: list out tracks and LSNs +Here we will give an annotated example which can be found in the +distribution as @file{example/tracks.c}. + +@smallexample + 1: #include + 2: #include + 3: #include + 4: int + 5: main(int argc, const char *argv[]) + 6: @{ + 7: CdIo_t *p_cdio = cdio_open ("/dev/cdrom", DRIVER_DEVICE); + 8: track_t first_track_num = cdio_get_first_track_num(p_cdio); + 9: track_t i_tracks = cdio_get_num_tracks(p_cdio); +10: int j, i=first_track_num; +11: +12: printf("CD-ROM Track List (%i - %i)\n", first_track_num, i_tracks); +13 +14: printf(" #: LSN\n"); +15: +16: for (j = 0; j < i_tracks; i++, j++) @{ +17: lsn_t lsn = cdio_get_track_lsn(p_cdio, i); +18: if (CDIO_INVALID_LSN != lsn) +19: printf("%3d: %06d\n", (int) i, lsn); +20: @} +21: printf("%3X: %06d leadout\n", CDIO_CDROM_LEADOUT_TRACK, +22: cdio_get_track_lsn(p_cdio, CDIO_CDROM_LEADOUT_TRACK)); +23: cdio_destroy(p_cdio); +24: return 0; +25: @} +@end smallexample + +Already from the beginning on line 2 we see something odd. The +@code{#include } is needed because @value{libcdio} +assumes type definitions exist for @code{uint32_t}, @code{uint16_t} +and so on. Alternatively you change line 2 to: + +@smallexample +#define HAVE_SYS_TYPES_H +@end smallexample + +and @code{} will insert line 2. If you use GNU autoconf +to configure your program, add @code{sys/types.h} to +@code{AC_HAVE_HEADERS} and @emph{it} will arrange for +@code{HAVE_SYS_TYPES_H} to get defined. If you don't have +@code{} but have some other include that defines these +types, put that instead of line 2. Or you could roll your own +typedefs. (Note: In the future, this will probably get ``fixed'' by +requiring glib.h.) + +Okay after getting over the hurdle of line 2, the next line pretty +straightforward: you need to include this to get cdio definitions. One +of the types that is defined via line 3 is @code{CdIo_t} and a pointer +that is used pretty much in all operations. Line 6 initializes the +variable @code{cdio} which we will be using in all of the subsequent +libcdio calls. It does this via a call to @code{cdio_open()}. + +The second parameter of @code{cdio_open} is DRIVER_UNKNOWN. For any +given installation a number of Compact Disc device drivers may be +available. In particular it's not uncommon to have several drivers +that can read CD disk-image formats as well as a driver that handles +some CD-ROM piece of hardware. Using DRIVER_UNKNOWN as that second +parameter we let the library select a driver amongst those that are +available; generally the first hardware driver that is available is +the one selected. + +If there is no CD in any of the CD-ROM drives or one does not have +access to the CD-ROM, it is possible that @value{libcdio} will find a +CD image in the directory you run this program and will pick a +suitable CD-image driver. If this is not what you want, but always +want some sort of CD-ROM driver (or failure if none), then use +DRIVER_DEVICE instead of DRIVER_UNKNOWN. + +Note that in contrast to what is typically done using ioctls to read a +CD, you don't issue any sort of CD-ROM read TOC command---that is all +done by the driver. Of course, the information that you get from +reading the TOC is often desired: many tracks are on the CD, or what +number the first one is called. This is done through calls on lines 8 +and 9. + +For each track, we call a cdio routine to get the logical sector +number, @code{cdio_get_track_lsn()} on line 17 and print the track +number and LSN value. Finally we print out the ``lead-out track'' +information and we finally call @code{cdio_destroy()} in line 23 to +indicate we're done with the CD. + +@node Example 2 +@section Example 2: list drivers available and default CD device + +One thing that's a bit hoaky in Example 1 is hard-coding the name of +the device used: @code{/dev/cdrom}. Although often this is the name of +a CD-ROM device on GNU/Linux and possibly some other Unix derivatives, +there are many OSs for which use a different device name. + +In the next example, we'll let the driver give us the name of the CD-ROM +device that is right for it. + +@smallexample + 1: #include + 2: #include + 3: #include + 4: int + 5: main(int argc, const char *argv[]) + 6: @{ + 7: CdIo_t *p_cdio = cdio_open (NULL, DRIVER_DEVICE); + 8: driver_id_t driver_id; + 9: +10: if (NULL != p_cdio) @{ +11: printf("The driver selected is %s\n", cdio_get_driver_name(p_cdio)); +12: printf("The default device for this driver is %s\n\n", +13: cdio_get_default_device(p_cdio)); +14: cdio_destroy(p_cdio); +15: @} else @{ +16: printf("Problem in trying to find a driver.\n\n"); +17: @} +18: +19: for (driver_id=CDIO_MIN_DRIVER; driver_id<=CDIO_MAX_DRIVER; driver_id++) +20: if (cdio_have_driver(driver_id)) +21: printf("We have: %s\n", cdio_driver_describe(driver_id)); +22: else +23: printf("We don't have: %s\n", cdio_driver_describe(driver_id)); +24: return 0; +25: @} +@end smallexample + + +@node Example 3 +@section Example 3: figure out what kind of CD (image) we've got + +In this example is a somewhat simplified program to show the use of +@command{cdio_guess_cd_type()} to figure out the kind of CD image +we've got. This can be found in the distribution as @file{example/sample3.c}. + +@smallexample +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#include + +static void +print_analysis(cdio_iso_analysis_t cdio_iso_analysis, + cdio_fs_anal_t fs, int first_data, unsigned int num_audio, + track_t i_tracks, track_t first_track_num, CdIo_t *cdio) +@{ + switch(CDIO_FSTYPE(fs)) @{ + case CDIO_FS_AUDIO: + break; + case CDIO_FS_ISO_9660: + printf("CD-ROM with ISO 9660 filesystem"); + if (fs & CDIO_FS_ANAL_JOLIET) @{ + printf(" and joliet extension level %d", cdio_iso_analysis.joliet_level); + @} + if (fs & CDIO_FS_ANAL_ROCKRIDGE) + printf(" and rockridge extensions"); + printf("\n"); + break; + case CDIO_FS_ISO_9660_INTERACTIVE: + printf("CD-ROM with CD-RTOS and ISO 9660 filesystem\n"); + break; + case CDIO_FS_HIGH_SIERRA: + printf("CD-ROM with High Sierra filesystem\n"); + break; + case CDIO_FS_INTERACTIVE: + printf("CD-Interactive%s\n", num_audio > 0 ? "/Ready" : ""); + break; + case CDIO_FS_HFS: + printf("CD-ROM with Macintosh HFS\n"); + break; + case CDIO_FS_ISO_HFS: + printf("CD-ROM with both Macintosh HFS and ISO 9660 filesystem\n"); + break; + case CDIO_FS_UFS: + printf("CD-ROM with Unix UFS\n"); + break; + case CDIO_FS_EXT2: + printf("CD-ROM with Linux second extended filesystem\n"); + break; + case CDIO_FS_3DO: + printf("CD-ROM with Panasonic 3DO filesystem\n"); + break; + case CDIO_FS_UNKNOWN: + printf("CD-ROM with unknown filesystem\n"); + break; + @} + switch(CDIO_FSTYPE(fs)) @{ + case CDIO_FS_ISO_9660: + case CDIO_FS_ISO_9660_INTERACTIVE: + case CDIO_FS_ISO_HFS: + printf("ISO 9660: %i blocks, label `%.32s'\n", + cdio_iso_analysis.isofs_size, cdio_iso_analysis.iso_label); + break; + @} + if (first_data == 1 && num_audio > 0) + printf("mixed mode CD "); + if (fs & CDIO_FS_ANAL_XA) + printf("XA sectors "); + if (fs & CDIO_FS_ANAL_MULTISESSION) + printf("Multisession"); + if (fs & CDIO_FS_ANAL_HIDDEN_TRACK) + printf("Hidden Track "); + if (fs & CDIO_FS_ANAL_PHOTO_CD) + printf("%sPhoto CD ", + num_audio > 0 ? " Portfolio " : ""); + if (fs & CDIO_FS_ANAL_CDTV) + printf("Commodore CDTV "); + if (first_data > 1) + printf("CD-Plus/Extra "); + if (fs & CDIO_FS_ANAL_BOOTABLE) + printf("bootable CD "); + if (fs & CDIO_FS_ANAL_VIDEOCD && num_audio == 0) @{ + printf("Video CD "); + @} + if (fs & CDIO_FS_ANAL_SVCD) + printf("Super Video CD (SVCD) or Chaoji Video CD (CVD)"); + if (fs & CDIO_FS_ANAL_CVD) + printf("Chaoji Video CD (CVD)"); + printf("\n"); +@} + +int +main(int argc, const char *argv[]) +@{ + CdIo_t *p_cdio = cdio_open (NULL, DRIVER_UNKNOWN); + cdio_fs_anal_t fs=0; + + track_t i_tracks; + track_t first_track_num; + lsn_t start_track; /* first sector of track */ + lsn_t data_start =0; /* start of data area */ + + int first_data = -1; /* # of first data track */ + int first_audio = -1; /* # of first audio track */ + unsigned int num_data = 0; /* # of data tracks */ + unsigned int num_audio = 0; /* # of audio tracks */ + unsigned int i; + + if (NULL == p_cdio) @{ + printf("Problem in trying to find a driver.\n\n"); + return 1; + @} + + first_track_num = cdio_get_first_track_num(p_cdio); + i_tracks = cdio_get_num_tracks(p_cdio); + + /* Count the number of data and audio tracks. */ + for (i = first_track_num; i <= i_tracks; i++) @{ + if (TRACK_FORMAT_AUDIO == cdio_get_track_format(p_cdio, i)) @{ + num_audio++; + if (-1 == first_audio) first_audio = i; + @} else @{ + num_data++; + if (-1 == first_data) first_data = i; + @} + @} + + /* try to find out what sort of CD we have */ + if (0 == num_data) @{ + printf("Audio CD\n"); + @} else @{ + /* we have data track(s) */ + int j; + cdio_iso_analysis_t cdio_iso_analysis; + + memset(&cdio_iso_analysis, 0, sizeof(cdio_iso_analysis)); + + for (j = 2, i = first_data; i <= i_tracks; i++) @{ + lsn_t lsn; + track_format_t track_format = cdio_get_track_format(p_cdio, i); + + lsn = cdio_get_track_lsn(p_cdio, i); + + switch ( track_format ) @{ + case TRACK_FORMAT_AUDIO: + case TRACK_FORMAT_ERROR: + break; + case TRACK_FORMAT_CDI: + case TRACK_FORMAT_XA: + case TRACK_FORMAT_DATA: + case TRACK_FORMAT_PSX: + ; + @} + + start_track = (i == 1) ? 0 : lsn; + + /* 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 + cdio_iso_analysis.isofs_size) + continue; + + fs = cdio_guess_cd_type(p_cdio, start_track, i, &cdio_iso_analysis); + + print_analysis(cdio_iso_analysis, fs, first_data, num_audio, + i_tracks, first_track_num, p_cdio); + + if ( !(CDIO_FSTYPE(fs) == CDIO_FS_ISO_9660 || + CDIO_FSTYPE(fs) == CDIO_FS_ISO_HFS || + CDIO_FSTYPE(fs) == CDIO_FS_ISO_9660_INTERACTIVE) ) + /* no method for non-ISO9660 multisessions */ + break; + @} + @} + cdio_destroy(p_cdio); + return 0; +@} +@end smallexample + +@node Example 4 +@section Example 4: use libiso9660 to extract a file from an ISO-9660 image + +Next a program to show using @command{libiso9660} to extract a file +from an ISO-9660 image. This can be found in the distribution as +@file{example/iso3.c}. A more complete and expanded version of this +is @command{iso-read}, part of this distribution. + +@smallexample +/* This is the ISO 9660 image. */ +#define ISO9660_IMAGE_PATH "../" +#define ISO9660_IMAGE ISO9660_IMAGE_PATH "test/copying.iso" + +#define LOCAL_FILENAME "copying" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define my_exit(rc) \ + fclose (p_outfd); \ + free(p_statbuf); \ + iso9660_close(p_iso); \ + return rc; \ + +int +main(int argc, const char *argv[]) +@{ + iso9660_stat_t *p_statbuf; + FILE *p_outfd; + int i; + + iso9660_t *p_iso = iso9660_open (ISO9660_IMAGE); + + if (NULL == p_iso) @{ + fprintf(stderr, "Sorry, couldn't open ISO 9660 image %s\n", ISO9660_IMAGE); + return 1; + @} + + p_statbuf = iso9660_ifs_stat_translate (p_iso, LOCAL_FILENAME); + + if (NULL == p_statbuf) + @{ + fprintf(stderr, + "Could not get ISO-9660 file information for file %s\n", + LOCAL_FILENAME); + iso9660_close(p_iso); + return 2; + @} + + if (!(p_outfd = fopen (LOCAL_FILENAME, "wb"))) + @{ + perror ("fopen()"); + free(p_statbuf); + iso9660_close(p_iso); + return 3; + @} + + /* Copy the blocks from the ISO-9660 filesystem to the local filesystem. */ + for (i = 0; i < p_statbuf->size; i += ISO_BLOCKSIZE) + @{ + char buf[ISO_BLOCKSIZE]; + + memset (buf, 0, ISO_BLOCKSIZE); + + if ( ISO_BLOCKSIZE != iso9660_iso_seek_read (p_iso, buf, p_statbuf->lsn + + (i / ISO_BLOCKSIZE), + 1) ) + @{ + fprintf(stderr, "Error reading ISO 9660 file at lsn %lu\n", + (long unsigned int) p_statbuf->lsn + (i / ISO_BLOCKSIZE)); + my_exit(4); + @} + + + fwrite (buf, ISO_BLOCKSIZE, 1, p_outfd); + + if (ferror (p_outfd)) + @{ + perror ("fwrite()"); + my_exit(5); + @} + @} + + fflush (p_outfd); + + /* Make sure the file size has the exact same byte size. Without the + truncate below, the file will a multiple of ISO_BLOCKSIZE. + */ + if (ftruncate (fileno (p_outfd), p_statbuf->size)) + perror ("ftruncate()"); + + my_exit(0); +@} +@end smallexample + +@node Example 5 +@section Example 5: list CD-Text and disc mode info + +Next a program to show using @command{libcdio} to list CD-TEXT data. +This can be found in the distribution as @file{example/cdtext.c}. + +@smallexample +/* Simple program to list CD-Text info of a Compact Disc using libcdio. */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include + + +static void +print_cdtext_track_info(CdIo_t *p_cdio, track_t i_track, const char *message) @{ + const cdtext_t *cdtext = cdio_get_cdtext(p_cdio, 0); + if (NULL != cdtext) @{ + cdtext_field_t i; + + printf("%s\n", message); + + for (i=0; i < MAX_CDTEXT_FIELDS; i++) @{ + if (cdtext->field[i]) @{ + printf("\t%s: %s\n", cdtext_field2str(i), cdtext->field[i]); + @} + @} + @} + +@} + +static void +print_disc_info(CdIo_t *p_cdio, track_t i_tracks, track_t i_first_track) @{ + track_t i_last_track = i_first_track+i_tracks; + discmode_t cd_discmode = cdio_get_discmode(p_cdio); + + printf("%s\n", discmode2str[cd_discmode]); + + print_cdtext_track_info(p_cdio, 0, "\nCD-Text for Disc:"); + for ( ; i_first_track < i_last_track; i_first_track++ ) @{ + char psz_msg[50]; + sprintf(msg, "CD-Text for Track %d:", i_first_track); + print_cdtext_track_info(p_cdio, i_first_track, psz_msg); + @} +@} + +int +main(int argc, const char *argv[]) +@{ + track_t i_first_track; + track_t i_tracks; + CdIo_t *p_cdio; + cdio = cdio_open (NULL, DRIVER_UNKNOWN); + i_first_track = cdio_get_first_track_num(p_cdio); + i_tracks = cdio_get_num_tracks(p_cdio); + + if (NULL == p_cdio) @{ + printf("Couldn't find CD\n"); + return 1; + @} else @{ + print_disc_info(p_cdio, i_tracks, i_first_track); + @} + + cdio_destroy(p_cdio); + + return 0; +@} +@end smallexample + +@node Example 6 +@section Example 6: Using MMC to run an @code{INQURY} command + +Now a program to show issuing a simple MMC command +(@code{INQUIRY}). This MMC command retrieves the vendor, model and +firmware revision number of a CD drive. For this command to work, +usually a CD to be loaded into the drive; odd since the CD itself is +not used. + +This can be found in the distribution as @file{example/mmc1.c}. + +@smallexample +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#include + +/* Set how long to wait for MMC commands to complete */ +#define DEFAULT_TIMEOUT_MS 10000 + +int +main(int argc, const char *argv[]) +@{ + CdIo_t *p_cdio; + + p_cdio = cdio_open (NULL, DRIVER_UNKNOWN); + + if (NULL == p_cdio) @{ + printf("Couldn't find CD\n"); + return 1; + @} else @{ + int i_status; /* Result of MMC command */ + char buf[36] = @{ 0, @}; /* Place to hold returned data */ + scsi_mmc_cdb_t cdb = @{@{0, @}@}; /* Command Descriptor Buffer */ + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_INQUIRY); + cdb.field[4] = sizeof(buf); + + i_status = scsi_mmc_run_cmd(p_cdio, DEFAULT_TIMEOUT_MS, + &cdb, SCSI_MMC_DATA_READ, + sizeof(buf), &buf); + if (i_status == 0) @{ + char psz_vendor[CDIO_MMC_HW_VENDOR_LEN+1]; + char psz_model[CDIO_MMC_HW_MODEL_LEN+1]; + char psz_rev[CDIO_MMC_HW_REVISION_LEN+1]; + + memcpy(psz_vendor, buf + 8, sizeof(psz_vendor)-1); + psz_vendor[sizeof(psz_vendor)-1] = '\0'; + memcpy(psz_model, + buf + 8 + CDIO_MMC_HW_VENDOR_LEN, + sizeof(psz_model)-1); + psz_model[sizeof(psz_model)-1] = '\0'; + memcpy(psz_rev, + buf + 8 + CDIO_MMC_HW_VENDOR_LEN +CDIO_MMC_HW_MODEL_LEN, + sizeof(psz_rev)-1); + psz_rev[sizeof(psz_rev)-1] = '\0'; + + printf("Vendor: %s\nModel: %s\nRevision: %s\n", + psz_vendor, psz_model, psz_rev); + @} else @{ + printf("Couldn't get INQUIRY data (vendor, model, and revision\n"); + @} + @} + + cdio_destroy(p_cdio); + + return 0; +@} +@end smallexample + +@node Example 7 +@section Example 7: Using the CD Paranoia library for CD-DA reading + +@smallexample +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +int +main(int argc, const char *argv[]) +@{ + cdrom_drive_t *d = NULL; /* Place to store handle given by cd-paranoia. */ + char **ppsz_cd_drives; /* List of all drives with a loaded CDDA in it. */ + + /* See if we can find a device with a loaded CD-DA in it. */ + ppsz_cd_drives = cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false); + + if (ppsz_cd_drives) @{ + /* Found such a CD-ROM with a CD-DA loaded. Use the first drive in + the list. */ + d=cdio_cddap_identify(*ppsz_cd_drives, 1, NULL); + @} else @{ + printf("Unable find or access a CD-ROM drive with an audio CD in it.\n"); + exit(1); + @} + + /* Don't need a list of CD's with CD-DA's any more. */ + cdio_free_device_list(ppsz_cd_drives); + + /* We'll set for verbose paranoia messages. */ + cdio_cddap_verbose_set(d, CDDA_MESSAGE_PRINTIT, CDDA_MESSAGE_PRINTIT); + + if ( 0 != cdio_cddap_open(d) ) @{ + printf("Unable to open disc.\n"); + exit(1); + @} + + /* Okay now set up to read up to the first 300 frames of the first + audio track of the Audio CD. */ + @{ + cdrom_paranoia_t *p = cdio_paranoia_init(d); + lsn_t i_first_lsn = cdio_cddap_disc_firstsector(d); + + if ( -1 == i_first_lsn ) @{ + printf("Trouble getting starting LSN\n"); + @} else @{ + lsn_t i_cursor; + track_t i_track = cdio_cddap_sector_gettrack(d, i_first_lsn); + lsn_t i_last_lsn = cdio_cddap_track_lastsector(d, i_track); + + /* For demo purposes we'll read only 300 frames (about 4 + seconds). We don't want this to take too long. On the other + hand, I suppose it should be something close to a real test. + */ + if ( i_last_lsn - i_first_lsn > 300) i_last_lsn = i_first_lsn + 299; + + printf("Reading track %d from LSN %ld to LSN %ld\n", i_track, + (long int) i_first_lsn, (long int) i_last_lsn); + + /* Set reading mode for full paranoia, but allow skipping sectors. */ + paranoia_modeset(p, PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP); + + paranoia_seek(p, i_first_lsn, SEEK_SET); + + for ( i_cursor = i_first_lsn; i_cursor <= i_last_lsn; i_cursor ++) @{ + /* read a sector */ + int16_t *p_readbuf=cdio_paranoia_read(p, NULL); + char *psz_err=cdio_cddap_errors(d); + char *psz_mes=cdio_cddap_messages(d); + + if (psz_mes || psz_err) + printf("%s%s\n", psz_mes ? psz_mes: "", psz_err ? psz_err: ""); + + if (psz_err) free(psz_err); + if (psz_mes) free(psz_mes); + if( !p_readbuf ) @{ + printf("paranoia read error. Stopping.\n"); + break; + @} + @} + @} + cdio_paranoia_free(p); + @} + + cdio_cdda_close(d); + + exit(0); +@} +@end smallexample + +Those who are die-hard cdparanoia programmers will notice that the +@value{libcdio} paranoia names are similar but a little bit +different. In particular instead of @code{paranoia_read} we have above +@code{cdio_paranoia_read} and instead of @code{cdda_open} we have +@code{cdio_cddap_open}. + +This was done intentionally so that it is possible for the original +paranoia program can co-exist both in source code and linked libraries +and not conflict with @value{libcdio}'s paranoia source and libraries. + +In general in place of any paranoia routine that begins +@code{paranoia_}, use @code{cdio_paranoia_} and in place of any +paranoia routine that begins @code{cdda_}, use @code{cdio_cddap_}. But +for a limited time @value{libcdio} will accept the old paranoia names +which may be useful for legacy paranoia code. The way this magic works +is by defining the old paranoia name to be the @value{libcdio} name. + +In the unusual case where you do want to use both the original +paranoia and @value{libcdio} routines in a single source, the C +preprocessor symbol @code{DO_NOT_WANT_PARANOIA_COMPATIBILITY} can be +@code{define}'d and this disables the @code{#define} substitution done +automatically. The may still be a problem with conflicting structure +definitions like @code{cdrom_drive_t}. + +@node All sample programs +@section A list of all sample programs in the @code{example} directory + +The @code{example} directory contains some simple examples of the use +of the @value{libcdio} library. + +A larger more-complicated example are the @command{cd-drive}, +@command{cd-info}, @command{cd-read}, @command{iso-info} and +@command{iso-info} programs in the @command{src} directory. + +Descriptions of the sample are as follows... + +@table @code + +@item @code{cdchange.c} + +A program to test if a CD has been changed since the last change test. + +@item @code{cdtext.c} + +A program to show CD-Text and CD disc mode info. + +@item @code{drives.c} + +A program to show drivers installed and what the default CD-ROM drive +is and what CD drives are available. + +@item @code{eject.c} + +A program eject a CD from a CD-ROM drive and then close the door again. + +@item @code{isolist.c} + +A program to show using @code{libiso9660} to list files in a + directory of an ISO-9660 image. + +@item @code{C++/isolist.cpp} + +The same program as @code{isolist.c} written in C++. + +@item @code{iso2.c} + +A program to show using @code{libiso9660} to extract a file from a +CDRWIN cue/bin CD image. + +@item @code{C++/iso2.cpp} + +The same program as @code{iso2.c} written in C++. + +@item @code{iso3.c} + +A program to show using libiso9660 to extract a file from an ISO-9660 +image. + +@item @code{C++/iso3.cpp} + +The same program as @code{iso3.c} written in C++. + +@item @code{isofuzzy.c} + +A program showing fuzzy ISO-9660 detection/reading. + +@item @code{mmc1.c} + +A program to show issuing a simple MMC command (@code{INQUIRY}). + +@item @code{C++/mmc1.cpp} + +The same program as @code{mmc1.c} written in C++. + +@item @code{mmc2.c} + +A more involved MMC command to list CD and drive features from a +SCSI-MMC @code{GET_CONFIGURATION} command. + +@item @code{mmc2a.c} + +Prints MMC @command{MODE_SENSE} page 2A parameters. +Page 2a are the CD/DVD Capabilities and Mechanical Status. + +@item @code{C++/mmc2.cpp} + +The same program as @code{mmc2.c} written in C++. + +@item @code{paranoia.c} + +A program to show using libcdio's version of the CD-DA paranoia. + +@item @code{paranoia2.c} + +A program to show using libcdio's version of the CD-DA paranoia +library. But in this version, we'll open a cdio object before calling +paranoia's open. I imagine in many cases such as media players this +may be what will be done since, one may want to get CDDB/CD-Text info +beforehand. + +@item @code{tracks.c} + +A simple program to list track numbers and logical sector numbers of a +Compact Disc using @value{libcdio}. + +@item @code{sample2.c} + +A simple program to show drivers installed and what the default CD-ROM +drive is. + +@item @code{sample3.c} + +A simple program to show the use of @code{cdio_guess_cd_type()}. Figures out +the kind of CD image we've got. + +@item @code{sample4.c} + + A slightly improved sample3 program: we handle cdio logging and take +an optional CD-location. + +@item @code{udf1.c} + +A program to show using libudf to list files in a directory of an UDF +image. + +@item @code{udf2.c} + +A program to show using libudf to extract a file from an UDF image. + +@end table + +@node Utility Programs +@chapter Diagnostic programs: @command{cd-drive}, @command{cd-info}, @command{cd-read}, @command{iso-info}, @command{iso-read} + +@menu +* cd-drive:: list out CD-ROM drive information +* cd-info:: list out CD or CD-image information +* cd-read:: read blocks of a CD or CD image +* iso-info:: list out ISO-9600 image information +* iso-read:: extract a file from an ISO 9660 image +@end menu + +@node cd-drive +@section @samp{cd-drive} + +@samp{cd-drive} lists out drive information, what features drive +supports, and information about what hardware drivers are available. + +@node cd-info +@section @samp{cd-info} + +@samp{cd-info} will print out the structure of a CD medium which could +either be a Compact Disc in a CD ROM or an CD image. It can try to +analyze the medium to give characteristics of the medium, such as how +many tracks are in the CD and the format of each track, whether a CD +contains a Video CD, CD-DA, PhotoCD, whether a track has an ISO-9660 +filesystem. + +@node cd-read +@section @samp{cd-read} + +@samp{cd-info} can be used to read blocks a CD medium which could +either be a Compact Disc in a CD ROM or an CD image. You specify the +beginning and ending LSN and what mode format to use in the reading. + +@node iso-info +@section @samp{iso-info} + +@samp{iso-info} can be used to print out the structure of an ISO 9660 +image. + +@node iso-read +@section @samp{iso-read} + +@samp{iso-read} can be used to extract a file in an ISO-9660 image. + +@node CD-ROM Access and Drivers +@chapter CD-ROM Access and Drivers + +@menu +* MMC:: ``SCSI'' Multimedia Commands (MMC) +* GNU/Linux:: GNU/Linux ioctl +* Microsoft:: Microsoft Windows ioctl and ASPI +* Solaris:: Solaris ATAPI and SCSI +* FreeBSD:: FreeBSD ioctl and CAM +* OS X:: OSX (non-exclussive access) +@end menu + +@node MMC +@section Multimedia Commands (MMC) + +In contrast to the rest of the sections in this chapter, MMC +(Multimedia commands) is not a driver per se, although many of the +CD-ROM drivers do in fact issue MMC commands. MMC commands +gives (in theory) a broad and uniform way to access a CD-ROM drive. + +If your CD-ROM drive understands MMC commands this is probably gives +the most flexibility in control. SCSI and ATAPI CD-ROM devices +generally support a fairly large set of MMC commands. + +The name ``SCSI MMC'' is often found in the literature in +specifications and on the Internet. The ``SCSI'' part is probably a +little bit misleading because a drive can understand ``SCSI MMC'' +commands but not use a SCSI interface---ATAPI CD-ROMs are one such +broad class of examples. In fact there are drivers to ``encapsulate'' +non-SCSI drives or a non-MMC-compliant drives and make them act like +MMC drives. I believe that many OS SCSI ``pass-through'' mechanisms do +roughly the same thing. + +The name ``SCSI MMC'' is no doubt due to the fact that these commands +grew out of the SCSI command set and thus were bundled in them. + +For clarity and precision we will use the term ``MMC'' rather than +``SCSI MMC''. + +One of the problems with MMC is that there are so many different +``standards''. In particular there are MMC +@url{ftp://ftp.t10.org/t10/drafts/mmc/}, MMC 2 +@url{ftp://ftp.t10.org/t10/drafts/mmc2/}, MMC 3 +@url{ftp://ftp.t10.org/t10/drafts/mmc3/}, MMC 4 +@url{ftp://ftp.t10.org/t10/drafts/mmc4/}, and MMC 5 +@url{ftp://ftp.t10.org/t10/drafts/mmc5/} standards several ``drafts'' +for each standard. The good news about ATAPI drives is that they too +understand some sort of MMC subset. The bad news (as I understand +it) is that they do not understand any full MMC command set. + +Another problem with the MMC commands related to the variations in +standards is the variation in the commands themselves and there are +perhaps two or three ways to do many of the basic commands like read a +CD frame. + +There seems to be a fascination with the number of bytes a command +takes in the MMC-specification world. (Size matters?) So often the +name of an operation will have a suffix with the number of bytes of +the command (actually in MMC jargon this is called a ``CDB'' +@cindex CDB (Command Descriptor Block) +or command descriptor block). So for example there is a 6-byte ``MODE +SELECT'' often called ``MODE SELECT 6'' and a 10-byte ``MODE SELECT'' +often called ``MODE SELECT 10''. Presumably the 6-byte command came +first and it was discovered that there was some deficiency causing the +longer command. In @value{libcdio} where there are two formats we add +the suffix in the name, e.g. @code{CDIO_MMC_GPCMD_MODE_SELECT_6} or +@code{CDIO_MMC_GPCMD_MODE_SELECT_10}. + +If the fascination and emphasis in the MMC specifications of CDB size +is a bit odd, equally so is the fact that this too often has bled +through at the OS programming API. However in @value{libcdio}, you +just give the opcode in @code{scsi_mmc_run_cmd()} and we'll do the +work to figure out how many bytes of the CDB are used. + +Down the line it is hoped that @value{libcdio} will have a way to +remove a distinction between the various alternative and +alternative-size MMC commands. In @code{cdio/scsi-mmc.h} you will +find a little bit of this for example via the routine +@code{scsi_mmc_get_drive_cap()}. However much more work is needed. + +@node GNU/Linux +@section GNU/Linux + +The GNU/Linux uses a hybrid of methods. Somethings are done vai ioctl +and some things via MMC. GNU/Linux has a rather nice and complete +ioctl mechanism. On the other hand, the MMC mechanism is more +universal. There are other ``access modes'' listed which are not +really access modes and should probably be redone/rethought. They are +just different ways to run the read command. But for completeness +These are ``READ_CD'' and ``READ_10''. + +@node Microsoft +@section Microsoft Windows ioctl and ASPI + +There are two CD drive access methods on Microsoft Windows platforms: +ioctl and ASPI. + +The ASPI interface specification was developed by Adaptec for +sending commands to a SCSI host adapter (such as those controlling CD +and DVD drives) and used on Window 9x/NT and later. Emulation for +ATAPI drives was added so that the same sets of commands worked those +even though the drives might not be SCSI nor might there even be a +SCSI controller attached. + +However in Windows NT/2K/XP, Microsoft provides their Win32 ioctl +interface, and has taken steps to make using ASPI more inaccessible +(e.g. requiring administrative access to use ASPI). + + +@node Solaris +@section Solaris ATAPI and SCSI + +There is currently only one CD drive access methods in Solaris: SCSI +(called ``USCSI'' or ``user SCSI'' in Solaris). There used to be an +ATAPI method and it could be resurrected if needed. USCSI was +preferred since on newer releases of Solaris and Solaris environments +one would needs to have root access for ATAPI. + +@node FreeBSD +@section FreeBSD ioctl and CAM + +There are two CD drive access methods on Solaris: ioctl and CAM +(common access method). CAM is preferred when possible, especially on +newer releases. However CAM is right now sort of a hybrid and includes +some ioctl code. + +More work on this driver is needed. Volunteers? + +@node OS X +@section OS X (non-exclusive access) + +A problem with OS/X is that if the OS thinks it understands the drive +it gains exclusive access to it and thus prevents a library like this +to get non-exclusive access. + +Currently @value{libcdio} access the CD-ROM non-exclusively. However +in order to be able to issue MMC, the current belief is that +exclusive access is needed. Probably in a future @value{libcdio}, +there will be some way to specify which kind of access is desired +(with the inherent consequences of each). + +More work on this driver is needed. Volunteers? + +@node Internal Program Organization +@chapter Internal Program Organization + +@subsection file organization + +Here is a list of @value{libcdio} directories. + +@itemize + +@item @code{include/cdio} + +This contains the headers that are public. One that will probably be +used quite a bit is @code{}. + +@item @code{lib} + +Code for installed libraries. See below for further breakout + +@item @code{lib/driver} + +Code for various OS-specific CD-ROM drivers, image drivers, and +common MMC routines. + +This code comprises @code{libcdio.a} (or the shared version of it). + +@item @code{lib/iso9660} + +Code for to extract or query ISO-9660 images. + +This code comprises @code{libiso9660.a} (or the shared version of it). + +@item @code{lib/paranoia} + +This is from cdparanoia. It is the OS- and hardware- dependent code to +detect and correct jitter for CD-DA CDs. + +@item @code{lib/cdda_interface} + +This is also from cdparanoia. It is the OS- and hardware- independent +code to detect and correct jitter for CD-DA CDs. + +@item @code{doc} + +A home for fine documentation such as this masterpiece. + +@item @code{example} + +Here you will find various small example programs using +@value{libcdio} which are largely for pedagogical purposes. You might +be able to find one that is similar to what you want to do that could +be extended. In fact some these are contain the kernel ideas behind of +some of the larger programs in @file{src}. + +@item @code{src} + +Various stand-alone utility programs. See below. + +@item @code{src/paranoia} + +@value{libcdio}'s version of @code{cdparanoia}. Except for the fact +that the back-end CD-reading code has been replaced by +@value{libcdio}'s routines the code is pretty much identical. + +@item @code{test} + +Regression tests + +@end itemize + +@subsection @samp{libcdio} + +@value{libcdio} exports one opaque type @code{CdIo_t}. Internally this +a structure containing an enumeration for the driver, a structure +containing function pointers and a generic ``environment'' pointer +which is passed as a parameter on a function call. See +@file{lib/driver/cdio_private.h}. The initialization routine for each +driver sets up the function pointers and allocates memory for the +environment. When a particular user-level cdio routine is called (e.g +@code{cdio_get_first_track_num} for lib/driver/track.c), the +environment pointer is passed to a device-specific routine which will +then cast this pointer into something of the appropriate type. + +Because function pointers are used, there can be and is quite a bit +of sharing of common routines. Some of the common routines are found +in the file @file{lib/driver/_cdio_generic.c}. + +Another set of routines that one is likely to find shared amongst +drivers are the MMC commands. These are located in +@file{lib/driver/scsi_mmc.c}. + +There is not only an attempt to share functions but we've tried to create +a generic CD structure @code{generic_img_private_t} of file +@file{lib/driver/generic.h}. By putting information into a common +structure, we increase the likelihood of being able to have a common +routine to perform some sort of function. + +The generic CD structure would also be useful in a utility to convert +one CD-image format to another. Basically the first image format is +``parsed'' into the common internal format and then from this +structure it is not parsed. + +@subsection @samp{libiso9660} + +To be completed.... + +@subsection Coding Conventions + +In @value{libcdio} there are a number of conventions used. If you +understand some of these conventions it may facilitate understanding +the code a little. + +@subsubsection namespace names + +For the most part, the visible external @value{libcdio} names follow +conventions so as not to be confused with other applications or +libraries. If you understand these conventions, there will be little +or no chance that the names you use will conflict with @value{libcdio} +and @code{libiso9660} and vice versa. + +All of the external @value{libcdio} C routines start out with +@code{cdio_}, e.g. @code{cdio_open}; as a corollary, the +@value{libcdio} CD-Paranoia routines start @code{cdio_cddap_}, +e.g. @code{cdio_cddap_open}. @code{libiso9660} routines start +@code{iso9660_}, e.g. @code{iso9660_open}. + +@value{libcdio} C-Preprocessor names generally start @code{CDIO_}, for +example @code{CDIO_CD_FRAMESIZE_RAW}; @code{libiso9660} +C-preprocessor names start @code{ISO9660_}, +e.g. @code{ISO9660_FRAMESIZE}. + +@subsubsection suffixes (type and structure names) + +A few suffixes are used in type and structure names: + +@itemize + +@item @code{_e} + +An enumeration tag. Generally though the same name will appear with the +@code{_t} suffix and probably that should be used instead. + +@item @code{_s} + +A structure tag. Generally though the same name will appear with the +@code{_t} suffix and probably that should be used instead. + +@item @code{_t} + +A type suffix. + +@end itemize + +@subsubsection prefixes (variable names) + +A number of prefixes are used in variable names here's what they mean + +@itemize +@item @code{i_} + +An integer type of some sort. A variable of this ilk one might find +being iterated over in @code{for} loops or used as the index of an +array for example. + +@item @code{b_} + +A Boolean type of some sort. A variable of this ilk one might find +being in an @code{if} condition for example. + +@item @code{p_} + +A pointer of some sort. A variable of this ilk, say +@code{p_foo} one is like likely to see @code{*p_foo} or +@code{p_foo->...}. + +@item @code{pp_} + +A pointer to a pointer of some sort. A variable of this ilk, say +@code{pp_foo} one is like likely to see @code{**p_foo} or +@code{p_foo[x][y]} for example + +@item @code{psz_} + +A @code{char *} pointer of some sort. A variable of this ilk, say +@code{psz_foo} may be used in a string operation. For example +@code{printf(%s\n", psz_foo)} or @code{strdup(psz_foo)}. + +@item @code{ppsz_} + +A pointer to a @code{char *} pointer of some sort. A variable of this +ilk, say @code{ppsz_foo} is used for example to return a list of +CD-ROM device names + +@end itemize + +There are a some other naming conventions. Generally if a routine +name starts @code{cdio_}, e.g. @code{cdio_open}, then it is an +externally visible routine in @code{libcdio}. If a name starts +@code{iso9660_}, e.g. @code{iso9660_is_dchar} then it is an externally +visible routine in @code{libiso9660}. If a name starts +@code{scsi_mmc_}, e.g. @code{scsi_mmc_get_discmode}, then it is an +externally visible MMC routine. (We don't have a separate library for +this yet. + +Names using entirely capital letters and that start @code{CDIO_} are +externally visible @code{#defines}. + + +@node ISO-9660 Character Sets +@appendix ISO-9660 Character Sets + +For a description of where are used see @xref{ISO 9660 Level 1}. + +@menu +* ISO646 d-Characters:: +* ISO646 a-Characters:: +@end menu + +@node ISO646 d-Characters +@appendixsec ISO646 d-Characters + +@example + | 0 1 2 3 4 5 6 7 +--+----------------- +0 | 0 P +1 | 1 A Q +2 | 2 B R +3 | 3 C S +4 | 4 D T +5 | 5 E U +6 | 6 F V +7 | 7 G W +8 | 8 H X +9 | 9 I Y +a | J Z +b | K +c | L +d | M +e | N +f | O _ +@end example + +@node ISO646 a-Characters +@appendixsec ISO646 a-Characters + +@example + | 0 1 2 3 4 5 6 7 +--+----------------- +0 | 0 P +1 | ! 1 A Q +2 | " 2 B R +3 | 3 C S +4 | 4 D T +5 | % 5 E U +6 | & 6 F V +7 | ' 7 G W +8 | ( 8 H X +9 | ) 9 I Y +a | * : J Z +b | + ; K +c | , < L +d | - = M +e | . > N +f | / ? O _ +@end example + +@node Glossary +@appendix Glossary + +@include glossary.texi + +@node GNU Free Documentation License +@appendix GNU Free Documentation License +@cindex FDL, GNU Free Documentation License + +@include fdl.texi + +@node General Index +@unnumbered General Index +@printindex cp + +@bye diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 00000000..f53478a0 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,3 @@ +/.deps +/Makefile +/Makefile.in diff --git a/example/C++/.cvsignore b/example/C++/.cvsignore new file mode 100644 index 00000000..039f6ccf --- /dev/null +++ b/example/C++/.cvsignore @@ -0,0 +1,15 @@ +Makefile +Makefile.in +.deps +.libs +copying +device +eject +isolist +isofile +isofile2 +mmc1 +mmc2 +paranoia +paranoia2 + diff --git a/example/C++/.gitignore b/example/C++/.gitignore new file mode 100644 index 00000000..f53478a0 --- /dev/null +++ b/example/C++/.gitignore @@ -0,0 +1,3 @@ +/.deps +/Makefile +/Makefile.in diff --git a/example/C++/Makefile.am b/example/C++/Makefile.am new file mode 100644 index 00000000..134cdab1 --- /dev/null +++ b/example/C++/Makefile.am @@ -0,0 +1,67 @@ +# $Id: Makefile.am,v 1.13 2008/08/31 13:38:21 flameeyes Exp $ +# +# Copyright (C) 2005, 2006, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +########################################################## +# Sample C++ programs using libcdio (without OO wrapper) +######################################################### +# +SUBDIRS = OO + +if BUILD_CD_PARANOIA +paranoia_progs = paranoia paranoia2 +endif +if BUILD_EXAMPLES +noinst_PROGRAMS = device eject isofile isofile2 isolist \ + mmc1 mmc2 $(paranoia_progs) +endif + +INCLUDES = -I$(top_srcdir) $(LIBCDIO_CFLAGS) + +device_DEPENDENCIES = $(LIBCDIO_DEPS) +device_SOURCES = device.cpp +device_LDADD = $(LIBCDIO_LIBS) + +eject_DEPENDENCIES = $(LIBCDIO_DEPS) +eject_SOURCES = eject.cpp +eject_LDADD = $(LIBCDIO_LIBS) + +if BUILD_CD_PARANOIA +paranoia_SOURCES = paranoia.cpp +paranoia_LDADD = $(LIBCDIO_PARANOIA_LIBS) $(LIBCDIO_CDDA_LIBS) \ + $(LIBCDIO_LIBS) +paranoia2_SOURCES = paranoia.cpp +paranoia2_LDADD = $(LIBCDIO_PARANOIA_LIBS) $(LIBCDIO_CDDA_LIBS) \ + $(LIBCDIO_LIBS) +endif + +isofile_SOURCES = isofile.cpp +isofile_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +isofile2_SOURCES = isofile2.cpp +isofile2_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +isolist_SOURCES = isolist.cpp +isolist_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) + +mmc1_SOURCES = mmc1.cpp +mmc1_DEPENDENCIES = $(LIBCDIO_DEPS) +mmc1_LDADD = $(LIBCDIO_LIBS) + +mmc2_SOURCES = mmc2.cpp +mmc2_DEPENDENCIES = $(LIBCDIO_DEPS) +mmc2_LDADD = $(LIBCDIO_LIBS) + +# iso programs create file "copying" +MOSTLYCLEANFILES = copying diff --git a/example/C++/OO/.cvsignore b/example/C++/OO/.cvsignore new file mode 100644 index 00000000..65fe9555 --- /dev/null +++ b/example/C++/OO/.cvsignore @@ -0,0 +1,17 @@ +.deps +.libs +Makefile +Makefile.am +Makefile.in +cdtext +copying +device +drives +eject +iso4 +isofile2 +isofile +isolist +mmc1 +mmc2 +tracks diff --git a/example/C++/OO/.gitignore b/example/C++/OO/.gitignore new file mode 100644 index 00000000..f53478a0 --- /dev/null +++ b/example/C++/OO/.gitignore @@ -0,0 +1,3 @@ +/.deps +/Makefile +/Makefile.in diff --git a/example/C++/OO/Makefile.am b/example/C++/OO/Makefile.am new file mode 100644 index 00000000..ee3031bf --- /dev/null +++ b/example/C++/OO/Makefile.am @@ -0,0 +1,69 @@ +# $Id: Makefile.am,v 1.13 2008/08/31 13:38:21 flameeyes Exp $ +# +# Copyright (C) 2005, 2006, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +############################################################ +# Sample C++ programs using libcdio++ (with C++ OO wrapper) +############################################################ +# +noinst_PROGRAMS = cdtext device drives eject \ + isofile isofile2 isolist iso4 mmc1 mmc2 tracks + +INCLUDES = -I$(top_srcdir)/include $(LIBCDIO_CFLAGS) + +cdtext_SOURCES = cdtext.cpp +cdtext_DEPENDENCIES = $(LIBCDIO_DEPS) +cdtext_LDADD = $(LIBCDIOPP_LIBS) $(LIBCDIO_LIBS) + +device_SOURCES = device.cpp +device_DEPENDENCIES = $(LIBCDIO_DEPS) +device_LDADD = $(LIBCDIOPP_LIBS) $(LIBCDIO_LIBS) + +drives_SOURCES = drives.cpp +drives_DEPENDENCIES = $(LIBCDIO_DEPS) +drives_LDADD = $(LIBCDIOPP_LIBS) $(LIBCDIO_LIBS) + +eject_SOURCES = eject.cpp +eject_DEPENDENCIES = $(LIBCDIO_DEPS) +eject_LDADD = $(LIBCDIOPP_LIBS) $(LIBCDIO_LIBS) + +isofile_SOURCES = isofile.cpp +isofile_LDADD = $(LIBISO9660PP_LIBS) $(LIBISO9660_LIBS) \ + $(LIBCDIOPP_LIBS) $(LTLIBICONV) + +isofile2_SOURCES = isofile2.cpp +isofile2_LDADD = $(LIBISO9660PP_LIBS) $(LIBISO9660_LIBS) \ + $(LIBCDIOPP_LIBS) $(LTLIBICONV) + +isolist_SOURCES = isolist.cpp +isolist_LDADD = $(LIBISO9660PP_LIBS) $(LIBISO9660_LIBS) \ + $(LIBCDIOPP_LIBS) $(LTLIBICONV) + +iso4_SOURCES = iso4.cpp +iso4_LDADD = $(LIBISO9660PP_LIBS) $(LIBISO9660_LIBS) \ + $(LIBCDIOPP_LIBS) $(LTLIBICONV) + +mmc1_SOURCES = mmc1.cpp +mmc1_DEPENDENCIES = $(LIBCDIO_DEPS) +mmc1_LDADD = $(LIBCDIOPP_LIBS) $(LIBCDIO_LIBS) + +mmc2_SOURCES = mmc2.cpp +mmc2_DEPENDENCIES = $(LIBCDIO_DEPS) +mmc2_LDADD = $(LIBCDIOPP_LIBS) $(LIBCDIO_LIBS) + +tracks_SOURCES = tracks.cpp +tracks_LDADD = $(LIBCDIOPP_LIBS) $(LIBCDIO_LIBS) + diff --git a/example/C++/OO/cdtext.cpp b/example/C++/OO/cdtext.cpp new file mode 100644 index 00000000..0f9b4e1c --- /dev/null +++ b/example/C++/OO/cdtext.cpp @@ -0,0 +1,105 @@ +/* + $Id: cdtext.cpp,v 1.4 2008/03/24 15:30:57 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to list CD-Text info of a Compact Disc using + libcdio. An optional drive name can be supplied as an argument. + See also corresponding C program of a similar name. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include + +/* Set up a CD-DA image to test on which is in the libcdio distribution. */ +#define CDDA_IMAGE_PATH "../../../test/" +#define CDDA_IMAGE CDDA_IMAGE_PATH "cdda.cue" + +static void +print_cdtext_track_info(CdioDevice *device, track_t i_track, + const char *psz_msg) { + cdtext_t *cdtext = device->getCdtext(0); + if (NULL != cdtext) { + cdtext_field_t i; + + printf("%s\n", psz_msg); + + for (i= (cdtext_field_t) MIN_CDTEXT_FIELD; i < MAX_CDTEXT_FIELDS; i++) { + if (cdtext->field[i]) { + printf("\t%s: %s\n", cdtext_field2str(i), cdtext->field[i]); + } + } + } +} + +static void +print_disc_info(CdioDevice *device, track_t i_tracks, track_t i_first_track) { + track_t i_last_track = i_first_track+i_tracks; + discmode_t cd_discmode = device->getDiscmode(); + + printf("%s\n", discmode2str[cd_discmode]); + + print_cdtext_track_info(device, 0, "\nCD-Text for Disc:"); + for ( ; i_first_track < i_last_track; i_first_track++ ) { + char psz_msg[50]; + sprintf(psz_msg, "CD-Text for Track %d:", i_first_track); + print_cdtext_track_info(device, i_first_track, psz_msg); + } +} + +int +main(int argc, const char *argv[]) +{ + track_t i_first_track; + track_t i_tracks; + CdioDevice *device = new CdioDevice; + const char *psz_drive = NULL; + + if (!device->open(CDDA_IMAGE, DRIVER_BINCUE)) { + printf("Couldn't open " CDDA_IMAGE " with BIN/CUE driver.\n"); + } else { + i_first_track = device->getFirstTrackNum(); + i_tracks = device->getNumTracks(); + print_disc_info(device, i_tracks, i_first_track); + } + + if (argc > 1) psz_drive = argv[1]; + + if (!device->open(psz_drive, DRIVER_DEVICE)) { + printf("Couldn't find CD\n"); + return 1; + } else { + i_first_track = device->getFirstTrackNum(); + i_tracks = device->getNumTracks(); + print_disc_info(device, i_tracks, i_first_track); + } + + delete(device); + + return 0; +} diff --git a/example/C++/OO/device.cpp b/example/C++/OO/device.cpp new file mode 100644 index 00000000..37fcbf22 --- /dev/null +++ b/example/C++/OO/device.cpp @@ -0,0 +1,138 @@ +/* + $Id: device.cpp,v 1.4 2008/03/24 15:30:57 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show drivers installed and what the default + CD-ROM drive is. See also corresponding C program of a similar + name. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include + +#define _(x) x + +/* Prints out drive capabilities */ +static void +print_drive_capabilities(cdio_drive_read_cap_t i_read_cap, + cdio_drive_write_cap_t i_write_cap, + cdio_drive_misc_cap_t i_misc_cap) +{ + if (CDIO_DRIVE_CAP_ERROR == i_misc_cap) { + printf("Error in getting drive hardware properties\n"); + } else { + printf(_("Hardware : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_FILE + ? "Disk Image" : "CD-ROM or DVD"); + printf(_("Can eject : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_EJECT ? "Yes" : "No"); + printf(_("Can close tray : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_CLOSE_TRAY ? "Yes" : "No"); + printf(_("Can disable manual eject : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_LOCK ? "Yes" : "No"); + printf(_("Can select juke-box disc : %s\n\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_SELECT_DISC ? "Yes" : "No"); + + printf(_("Can set drive speed : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_SELECT_SPEED ? "Yes" : "No"); + printf(_("Can detect if CD changed : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED ? "Yes" : "No"); + printf(_("Can read multiple sessions : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_MULTI_SESSION ? "Yes" : "No"); + printf(_("Can hard reset device : %s\n\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_RESET ? "Yes" : "No"); + } + + + if (CDIO_DRIVE_CAP_ERROR == i_read_cap) { + printf("Error in getting drive reading properties\n"); + } else { + printf("Reading....\n"); + printf(_(" Can play audio : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_AUDIO ? "Yes" : "No"); + printf(_(" Can read CD-R : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_CD_R ? "Yes" : "No"); + printf(_(" Can read CD-RW : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_CD_RW ? "Yes" : "No"); + printf(_(" Can read DVD-ROM : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_DVD_ROM ? "Yes" : "No"); + } + + + if (CDIO_DRIVE_CAP_ERROR == i_write_cap) { + printf("Error in getting drive writing properties\n"); + } else { + printf("\nWriting....\n"); + printf(_(" Can write CD-RW : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_CD_RW ? "Yes" : "No"); + printf(_(" Can write DVD-R : %s\n"), + i_write_cap & CDIO_DRIVE_CAP_READ_DVD_R ? "Yes" : "No"); + printf(_(" Can write DVD-RAM : %s\n"), + i_write_cap & CDIO_DRIVE_CAP_READ_DVD_RAM ? "Yes" : "No"); + } +} + +int +main(int argc, const char *argv[]) +{ + CdioDevice device; + + if (device.open(NULL)) { + char *default_device = device.getDevice(); + cdio_drive_read_cap_t i_read_cap; + cdio_drive_write_cap_t i_write_cap; + cdio_drive_misc_cap_t i_misc_cap; + + printf("The driver selected is %s\n", device.getDriverName()); + + if (default_device) + printf("The default device for this driver is %s\n", default_device); + + device.getDriveCap(i_read_cap, i_write_cap, i_misc_cap); + print_drive_capabilities(i_read_cap, i_write_cap, i_misc_cap); + + free(default_device); + printf("\n"); + + } else { + printf("Problem in trying to find a driver.\n\n"); + } + + { + driver_id_t driver_id; + for (driver_id=CDIO_MIN_DRIVER; driver_id<=CDIO_MAX_DRIVER; driver_id++) + if (cdio_have_driver(driver_id)) + printf("We have: %s\n", cdio_driver_describe(driver_id)); + else + printf("We don't have: %s\n", cdio_driver_describe(driver_id)); + } + + return 0; + +} diff --git a/example/C++/OO/drives.cpp b/example/C++/OO/drives.cpp new file mode 100644 index 00000000..1f50e345 --- /dev/null +++ b/example/C++/OO/drives.cpp @@ -0,0 +1,90 @@ +/* + $Id: drives.cpp,v 1.6 2008/03/24 15:30:57 karl Exp $ + + Copyright (C) 2003, 2004, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show drivers installed and what the default + CD-ROM drive is and what CD drives are available. */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +#include +#include + +static void +log_handler (cdio_log_level_t level, const char message[]) +{ + switch(level) { + case CDIO_LOG_DEBUG: + case CDIO_LOG_INFO: + return; + default: + printf("cdio %d message: %s\n", level, message); + } +} + +static void +print_drive_class(const char *psz_msg, cdio_fs_anal_t bitmask, + bool b_any=false) { + char **ppsz_cd_drives=NULL, **c; + + printf("%s...\n", psz_msg); + ppsz_cd_drives = getDevices(NULL, bitmask, b_any); + if (NULL != ppsz_cd_drives) + for( c = ppsz_cd_drives; *c != NULL; c++ ) { + printf("Drive %s\n", *c); + } + + freeDeviceList(ppsz_cd_drives); + printf("-----\n"); +} + +int +main(int argc, const char *argv[]) +{ + char **ppsz_cd_drives=NULL, **c; + + cdio_log_set_handler (log_handler); + + /* Print out a list of CD-drives */ + printf("All CD-ROM/DVD drives...\n"); + ppsz_cd_drives = getDevices(); + if (NULL != ppsz_cd_drives) + for( c = ppsz_cd_drives; *c != NULL; c++ ) { + printf("Drive %s\n", *c); + } + + freeDeviceList(ppsz_cd_drives); + + print_drive_class("All CD-ROM drives (again)", CDIO_FS_MATCH_ALL); + print_drive_class("CD-ROM drives with a CD-DA loaded...", CDIO_FS_AUDIO); + print_drive_class("CD-ROM drives with some sort of ISO 9660 filesystem...", + CDIO_FS_ANAL_ISO9660_ANY, true); + print_drive_class("(S)VCD drives...", CDIO_FS_ANAL_VCD_ANY, true); + return 0; + +} diff --git a/example/C++/OO/eject.cpp b/example/C++/OO/eject.cpp new file mode 100644 index 00000000..50246f86 --- /dev/null +++ b/example/C++/OO/eject.cpp @@ -0,0 +1,83 @@ +/* + $Id: eject.cpp,v 1.7 2008/03/24 15:30:57 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to eject a CD-ROM drive door and then close it again. + + If a single argument is given, it is used as the CD-ROM device to + eject/close. Otherwise a CD-ROM drive will be scanned for. + + See also corresponding C program of a similar name. +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +int +main(int argc, const char *argv[]) +{ + driver_return_code_t ret; + driver_id_t driver_id = DRIVER_DEVICE; + char *psz_drive = NULL; + CdioDevice device; + + if (argc > 1) + psz_drive = strdup(argv[1]); + + if (!psz_drive) { + psz_drive = getDefaultDevice(driver_id); + if (!psz_drive) { + printf("Can't find a CD-ROM to perform eject operation\n"); + exit(1); + } + } + try { + ejectMedia(psz_drive); + printf("CD in CD-ROM drive %s ejected.\n", psz_drive); + } + catch ( DriverOpUninit e ) { + printf("Can't Eject CD from CD-ROM drive: driver is not initialized.\n", + psz_drive); + } + catch ( DriverOpException e ) { + printf("Ejecting CD from CD-ROM drive %s operation error:\n\t%s.\n", + psz_drive, e.get_msg()); + } + + try { + closeTray(psz_drive); + printf("Closed CD-ROM %s tray.\n", psz_drive); + } + catch ( DriverOpException e ) { + printf("Closing CD-ROM %s tray operation error error:\n\t%s.\n", + psz_drive, e.get_msg()); + } + free(psz_drive); + + return 0; +} diff --git a/example/C++/OO/iso4.cpp b/example/C++/OO/iso4.cpp new file mode 100644 index 00000000..dcfd64cb --- /dev/null +++ b/example/C++/OO/iso4.cpp @@ -0,0 +1,118 @@ +/* + $Id: iso4.cpp,v 1.2 2008/03/24 15:30:57 karl Exp $ + + Copyright (C) 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libiso9660 to list files in a directory of + an ISO-9660 image and give some iso9660 information. See the code + to iso-info for a more complete example. + + If a single argument is given, it is used as the ISO 9660 image to + use in the listing. Otherwise a compiled-in default ISO 9660 image + name (that comes with the libcdio distribution) will be used. + + This program can be compiled with either a C or C++ compiler. In + the distributuion we perfer C++ just to make sure we haven't broken + things on the C++ side. + */ + +/* Set up a CD-DA image to test on which is in the libcdio distribution. */ +#define ISO9660_IMAGE_PATH "../../../" +#define ISO9660_IMAGE ISO9660_IMAGE_PATH "test/isofs-m1.cue" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define print_vd_info(title, fn) \ + psz_str = p_pvd->fn(); \ + if (psz_str) { \ + printf(title ": %s\n", psz_str); \ + free(psz_str); \ + psz_str = NULL; \ + } + +int +main(int argc, const char *argv[]) +{ + stat_vector_t stat_vector; + ISO9660::FS *p_iso = new ISO9660::FS; + char const *psz_fname; + const char *psz_path="/"; + ISO9660::PVD *p_pvd; + + if (argc > 1) + psz_fname = argv[1]; + else + psz_fname = ISO9660_IMAGE; + + if (!p_iso->open(psz_fname, DRIVER_UNKNOWN)) { + fprintf(stderr, "Sorry, couldn't open %s as a CD or CD image.\n", + psz_fname); + return 1; + } + + p_pvd = p_iso->read_pvd(); + if (p_pvd) { + char *psz_str = NULL; + print_vd_info("Application", get_application_id); + print_vd_info("Preparer ", get_preparer_id); + print_vd_info("Publisher ", get_publisher_id); + print_vd_info("System ", get_system_id); + print_vd_info("Volume ", get_volume_id); + print_vd_info("Volume Set ", get_volumeset_id); + } + + if (p_iso->readdir (psz_path, stat_vector)) + { + /* Iterate over the list of files. */ + stat_vector_iterator_t i; + for(i=stat_vector.begin(); i != stat_vector.end(); ++i) + { + char filename[4096]; + ISO9660::Stat *p_s = *i; + iso9660_name_translate(p_s->p_stat->filename, filename); + printf ("%s [LSN %6d] %8u %s%s\n", + 2 == p_s->p_stat->type ? "d" : "-", + p_s->p_stat->lsn, p_s->p_stat->size, psz_path, filename); + delete(p_s); + } + + stat_vector.clear(); + } + + delete(p_iso); + return 0; +} + diff --git a/example/C++/OO/isofile.cpp b/example/C++/OO/isofile.cpp new file mode 100644 index 00000000..781ba303 --- /dev/null +++ b/example/C++/OO/isofile.cpp @@ -0,0 +1,158 @@ +/* + $Id: isofile.cpp,v 1.2 2008/03/24 15:30:57 karl Exp $ + + Copyright (C) 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libiso9660 to extract a file from an + ISO-9660 image. + + If a single argument is given, it is used as the ISO 9660 image to + use in the extraction. Otherwise a compiled in default ISO 9660 image + name (that comes with the libcdio distribution) will be used. + */ + +/* This is the ISO 9660 image. */ +#define ISO9660_IMAGE_PATH "../../../" +#define ISO9660_IMAGE ISO9660_IMAGE_PATH "test/copying.iso" + +#define LOCAL_FILENAME "copying" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "portable.h" + +#include +#include + +#include + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define CEILING(x, y) ((x+(y-1))/y) + +#define my_exit(rc) \ + fclose (p_outfd); \ + delete (p_stat); \ + delete (p_iso); \ + return rc; \ + +int +main(int argc, const char *argv[]) +{ + ISO9660::Stat *p_stat; + FILE *p_outfd; + int i; + char const *psz_image; + char const *psz_fname; + ISO9660::IFS *p_iso = new ISO9660::IFS; + + if (argc > 3) { + printf("usage %s [ISO9660-image.ISO [filename]]\n", argv[0]); + printf("Extracts filename from ISO-9660-image.ISO.\n"); + return 1; + } + + if (argc > 1) + psz_image = argv[1]; + else + psz_image = ISO9660_IMAGE; + + if (argc > 2) + psz_fname = argv[2]; + else + psz_fname = LOCAL_FILENAME; + + if (!p_iso->open(psz_image)) { + fprintf(stderr, "Sorry, couldn't open ISO 9660 image %s\n", psz_image); + return 1; + } + + p_stat = p_iso->stat(psz_fname, true); + + if (!p_stat) + { + fprintf(stderr, + "Could not get ISO-9660 file information for file %s\n", + psz_fname); + delete(p_iso); + return 2; + } + + if (!(p_outfd = fopen (psz_fname, "wb"))) + { + perror ("fopen()"); + delete (p_stat); + delete (p_iso); + return 3; + } + + /* Copy the blocks from the ISO-9660 filesystem to the local filesystem. */ + { + const unsigned int i_blocks = CEILING(p_stat->p_stat->size, ISO_BLOCKSIZE); + for (i = 0; i < i_blocks ; i++) + { + char buf[ISO_BLOCKSIZE]; + const lsn_t lsn = p_stat->p_stat->lsn + i; + + memset (buf, 0, ISO_BLOCKSIZE); + + if ( ISO_BLOCKSIZE != p_iso->seek_read (buf, lsn, 1) ) + { + fprintf(stderr, "Error reading ISO 9660 file %s at LSN %lu\n", + psz_fname, (long unsigned int) lsn); + my_exit(4); + } + + fwrite (buf, ISO_BLOCKSIZE, 1, p_outfd); + + if (ferror (p_outfd)) + { + perror ("fwrite()"); + my_exit(5); + } + } + } + + fflush (p_outfd); + + /* Make sure the file size has the exact same byte size. Without the + truncate below, the file will a multiple of ISO_BLOCKSIZE. + */ + if (ftruncate (fileno (p_outfd), p_stat->p_stat->size)) + perror ("ftruncate()"); + + printf("Extraction of file '%s' from %s successful.\n", + psz_fname, psz_image); + + my_exit(0); +} diff --git a/example/C++/OO/isofile2.cpp b/example/C++/OO/isofile2.cpp new file mode 100644 index 00000000..38926ebb --- /dev/null +++ b/example/C++/OO/isofile2.cpp @@ -0,0 +1,178 @@ +/* + $Id: isofile2.cpp,v 1.2 2008/03/24 15:30:57 karl Exp $ + + Copyright (C) 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libiso9660 to extract a file from a + CUE/BIN CD image. + + If a single argument is given, it is used as the CUE file of a CD image + to use. Otherwise a compiled-in default image name (that + comes with the libcdio distribution) will be used. + + This program can be compiled with either a C or C++ compiler. In + the distribution we prefer C++ just to make sure we haven't broken + things on the C++ side. + */ + +/* This is the CD-image with an ISO-9660 filesystem */ +#define ISO9660_IMAGE_PATH "../../../" +#define ISO9660_IMAGE ISO9660_IMAGE_PATH "test/isofs-m1.cue" + +#define ISO9660_PATH "/" +#define ISO9660_FILENAME "COPYING" +#define LOCAL_FILENAME "copying" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "portable.h" + +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define CEILING(x, y) ((x+(y-1))/y) + +#define my_exit(rc) \ + fclose (p_outfd); \ + delete (p_stat); \ + delete (p_iso); \ + return rc; \ + + +int +main(int argc, const char *argv[]) +{ + ISO9660::Stat *p_stat; + FILE *p_outfd; + unsigned int i; + char const *psz_image; + char const *psz_fname; + char translated_name[256]; + char untranslated_name[256] = ISO9660_PATH; + ISO9660::FS *p_iso = new ISO9660::FS; + + if (argc > 3) { + printf("usage %s [CD-ROM-or-image [filename]]\n", argv[0]); + printf("Extracts filename from CD-ROM-or-image.\n"); + return 1; + } + + if (argc > 1) + psz_image = argv[1]; + else + psz_image = ISO9660_IMAGE; + + if (argc > 2) + psz_fname = argv[2]; + else + psz_fname = ISO9660_FILENAME; + + strcat(untranslated_name, psz_fname); + + if (!p_iso->open(psz_image, DRIVER_UNKNOWN)) { + fprintf(stderr, "Sorry, couldn't open %s\n", psz_image); + return 1; + } + + p_stat = p_iso->stat(psz_fname); + + if (!p_stat) + { + fprintf(stderr, + "Could not get ISO-9660 file information for file %s\n", + untranslated_name); + delete(p_iso); + return 2; + } + + iso9660_name_translate(psz_fname, translated_name); + + if (!(p_outfd = fopen (translated_name, "wb"))) + { + perror ("fopen()"); + delete (p_stat); + delete (p_iso); + return 3; + } + + /* Copy the blocks from the ISO-9660 filesystem to the local filesystem. */ + { + const unsigned int i_blocks = CEILING(p_stat->p_stat->size, ISO_BLOCKSIZE); + for (i = 0; i < i_blocks; i ++) + { + char buf[ISO_BLOCKSIZE]; + const lsn_t lsn = p_stat->p_stat->lsn + i; + + memset (buf, 0, ISO_BLOCKSIZE); + + try { + p_iso->readDataBlocks(buf, lsn, ISO_BLOCKSIZE); + } + catch ( DriverOpException e ) { + fprintf(stderr, "Error reading ISO 9660 file at lsn %lu:\n\t%s.\n", + (long unsigned int) lsn, e.get_msg()); + my_exit(4); + } + + fwrite (buf, ISO_BLOCKSIZE, 1, p_outfd); + + if (ferror (p_outfd)) + { + perror ("fwrite()"); + my_exit(5); + } + } + } + + + fflush (p_outfd); + + /* Make sure the file size has the exact same byte size. Without the + truncate below, the file will a multiple of ISO_BLOCKSIZE. + */ + if (ftruncate (fileno (p_outfd), p_stat->p_stat->size)) + perror ("ftruncate()"); + + printf("Extraction of file '%s' from '%s' successful.\n", + translated_name, untranslated_name); + + my_exit(0); +} diff --git a/example/C++/OO/isolist.cpp b/example/C++/OO/isolist.cpp new file mode 100644 index 00000000..0685bd2c --- /dev/null +++ b/example/C++/OO/isolist.cpp @@ -0,0 +1,117 @@ +/* + $Id: isolist.cpp,v 1.2 2008/03/24 15:30:57 karl Exp $ + + Copyright (C) 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libiso9660 to list files in a directory of + an ISO-9660 image and give some iso9660 information. See the code + to iso-info for a more complete example. + + If a single argument is given, it is used as the ISO 9660 image to + use in the listing. Otherwise a compiled-in default ISO 9660 image + name (that comes with the libcdio distribution) will be used. + + This program can be compiled with either a C or C++ compiler. In + the distributuion we perfer C++ just to make sure we haven't broken + things on the C++ side. + */ + +/* Set up a CD-DA image to test on which is in the libcdio distribution. */ +#define ISO9660_IMAGE_PATH "../../../" +#define ISO9660_IMAGE ISO9660_IMAGE_PATH "test/copying.iso" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define print_vd_info(title, fn) \ + if (p_iso->fn(psz_str)) { \ + printf(title ": %s\n", psz_str); \ + } \ + free(psz_str); \ + psz_str = NULL; + + +int +main(int argc, const char *argv[]) +{ + stat_vector_t stat_vector; + ISO9660::IFS *p_iso = new ISO9660::IFS; + char const *psz_fname; + const char *psz_path="/"; + + if (argc > 1) + psz_fname = argv[1]; + else + psz_fname = ISO9660_IMAGE; + + if (!p_iso->open(psz_fname)) { + fprintf(stderr, "Sorry, couldn't open %s as an ISO-9660 image\n", + psz_fname); + return 1; + } + + /* Show basic CD info from the Primary Volume Descriptor. */ + { + char *psz_str = NULL; + print_vd_info("Application", get_application_id); + print_vd_info("Preparer ", get_preparer_id); + print_vd_info("Publisher ", get_publisher_id); + print_vd_info("System ", get_system_id); + print_vd_info("Volume ", get_volume_id); + print_vd_info("Volume Set ", get_volumeset_id); + } + + if (p_iso->readdir (psz_path, stat_vector)) + { + /* Iterate over the list of files. */ + stat_vector_iterator_t i; + for(i=stat_vector.begin(); i != stat_vector.end(); ++i) + { + char filename[4096]; + ISO9660::Stat *p_s = *i; + iso9660_name_translate(p_s->p_stat->filename, filename); + printf ("%s [LSN %6d] %8u %s%s\n", + 2 == p_s->p_stat->type ? "d" : "-", + p_s->p_stat->lsn, p_s->p_stat->size, psz_path, filename); + delete(p_s); + } + + stat_vector.clear(); + } + + delete(p_iso); + return 0; +} + diff --git a/example/C++/OO/mmc1.cpp b/example/C++/OO/mmc1.cpp new file mode 100644 index 00000000..86872c47 --- /dev/null +++ b/example/C++/OO/mmc1.cpp @@ -0,0 +1,85 @@ +/* + $Id: mmc1.cpp,v 1.2 2008/03/24 15:30:57 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Sample program to show use of the MMC interface. + An optional drive name can be supplied as an argument. + This basically the libdio mmc_get_hwinfo() routine. + See also corresponding C and non OO C++ program. +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include + +/* Set how long to wait for MMC commands to complete */ +#define DEFAULT_TIMEOUT_MS 10000 + +int +main(int argc, const char *argv[]) +{ + CdioDevice device; + const char *psz_drive = NULL; + + if (argc > 1) psz_drive = argv[1]; + + if (!device.open(psz_drive)) { + printf("Couldn't find CD\n"); + return 1; + } else { + int i_status; /* Result of MMC command */ + char buf[36] = { 0, }; /* Place to hold returned data */ + mmc_cdb_t cdb = {{0, }}; /* Command Descriptor Buffer */ + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_INQUIRY); + cdb.field[4] = sizeof(buf); + + i_status = device.mmcRunCmd(DEFAULT_TIMEOUT_MS, &cdb, + SCSI_MMC_DATA_READ, sizeof(buf), &buf); + if (i_status == 0) { + char psz_vendor[CDIO_MMC_HW_VENDOR_LEN+1]; + char psz_model[CDIO_MMC_HW_MODEL_LEN+1]; + char psz_rev[CDIO_MMC_HW_REVISION_LEN+1]; + + memcpy(psz_vendor, buf + 8, sizeof(psz_vendor)-1); + psz_vendor[sizeof(psz_vendor)-1] = '\0'; + memcpy(psz_model, + buf + 8 + CDIO_MMC_HW_VENDOR_LEN, + sizeof(psz_model)-1); + psz_model[sizeof(psz_model)-1] = '\0'; + memcpy(psz_rev, + buf + 8 + CDIO_MMC_HW_VENDOR_LEN +CDIO_MMC_HW_MODEL_LEN, + sizeof(psz_rev)-1); + psz_rev[sizeof(psz_rev)-1] = '\0'; + + printf("Vendor: %s\nModel: %s\nRevision: %s\n", + psz_vendor, psz_model, psz_rev); + } else { + printf("Couldn't get INQUIRY data (vendor, model, and revision).\n"); + } + } + + return 0; +} diff --git a/example/C++/OO/mmc2.cpp b/example/C++/OO/mmc2.cpp new file mode 100644 index 00000000..05999c7f --- /dev/null +++ b/example/C++/OO/mmc2.cpp @@ -0,0 +1,189 @@ +/* + $Id: mmc2.cpp,v 1.2 2008/03/24 15:30:57 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* A program to using the MMC interface to list CD and drive features + from the MMC GET_CONFIGURATION command . */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#ifdef HAVE_SYS_TYPE_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include + +/* Set how long do wto wait for SCSI-MMC commands to complete */ +#define DEFAULT_TIMEOUT_MS 10000 + +int +main(int argc, const char *argv[]) +{ + CdioDevice device; + const char *psz_drive = NULL; + + if (argc > 1) psz_drive = argv[1]; + + if (!device.open(psz_drive)) { + printf("Couldn't find CD\n"); + return 1; + } else { + int i_status; /* Result of MMC command */ + uint8_t buf[500] = { 0, }; /* Place to hold returned data */ + mmc_cdb_t cdb = {{0, }}; /* Command Descriptor Buffer */ + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_GET_CONFIGURATION); + CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(buf)); + cdb.field[1] = CDIO_MMC_GET_CONF_ALL_FEATURES; + cdb.field[3] = 0x0; + + i_status = device.mmcRunCmd(0, &cdb, SCSI_MMC_DATA_READ, sizeof(buf), + &buf); + if (i_status == 0) { + uint8_t *p; + uint32_t i_data; + uint8_t *p_max = buf + 65530; + + i_data = (unsigned int) CDIO_MMC_GET_LEN32(buf); + /* set to first sense feature code, and then walk through the masks */ + p = buf + 8; + while( (p < &(buf[i_data])) && (p < p_max) ) { + uint16_t i_feature; + uint8_t i_feature_additional = p[3]; + + i_feature = CDIO_MMC_GET_LEN16(p); + { + uint8_t *q; + const char *feature_str = mmc_feature2str(i_feature); + printf("%s Feature\n", feature_str); + switch( i_feature ) + { + case CDIO_MMC_FEATURE_PROFILE_LIST: + for ( q = p+4 ; q < p + i_feature_additional ; q += 4 ) { + int i_profile=CDIO_MMC_GET_LEN16(q); + const char *feature_profile_str = + mmc_feature_profile2str(i_profile); + printf( "\t%s", feature_profile_str ); + if (q[2] & 1) { + printf(" - on"); + } + printf("\n"); + } + printf("\n"); + + break; + case CDIO_MMC_FEATURE_CORE: + { + uint8_t *q = p+4; + uint32_t i_interface_standard = CDIO_MMC_GET_LEN32(q); + switch(i_interface_standard) { + case 0: + printf("\tunspecified interface\n"); + break; + case 1: + printf("\tSCSI interface\n"); + break; + case 2: + printf("\tATAPI interface\n"); + break; + case 3: + printf("\tIEEE 1394 interface\n"); + break; + case 4: + printf("\tIEEE 1394A interface\n"); + break; + case 5: + printf("\tFibre Channel interface\n"); + } + printf("\n"); + break; + } + case CDIO_MMC_FEATURE_REMOVABLE_MEDIUM: + switch(p[4] >> 5) { + case 0: + printf("\tCaddy/Slot type loading mechanism\n"); + break; + case 1: + printf("\tTray type loading mechanism\n"); + break; + case 2: + printf("\tPop-up type loading mechanism\n"); + break; + case 4: + printf("\tEmbedded changer with individually changeable discs\n"); + break; + case 5: + printf("\tEmbedded changer using a magazine mechanism\n"); + break; + default: + printf("\tUnknown changer mechanism\n"); + } + + printf("\tcan%s eject the medium or magazine via the normal " + "START/STOP command\n", + (p[4] & 8) ? "": "not"); + printf("\tcan%s be locked into the Logical Unit\n", + (p[4] & 1) ? "": "not"); + printf("\n"); + break; + case CDIO_MMC_FEATURE_CD_READ: + printf("CD Read Feature\n"); + printf("\tC2 Error pointers are %ssupported\n", + (p[4] & 2) ? "": "not "); + printf("\tCD-Text is %ssupported\n", + (p[4] & 1) ? "": "not "); + printf("\n"); + break; + case CDIO_MMC_FEATURE_CDDA_EXT_PLAY: + printf("\tSCAN command is %ssupported\n", + (p[4] & 4) ? "": "not "); + printf("\taudio channels can %sbe muted separately\n", + (p[4] & 2) ? "": "not "); + printf("\taudio channels can %shave separate volume levels\n", + (p[4] & 1) ? "": "not "); + { + uint8_t *q = p+6; + uint16_t i_vol_levels = CDIO_MMC_GET_LEN16(q); + printf("\t%d volume levels can be set\n", i_vol_levels); + } + printf("\n"); + break; + case CDIO_MMC_FEATURE_LU_SN: { + uint8_t i_serial = *(p+3); + char serial[257] = { '\0', }; + memcpy(serial, p+4, i_serial); + printf("\t%s\n\n", serial); + break; + } + default: + printf("\n"); + break; + } + p += i_feature_additional + 4; + } + } + } else { + printf("Didn't get all feature codes\n"); + } + } + + return 0; +} diff --git a/example/C++/OO/tracks.cpp b/example/C++/OO/tracks.cpp new file mode 100644 index 00000000..2acc142c --- /dev/null +++ b/example/C++/OO/tracks.cpp @@ -0,0 +1,64 @@ +/* + $Id: tracks.cpp,v 1.2 2008/03/24 15:30:57 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to list track numbers and logical sector numbers of + a Compact Disc using libcdio. */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +int +main(int argc, const char *argv[]) +{ + CdioDevice device; + track_t i_first_track; + track_t i_tracks; + int j, i; + CdioTrack *track; + + if (!device.open (NULL)) { + printf("Couldn't find a driver.. leaving.\n"); + return 1; + } + + i_tracks = device.getNumTracks(); + i_first_track = i = device.getFirstTrackNum(); + + printf("CD-ROM Track List (%i - %i)\n", i_first_track, i_tracks); + + printf(" #: LSN\n"); + + for (j = 0; j < i_tracks; i++, j++) { + track = device.getTrackFromNum(i); + lsn_t lsn = track->getLsn(); + if (CDIO_INVALID_LSN != lsn) + printf("%3d: %06lu\n", (int) i, (long unsigned int) lsn); + delete(track); + } + + track = device.getTrackFromNum(CDIO_CDROM_LEADOUT_TRACK); + printf("%3X: %06lu leadout\n", CDIO_CDROM_LEADOUT_TRACK, + (long unsigned int) track->getLsn()); + delete(track); + return 0; +} diff --git a/example/C++/README b/example/C++/README new file mode 100644 index 00000000..b648de99 --- /dev/null +++ b/example/C++/README @@ -0,0 +1,41 @@ +$Id: README,v 1.4 2006/04/15 16:22:49 rocky Exp $ + +This directory contains some simple C++ examples of the use of the libcdio +library. + +Descriptions of the programs in this example directory are as follows... + +device.cpp: A program to show drivers installed and what the default + CD-ROM drive is and what CD drives are available. + +eject.cpp: A program eject a CD from a CD-ROM drive and then close the door + again. + +isofile.cpp: A program to show using libiso9660 to extract a file from an + ISO-9660 image. + +isofile2.cpp: A program to show using libiso9660 to extract a file + from a CDRWIN cue/bin CD image. + +isolist.cpp: A program to show using libiso9660 to list files in a + directory of an ISO-9660 image and give basic iso9660 + information. + +mmc1.cpp: A program to show issuing a simple MMC command (INQUIRY). + +mmc2.cpp: A more involved MMC command to list features from + a MMC GET_CONFIGURATION command. + +paranoia.cpp: A program to show using CD-DA paranoia (a library for jitter + detection and audio-read error correction). This program uses + an interface compatible (mostly) with cdparanoia. + +paranoia2.cpp: Another program to show using CD-DA paranoia using a more + libcdio-oriented initialization. Probably more suited to + things that otherwise use libcdio such as media players + (e.g. for getting CDDB or CD-Text info) + + + + + diff --git a/example/C++/device.cpp b/example/C++/device.cpp new file mode 100644 index 00000000..75b59bcd --- /dev/null +++ b/example/C++/device.cpp @@ -0,0 +1,140 @@ +/* + $Id: device.cpp,v 1.3 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show drivers installed and what the default + CD-ROM drive is. See also corresponding C program of a similar + name. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include + +#define _(x) x + +/* Prints out drive capabilities */ +static void +print_drive_capabilities(cdio_drive_read_cap_t i_read_cap, + cdio_drive_write_cap_t i_write_cap, + cdio_drive_misc_cap_t i_misc_cap) +{ + if (CDIO_DRIVE_CAP_ERROR == i_misc_cap) { + printf("Error in getting drive hardware properties\n"); + } else { + printf(_("Hardware : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_FILE + ? "Disk Image" : "CD-ROM or DVD"); + printf(_("Can eject : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_EJECT ? "Yes" : "No"); + printf(_("Can close tray : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_CLOSE_TRAY ? "Yes" : "No"); + printf(_("Can disable manual eject : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_LOCK ? "Yes" : "No"); + printf(_("Can select juke-box disc : %s\n\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_SELECT_DISC ? "Yes" : "No"); + + printf(_("Can set drive speed : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_SELECT_SPEED ? "Yes" : "No"); + printf(_("Can detect if CD changed : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED ? "Yes" : "No"); + printf(_("Can read multiple sessions : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_MULTI_SESSION ? "Yes" : "No"); + printf(_("Can hard reset device : %s\n\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_RESET ? "Yes" : "No"); + } + + + if (CDIO_DRIVE_CAP_ERROR == i_read_cap) { + printf("Error in getting drive reading properties\n"); + } else { + printf("Reading....\n"); + printf(_(" Can play audio : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_AUDIO ? "Yes" : "No"); + printf(_(" Can read CD-R : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_CD_R ? "Yes" : "No"); + printf(_(" Can read CD-RW : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_CD_RW ? "Yes" : "No"); + printf(_(" Can read DVD-ROM : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_DVD_ROM ? "Yes" : "No"); + } + + + if (CDIO_DRIVE_CAP_ERROR == i_write_cap) { + printf("Error in getting drive writing properties\n"); + } else { + printf("\nWriting....\n"); + printf(_(" Can write CD-RW : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_CD_RW ? "Yes" : "No"); + printf(_(" Can write DVD-R : %s\n"), + i_write_cap & CDIO_DRIVE_CAP_READ_DVD_R ? "Yes" : "No"); + printf(_(" Can write DVD-RAM : %s\n"), + i_write_cap & CDIO_DRIVE_CAP_READ_DVD_RAM ? "Yes" : "No"); + } +} + +inline +driver_id_t &operator++(driver_id_t &d) +{ + return d = driver_id_t(d + 1); +} + +int +main(int argc, const char *argv[]) +{ + CdIo_t *p_cdio = cdio_open (NULL, DRIVER_UNKNOWN); + driver_id_t driver_id; + + if (NULL != p_cdio) { + char *default_device = cdio_get_default_device(p_cdio); + cdio_drive_read_cap_t i_read_cap; + cdio_drive_write_cap_t i_write_cap; + cdio_drive_misc_cap_t i_misc_cap; + + printf("The driver selected is %s\n", cdio_get_driver_name(p_cdio)); + + if (default_device) + printf("The default device for this driver is %s\n", default_device); + + cdio_get_drive_cap(p_cdio, &i_read_cap, &i_write_cap, &i_misc_cap); + print_drive_capabilities(i_read_cap, i_write_cap, i_misc_cap); + + free(default_device); + cdio_destroy(p_cdio); + printf("\n"); + + } else { + printf("Problem in trying to find a driver.\n\n"); + } + + for (driver_id=CDIO_MIN_DRIVER; driver_id<=CDIO_MAX_DRIVER; ++driver_id) + if (cdio_have_driver(driver_id)) + printf("We have: %s\n", cdio_driver_describe(driver_id)); + else + printf("We don't have: %s\n", cdio_driver_describe(driver_id)); + return 0; +} diff --git a/example/C++/eject.cpp b/example/C++/eject.cpp new file mode 100644 index 00000000..f719f064 --- /dev/null +++ b/example/C++/eject.cpp @@ -0,0 +1,99 @@ +/* + $Id: eject.cpp,v 1.4 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to eject a CD-ROM drive door and then close it again. + + If a single argument is given, it is used as the CD-ROM device to + eject/close. Otherwise a CD-ROM drive will be scanned for. +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#ifdef HAVE_STRING_H +#include +#endif + +int +main(int argc, const char *argv[]) +{ + driver_return_code_t ret; + driver_id_t driver_id = DRIVER_DEVICE; + char *psz_drive = NULL; + + if (argc > 1) + psz_drive = strdup(argv[1]); + + if (!psz_drive) { + psz_drive = cdio_get_default_device_driver(&driver_id); + if (!psz_drive) { + printf("Can't find a CD-ROM to eject\n"); + exit(1); + } + } + ret = cdio_eject_media_drive(psz_drive); + switch(ret) { + case DRIVER_OP_UNSUPPORTED: + printf("Eject not supported for %s.\n", psz_drive); + break; + case DRIVER_OP_SUCCESS: + printf("CD-ROM drive %s ejected.\n", psz_drive); + break; + default: + printf("Eject of CD-ROM drive %s failed.\n", psz_drive); + break; + } + + if (DRIVER_OP_SUCCESS == cdio_close_tray(psz_drive, &driver_id)) { + printf("Closed tray of CD-ROM drive %s.\n", psz_drive); + } else { + printf("Closing tray of CD-ROM drive %s failed.\n", psz_drive); + } + free(psz_drive); + + ret = cdio_eject_media_drive(NULL); + switch(ret) { + case DRIVER_OP_UNSUPPORTED: + printf("Eject not supported for default device.\n"); + break; + case DRIVER_OP_SUCCESS: + printf("CD-ROM drive ejected for default device.\n"); + break; + default: + printf("Eject of CD-ROM drive failed for default device.\n"); + break; + } + + driver_id = DRIVER_DEVICE; + if (DRIVER_OP_SUCCESS == cdio_close_tray(NULL, &driver_id)) { + printf("Closed tray of CD-ROM drive for default disc driver:\n\t%s\n", + cdio_driver_describe(driver_id)); + } else { + printf("Closing tray of CD-ROM drive failed for default " + "disc driver:\n\t%s\n", cdio_driver_describe(driver_id)); + } + + return 0; +} diff --git a/example/C++/isofile.cpp b/example/C++/isofile.cpp new file mode 100644 index 00000000..1b541bfa --- /dev/null +++ b/example/C++/isofile.cpp @@ -0,0 +1,161 @@ +/* + $Id: isofile.cpp,v 1.2 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2004, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libiso9660 to extract a file from an + ISO-9660 image. + + If a single argument is given, it is used as the ISO 9660 image to + use in the extraction. Otherwise a compiled in default ISO 9660 image + name (that comes with the libcdio distribution) will be used. + */ + +/* This is the ISO 9660 image. */ +#define ISO9660_IMAGE_PATH "../../" +#define ISO9660_IMAGE ISO9660_IMAGE_PATH "test/copying.iso" + +#define LOCAL_FILENAME "copying" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "portable.h" + +#include +#include +#include + +#include + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define CEILING(x, y) ((x+(y-1))/y) + +#define my_exit(rc) \ + fclose (p_outfd); \ + free(p_statbuf); \ + iso9660_close(p_iso); \ + return rc; \ + +int +main(int argc, const char *argv[]) +{ + iso9660_stat_t *p_statbuf; + FILE *p_outfd; + int i; + char const *psz_image; + char const *psz_fname; + iso9660_t *p_iso; + + if (argc > 3) { + printf("usage %s [ISO9660-image.ISO [filename]]\n", argv[0]); + printf("Extracts filename from ISO-9660-image.ISO.\n"); + return 1; + } + + if (argc > 1) + psz_image = argv[1]; + else + psz_image = ISO9660_IMAGE; + + if (argc > 2) + psz_fname = argv[2]; + else + psz_fname = LOCAL_FILENAME; + + p_iso = iso9660_open (psz_image); + + if (NULL == p_iso) { + fprintf(stderr, "Sorry, couldn't open ISO 9660 image %s\n", psz_image); + return 1; + } + + p_statbuf = iso9660_ifs_stat_translate (p_iso, psz_fname); + + if (NULL == p_statbuf) + { + fprintf(stderr, + "Could not get ISO-9660 file information for file %s\n", + psz_fname); + iso9660_close(p_iso); + return 2; + } + + if (!(p_outfd = fopen (psz_fname, "wb"))) + { + perror ("fopen()"); + free(p_statbuf); + iso9660_close(p_iso); + return 3; + } + + /* Copy the blocks from the ISO-9660 filesystem to the local filesystem. */ + { + const unsigned int i_blocks = CEILING(p_statbuf->size, ISO_BLOCKSIZE); + for (i = 0; i < i_blocks ; i++) + { + char buf[ISO_BLOCKSIZE]; + const lsn_t lsn = p_statbuf->lsn + i; + + memset (buf, 0, ISO_BLOCKSIZE); + + if ( ISO_BLOCKSIZE != iso9660_iso_seek_read (p_iso, buf, lsn, 1) ) + { + fprintf(stderr, "Error reading ISO 9660 file %s at LSN %lu\n", + psz_fname, (long unsigned int) lsn); + my_exit(4); + } + + fwrite (buf, ISO_BLOCKSIZE, 1, p_outfd); + + if (ferror (p_outfd)) + { + perror ("fwrite()"); + my_exit(5); + } + } + } + + fflush (p_outfd); + + /* Make sure the file size has the exact same byte size. Without the + truncate below, the file will a multiple of ISO_BLOCKSIZE. + */ + if (ftruncate (fileno (p_outfd), p_statbuf->size)) + perror ("ftruncate()"); + + printf("Extraction of file '%s' from %s successful.\n", + psz_fname, psz_image); + + my_exit(0); +} diff --git a/example/C++/isofile2.cpp b/example/C++/isofile2.cpp new file mode 100644 index 00000000..28835cab --- /dev/null +++ b/example/C++/isofile2.cpp @@ -0,0 +1,161 @@ +/* + $Id: isofile2.cpp,v 1.2 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2004, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libiso9660 to extract a file from an + ISO-9660 image. + + If a single argument is given, it is used as the ISO 9660 image to + use in the extraction. Otherwise a compiled in default ISO 9660 image + name (that comes with the libcdio distribution) will be used. + */ + +/* This is the ISO 9660 image. */ +#define ISO9660_IMAGE_PATH "../../" +#define ISO9660_IMAGE ISO9660_IMAGE_PATH "test/copying.iso" + +#define LOCAL_FILENAME "copying" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "portable.h" + +#include +#include +#include + +#include + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define CEILING(x, y) ((x+(y-1))/y) + +#define my_exit(rc) \ + fclose (p_outfd); \ + free(p_statbuf); \ + iso9660_close(p_iso); \ + return rc; \ + +int +main(int argc, const char *argv[]) +{ + iso9660_stat_t *p_statbuf; + FILE *p_outfd; + int i; + char const *psz_image; + char const *psz_fname; + iso9660_t *p_iso; + + if (argc > 3) { + printf("usage %s [ISO9660-image.ISO [filename]]\n", argv[0]); + printf("Extracts filename from ISO-9660-image.ISO.\n"); + return 1; + } + + if (argc > 1) + psz_image = argv[1]; + else + psz_image = ISO9660_IMAGE; + + if (argc > 2) + psz_fname = argv[2]; + else + psz_fname = LOCAL_FILENAME; + + p_iso = iso9660_open (psz_image); + + if (NULL == p_iso) { + fprintf(stderr, "Sorry, couldn't open ISO 9660 image %s\n", psz_image); + return 1; + } + + p_statbuf = iso9660_ifs_stat_translate (p_iso, psz_fname); + + if (NULL == p_statbuf) + { + fprintf(stderr, + "Could not get ISO-9660 file information for file %s\n", + psz_fname); + iso9660_close(p_iso); + return 2; + } + + if (!(p_outfd = fopen (psz_fname, "wb"))) + { + perror ("fopen()"); + free(p_statbuf); + iso9660_close(p_iso); + return 3; + } + + /* Copy the blocks from the ISO-9660 filesystem to the local filesystem. */ + { + const unsigned int i_blocks = CEILING(p_statbuf->size, ISO_BLOCKSIZE); + for (i = 0; i < i_blocks ; i++) + { + char buf[ISO_BLOCKSIZE]; + const lsn_t lsn = p_statbuf->lsn + i; + + memset (buf, 0, ISO_BLOCKSIZE); + + if ( ISO_BLOCKSIZE != iso9660_iso_seek_read (p_iso, buf, lsn, 1) ) + { + fprintf(stderr, "Error reading ISO 9660 file %s at LSN %lu\n", + psz_fname, (long unsigned int) lsn); + my_exit(4); + } + + fwrite (buf, ISO_BLOCKSIZE, 1, p_outfd); + + if (ferror (p_outfd)) + { + perror ("fwrite()"); + my_exit(5); + } + } + } + + fflush (p_outfd); + + /* Make sure the file size has the exact same byte size. Without the + truncate below, the file will a multiple of ISO_BLOCKSIZE. + */ + if (ftruncate (fileno (p_outfd), p_statbuf->size)) + perror ("ftruncate()"); + + printf("Extraction of file '%s' from %s successful.\n", + psz_fname, psz_image); + + my_exit(0); +} diff --git a/example/C++/isolist.cpp b/example/C++/isolist.cpp new file mode 100644 index 00000000..269ac3f6 --- /dev/null +++ b/example/C++/isolist.cpp @@ -0,0 +1,121 @@ +/* + $Id: isolist.cpp,v 1.2 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2004, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libiso9660 to list files in a directory of + an ISO-9660 image and give some iso9660 information. See the code + to iso-info for a more complete example. + + If a single argument is given, it is used as the ISO 9660 image to + use in the listing. Otherwise a compiled-in default ISO 9660 image + name (that comes with the libcdio distribution) will be used. + + This program can be compiled with either a C or C++ compiler. In + the distributuion we perfer C++ just to make sure we haven't broken + things on the C++ side. + */ + +/* Set up a CD-DA image to test on which is in the libcdio distribution. */ +#define ISO9660_IMAGE_PATH "../../" +#define ISO9660_IMAGE ISO9660_IMAGE_PATH "test/copying.iso" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define print_vd_info(title, fn) \ + if (fn(p_iso, &psz_str)) { \ + printf(title ": %s\n", psz_str); \ + } \ + free(psz_str); \ + psz_str = NULL; + + +int +main(int argc, const char *argv[]) +{ + CdioList_t *p_entlist; + CdioListNode_t *p_entnode; + char const *psz_fname; + iso9660_t *p_iso; + const char *psz_path="/"; + + if (argc > 1) + psz_fname = argv[1]; + else + psz_fname = ISO9660_IMAGE; + + p_iso = iso9660_open (psz_fname); + + if (NULL == p_iso) { + fprintf(stderr, "Sorry, couldn't open %s as an ISO-9660 image\n", + psz_fname); + return 1; + } + + /* Show basic CD info from the Primary Volume Descriptor. */ + { + char *psz_str = NULL; + print_vd_info("Application", iso9660_ifs_get_application_id); + print_vd_info("Preparer ", iso9660_ifs_get_preparer_id); + print_vd_info("Publisher ", iso9660_ifs_get_publisher_id); + print_vd_info("System ", iso9660_ifs_get_system_id); + print_vd_info("Volume ", iso9660_ifs_get_volume_id); + print_vd_info("Volume Set ", iso9660_ifs_get_volumeset_id); + } + + p_entlist = iso9660_ifs_readdir (p_iso, psz_path); + + /* Iterate over the list of nodes that iso9660_ifs_readdir gives */ + + if (p_entlist) { + _CDIO_LIST_FOREACH (p_entnode, p_entlist) + { + char filename[4096]; + iso9660_stat_t *p_statbuf = + (iso9660_stat_t *) _cdio_list_node_data (p_entnode); + iso9660_name_translate(p_statbuf->filename, filename); + printf ("%s [LSN %6d] %8u %s%s\n", + 2 == p_statbuf->type ? "d" : "-", + p_statbuf->lsn, p_statbuf->size, psz_path, filename); + } + + _cdio_list_free (p_entlist, true); + } + + + iso9660_close(p_iso); + return 0; +} + diff --git a/example/C++/mmc1.cpp b/example/C++/mmc1.cpp new file mode 100644 index 00000000..ce54fb33 --- /dev/null +++ b/example/C++/mmc1.cpp @@ -0,0 +1,86 @@ +/* + $Id: mmc1.cpp,v 1.4 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Sample program to show use of the MMC interface. + An optional drive name can be supplied as an argument. + This basically the libdio mmc_get_hwinfo() routine. + See also corresponding C and OO C++ program. +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#include + +/* Set how long to wait for MMC commands to complete */ +#define DEFAULT_TIMEOUT_MS 10000 + +int +main(int argc, const char *argv[]) +{ + CdIo_t *p_cdio; + const char *psz_drive = NULL; + + if (argc > 1) psz_drive = argv[1]; + + p_cdio = cdio_open (psz_drive, DRIVER_UNKNOWN); + + if (NULL == p_cdio) { + printf("Couldn't find CD\n"); + return 1; + } else { + int i_status; /* Result of MMC command */ + char buf[36] = { 0, }; /* Place to hold returned data */ + mmc_cdb_t cdb = {{0, }}; /* Command Descriptor Buffer */ + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_INQUIRY); + cdb.field[4] = sizeof(buf); + + i_status = mmc_run_cmd(p_cdio, DEFAULT_TIMEOUT_MS, &cdb, + SCSI_MMC_DATA_READ, sizeof(buf), &buf); + if (i_status == 0) { + char psz_vendor[CDIO_MMC_HW_VENDOR_LEN+1]; + char psz_model[CDIO_MMC_HW_MODEL_LEN+1]; + char psz_rev[CDIO_MMC_HW_REVISION_LEN+1]; + + memcpy(psz_vendor, buf + 8, sizeof(psz_vendor)-1); + psz_vendor[sizeof(psz_vendor)-1] = '\0'; + memcpy(psz_model, + buf + 8 + CDIO_MMC_HW_VENDOR_LEN, + sizeof(psz_model)-1); + psz_model[sizeof(psz_model)-1] = '\0'; + memcpy(psz_rev, + buf + 8 + CDIO_MMC_HW_VENDOR_LEN +CDIO_MMC_HW_MODEL_LEN, + sizeof(psz_rev)-1); + psz_rev[sizeof(psz_rev)-1] = '\0'; + + printf("Vendor: %s\nModel: %s\nRevision: %s\n", + psz_vendor, psz_model, psz_rev); + } else { + printf("Couldn't get INQUIRY data (vendor, model, and revision).\n"); + } + } + + cdio_destroy(p_cdio); + + return 0; +} diff --git a/example/C++/mmc2.cpp b/example/C++/mmc2.cpp new file mode 100644 index 00000000..0105d278 --- /dev/null +++ b/example/C++/mmc2.cpp @@ -0,0 +1,187 @@ +/* + $Id: mmc2.cpp,v 1.3 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* A program to using the MMC interface to list CD and drive features + from the MMC GET_CONFIGURATION command . */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#include + +/* Set how long do wto wait for SCSI-MMC commands to complete */ +#define DEFAULT_TIMEOUT_MS 10000 + +int +main(int argc, const char *argv[]) +{ + CdIo_t *p_cdio; + + p_cdio = cdio_open (NULL, DRIVER_UNKNOWN); + + if (NULL == p_cdio) { + printf("Couldn't find CD\n"); + return 1; + } else { + int i_status; /* Result of MMC command */ + uint8_t buf[500] = { 0, }; /* Place to hold returned data */ + mmc_cdb_t cdb = {{0, }}; /* Command Descriptor Buffer */ + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_GET_CONFIGURATION); + CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(buf)); + cdb.field[1] = CDIO_MMC_GET_CONF_ALL_FEATURES; + cdb.field[3] = 0x0; + + i_status = mmc_run_cmd(p_cdio, 0, &cdb, SCSI_MMC_DATA_READ, sizeof(buf), + &buf); + if (i_status == 0) { + uint8_t *p; + uint32_t i_data; + uint8_t *p_max = buf + 65530; + + i_data = (unsigned int) CDIO_MMC_GET_LEN32(buf); + /* set to first sense feature code, and then walk through the masks */ + p = buf + 8; + while( (p < &(buf[i_data])) && (p < p_max) ) { + uint16_t i_feature; + uint8_t i_feature_additional = p[3]; + + i_feature = CDIO_MMC_GET_LEN16(p); + { + uint8_t *q; + const char *feature_str = mmc_feature2str(i_feature); + printf("%s Feature\n", feature_str); + switch( i_feature ) + { + case CDIO_MMC_FEATURE_PROFILE_LIST: + for ( q = p+4 ; q < p + i_feature_additional ; q += 4 ) { + int i_profile=CDIO_MMC_GET_LEN16(q); + const char *feature_profile_str = + mmc_feature_profile2str(i_profile); + printf( "\t%s", feature_profile_str ); + if (q[2] & 1) { + printf(" - on"); + } + printf("\n"); + } + printf("\n"); + + break; + case CDIO_MMC_FEATURE_CORE: + { + uint8_t *q = p+4; + uint32_t i_interface_standard = CDIO_MMC_GET_LEN32(q); + switch(i_interface_standard) { + case 0: + printf("\tunspecified interface\n"); + break; + case 1: + printf("\tSCSI interface\n"); + break; + case 2: + printf("\tATAPI interface\n"); + break; + case 3: + printf("\tIEEE 1394 interface\n"); + break; + case 4: + printf("\tIEEE 1394A interface\n"); + break; + case 5: + printf("\tFibre Channel interface\n"); + } + printf("\n"); + break; + } + case CDIO_MMC_FEATURE_REMOVABLE_MEDIUM: + switch(p[4] >> 5) { + case 0: + printf("\tCaddy/Slot type loading mechanism\n"); + break; + case 1: + printf("\tTray type loading mechanism\n"); + break; + case 2: + printf("\tPop-up type loading mechanism\n"); + break; + case 4: + printf("\tEmbedded changer with individually changeable discs\n"); + break; + case 5: + printf("\tEmbedded changer using a magazine mechanism\n"); + break; + default: + printf("\tUnknown changer mechanism\n"); + } + + printf("\tcan%s eject the medium or magazine via the normal " + "START/STOP command\n", + (p[4] & 8) ? "": "not"); + printf("\tcan%s be locked into the Logical Unit\n", + (p[4] & 1) ? "": "not"); + printf("\n"); + break; + case CDIO_MMC_FEATURE_CD_READ: + printf("CD Read Feature\n"); + printf("\tC2 Error pointers are %ssupported\n", + (p[4] & 2) ? "": "not "); + printf("\tCD-Text is %ssupported\n", + (p[4] & 1) ? "": "not "); + printf("\n"); + break; + case CDIO_MMC_FEATURE_CDDA_EXT_PLAY: + printf("\tSCAN command is %ssupported\n", + (p[4] & 4) ? "": "not "); + printf("\taudio channels can %sbe muted separately\n", + (p[4] & 2) ? "": "not "); + printf("\taudio channels can %shave separate volume levels\n", + (p[4] & 1) ? "": "not "); + { + uint8_t *q = p+6; + uint16_t i_vol_levels = CDIO_MMC_GET_LEN16(q); + printf("\t%d volume levels can be set\n", i_vol_levels); + } + printf("\n"); + break; + case CDIO_MMC_FEATURE_LU_SN: { + uint8_t i_serial = *(p+3); + char serial[257] = { '\0', }; + memcpy(serial, p+4, i_serial); + printf("\t%s\n\n", serial); + break; + } + default: + printf("\n"); + break; + } + p += i_feature_additional + 4; + } + } + } else { + printf("Didn't get all feature codes\n"); + } + } + + cdio_destroy(p_cdio); + + return 0; +} diff --git a/example/C++/paranoia.cpp b/example/C++/paranoia.cpp new file mode 100644 index 00000000..45207de5 --- /dev/null +++ b/example/C++/paranoia.cpp @@ -0,0 +1,119 @@ +/* + $Id: paranoia.cpp,v 1.3 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libcdio's version of the CD-DA paranoia. + library. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +int +main(int argc, const char *argv[]) +{ + cdrom_drive_t *d = NULL; /* Place to store handle given by cd-paranoia. */ + char **ppsz_cd_drives; /* List of all drives with a loaded CDDA in it. */ + + /* See if we can find a device with a loaded CD-DA in it. */ + ppsz_cd_drives = cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false); + + if (ppsz_cd_drives) { + /* Found such a CD-ROM with a CD-DA loaded. Use the first drive in + the list. */ + d=cdda_identify(*ppsz_cd_drives, 1, NULL); + } else { + printf("Unable find or access a CD-ROM drive with an audio CD in it.\n"); + exit(1); + } + + /* Don't need a list of CD's with CD-DA's any more. */ + cdio_free_device_list(ppsz_cd_drives); + + if ( !d ) { + printf("Unable to identify audio CD disc.\n"); + exit(1); + } + + /* We'll set for verbose paranoia messages. */ + cdda_verbose_set(d, CDDA_MESSAGE_PRINTIT, CDDA_MESSAGE_PRINTIT); + + if ( 0 != cdda_open(d) ) { + printf("Unable to open disc.\n"); + exit(1); + } + + /* Okay now set up to read up to the first 300 frames of the first + audio track of the Audio CD. */ + { + cdrom_paranoia_t *p = paranoia_init(d); + lsn_t i_first_lsn = cdda_disc_firstsector(d); + + if ( -1 == i_first_lsn ) { + printf("Trouble getting starting LSN\n"); + } else { + lsn_t i_cursor; + track_t i_track = cdda_sector_gettrack(d, i_first_lsn); + lsn_t i_last_lsn = cdda_track_lastsector(d, i_track); + + /* For demo purposes we'll read only 300 frames (about 4 + seconds). We don't want this to take too long. On the other + hand, I suppose it should be something close to a real test. + */ + if ( i_last_lsn - i_first_lsn > 300) i_last_lsn = i_first_lsn + 299; + + printf("Reading track %d from LSN %ld to LSN %ld\n", i_track, + (long int) i_first_lsn, (long int) i_last_lsn); + + /* Set reading mode for full paranoia, but allow skipping sectors. */ + paranoia_modeset(p, PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP); + + paranoia_seek(p, i_first_lsn, SEEK_SET); + + for ( i_cursor = i_first_lsn; i_cursor <= i_last_lsn; i_cursor ++) { + /* read a sector */ + int16_t *p_readbuf=paranoia_read(p, NULL); + char *psz_err=cdda_errors(d); + char *psz_mes=cdda_messages(d); + + if (psz_mes || psz_err) + printf("%s%s\n", psz_mes ? psz_mes: "", psz_err ? psz_err: ""); + + if (psz_err) free(psz_err); + if (psz_mes) free(psz_mes); + if( !p_readbuf ) { + printf("paranoia read error. Stopping.\n"); + break; + } + } + } + paranoia_free(p); + } + + cdda_close(d); + + exit(0); +} diff --git a/example/C++/paranoia2.cpp b/example/C++/paranoia2.cpp new file mode 100644 index 00000000..8ccb6f49 --- /dev/null +++ b/example/C++/paranoia2.cpp @@ -0,0 +1,101 @@ +/* + $Id: paranoia2.cpp,v 1.2 2008/03/24 15:30:57 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libcdio's version of the CD-DA + paranoia library. But in this version, we'll open a cdio object before + calling paranoia's open. I imagine in many cases such as media + players this may be what will be done since, one may want to get + CDDB/CD-Text info beforehand. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +int +main(int argc, const char *argv[]) +{ + cdrom_drive_t *d = NULL; /* Place to store handle given by cd-paranoia. */ + char **ppsz_cd_drives; /* List of all drives with a loaded CDDA in it. */ + CdIo_t *p_cdio = NULL; + + /* See if we can find a device with a loaded CD-DA in it. */ + ppsz_cd_drives = cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false); + + if (ppsz_cd_drives) { + /* Found such a CD-ROM with a CD-DA loaded. Use the first drive in + the list. */ + p_cdio = cdio_open(*ppsz_cd_drives, DRIVER_UNKNOWN); + d=cdio_cddap_identify_cdio(p_cdio, 1, NULL); + } else { + printf("Unable find or access a CD-ROM drive with an audio CD in it.\n"); + exit(1); + } + + /* Don't need a list of CD's with CD-DA's any more. */ + cdio_free_device_list(ppsz_cd_drives); + + if ( !d ) { + printf("Unable to identify audio CD disc.\n"); + exit(1); + } + + /* We'll set for verbose paranoia messages. */ + cdio_cddap_verbose_set(d, CDDA_MESSAGE_PRINTIT, CDDA_MESSAGE_PRINTIT); + + if ( 0 != cdio_cddap_open(d) ) { + printf("Unable to open disc.\n"); + exit(1); + } + + /* In the paranoia example was a reading. Here we are going to do + something trivial (but I think neat any way - get the Endian-ness + of the drive. */ + { + const int i_endian = data_bigendianp(d); + switch (i_endian) { + case 0: + printf("Drive returns audio data Little Endian." + " Your drive is like most.\n"); + break; + case 1: + printf("Drive returns audio data Big Endian.\n"); + break; + case -1: + printf("Don't know whether drive is Big or Little Endian.\n"); + break; + default: + printf("Whoah - got a return result I'm not expecting %d.\n", + i_endian); + break; + } + } + + cdio_cddap_close_no_free_cdio(d); + cdio_destroy( p_cdio ); + + exit(0); +} diff --git a/example/Makefile.am b/example/Makefile.am new file mode 100644 index 00000000..6f38b041 --- /dev/null +++ b/example/Makefile.am @@ -0,0 +1,97 @@ +# $Id: Makefile.am,v 1.44 2008/08/31 13:38:21 flameeyes Exp $ +# +# Copyright (C) 2003, 2004, 2005, 2006, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +#################################################### +# Sample programs +#################################################### +# +if ENABLE_CPP +SUBDIRS = C++ +endif +if BUILD_CD_PARANOIA +paranoia_progs = paranoia paranoia2 +endif +if BUILD_EXAMPLES +noinst_PROGRAMS = audio cdchange cdtext device drives eject \ + isofile isofile2 isofuzzy isolist isolsn \ + mmc1 mmc2 mmc2a mmc3 $(paranoia_progs) tracks \ + sample3 sample4 udf1 udffile cdio-eject +endif + +INCLUDES = -I$(top_srcdir) $(LIBCDIO_CFLAGS) + +audio_DEPENDENCIES = $(LIBCDIO_DEPS) +audio_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) + +cdchange_DEPENDENCIES = $(LIBCDIO_DEPS) +cdchange_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) + +cdtext_DEPENDENCIES = $(LIBCDIO_DEPS) +cdtext_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) + +device_DEPENDENCIES = $(LIBCDIO_DEPS) +device_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) + +drives_DEPENDENCIES = $(LIBCDIO_DEPS) +drives_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) + +eject_DEPENDENCIES = $(LIBCDIO_DEPS) +eject_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) + +cdio_eject_DEPENDENCIES = $(LIBCDIO_DEPS) +cdio_eject_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) + +if BUILD_CD_PARANOIA +paranoia_LDADD = $(LIBCDIO_PARANOIA_LIBS) $(LIBCDIO_CDDA_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +paranoia2_LDADD = $(LIBCDIO_PARANOIA_LIBS) $(LIBCDIO_CDDA_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +endif + +isofile_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +isofile2_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +isofuzzy_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +isolist_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +isolsn_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) + +mmc1_DEPENDENCIES = $(LIBCDIO_DEPS) +mmc1_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) +mmc2_DEPENDENCIES = $(LIBCDIO_DEPS) +mmc2_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) + +mmc2a_DEPENDENCIES = $(LIBCDIO_DEPS) +mmc2a_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) + +mmc3_DEPENDENCIES = $(LIBCDIO_DEPS) +mmc3_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) + +sample3_DEPENDENCIES = $(LIBCDIO_DEPS) +sample3_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) + +sample4_DEPENDENCIES = $(LIBCDIO_DEPS) +sample4_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) + +tracks_DEPENDENCIES = $(LIBCDIO_DEPS) +tracks_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) + +udf1_DEPENDENCIES = $(LIBUDF_LIBS) $(LIBCDIO_DEPS) +udf1_LDADD = $(LIBUDF_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) + +udffile_DEPENDENCIES = $(LIBUDF_LIBS) $(LIBCDIO_DEPS) +udffile_LDADD = $(LIBUDF_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) + + +# iso programs create file "copying" +MOSTLYCLEANFILES = copying diff --git a/example/README b/example/README deleted file mode 100644 index 1db482bd..00000000 --- a/example/README +++ /dev/null @@ -1,86 +0,0 @@ -$Id: README,v 1.28 2007/08/12 00:56:10 rocky Exp $ - -This directory contains some simple examples of the use of the libcdio -library. - -One might also possibly find useful C code among the regression tests -(directory test), e.g. testbincue.c, testdefault.c, testiso9660.c, -testparanoia.c, or testtoc.c - -Larger more-complicated examples are the cd-drive, cd-info, cd-read, -cdda-player, iso-info and iso-read programs in the src directory. - -And going further there's the cd-paranoia program (in -src/cd-paranoia), and "real-world' code in the xine VCD plugin, or the -vlc CD-DA plugin which are part of those distributions. - -Descriptions of the programs in this example directory are as follows... - -audio.c: Sample program to show audio controls. - -cdchange.c: A program to test if a CD has been changed since the last - change test. - -cdio-eject.c: a stripped-down "eject" command to open or close a CDROM - tray - -cdtext.c: A program to show CD-Text and CD disc mode info. - -drives.c: A program to show drivers installed and what the default - CD-ROM drive is and what CD drives are available. - -eject.c: A program eject a CD from a CD-ROM drive and then close the door - again. - -isofile.c: A program to show using libiso9660 to extract a file from an - ISO-9660 image. - -isofile2.c: A program to show using libiso9660 to extract a file - from a CDRWIN cue/bin CD image. - -isofuzzy.c : A program showing fuzzy ISO-9660 detection/reading. - -isolist.c: A program to show using libiso9660 to list files in a - directory of an ISO-9660 image and give basic iso9660 - information. - -isolsn.c: A program to show using libiso9660 to get the file - path for a given LSN. - -mmc1.c: A program to show issuing a simple MMC command (INQUIRY). - -mmc2.c: A more involved MMC command to list features from - a MMC GET_CONFIGURATION command. - -mmc2a.c: Show MODE_SENSE page 2A paramaters: - CD/DVD Capabilities and Mechanical Status Page - -paranoia: A program to show using CD-DA paranoia (a library for jitter - detection and audio-read error correction). This program uses - an interface compatible (mostly) with cdparanoia. - -paranoia2: Another program to show using CD-DA paranoia using a more - libcdio-oriented initialization. Probably more suited to - things that otherwise use libcdio such as media players - (e.g. for getting CDDB or CD-Text info) - -sample2.c: A simple program to show drivers installed and what the - default CD-ROM drive is. - -sample3.c: A simple program to show the use of cdio_guess_cd_type(). - Figure out the kind of CD image we've got. - -sample4.c: A slightly improved sample3 program: we handle cdio logging - and take an optional CD-location. - -tracks.c: A program to list track numbers and logical sector - numbers of a Compact Disc using libcdio. - -udf1.c: A program to show using libudf to list files in a directory of - an UDF image. - -udf2.c: A program to show using libudf to extract a file from - an UDF image. - -Many of the above programs can be compiled in C++. See that directory -for C++ examples which include some of the above. diff --git a/example/audio.c b/example/audio.c new file mode 100644 index 00000000..1d13edfc --- /dev/null +++ b/example/audio.c @@ -0,0 +1,490 @@ +/* + $Id: audio.c,v 1.10 2008/06/19 15:44:10 flameeyes Exp $ + + Copyright (C) 2005, 2008 Rocky Bernstein + + Adapted from Gerd Knorr's player.c program + Copyright (C) 1997, 1998 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* A program to show use of audio controls. For a more expanded + CDDA player program using curses display see cdda-player in this + distribution. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#include + +#include + +#ifdef HAVE_GETOPT_H +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +#include +#include +#include +#include + +static bool play_track(track_t t1, track_t t2); + +static CdIo_t *p_cdio = NULL; /* libcdio handle */ +static driver_id_t driver_id = DRIVER_DEVICE; + +/* cdrom data */ +static track_t i_first_track; +static track_t i_last_track; +static track_t i_first_audio_track; +static track_t i_last_audio_track; +static track_t i_tracks; +static msf_t toc[CDIO_CDROM_LEADOUT_TRACK+1]; +static cdio_subchannel_t sub; /* subchannel last time read */ +static int i_data; /* # of data tracks present ? */ +static int start_track = 0; +static int stop_track = 0; +static int one_track = 0; + +static bool b_cd = false; +static bool auto_mode = false; +static bool b_verbose = false; +static bool debug = false; +static bool b_record = false; /* we have a record for + the inserted CD */ + +static char *psz_device=NULL; +static char *psz_program; + +inline static void +xperror(const char *psz_msg) +{ + if (b_verbose) { + fprintf(stderr, "error: "); + perror(psz_msg); + } + return; +} + + +static void +oops(const char *psz_msg, int rc) +{ + cdio_destroy (p_cdio); + free (psz_device); + exit (rc); +} + +/* ---------------------------------------------------------------------- */ + +/*! Stop playing audio CD */ +static bool +cd_stop(CdIo_t *p_cdio) +{ + bool b_ok = true; + if (b_cd && p_cdio) { + i_last_audio_track = CDIO_INVALID_TRACK; + b_ok = DRIVER_OP_SUCCESS == cdio_audio_stop(p_cdio); + if ( !b_ok ) + xperror("stop"); + } + return b_ok; +} + +/*! Eject CD */ +static bool +cd_eject(void) +{ + bool b_ok = true; + if (p_cdio) { + cd_stop(p_cdio); + b_ok = DRIVER_OP_SUCCESS == cdio_eject_media(&p_cdio); + if (!b_ok) + xperror("eject"); + b_cd = false; + p_cdio = NULL; + } + return b_ok; +} + +/*! Close CD tray */ +static bool +cd_close(const char *psz_device) +{ + bool b_ok = true; + if (!b_cd) { + b_ok = DRIVER_OP_SUCCESS == cdio_close_tray(psz_device, &driver_id); + if (!b_ok) + xperror("close"); + } + return b_ok; +} + +/*! Pause playing audio CD */ +static bool +cd_pause(CdIo_t *p_cdio) +{ + bool b_ok = true; + if (sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) { + b_ok = DRIVER_OP_SUCCESS == cdio_audio_pause(p_cdio); + if (!b_ok) + xperror("pause"); + } + return b_ok; +} + +/*! Get status/track/position info of an audio CD */ +static bool +read_subchannel(CdIo_t *p_cdio) +{ + bool b_ok = true; + if (!b_cd) return false; + + b_ok = DRIVER_OP_SUCCESS == cdio_audio_read_subchannel(p_cdio, &sub); + if (!b_ok) { + xperror("read subchannel"); + b_cd = 0; + } + if (auto_mode && sub.audio_status == CDIO_MMC_READ_SUB_ST_COMPLETED) + cd_eject(); + return b_ok; +} + +/*! Read CD TOC and set CD information. */ +static void +read_toc(CdIo_t *p_cdio) +{ + track_t i; + + i_first_track = cdio_get_first_track_num(p_cdio); + i_last_track = cdio_get_last_track_num(p_cdio); + i_tracks = cdio_get_num_tracks(p_cdio); + i_first_audio_track = i_first_track; + i_last_audio_track = i_last_track; + + + if ( CDIO_INVALID_TRACK == i_first_track || + CDIO_INVALID_TRACK == i_last_track ) { + xperror("read toc header"); + b_cd = false; + b_record = false; + } else { + b_cd = true; + i_data = 0; + for (i = i_first_track; i <= i_last_track+1; i++) { + if ( !cdio_get_track_msf(p_cdio, i, &(toc[i])) ) + { + xperror("read toc entry"); + b_cd = false; + return; + } + if ( TRACK_FORMAT_AUDIO != cdio_get_track_format(p_cdio, i) ) { + if ((i != i_last_track+1) ) { + i_data++; + if (i == i_first_track) { + if (i == i_last_track) + i_first_audio_track = CDIO_CDROM_LEADOUT_TRACK; + else + i_first_audio_track++; + } + } + } + } + b_record = true; + read_subchannel(p_cdio); + if (auto_mode && sub.audio_status != CDIO_MMC_READ_SUB_ST_PLAY) + play_track(1, CDIO_CDROM_LEADOUT_TRACK); + } +} + +/*! Play an audio track. */ +static bool +play_track(track_t i_start_track, track_t i_end_track) +{ + bool b_ok = true; + + if (!b_cd) { + cd_close(psz_device); + read_toc(p_cdio); + } + + read_subchannel(p_cdio); + if (!b_cd || i_first_track == CDIO_CDROM_LEADOUT_TRACK) + return false; + + if (debug) + fprintf(stderr,"play tracks: %d-%d => ", i_start_track, i_end_track); + if (i_start_track < i_first_track) i_start_track = i_first_track; + if (i_start_track > i_last_audio_track) i_start_track = i_last_audio_track; + if (i_end_track < i_first_track) i_end_track = i_first_track; + if (i_end_track > i_last_audio_track) i_end_track = i_last_audio_track; + if (debug) + fprintf(stderr,"%d-%d\n",i_start_track, i_end_track); + + cd_pause(p_cdio); + b_ok = (DRIVER_OP_SUCCESS == cdio_audio_play_msf(p_cdio, + &(toc[i_start_track]), + &(toc[i_end_track])) ); + if (!b_ok) xperror("play"); + return b_ok; +} + +static void +usage(char *prog) +{ + fprintf(stderr, + "%s is a simple interface to issuing CD audio comamnds\n" + "\n" + "usage: %s [options] [device]\n" + "\n" + "default for to search for a CD-ROM device with a CD-DA loaded\n" + "\n" + "These command line options available:\n" + " -h print this help\n" + " -a start up in auto-mode\n" + " -v verbose\n" + "\n" + " Use only one of these:\n" + " -C close CD-ROM tray. If you use this option,\n" + " a CD-ROM device name must be specified.\n" + " -p play the whole CD\n" + " -t n play track >n<\n" + " -t a-b play all tracks between a and b (inclusive)\n" + " -L set volume level\n" + " -s stop playing\n" + " -S list audio subchannel information\n" + " -e eject cdrom\n" + "\n" + "That's all. Oh, maybe a few words more about the auto-mode. This\n" + "is the 'dont-touch-any-key' feature. You load a CD, player starts\n" + "to play it, and when it is done it ejects the CD. Start it that\n" + "way on a spare console and forget about it...\n" + "\n" + "(c) 1997,98 Gerd Knorr \n" + "(c) 2005 Rocky Bernstein \n" + , prog, prog); +} + +typedef enum { + NO_OP=0, + PLAY_CD=1, + PLAY_TRACK=2, + STOP_PLAYING=3, + EJECT_CD=4, + CLOSE_CD=5, + SET_VOLUME=6, + LIST_SUBCHANNEL=7, +} cd_operation_t; + +int +main(int argc, char *argv[]) +{ + int c, nostop=0; + char *h; + int i_rc = 0; + int i_volume_level = -1; + cd_operation_t todo = NO_OP; /* operation to do in non-interactive mode */ + + psz_program = strrchr(argv[0],'/'); + psz_program = psz_program ? psz_program+1 : argv[0]; + + /* parse options */ + while ( 1 ) { + if (-1 == (c = getopt(argc, argv, "aCdehkpL:sSt:vx"))) + break; + switch (c) { + case 'v': + b_verbose = true; + break; + case 'd': + debug = 1; + break; + case 'a': + auto_mode = 1; + break; + case 'L': + if (NULL != (h = strchr(optarg,'-'))) { + i_volume_level = atoi(optarg); + todo = SET_VOLUME; + } + case 't': + if (NULL != (h = strchr(optarg,'-'))) { + *h = 0; + start_track = atoi(optarg); + stop_track = atoi(h+1)+1; + if (0 == start_track) start_track = 1; + if (1 == stop_track) stop_track = CDIO_CDROM_LEADOUT_TRACK; + } else { + start_track = atoi(optarg); + stop_track = start_track+1; + one_track = 1; + } + todo = PLAY_TRACK; + break; + case 'p': + todo = PLAY_CD; + break; + case 'C': + todo = CLOSE_CD; + break; + break; + case 's': + todo = STOP_PLAYING; + break; + case 'S': + todo = LIST_SUBCHANNEL; + break; + case 'e': + todo = EJECT_CD; + break; + case 'h': + usage(psz_program); + exit(1); + default: + usage(psz_program); + exit(1); + } + } + + if (argc > optind) { + psz_device = strdup(argv[optind]); + } else { + char **ppsz_cdda_drives=NULL; + char **ppsz_all_cd_drives = cdio_get_devices_ret(&driver_id); + + if (!ppsz_all_cd_drives) { + fprintf(stderr, "Can't find a CD-ROM drive\n"); + exit(2); + } + ppsz_cdda_drives = cdio_get_devices_with_cap(ppsz_all_cd_drives, + CDIO_FS_AUDIO, false); + if (!ppsz_cdda_drives || !ppsz_cdda_drives[0]) { + fprintf(stderr, "Can't find a CD-ROM drive with a CD-DA in it\n"); + exit(3); + } + psz_device = strdup(ppsz_cdda_drives[0]); + cdio_free_device_list(ppsz_all_cd_drives); + cdio_free_device_list(ppsz_cdda_drives); + } + + if (!b_cd && todo != EJECT_CD) { + cd_close(psz_device); + } + + /* open device */ + if (b_verbose) + fprintf(stderr,"open %s... ", psz_device); + + p_cdio = cdio_open (psz_device, driver_id); + + if (!p_cdio) { + if (b_verbose) + fprintf(stderr, "error: %s\n", strerror(errno)); + else + fprintf(stderr, "open %s: %s\n", psz_device, strerror(errno)); + exit(1); + } else + if (b_verbose) + fprintf(stderr,"ok\n"); + + { + nostop=1; + if (EJECT_CD == todo) { + i_rc = cd_eject() ? 0 : 1; + } else { + read_toc(p_cdio); + if (!b_cd) { + cd_close(psz_device); + read_toc(p_cdio); + } + if (b_cd) + switch (todo) { + case NO_OP: + break; + case STOP_PLAYING: + i_rc = cd_stop(p_cdio) ? 0 : 1; + break; + case EJECT_CD: + /* Should have been handled above. */ + cd_eject(); + break; + case PLAY_TRACK: + /* play just this one track */ + play_track(start_track, stop_track); + break; + case PLAY_CD: + play_track(1,CDIO_CDROM_LEADOUT_TRACK); + break; + case CLOSE_CD: + i_rc = cdio_close_tray(psz_device, NULL) ? 0 : 1; + break; + case SET_VOLUME: + { + cdio_audio_volume_t volume; + volume.level[0] = i_volume_level; + i_rc = (DRIVER_OP_SUCCESS == cdio_audio_set_volume(p_cdio, + &volume)) + ? 0 : 1; + break; + } + case LIST_SUBCHANNEL: + if (read_subchannel(p_cdio)) { + if (sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED || + sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) { + { + printf("track %2d - %02x:%02x (%02x:%02x abs) ", + sub.track, sub.rel_addr.m, sub.rel_addr.s, + sub.abs_addr.m, sub.abs_addr.s); + } + } + printf("drive state: %s\n", + mmc_audio_state2str(sub.audio_status)); + } else { + i_rc = 1; + } + break; + } + else { + fprintf(stderr,"no CD in drive (%s)\n", psz_device); + } + } + } + + if (!nostop) cd_stop(p_cdio); + oops("bye", i_rc); + + return 0; /* keep compiler happy */ +} diff --git a/example/cdchange.c b/example/cdchange.c new file mode 100644 index 00000000..a961180d --- /dev/null +++ b/example/cdchange.c @@ -0,0 +1,100 @@ +/* + $Id: cdchange.c,v 1.9 2008/06/25 08:01:53 rocky Exp $ + + Copyright (C) 2005, 2006, 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Test media changed */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#ifdef HAVE_LIMITS_H +#include +#endif + +#ifdef HAVE_STDLIB_H +# include +#endif + +#ifdef HAVE_STRING_H +# include +#endif + +#ifdef HAVE_ERRNO_H +# include +#endif + +#ifdef HAVE_WINDOWS_H +# include +#endif + +#ifndef HAVE_SLEEP +static void +sleep(unsigned int ms) +{ +#if defined(_WIN32) + Sleep(ms); +#else +#error sleep() unimplemented +#endif +} +#endif + +#include +int +main(int argc, const char *argv[]) +{ + CdIo_t *p_cdio; + unsigned long i_sleep=30; + + if (argc > 1) { + p_cdio = cdio_open (argv[1], DRIVER_DEVICE); + if (argc > 2) { + errno = 0; + i_sleep = strtol(argv[2], (char **)NULL, 10); + if ( (LONG_MIN == i_sleep || LONG_MAX == i_sleep) && errno != 0 ) { + printf("Invalid sleep parameter %s\n", argv[2]); + printf("Error reported back from strtol: %s\n", strerror(errno)); + return 2; + } + } + } else { + p_cdio = cdio_open (NULL, DRIVER_DEVICE); + } + + if (NULL == p_cdio) { + printf("Couldn't find a driver.. leaving.\n"); + return 1; + } + + if (cdio_get_media_changed(p_cdio)) + printf("Initial media status: changed\n"); + else + printf("Initial media status: not changed\n"); + + printf("Giving you %lu seconds to change CD if you want to do so.\n", + i_sleep); + sleep(30); + if (cdio_get_media_changed(p_cdio)) + printf("Media status: changed\n"); + else + printf("Media status: not changed\n"); + + cdio_destroy(p_cdio); + return 0; +} diff --git a/example/cdio-eject.c b/example/cdio-eject.c new file mode 100644 index 00000000..911424bc --- /dev/null +++ b/example/cdio-eject.c @@ -0,0 +1,82 @@ +/* + $Id: cdio-eject.c,v 1.4 2008/03/24 15:30:55 karl Exp $ + + Copyright (C) 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +static void usage(char * progname) + { + fprintf(stderr, "Usage: %s [-t] \n", progname); + } + +int main(int argc, char ** argv) + { + driver_return_code_t err; + int close_tray = 0; + const char * device = NULL; + + if(argc < 2 || argc > 3) + { + usage(argv[0]); + return -1; + } + + if((argc == 3) && strcmp(argv[1], "-t")) + { + usage(argv[0]); + return -1; + } + + if(argc == 2) + device = argv[1]; + else if(argc == 3) + { + close_tray = 1; + device = argv[2]; + } + + if(close_tray) + { + err = cdio_close_tray(device, NULL); + if(err) + { + fprintf(stderr, "Closing tray failed for device %s: %s\n", + device, cdio_driver_errmsg(err)); + return -1; + } + } + else + { + err = cdio_eject_media_drive(device); + if(err) + { + fprintf(stderr, "Ejecting failed for device %s: %s\n", + device, cdio_driver_errmsg(err)); + return -1; + } + } + + return 0; + + } diff --git a/example/cdtext.c b/example/cdtext.c new file mode 100644 index 00000000..9a0b2fbf --- /dev/null +++ b/example/cdtext.c @@ -0,0 +1,101 @@ +/* + $Id: cdtext.c,v 1.5 2008/03/24 15:30:55 karl Exp $ + + Copyright (C) 2004, 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to list CD-Text info of a Compact Disc using + libcdio. See also corresponding C++ programs of similar names. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +#include + + +static void +print_cdtext_track_info(CdIo_t *p_cdio, track_t i_track, const char *psz_msg) { + const cdtext_t *cdtext = cdio_get_cdtext(p_cdio, 0); + if (NULL != cdtext) { + cdtext_field_t i; + + printf("%s\n", psz_msg); + + for (i=0; i < MAX_CDTEXT_FIELDS; i++) { + if (cdtext->field[i]) { + printf("\t%s: %s\n", cdtext_field2str(i), cdtext->field[i]); + } + } + } + +} + +static void +print_disc_info(CdIo_t *p_cdio, track_t i_tracks, track_t i_first_track) { + track_t i_last_track = i_first_track+i_tracks; + discmode_t cd_discmode = cdio_get_discmode(p_cdio); + + printf("%s\n", discmode2str[cd_discmode]); + + print_cdtext_track_info(p_cdio, 0, "\nCD-Text for Disc:"); + for ( ; i_first_track < i_last_track; i_first_track++ ) { + char psz_msg[50]; + snprintf(psz_msg, sizeof(psz_msg), "CD-Text for Track %d:", i_first_track); + print_cdtext_track_info(p_cdio, i_first_track, psz_msg); + } +} + +int +main(int argc, const char *argv[]) +{ + track_t i_first_track; + track_t i_tracks; + CdIo_t *p_cdio = cdio_open ("../test/cdda.cue", DRIVER_BINCUE); + + + if (NULL == p_cdio) { + printf("Couldn't open ../test/cdda.cue with BIN/CUE driver.\n"); + } else { + i_first_track = cdio_get_first_track_num(p_cdio); + i_tracks = cdio_get_num_tracks(p_cdio); + print_disc_info(p_cdio, i_tracks, i_first_track); + cdio_destroy(p_cdio); + } + + p_cdio = cdio_open (NULL, DRIVER_DEVICE); + i_first_track = cdio_get_first_track_num(p_cdio); + i_tracks = cdio_get_num_tracks(p_cdio); + + if (NULL == p_cdio) { + printf("Couldn't find CD\n"); + return 1; + } else { + print_disc_info(p_cdio, i_tracks, i_first_track); + } + + cdio_destroy(p_cdio); + + return 0; +} diff --git a/example/device.c b/example/device.c new file mode 100644 index 00000000..6a7b8f8e --- /dev/null +++ b/example/device.c @@ -0,0 +1,135 @@ +/* + $Id: device.c,v 1.3 2008/03/24 15:30:55 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show drivers installed and what the default + CD-ROM drive is. See also corresponding C++ programs of similar + names .*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include + +#define _(x) x + +/* Prints out drive capabilities */ +static void +print_drive_capabilities(cdio_drive_read_cap_t i_read_cap, + cdio_drive_write_cap_t i_write_cap, + cdio_drive_misc_cap_t i_misc_cap) +{ + if (CDIO_DRIVE_CAP_ERROR == i_misc_cap) { + printf("Error in getting drive hardware properties\n"); + } else { + printf(_("Hardware : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_FILE + ? "Disk Image" : "CD-ROM or DVD"); + printf(_("Can eject : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_EJECT ? "Yes" : "No"); + printf(_("Can close tray : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_CLOSE_TRAY ? "Yes" : "No"); + printf(_("Can disable manual eject : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_LOCK ? "Yes" : "No"); + printf(_("Can select juke-box disc : %s\n\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_SELECT_DISC ? "Yes" : "No"); + + printf(_("Can set drive speed : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_SELECT_SPEED ? "Yes" : "No"); + printf(_("Can detect if CD changed : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED ? "Yes" : "No"); + printf(_("Can read multiple sessions : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_MULTI_SESSION ? "Yes" : "No"); + printf(_("Can hard reset device : %s\n\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_RESET ? "Yes" : "No"); + } + + + if (CDIO_DRIVE_CAP_ERROR == i_read_cap) { + printf("Error in getting drive reading properties\n"); + } else { + printf("Reading....\n"); + printf(_(" Can play audio : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_AUDIO ? "Yes" : "No"); + printf(_(" Can read CD-R : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_CD_R ? "Yes" : "No"); + printf(_(" Can read CD-RW : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_CD_RW ? "Yes" : "No"); + printf(_(" Can read DVD-ROM : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_DVD_ROM ? "Yes" : "No"); + } + + + if (CDIO_DRIVE_CAP_ERROR == i_write_cap) { + printf("Error in getting drive writing properties\n"); + } else { + printf("\nWriting....\n"); + printf(_(" Can write CD-RW : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_CD_RW ? "Yes" : "No"); + printf(_(" Can write DVD-R : %s\n"), + i_write_cap & CDIO_DRIVE_CAP_READ_DVD_R ? "Yes" : "No"); + printf(_(" Can write DVD-RAM : %s\n"), + i_write_cap & CDIO_DRIVE_CAP_READ_DVD_RAM ? "Yes" : "No"); + } +} + +int +main(int argc, const char *argv[]) +{ + CdIo_t *p_cdio = cdio_open (NULL, DRIVER_UNKNOWN); + + if (NULL != p_cdio) { + char *default_device = cdio_get_default_device(p_cdio); + cdio_drive_read_cap_t i_read_cap; + cdio_drive_write_cap_t i_write_cap; + cdio_drive_misc_cap_t i_misc_cap; + + printf("The driver selected is %s\n", cdio_get_driver_name(p_cdio)); + + if (default_device) + printf("The default device for this driver is %s\n", default_device); + + cdio_get_drive_cap(p_cdio, &i_read_cap, &i_write_cap, &i_misc_cap); + print_drive_capabilities(i_read_cap, i_write_cap, i_misc_cap); + + free(default_device); + cdio_destroy(p_cdio); + printf("\n"); + + } else { + printf("Problem in trying to find a driver.\n\n"); + } + + { + driver_id_t driver_id; + for (driver_id=CDIO_MIN_DRIVER; driver_id<=CDIO_MAX_DRIVER; driver_id++) + if (cdio_have_driver(driver_id)) + printf("We have: %s\n", cdio_driver_describe(driver_id)); + else + printf("We don't have: %s\n", cdio_driver_describe(driver_id)); + } + return 0; +} diff --git a/example/drives.c b/example/drives.c new file mode 100644 index 00000000..a0ceb19a --- /dev/null +++ b/example/drives.c @@ -0,0 +1,93 @@ +/* + $Id: drives.c,v 1.6 2008/03/24 15:30:55 karl Exp $ + + Copyright (C) 2003, 2004, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show drivers installed and what the default + CD-ROM drive is and what CD drives are available. */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +#include +#include + +static void +log_handler (cdio_log_level_t level, const char message[]) +{ + switch(level) { + case CDIO_LOG_DEBUG: + case CDIO_LOG_INFO: + return; + default: + printf("cdio %d message: %s\n", level, message); + } +} + +static void +print_drive_class(const char *psz_msg, cdio_fs_anal_t bitmask, bool b_any) { + char **ppsz_cd_drives=NULL, **c; + + printf("%s...\n", psz_msg); + ppsz_cd_drives = cdio_get_devices_with_cap(NULL, bitmask, b_any); + if (NULL != ppsz_cd_drives) + for( c = ppsz_cd_drives; *c != NULL; c++ ) { + printf("Drive %s\n", *c); + } + + cdio_free_device_list(ppsz_cd_drives); + printf("-----\n"); +} + +int +main(int argc, const char *argv[]) +{ + char **ppsz_cd_drives=NULL, **c; + + cdio_log_set_handler (log_handler); + + /* Print out a list of CD-drives */ + ppsz_cd_drives = cdio_get_devices(DRIVER_DEVICE); + if (NULL != ppsz_cd_drives) + for( c = ppsz_cd_drives; *c != NULL; c++ ) { + printf("Drive %s\n", *c); + } + + cdio_free_device_list(ppsz_cd_drives); + ppsz_cd_drives = NULL; + + printf("-----\n"); + + /* Print out a list of CD-drives the harder way. */ + print_drive_class("All CD-ROM drives (again)", CDIO_FS_MATCH_ALL, false); + print_drive_class("CD-ROM drives with a CD-DA loaded...", + CDIO_FS_AUDIO, false); + print_drive_class("CD-ROM drives with some sort of ISO 9660 filesystem...", + CDIO_FS_ANAL_ISO9660_ANY, true); + print_drive_class("(S)VCD drives...", CDIO_FS_ANAL_VCD_ANY, true); + return 0; + +} diff --git a/example/eject.c b/example/eject.c new file mode 100644 index 00000000..1504bcf7 --- /dev/null +++ b/example/eject.c @@ -0,0 +1,101 @@ +/* + $Id: eject.c,v 1.5 2008/03/24 15:30:55 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to eject a CD-ROM drive door and then close it again. + + If a single argument is given, it is used as the CD-ROM device to + eject/close. Otherwise a CD-ROM drive will be scanned for. + + See also corresponding C++ program of a similar name. +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#ifdef HAVE_STRING_H +#include +#endif + +int +main(int argc, const char *argv[]) +{ + driver_return_code_t ret; + driver_id_t driver_id = DRIVER_DEVICE; + char *psz_drive = NULL; + + if (argc > 1) + psz_drive = strdup(argv[1]); + + if (!psz_drive) { + psz_drive = cdio_get_default_device_driver(&driver_id); + if (!psz_drive) { + printf("Can't find a CD-ROM to eject\n"); + exit(1); + } + } + ret = cdio_eject_media_drive(psz_drive); + switch(ret) { + case DRIVER_OP_UNSUPPORTED: + printf("Eject not supported for %s.\n", psz_drive); + break; + case DRIVER_OP_SUCCESS: + printf("CD-ROM drive %s ejected.\n", psz_drive); + break; + default: + printf("Eject of CD-ROM drive %s failed.\n", psz_drive); + break; + } + + if (DRIVER_OP_SUCCESS == cdio_close_tray(psz_drive, &driver_id)) { + printf("Closed tray of CD-ROM drive %s.\n", psz_drive); + } else { + printf("Closing tray of CD-ROM drive %s failed.\n", psz_drive); + } + free(psz_drive); + + ret = cdio_eject_media_drive(NULL); + switch(ret) { + case DRIVER_OP_UNSUPPORTED: + printf("Eject not supported for default device.\n"); + break; + case DRIVER_OP_SUCCESS: + printf("CD-ROM drive ejected for default device.\n"); + break; + default: + printf("Eject of CD-ROM drive failed for default device.\n"); + break; + } + + driver_id = DRIVER_DEVICE; + if (DRIVER_OP_SUCCESS == cdio_close_tray(NULL, &driver_id)) { + printf("Closed tray of CD-ROM drive for default disc driver:\n\t%s\n", + cdio_driver_describe(driver_id)); + } else { + printf("Closing tray of CD-ROM drive failed for default " + "disc driver:\n\t%s\n", cdio_driver_describe(driver_id)); + } + + return 0; +} diff --git a/example/isofile.c b/example/isofile.c new file mode 100644 index 00000000..f95f27f5 --- /dev/null +++ b/example/isofile.c @@ -0,0 +1,161 @@ +/* + $Id: isofile.c,v 1.2 2008/03/24 15:30:55 karl Exp $ + + Copyright (C) 2004, 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libiso9660 to extract a file from an + ISO-9660 image. + + If a single argument is given, it is used as the ISO 9660 image to + use in the extraction. Otherwise a compiled in default ISO 9660 image + name (that comes with the libcdio distribution) will be used. + */ + +/* This is the ISO 9660 image. */ +#define ISO9660_IMAGE_PATH "../" +#define ISO9660_IMAGE ISO9660_IMAGE_PATH "test/copying.iso" + +#define LOCAL_FILENAME "copying" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "portable.h" + +#include +#include +#include + +#include + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define CEILING(x, y) ((x+(y-1))/y) + +#define my_exit(rc) \ + fclose (p_outfd); \ + free(p_statbuf); \ + iso9660_close(p_iso); \ + return rc; \ + +int +main(int argc, const char *argv[]) +{ + iso9660_stat_t *p_statbuf; + FILE *p_outfd; + int i; + char const *psz_image; + char const *psz_fname; + iso9660_t *p_iso; + + if (argc > 3) { + printf("usage %s [ISO9660-image.ISO [filename]]\n", argv[0]); + printf("Extracts filename from ISO-9660-image.ISO\n"); + return 1; + } + + if (argc > 1) + psz_image = argv[1]; + else + psz_image = ISO9660_IMAGE; + + if (argc > 2) + psz_fname = argv[2]; + else + psz_fname = LOCAL_FILENAME; + + p_iso = iso9660_open (psz_image); + + if (NULL == p_iso) { + fprintf(stderr, "Sorry, couldn't open ISO 9660 image %s\n", psz_image); + return 1; + } + + p_statbuf = iso9660_ifs_stat_translate (p_iso, psz_fname); + + if (NULL == p_statbuf) + { + fprintf(stderr, + "Could not get ISO-9660 file information for file %s\n", + psz_fname); + iso9660_close(p_iso); + return 2; + } + + if (!(p_outfd = fopen (psz_fname, "wb"))) + { + perror ("fopen()"); + free(p_statbuf); + iso9660_close(p_iso); + return 3; + } + + /* Copy the blocks from the ISO-9660 filesystem to the local filesystem. */ + { + const unsigned int i_blocks = CEILING(p_statbuf->size, ISO_BLOCKSIZE); + for (i = 0; i < i_blocks ; i++) + { + char buf[ISO_BLOCKSIZE]; + const lsn_t lsn = p_statbuf->lsn + i; + + memset (buf, 0, ISO_BLOCKSIZE); + + if ( ISO_BLOCKSIZE != iso9660_iso_seek_read (p_iso, buf, lsn, 1) ) + { + fprintf(stderr, "Error reading ISO 9660 file %s at LSN %lu\n", + psz_fname, (long unsigned int) lsn); + my_exit(4); + } + + fwrite (buf, ISO_BLOCKSIZE, 1, p_outfd); + + if (ferror (p_outfd)) + { + perror ("fwrite()"); + my_exit(5); + } + } + } + + fflush (p_outfd); + + /* Make sure the file size has the exact same byte size. Without the + truncate below, the file will a multiple of ISO_BLOCKSIZE. + */ + if (ftruncate (fileno (p_outfd), p_statbuf->size)) + perror ("ftruncate()"); + + printf("Extraction of file '%s' from %s successful.\n", + psz_fname, psz_image); + + my_exit(0); +} diff --git a/example/isofile2.c b/example/isofile2.c new file mode 100644 index 00000000..66237bfa --- /dev/null +++ b/example/isofile2.c @@ -0,0 +1,180 @@ +/* + $Id: isofile2.c,v 1.2 2008/03/24 15:30:55 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2006 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libiso9660 to extract a file from a + CUE/BIN CD image. + + If a single argument is given, it is used as the CUE file of a CD image + to use. Otherwise a compiled-in default image name (that + comes with the libcdio distribution) will be used. + + This program can be compiled with either a C or C++ compiler. In + the distribution we prefer C++ just to make sure we haven't broken + things on the C++ side. + */ + +/* This is the CD-image with an ISO-9660 filesystem */ +#define ISO9660_IMAGE_PATH "../" +#define ISO9660_IMAGE ISO9660_IMAGE_PATH "test/isofs-m1.cue" + +#define ISO9660_PATH "/" +#define ISO9660_FILENAME "COPYING" +#define LOCAL_FILENAME "copying" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "portable.h" + +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define CEILING(x, y) ((x+(y-1))/y) + +#define my_exit(rc) \ + fclose (p_outfd); \ + free(p_statbuf); \ + cdio_destroy(p_cdio); \ + return rc; \ + + +int +main(int argc, const char *argv[]) +{ + iso9660_stat_t *p_statbuf; + FILE *p_outfd; + int i; + char const *psz_image; + char const *psz_fname; + char translated_name[256]; + char untranslated_name[256] = ISO9660_PATH; + CdIo_t *p_cdio; + unsigned int i_fname=sizeof(ISO9660_FILENAME); + + if (argc > 3) { + printf("usage %s [CD-ROM-or-image [filename]]\n", argv[0]); + printf("Extracts filename from CD-ROM-or-image.\n"); + return 1; + } + + if (argc > 1) + psz_image = argv[1]; + else + psz_image = ISO9660_IMAGE; + + if (argc > 2) { + psz_fname = argv[2]; + i_fname = strlen(psz_fname)+1; + } else + psz_fname = ISO9660_FILENAME; + + strncat(untranslated_name, psz_fname, i_fname); + + p_cdio = cdio_open (psz_image, DRIVER_UNKNOWN); + if (!p_cdio) { + fprintf(stderr, "Sorry, couldn't open %s\n", psz_image); + return 1; + } + + p_statbuf = iso9660_fs_stat (p_cdio, untranslated_name); + + if (NULL == p_statbuf) + { + fprintf(stderr, + "Could not get ISO-9660 file information for file %s\n", + untranslated_name); + cdio_destroy(p_cdio); + return 2; + } + + iso9660_name_translate(psz_fname, translated_name); + + if (!(p_outfd = fopen (translated_name, "wb"))) + { + perror ("fopen()"); + cdio_destroy(p_cdio); + free(p_statbuf); + return 3; + } + + /* Copy the blocks from the ISO-9660 filesystem to the local filesystem. */ + { + const unsigned int i_blocks = CEILING(p_statbuf->size, ISO_BLOCKSIZE); + for (i = 0; i < i_blocks; i ++) + { + char buf[ISO_BLOCKSIZE]; + const lsn_t lsn = p_statbuf->lsn + i; + + memset (buf, 0, ISO_BLOCKSIZE); + + if ( 0 != cdio_read_data_sectors (p_cdio, buf, lsn, ISO_BLOCKSIZE, 1) ) + { + fprintf(stderr, "Error reading ISO 9660 file at lsn %lu\n", + (long unsigned int) p_statbuf->lsn); + my_exit(4); + } + + + fwrite (buf, ISO_BLOCKSIZE, 1, p_outfd); + + if (ferror (p_outfd)) + { + perror ("fwrite()"); + my_exit(5); + } + } + } + + + fflush (p_outfd); + + /* Make sure the file size has the exact same byte size. Without the + truncate below, the file will a multiple of ISO_BLOCKSIZE. + */ + if (ftruncate (fileno (p_outfd), p_statbuf->size)) + perror ("ftruncate()"); + + printf("Extraction of file '%s' from '%s' successful.\n", + translated_name, untranslated_name); + + my_exit(0); +} diff --git a/example/isofuzzy.c b/example/isofuzzy.c new file mode 100644 index 00000000..71153d6f --- /dev/null +++ b/example/isofuzzy.c @@ -0,0 +1,99 @@ +/* + $Id: isofuzzy.c,v 1.3 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Program to show using libiso9660 with fuzzy search to get file info. + + If a single argument is given, it is used as the ISO 9660 image. + Otherwise we use a compiled-in default ISO 9660 image + name. + */ + +/* This is the BIN we think there is an ISO 9660 image inside of. */ +#define ISO9660_IMAGE_PATH "/tmp/" +#define ISO9660_IMAGE ISO9660_IMAGE_PATH "vcd_demo.bin" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "portable.h" + +#include +#include +#include + +#include + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +int +main(int argc, const char *argv[]) +{ + CdioList_t *entlist; + CdioListNode_t *entnode; + char const *psz_fname; + iso9660_t *p_iso; + + if (argc > 1) + psz_fname = argv[1]; + else + psz_fname = ISO9660_IMAGE; + + p_iso = iso9660_open_fuzzy (psz_fname, 5); + + if (NULL == p_iso) { + fprintf(stderr, "Sorry, could not find an ISO 9660 image from %s\n", + psz_fname); + return 1; + } + + entlist = iso9660_ifs_readdir (p_iso, "/"); + + /* Iterate over the list of nodes that iso9660_ifs_readdir gives */ + + if (entlist) { + _CDIO_LIST_FOREACH (entnode, entlist) + { + char filename[4096]; + iso9660_stat_t *p_statbuf = + (iso9660_stat_t *) _cdio_list_node_data (entnode); + iso9660_name_translate(p_statbuf->filename, filename); + printf ("/%s\n", filename); + } + + _cdio_list_free (entlist, true); + } + + iso9660_close(p_iso); + exit(0); +} diff --git a/example/isolist.c b/example/isolist.c new file mode 100644 index 00000000..e19da701 --- /dev/null +++ b/example/isolist.c @@ -0,0 +1,119 @@ +/* + $Id: isolist.c,v 1.3 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2004, 2005, 2006, 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libiso9660 to list files in a directory of + an ISO-9660 image and give some iso9660 information. See the code + to iso-info for a more complete example. + + If a single argument is given, it is used as the ISO 9660 image to + use in the listing. Otherwise a compiled-in default ISO 9660 image + name (that comes with the libcdio distribution) will be used. + */ + +/* Set up a CD-DA image to test on which is in the libcdio distribution. */ +#define ISO9660_IMAGE_PATH "../test/" +#define ISO9660_IMAGE ISO9660_IMAGE_PATH "copying.iso" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define print_vd_info(title, fn) \ + if (fn(p_iso, &psz_str)) { \ + printf(title ": %s\n", psz_str); \ + } \ + free(psz_str); \ + psz_str = NULL; + + +int +main(int argc, const char *argv[]) +{ + CdioList_t *p_entlist; + CdioListNode_t *p_entnode; + char const *psz_fname; + iso9660_t *p_iso; + const char *psz_path="/"; + + if (argc > 1) + psz_fname = argv[1]; + else + psz_fname = ISO9660_IMAGE; + + p_iso = iso9660_open (psz_fname); + + if (NULL == p_iso) { + fprintf(stderr, "Sorry, couldn't open %s as an ISO-9660 image\n", + psz_fname); + return 1; + } + + /* Show basic CD info from the Primary Volume Descriptor. */ + { + char *psz_str = NULL; + print_vd_info("Application", iso9660_ifs_get_application_id); + print_vd_info("Preparer ", iso9660_ifs_get_preparer_id); + print_vd_info("Publisher ", iso9660_ifs_get_publisher_id); + print_vd_info("System ", iso9660_ifs_get_system_id); + print_vd_info("Volume ", iso9660_ifs_get_volume_id); + print_vd_info("Volume Set ", iso9660_ifs_get_volumeset_id); + } + + p_entlist = iso9660_ifs_readdir (p_iso, psz_path); + + /* Iterate over the list of nodes that iso9660_ifs_readdir gives */ + + if (p_entlist) { + _CDIO_LIST_FOREACH (p_entnode, p_entlist) + { + char filename[4096]; + iso9660_stat_t *p_statbuf = + (iso9660_stat_t *) _cdio_list_node_data (p_entnode); + iso9660_name_translate(p_statbuf->filename, filename); + printf ("%s [LSN %6d] %8u %s%s\n", + _STAT_DIR == p_statbuf->type ? "d" : "-", + p_statbuf->lsn, p_statbuf->size, psz_path, filename); + } + + _cdio_list_free (p_entlist, true); + } + + + iso9660_close(p_iso); + return 0; +} + diff --git a/example/isolsn.c b/example/isolsn.c new file mode 100644 index 00000000..f2a6509d --- /dev/null +++ b/example/isolsn.c @@ -0,0 +1,98 @@ +/* + $Id: isolsn.c,v 1.2 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2004, 2005, 2006, 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libiso9660 to get a file path + for a given LSN of an ISO-9660 image. + + If a single argument is given, it is used as the LSN to search for. + Otherwise we use a built-in default value. + + If a second argument is given, it is ISO 9660 image to use in the + listing. Otherwise a compiled-in default ISO 9660 image name (that + comes with the libcdio distribution) will be used. + */ + +/* Set up a CD-DA image to test on which is in the libcdio distribution. */ +#define ISO9660_IMAGE_PATH "../test/" +#define ISO9660_IMAGE ISO9660_IMAGE_PATH "copying.iso" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define print_vd_info(title, fn) \ + if (fn(p_iso, &psz_str)) { \ + printf(title ": %s\n", psz_str); \ + } \ + free(psz_str); \ + psz_str = NULL; + + +int +main(int argc, const char *argv[]) +{ + char const *psz_fname; + iso9660_t *p_iso; + lsn_t lsn = 24; + char *psz_path = NULL; + + if (argc > 1) + lsn = strtol(argv[1], (char **)NULL, 10); + + if (argc > 2) + psz_fname = argv[2]; + else + psz_fname = ISO9660_IMAGE; + + p_iso = iso9660_open (psz_fname); + + if (NULL == p_iso) { + fprintf(stderr, "Sorry, couldn't open %s as an ISO-9660 image\n", + psz_fname); + return 1; + } + + iso9660_ifs_find_lsn_with_path (p_iso, lsn, &psz_path); + if (psz_path != NULL) { + printf("File at LSN %u is %s\n", lsn, psz_path); + free(psz_path); + } + + iso9660_close(p_iso); + return 0; +} + diff --git a/example/mmc1.c b/example/mmc1.c new file mode 100644 index 00000000..822e1567 --- /dev/null +++ b/example/mmc1.c @@ -0,0 +1,89 @@ +/* + $Id: mmc1.c,v 1.7 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Sample program to show use of the MMC interface. + An optional drive name can be supplied as an argument. + This basically the libdio mmc_get_hwinfo() routine. + See also corresponding C++ programs. +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include +#include + +/* Set how long to wait for MMC commands to complete */ +#define DEFAULT_TIMEOUT_MS 10000 + +int +main(int argc, const char *argv[]) +{ + CdIo_t *p_cdio; + const char *psz_drive = NULL; + + if (argc > 1) psz_drive = argv[1]; + p_cdio = cdio_open (psz_drive, DRIVER_UNKNOWN); + + if (!p_cdio) { + printf("Couldn't find CD\n"); + return 1; + } else { + int i_status; /* Result of MMC command */ + char buf[36] = { 0, }; /* Place to hold returned data */ + mmc_cdb_t cdb = {{0, }}; /* Command Descriptor Buffer */ + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_INQUIRY); + cdb.field[4] = sizeof(buf); + + i_status = mmc_run_cmd(p_cdio, DEFAULT_TIMEOUT_MS, &cdb, + SCSI_MMC_DATA_READ, sizeof(buf), &buf); + if (i_status == 0) { + char psz_vendor[CDIO_MMC_HW_VENDOR_LEN+1]; + char psz_model[CDIO_MMC_HW_MODEL_LEN+1]; + char psz_rev[CDIO_MMC_HW_REVISION_LEN+1]; + + memcpy(psz_vendor, buf + 8, sizeof(psz_vendor)-1); + psz_vendor[sizeof(psz_vendor)-1] = '\0'; + memcpy(psz_model, + buf + 8 + CDIO_MMC_HW_VENDOR_LEN, + sizeof(psz_model)-1); + psz_model[sizeof(psz_model)-1] = '\0'; + memcpy(psz_rev, + buf + 8 + CDIO_MMC_HW_VENDOR_LEN +CDIO_MMC_HW_MODEL_LEN, + sizeof(psz_rev)-1); + psz_rev[sizeof(psz_rev)-1] = '\0'; + + printf("Vendor: %s\nModel: %s\nRevision: %s\n", + psz_vendor, psz_model, psz_rev); + } else { + printf("Couldn't get INQUIRY data (vendor, model, and revision).\n"); + } + } + + cdio_destroy(p_cdio); + + return 0; +} diff --git a/example/mmc2.c b/example/mmc2.c new file mode 100644 index 00000000..3fcf1a5d --- /dev/null +++ b/example/mmc2.c @@ -0,0 +1,196 @@ +/* + $Id: mmc2.c,v 1.8 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* A program to using the MMC interface to list CD and drive features + from the MMC GET_CONFIGURATION command . */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include +#include + +int +main(int argc, const char *argv[]) +{ + CdIo_t *p_cdio; + + const char *psz_drive = NULL; + + if (argc > 1) psz_drive = argv[1]; + p_cdio = cdio_open (psz_drive, DRIVER_DEVICE); + + if (NULL == p_cdio) { + printf("Couldn't find CD\n"); + return 1; + } else { + int i_status; /* Result of MMC command */ + uint8_t buf[500] = { 0, }; /* Place to hold returned data */ + mmc_cdb_t cdb = {{0, }}; /* Command Descriptor Buffer */ + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_GET_CONFIGURATION); + CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(buf)); + cdb.field[1] = CDIO_MMC_GET_CONF_ALL_FEATURES; + cdb.field[3] = 0x0; + + i_status = mmc_run_cmd(p_cdio, 0, &cdb, SCSI_MMC_DATA_READ, sizeof(buf), + &buf); + if (i_status == 0) { + uint8_t *p; + uint32_t i_data; + uint8_t *p_max = buf + 65530; + + i_data = (unsigned int) CDIO_MMC_GET_LEN32(buf); + /* set to first sense feature code, and then walk through the masks */ + p = buf + 8; + while( (p < &(buf[i_data])) && (p < p_max) ) { + uint16_t i_feature; + uint8_t i_feature_additional = p[3]; + + i_feature = CDIO_MMC_GET_LEN16(p); + { + uint8_t *q; + const char *feature_str = mmc_feature2str(i_feature); + printf("%s Feature\n", feature_str); + switch( i_feature ) + { + case CDIO_MMC_FEATURE_PROFILE_LIST: + for ( q = p+4 ; q < p + i_feature_additional ; q += 4 ) { + int i_profile=CDIO_MMC_GET_LEN16(q); + const char *feature_profile_str = + mmc_feature_profile2str(i_profile); + printf( "\t%s", feature_profile_str ); + if (q[2] & 1) { + printf(" - on"); + } + printf("\n"); + } + printf("\n"); + + break; + case CDIO_MMC_FEATURE_CORE: + { + uint8_t *q = p+4; + uint32_t i_interface_standard = CDIO_MMC_GET_LEN32(q); + switch(i_interface_standard) { + case 0: + printf("\tunspecified interface.\n"); + break; + case 1: + printf("\tSCSI interface.\n"); + break; + case 2: + printf("\tATAPI interface.\n"); + break; + case 3: + printf("\tIEEE 1394 interface.\n"); + break; + case 4: + printf("\tIEEE 1394A interface.\n"); + break; + case 5: + printf("\tFibre Channel interface.\n"); + } + printf("\n"); + break; + } + case CDIO_MMC_FEATURE_REMOVABLE_MEDIUM: + switch(p[4] >> 5) { + case 0: + printf("\tCaddy/Slot type loading mechanism,\n"); + break; + case 1: + printf("\tTray type loading mechanism,\n"); + break; + case 2: + printf("\tPop-up type loading mechanism,\n"); + break; + case 4: + printf("\tEmbedded changer with individually changeable discs,\n"); + break; + case 5: + printf("\tEmbedded changer using a magazine mechanism,\n"); + break; + default: + printf("\tUnknown changer mechanism,\n"); + } + + printf("\tcan%s eject the medium or magazine via the normal " + "START/STOP command,\n", + (p[4] & 8) ? "": "not"); + printf("\tcan%s be locked into the Logical Unit.\n", + (p[4] & 1) ? "": "not"); + printf("\n"); + break; + case CDIO_MMC_FEATURE_CD_READ: + printf("CD Read Feature\n"); + printf("\tC2 Error pointers are %ssupported,\n", + (p[4] & 2) ? "": "not "); + printf("\tCD-Text is %ssupported.\n", + (p[4] & 1) ? "": "not "); + printf("\n"); + break; + case CDIO_MMC_FEATURE_CDDA_EXT_PLAY: + printf("\tSCAN command is %ssupported,\n", + (p[4] & 4) ? "": "not "); + printf("\taudio channels can %sbe muted separately,\n", + (p[4] & 2) ? "": "not "); + printf("\taudio channels can %shave separate volume levels.\n", + (p[4] & 1) ? "": "not "); + { + uint8_t *q = p+6; + uint16_t i_vol_levels = CDIO_MMC_GET_LEN16(q); + printf("\t%d volume levels can be set\n", i_vol_levels); + } + printf("\n"); + break; + case CDIO_MMC_FEATURE_LU_SN: { + uint8_t i_serial = *(p+3); + char serial[257] = { '\0', }; + memcpy(serial, p+4, i_serial); + printf("\t%s\n\n", serial); + break; + } + default: + printf("\n"); + break; + } + p += i_feature_additional + 4; + } + } + } else { + printf("Didn't get all feature codes.\n"); + } + } + + if (mmc_have_interface(p_cdio, CDIO_MMC_FEATURE_INTERFACE_ATAPI)) + printf("CD-ROM is an ATAPI interface.\n"); + else + printf("CD-ROM is not an ATAPI interface.\n"); + + cdio_destroy(p_cdio); + + return 0; +} diff --git a/example/mmc2a.c b/example/mmc2a.c new file mode 100644 index 00000000..800e04a8 --- /dev/null +++ b/example/mmc2a.c @@ -0,0 +1,234 @@ +/* + $Id: mmc2a.c,v 1.5 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Sample program to show use of the MMC interface. + An optional drive name can be supplied as an argument. + This basically calls to the libdio mmc_mode_sense_10() and mmc_mode_sense_6 + routines. +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif + +#include +#include + +static void +print_mode_sense (const char *psz_drive, const char *six_or_ten, + const uint8_t buf[22]) +{ + printf("Mode sense %s information for %s:\n", six_or_ten, psz_drive); + if (buf[2] & 0x01) { + printf("\tReads CD-R media.\n"); + } + if (buf[2] & 0x02) { + printf("\tReads CD-RW media.\n"); + } + if (buf[2] & 0x04) { + printf("\tReads fixed-packet tracks when Addressing type is method 2.\n"); + } + if (buf[2] & 0x08) { + printf("\tReads DVD ROM media.\n"); + } + if (buf[2] & 0x10) { + printf("\tReads DVD-R media.\n"); + } + if (buf[2] & 0x20) { + printf("\tReads DVD-RAM media.\n"); + } + if (buf[2] & 0x40) { + printf("\tReads DVD-RAM media.\n"); + } + if (buf[3] & 0x01) { + printf("\tWrites CD-R media.\n"); + } + if (buf[3] & 0x02) { + printf("\tWrites CD-RW media.\n"); + } + if (buf[3] & 0x04) { + printf("\tSupports emulation write.\n"); + } + if (buf[3] & 0x10) { + printf("\tWrites DVD-R media.\n"); + } + if (buf[3] & 0x20) { + printf("\tWrites DVD-RAM media.\n"); + } + if (buf[4] & 0x01) { + printf("\tCan play audio.\n"); + } + if (buf[4] & 0x02) { + printf("\tDelivers composition A/V stream.\n"); + } + if (buf[4] & 0x04) { + printf("\tSupports digital output on port 2.\n"); + } + if (buf[4] & 0x08) { + printf("\tSupports digital output on port 1.\n"); + } + if (buf[4] & 0x10) { + printf("\tReads Mode-2 form 1 (e.g. XA) media.\n"); + } + if (buf[4] & 0x20) { + printf("\tReads Mode-2 form 2 media.\n"); + } + if (buf[4] & 0x40) { + printf("\tReads multi-session CD media.\n"); + } + if (buf[4] & 0x80) { + printf("\tSupports Buffer under-run free recording on CD-R/RW media.\n"); + } + if (buf[4] & 0x01) { + printf("\tCan read audio data with READ CD.\n"); + } + if (buf[4] & 0x02) { + printf("\tREAD CD data stream is accurate.\n"); + } + if (buf[5] & 0x04) { + printf("\tReads R-W subchannel information.\n"); + } + if (buf[5] & 0x08) { + printf("\tReads de-interleaved R-W subchannel.\n"); + } + if (buf[5] & 0x10) { + printf("\tSupports C2 error pointers.\n"); + } + if (buf[5] & 0x20) { + printf("\tReads ISRC information.\n"); + } + if (buf[5] & 0x40) { + printf("\tReads ISRC informaton.\n"); + } + if (buf[5] & 0x40) { + printf("\tReads media catalog number (MCN also known as UPC).\n"); + } + if (buf[5] & 0x80) { + printf("\tReads bar codes.\n"); + } + if (buf[6] & 0x01) { + printf("\tPREVENT/ALLOW may lock media.\n"); + } + printf("\tLock state is %slocked.\n", (buf[6] & 0x02) ? "" : "un"); + printf("\tPREVENT/ALLOW jumper is %spresent.\n", (buf[6] & 0x04) ? "": "not "); + if (buf[6] & 0x08) { + printf("\tEjects media with START STOP UNIT.\n"); + } + { + const unsigned int i_load_type = (buf[6]>>5 & 0x07); + printf("\tLoading mechanism type is %d: ", i_load_type); + switch (buf[6]>>5 & 0x07) { + case 0: + printf("caddy type loading mechanism.\n"); + break; + case 1: + printf("tray type loading mechanism.\n"); + break; + case 2: + printf("popup type loading mechanism.\n"); + break; + case 3: + printf("reserved\n"); + break; + case 4: + printf("changer with individually changeable discs.\n"); + break; + case 5: + printf("changer using Magazine mechanism.\n"); + break; + case 6: + printf("changer using Magazine mechanism.\n"); + break; + default: + printf("Invalid.\n"); + break; + } + } + + if (buf[7] & 0x01) { + printf("\tVolume controls each channel separately.\n"); + } + if (buf[7] & 0x02) { + printf("\tHas a changer that supports disc present reporting.\n"); + } + if (buf[7] & 0x04) { + printf("\tCan load empty slot in changer.\n"); + } + if (buf[7] & 0x08) { + printf("\tSide change capable.\n"); + } + if (buf[7] & 0x10) { + printf("\tReads raw R-W subchannel information from lead in.\n"); + } + { + const unsigned int i_speed_Kbs = CDIO_MMC_GETPOS_LEN16(buf, 8); + printf("\tMaximum read speed is %d K bytes/sec (about %dX)\n", + i_speed_Kbs, i_speed_Kbs / 176) ; + } + printf("\tNumber of Volume levels is %d\n", CDIO_MMC_GETPOS_LEN16(buf, 10)); + printf("\tBuffers size for data is %d KB\n", CDIO_MMC_GETPOS_LEN16(buf, 12)); + printf("\tCurrent read speed is %d KB\n", CDIO_MMC_GETPOS_LEN16(buf, 14)); + printf("\tMaximum write speed is %d KB\n", CDIO_MMC_GETPOS_LEN16(buf, 18)); + printf("\tCurrent write speed is %d KB\n", CDIO_MMC_GETPOS_LEN16(buf, 28)); +} + + +int +main(int argc, const char *argv[]) +{ + CdIo_t *p_cdio; + const char *psz_drive = NULL; + + if (argc > 1) psz_drive = argv[1]; + p_cdio = cdio_open (psz_drive, DRIVER_UNKNOWN); + + if (!p_cdio) { + printf("Couldn't find CD\n"); + return 1; + } else { + uint8_t buf[22] = { 0, }; /* Place to hold returned data */ + char *psz_cd = cdio_get_default_device(p_cdio); + if (DRIVER_OP_SUCCESS == mmc_mode_sense_6(p_cdio, buf, sizeof(buf), + CDIO_MMC_CAPABILITIES_PAGE) ) { + print_mode_sense(psz_cd, "6", buf); + } else { + printf("Couldn't get MODE_SENSE 6 data.\n"); + } + if (DRIVER_OP_SUCCESS == mmc_mode_sense_10(p_cdio, buf, sizeof(buf), + CDIO_MMC_CAPABILITIES_PAGE) ) { + print_mode_sense(psz_cd, "10", buf); + } else { + printf("Couldn't get MODE_SENSE 10 data.\n"); + } + free(psz_cd); + } + + cdio_destroy(p_cdio); + + return 0; +} diff --git a/example/mmc3.c b/example/mmc3.c new file mode 100644 index 00000000..23f9680a --- /dev/null +++ b/example/mmc3.c @@ -0,0 +1,130 @@ +/* + $Id: mmc3.c,v 1.2 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show use of SCSI MMC interface. Is basically the + the libdio scsi_mmc_get_hwinfo() routine. +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#include +#include + +/* Set how long to wait for MMC commands to complete */ +#define DEFAULT_TIMEOUT_MS 10000 + +int +main(int argc, const char *argv[]) +{ + CdIo_t *p_cdio; + driver_return_code_t ret; + driver_id_t driver_id = DRIVER_DEVICE; + char *psz_drive = NULL; + bool do_eject = false; + + if (argc > 1) + psz_drive = strdup(argv[1]); + + if (!psz_drive) { + psz_drive = cdio_get_default_device_driver(&driver_id); + if (!psz_drive) { + printf("Can't find a CD-ROM\n"); + exit(1); + } + } + + p_cdio = cdio_open (psz_drive, driver_id); + if (!p_cdio) { + printf("Can't open %s\n", psz_drive); + exit(2); + } + + ret = mmc_get_tray_status(p_cdio); + switch(ret) { + case 0: + printf("CD-ROM drive %s is closed.\n", psz_drive); + do_eject = true; + break; + case 1: + printf("CD-ROM drive %s is open.\n", psz_drive); + break; + default: + printf("Error status for drive %s: %s.\n", psz_drive, + cdio_driver_errmsg(ret)); + return 1; + } + + ret = mmc_get_media_changed(p_cdio); + switch(ret) { + case 0: + printf("CD-ROM drive %s media not changed since last test.\n", psz_drive); + break; + case 1: + printf("CD-ROM drive %s media changed since last test.\n", psz_drive); + break; + default: + printf("Error status for drive %s: %s.\n", psz_drive, + cdio_driver_errmsg(ret)); + return 1; + } + + if (do_eject) + ret = cdio_eject_media_drive(psz_drive); + else + ret = cdio_close_tray(psz_drive, &driver_id); + + ret = mmc_get_tray_status(p_cdio); + switch(ret) { + case 0: + printf("CD-ROM drive %s is closed.\n", psz_drive); + break; + case 1: + printf("CD-ROM drive %s is open.\n", psz_drive); + break; + default: + printf("Error status for drive %s: %s.\n", psz_drive, + cdio_driver_errmsg(ret)); + return 1; + } + + ret = mmc_get_media_changed(p_cdio); + switch(ret) { + case 0: + printf("CD-ROM drive %s media not changed since last test.\n", psz_drive); + break; + case 1: + printf("CD-ROM drive %s media changed since last test.\n", psz_drive); + break; + default: + printf("Error status for drive %s: %s.\n", psz_drive, + cdio_driver_errmsg(ret)); + return 1; + } + + free(psz_drive); + cdio_destroy(p_cdio); + + return 0; +} diff --git a/example/paranoia.c b/example/paranoia.c new file mode 100644 index 00000000..fa8eef1a --- /dev/null +++ b/example/paranoia.c @@ -0,0 +1,119 @@ +/* + $Id: paranoia.c,v 1.9 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libcdio's version of the CD-DA paranoia. + library. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +int +main(int argc, const char *argv[]) +{ + cdrom_drive_t *d = NULL; /* Place to store handle given by cd-paranoia. */ + char **ppsz_cd_drives; /* List of all drives with a loaded CDDA in it. */ + + /* See if we can find a device with a loaded CD-DA in it. */ + ppsz_cd_drives = cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false); + + if (ppsz_cd_drives) { + /* Found such a CD-ROM with a CD-DA loaded. Use the first drive in + the list. */ + d=cdda_identify(*ppsz_cd_drives, 1, NULL); + } else { + printf("Unable find or access a CD-ROM drive with an audio CD in it.\n"); + exit(1); + } + + /* Don't need a list of CD's with CD-DA's any more. */ + cdio_free_device_list(ppsz_cd_drives); + + if ( !d ) { + printf("Unable to identify audio CD disc.\n"); + exit(1); + } + + /* We'll set for verbose paranoia messages. */ + cdda_verbose_set(d, CDDA_MESSAGE_PRINTIT, CDDA_MESSAGE_PRINTIT); + + if ( 0 != cdda_open(d) ) { + printf("Unable to open disc.\n"); + exit(1); + } + + /* Okay now set up to read up to the first 300 frames of the first + audio track of the Audio CD. */ + { + cdrom_paranoia_t *p = paranoia_init(d); + lsn_t i_first_lsn = cdda_disc_firstsector(d); + + if ( -1 == i_first_lsn ) { + printf("Trouble getting starting LSN\n"); + } else { + lsn_t i_cursor; + track_t i_track = cdda_sector_gettrack(d, i_first_lsn); + lsn_t i_last_lsn = cdda_track_lastsector(d, i_track); + + /* For demo purposes we'll read only 300 frames (about 4 + seconds). We don't want this to take too long. On the other + hand, I suppose it should be something close to a real test. + */ + if ( i_last_lsn - i_first_lsn > 300) i_last_lsn = i_first_lsn + 299; + + printf("Reading track %d from LSN %ld to LSN %ld\n", i_track, + (long int) i_first_lsn, (long int) i_last_lsn); + + /* Set reading mode for full paranoia, but allow skipping sectors. */ + paranoia_modeset(p, PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP); + + paranoia_seek(p, i_first_lsn, SEEK_SET); + + for ( i_cursor = i_first_lsn; i_cursor <= i_last_lsn; i_cursor ++) { + /* read a sector */ + int16_t *p_readbuf=paranoia_read(p, NULL); + char *psz_err=cdda_errors(d); + char *psz_mes=cdda_messages(d); + + if (psz_mes || psz_err) + printf("%s%s\n", psz_mes ? psz_mes: "", psz_err ? psz_err: ""); + + if (psz_err) free(psz_err); + if (psz_mes) free(psz_mes); + if( !p_readbuf ) { + printf("paranoia read error. Stopping.\n"); + break; + } + } + } + paranoia_free(p); + } + + cdda_close(d); + + exit(0); +} diff --git a/example/paranoia2.c b/example/paranoia2.c new file mode 100644 index 00000000..947dccae --- /dev/null +++ b/example/paranoia2.c @@ -0,0 +1,102 @@ +/* + $Id: paranoia2.c,v 1.8 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libcdio's version of the CD-DA + paranoia library. But in this version, we'll open a cdio object before + calling paranoia's open. I imagine in many cases such as media + players this may be what will be done since, one may want to get + CDDB/CD-Text info beforehand. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +int +main(int argc, const char *argv[]) +{ + cdrom_drive_t *d = NULL; /* Place to store handle given by cd-paranoia. */ + char **ppsz_cd_drives; /* List of all drives with a loaded CDDA in it. */ + CdIo_t *p_cdio = NULL; + + /* See if we can find a device with a loaded CD-DA in it. */ + ppsz_cd_drives = cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false); + + if (ppsz_cd_drives) { + /* Found such a CD-ROM with a CD-DA loaded. Use the first drive in + the list. */ + p_cdio = cdio_open(*ppsz_cd_drives, DRIVER_UNKNOWN); + d=cdio_cddap_identify_cdio(p_cdio, 1, NULL); + } else { + printf("Unable find or access a CD-ROM drive with an audio CD in it.\n"); + exit(1); + } + + /* Don't need a list of CD's with CD-DA's any more. */ + cdio_free_device_list(ppsz_cd_drives); + + if ( !d ) { + printf("Unable to identify audio CD disc.\n"); + exit(1); + } + + /* We'll set for verbose paranoia messages. */ + cdio_cddap_verbose_set(d, CDDA_MESSAGE_PRINTIT, CDDA_MESSAGE_PRINTIT); + + if ( 0 != cdio_cddap_open(d) ) { + printf("Unable to open disc.\n"); + exit(1); + } + + /* In the paranoia example was a reading. Here we are going to do + something trivial (but I think neat any way - get the Endian-ness + of the drive. */ + { + const int i_endian = data_bigendianp(d); + switch (i_endian) { + case 0: + printf("Drive returns audio data Little Endian." + " Your drive is like most.\n"); + break; + case 1: + printf("Drive returns audio data Big Endian.\n"); + break; + case -1: + printf("Don't know whether drive is Big or Little Endian.\n"); + break; + default: + printf("Whoah - got a return result I'm not expecting %d.\n", + i_endian); + break; + } + } + + cdio_cddap_close_no_free_cdio(d); + cdio_destroy( p_cdio ); + + exit(0); +} diff --git a/example/sample3.c b/example/sample3.c new file mode 100644 index 00000000..c157e8a0 --- /dev/null +++ b/example/sample3.c @@ -0,0 +1,200 @@ +/* + $Id: sample3.c,v 1.10 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2003, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + A somewhat simplified program to show the use of cdio_guess_cd_type(). + Figure out the kind of CD image we've got. +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#include + +static void +print_analysis(cdio_iso_analysis_t cdio_iso_analysis, + cdio_fs_anal_t fs, int first_data, unsigned int num_audio, + track_t num_tracks, track_t first_track_num, CdIo_t *p_cdio) +{ + switch(CDIO_FSTYPE(fs)) { + case CDIO_FS_AUDIO: + break; + case CDIO_FS_ISO_9660: + printf("CD-ROM with ISO 9660 filesystem"); + if (fs & CDIO_FS_ANAL_JOLIET) { + printf(" and joliet extension level %d", cdio_iso_analysis.joliet_level); + } + if (fs & CDIO_FS_ANAL_ROCKRIDGE) + printf(" and rockridge extensions"); + printf("\n"); + break; + case CDIO_FS_ISO_9660_INTERACTIVE: + printf("CD-ROM with CD-RTOS and ISO 9660 filesystem\n"); + break; + case CDIO_FS_HIGH_SIERRA: + printf("CD-ROM with High Sierra filesystem\n"); + break; + case CDIO_FS_INTERACTIVE: + printf("CD-Interactive%s\n", num_audio > 0 ? "/Ready" : ""); + break; + case CDIO_FS_HFS: + printf("CD-ROM with Macintosh HFS\n"); + break; + case CDIO_FS_ISO_HFS: + printf("CD-ROM with both Macintosh HFS and ISO 9660 filesystem\n"); + break; + case CDIO_FS_UFS: + printf("CD-ROM with Unix UFS\n"); + break; + case CDIO_FS_EXT2: + printf("CD-ROM with Linux second extended filesystem\n"); + break; + case CDIO_FS_3DO: + printf("CD-ROM with Panasonic 3DO filesystem\n"); + break; + case CDIO_FS_UNKNOWN: + printf("CD-ROM with unknown filesystem\n"); + break; + } + switch(CDIO_FSTYPE(fs)) { + case CDIO_FS_ISO_9660: + case CDIO_FS_ISO_9660_INTERACTIVE: + case CDIO_FS_ISO_HFS: + printf("ISO 9660: %i blocks, label `%.32s'\n", + cdio_iso_analysis.isofs_size, cdio_iso_analysis.iso_label); + break; + } + if (first_data == 1 && num_audio > 0) + printf("mixed mode CD "); + if (fs & CDIO_FS_ANAL_XA) + printf("XA sectors "); + if (fs & CDIO_FS_ANAL_MULTISESSION) + printf("Multisession"); + if (fs & CDIO_FS_ANAL_HIDDEN_TRACK) + printf("Hidden Track "); + if (fs & CDIO_FS_ANAL_PHOTO_CD) + printf("%sPhoto CD ", + num_audio > 0 ? " Portfolio " : ""); + if (fs & CDIO_FS_ANAL_CDTV) + printf("Commodore CDTV "); + if (first_data > 1) + printf("CD-Plus/Extra "); + if (fs & CDIO_FS_ANAL_BOOTABLE) + printf("bootable CD "); + if (fs & CDIO_FS_ANAL_VIDEOCD && num_audio == 0) { + printf("Video CD "); + } + if (fs & CDIO_FS_ANAL_SVCD) + printf("Super Video CD (SVCD) or Chaoji Video CD (CVD)"); + if (fs & CDIO_FS_ANAL_CVD) + printf("Chaoji Video CD (CVD)"); + printf("\n"); +} + +int +main(int argc, const char *argv[]) +{ + CdIo_t *p_cdio = cdio_open (NULL, DRIVER_UNKNOWN); + cdio_fs_anal_t fs=0; + + track_t num_tracks; + track_t first_track_num; + lsn_t start_track; /* first sector of track */ + lsn_t data_start =0; /* start of data area */ + + int first_data = -1; /* # of first data track */ + int first_audio = -1; /* # of first audio track */ + unsigned int num_data = 0; /* # of data tracks */ + unsigned int num_audio = 0; /* # of audio tracks */ + unsigned int i; + + if (NULL == p_cdio) { + printf("Problem in trying to find a driver.\n\n"); + return 1; + } + + first_track_num = cdio_get_first_track_num(p_cdio); + num_tracks = cdio_get_num_tracks(p_cdio); + + /* Count the number of data and audio tracks. */ + for (i = first_track_num; i <= num_tracks; i++) { + if (TRACK_FORMAT_AUDIO == cdio_get_track_format(p_cdio, i)) { + num_audio++; + if (-1 == first_audio) first_audio = i; + } else { + num_data++; + if (-1 == first_data) first_data = i; + } + } + + /* try to find out what sort of CD we have */ + if (0 == num_data) { + printf("Audio CD\n"); + } else { + /* we have data track(s) */ + int j; + cdio_iso_analysis_t cdio_iso_analysis; + + memset(&cdio_iso_analysis, 0, sizeof(cdio_iso_analysis)); + + for (j = 2, i = first_data; i <= num_tracks; i++) { + lsn_t lsn; + track_format_t track_format = cdio_get_track_format(p_cdio, i); + + lsn = cdio_get_track_lsn(p_cdio, i); + + switch ( track_format ) { + case TRACK_FORMAT_AUDIO: + case TRACK_FORMAT_ERROR: + break; + case TRACK_FORMAT_CDI: + case TRACK_FORMAT_XA: + case TRACK_FORMAT_DATA: + case TRACK_FORMAT_PSX: + ; + } + + start_track = (i == 1) ? 0 : lsn; + + /* 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 + cdio_iso_analysis.isofs_size) + continue; + + fs = cdio_guess_cd_type(p_cdio, start_track, i, &cdio_iso_analysis); + + print_analysis(cdio_iso_analysis, fs, first_data, num_audio, + num_tracks, first_track_num, p_cdio); + + if ( !(CDIO_FSTYPE(fs) == CDIO_FS_ISO_9660 || + CDIO_FSTYPE(fs) == CDIO_FS_ISO_HFS || + CDIO_FSTYPE(fs) == CDIO_FS_ISO_9660_INTERACTIVE) ) + /* no method for non-ISO9660 multisessions */ + break; + } + } + cdio_destroy(p_cdio); + return 0; +} diff --git a/example/sample4.c b/example/sample4.c new file mode 100644 index 00000000..ba0bcfcc --- /dev/null +++ b/example/sample4.c @@ -0,0 +1,221 @@ +/* + $Id: sample4.c,v 1.8 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2003, 2004 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + A slightly improved sample3 program: we handle cdio logging and + take an optional CD-location. +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#include +#include + +static void +log_handler (cdio_log_level_t level, const char message[]) +{ + switch(level) { + case CDIO_LOG_DEBUG: + case CDIO_LOG_INFO: + return; + default: + printf("cdio %d message: %s\n", level, message); + } +} + +static void +print_analysis(cdio_iso_analysis_t cdio_iso_analysis, + cdio_fs_anal_t fs, int first_data, unsigned int num_audio, + track_t num_tracks, track_t first_track_num, CdIo_t *p_cdio) +{ + switch(CDIO_FSTYPE(fs)) { + case CDIO_FS_AUDIO: + break; + case CDIO_FS_ISO_9660: + printf("CD-ROM with ISO 9660 filesystem"); + if (fs & CDIO_FS_ANAL_JOLIET) { + printf(" and joliet extension level %d", cdio_iso_analysis.joliet_level); + } + if (fs & CDIO_FS_ANAL_ROCKRIDGE) + printf(" and rockridge extensions"); + printf("\n"); + break; + case CDIO_FS_ISO_9660_INTERACTIVE: + printf("CD-ROM with CD-RTOS and ISO 9660 filesystem\n"); + break; + case CDIO_FS_HIGH_SIERRA: + printf("CD-ROM with High Sierra filesystem\n"); + break; + case CDIO_FS_INTERACTIVE: + printf("CD-Interactive%s\n", num_audio > 0 ? "/Ready" : ""); + break; + case CDIO_FS_HFS: + printf("CD-ROM with Macintosh HFS\n"); + break; + case CDIO_FS_ISO_HFS: + printf("CD-ROM with both Macintosh HFS and ISO 9660 filesystem\n"); + break; + case CDIO_FS_UFS: + printf("CD-ROM with Unix UFS\n"); + break; + case CDIO_FS_EXT2: + printf("CD-ROM with Linux second extended filesystem\n"); + break; + case CDIO_FS_3DO: + printf("CD-ROM with Panasonic 3DO filesystem\n"); + break; + case CDIO_FS_UNKNOWN: + printf("CD-ROM with unknown filesystem\n"); + break; + } + switch(CDIO_FSTYPE(fs)) { + case CDIO_FS_ISO_9660: + case CDIO_FS_ISO_9660_INTERACTIVE: + case CDIO_FS_ISO_HFS: + printf("ISO 9660: %i blocks, label `%.32s'\n", + cdio_iso_analysis.isofs_size, cdio_iso_analysis.iso_label); + break; + } + if (first_data == 1 && num_audio > 0) + printf("mixed mode CD "); + if (fs & CDIO_FS_ANAL_XA) + printf("XA sectors "); + if (fs & CDIO_FS_ANAL_MULTISESSION) + printf("Multisession"); + if (fs & CDIO_FS_ANAL_HIDDEN_TRACK) + printf("Hidden Track "); + if (fs & CDIO_FS_ANAL_PHOTO_CD) + printf("%sPhoto CD ", + num_audio > 0 ? " Portfolio " : ""); + if (fs & CDIO_FS_ANAL_CDTV) + printf("Commodore CDTV "); + if (first_data > 1) + printf("CD-Plus/Extra "); + if (fs & CDIO_FS_ANAL_BOOTABLE) + printf("bootable CD "); + if (fs & CDIO_FS_ANAL_VIDEOCD && num_audio == 0) { + printf("Video CD "); + } + if (fs & CDIO_FS_ANAL_SVCD) + printf("Super Video CD (SVCD) or Chaoji Video CD (CVD)"); + if (fs & CDIO_FS_ANAL_CVD) + printf("Chaoji Video CD (CVD)"); + printf("\n"); +} + +int +main(int argc, const char *argv[]) +{ + CdIo_t *p_cdio; + cdio_fs_anal_t fs=0; + + track_t num_tracks; + track_t first_track_num; + lsn_t start_track; /* first sector of track */ + lsn_t data_start =0; /* start of data area */ + + int first_data = -1; /* # of first data track */ + int first_audio = -1; /* # of first audio track */ + unsigned int num_data = 0; /* # of data tracks */ + unsigned int num_audio = 0; /* # of audio tracks */ + unsigned int i; + char *cd_image_name = NULL; + + if (argc > 1) + cd_image_name = strdup(argv[1]); + + cdio_log_set_handler (log_handler); + + p_cdio = cdio_open (cd_image_name, DRIVER_UNKNOWN); + + if (NULL == p_cdio) { + printf("Problem in trying to find a driver.\n\n"); + return 1; + } + + first_track_num = cdio_get_first_track_num(p_cdio); + num_tracks = cdio_get_num_tracks(p_cdio); + + /* Count the number of data and audio tracks. */ + for (i = first_track_num; i <= num_tracks; i++) { + if (TRACK_FORMAT_AUDIO == cdio_get_track_format(p_cdio, i)) { + num_audio++; + if (-1 == first_audio) first_audio = i; + } else { + num_data++; + if (-1 == first_data) first_data = i; + } + } + + /* try to find out what sort of CD we have */ + if (0 == num_data) { + printf("Audio CD\n"); + } else { + /* we have data track(s) */ + int j; + cdio_iso_analysis_t cdio_iso_analysis; + + memset(&cdio_iso_analysis, 0, sizeof(cdio_iso_analysis)); + + for (j = 2, i = first_data; i <= num_tracks; i++) { + lsn_t lsn; + track_format_t track_format = cdio_get_track_format(p_cdio, i); + + lsn = cdio_get_track_lsn(p_cdio, i); + + switch ( track_format ) { + case TRACK_FORMAT_AUDIO: + case TRACK_FORMAT_ERROR: + break; + case TRACK_FORMAT_CDI: + case TRACK_FORMAT_XA: + case TRACK_FORMAT_DATA: + case TRACK_FORMAT_PSX: + ; + } + + start_track = (i == 1) ? 0 : lsn; + + /* 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 + cdio_iso_analysis.isofs_size) + continue; + + fs = cdio_guess_cd_type(p_cdio, start_track, i, &cdio_iso_analysis); + + print_analysis(cdio_iso_analysis, fs, first_data, num_audio, + num_tracks, first_track_num, p_cdio); + + if ( !(CDIO_FSTYPE(fs) == CDIO_FS_ISO_9660 || + CDIO_FSTYPE(fs) == CDIO_FS_ISO_HFS || + CDIO_FSTYPE(fs) == CDIO_FS_ISO_9660_INTERACTIVE) ) + /* no method for non-ISO9660 multisessions */ + break; + } + } + cdio_destroy(p_cdio); + return 0; +} diff --git a/example/tracks.c b/example/tracks.c new file mode 100644 index 00000000..dc3d5381 --- /dev/null +++ b/example/tracks.c @@ -0,0 +1,64 @@ +/* + $Id: tracks.c,v 1.7 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to list track numbers and logical sector numbers of + a Compact Disc using libcdio. */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +int +main(int argc, const char *argv[]) +{ + CdIo_t *p_cdio = cdio_open (NULL, DRIVER_UNKNOWN); + track_t i_first_track; + track_t i_tracks; + int j, i; + + + if (NULL == p_cdio) { + printf("Couldn't find a driver.. leaving.\n"); + return 1; + } + + printf("Disc last LSN: %d\n", cdio_get_disc_last_lsn(p_cdio)); + + i_tracks = cdio_get_num_tracks(p_cdio); + i_first_track = i = cdio_get_first_track_num(p_cdio); + + printf("CD-ROM Track List (%i - %i)\n", i_first_track, + i_first_track+i_tracks-1); + + printf(" #: LSN\n"); + + for (j = 0; j < i_tracks; i++, j++) { + lsn_t lsn = cdio_get_track_lsn(p_cdio, i); + if (CDIO_INVALID_LSN != lsn) + printf("%3d: %06lu\n", (int) i, (long unsigned int) lsn); + } + printf("%3X: %06lu leadout\n", CDIO_CDROM_LEADOUT_TRACK, + (long unsigned int) cdio_get_track_lsn(p_cdio, + CDIO_CDROM_LEADOUT_TRACK)); + cdio_destroy(p_cdio); + return 0; +} diff --git a/example/udf1.c b/example/udf1.c new file mode 100644 index 00000000..c9f49c36 --- /dev/null +++ b/example/udf1.c @@ -0,0 +1,144 @@ +/* + $Id: udf1.c,v 1.19 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libudf to list files in a directory of + an UDF image. + */ + +/* This is the UDF image. */ +#define UDF_IMAGE_PATH "../" +#define UDF_IMAGE "/src2/cd-images/udf/UDF102ISO.iso" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define udf_PATH_DELIMITERS "/\\" + +static void +print_file_info(const udf_dirent_t *p_udf_dirent, const char* psz_dirname) +{ + time_t mod_time = udf_get_modification_time(p_udf_dirent); + char psz_mode[11]="invalid"; + const char *psz_fname= psz_dirname + ? psz_dirname : udf_get_filename(p_udf_dirent); + + /* Print directory attributes*/ + printf("%s ", udf_mode_string(udf_get_posix_filemode(p_udf_dirent), + psz_mode)); + printf("%4d ", udf_get_link_count(p_udf_dirent)); + printf("%lu ", (long unsigned int) udf_get_file_length(p_udf_dirent)); + printf("%s %s", *psz_fname ? psz_fname : "/", ctime(&mod_time)); +} + +static udf_dirent_t * +list_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const char *psz_path) +{ + if (!p_udf_dirent) return NULL; + + print_file_info(p_udf_dirent, psz_path); + + while (udf_readdir(p_udf_dirent)) { + + if (udf_is_dir(p_udf_dirent)) { + + udf_dirent_t *p_udf_dirent2 = udf_opendir(p_udf_dirent); + if (p_udf_dirent2) { + const char *psz_dirname = udf_get_filename(p_udf_dirent); + const unsigned int i_newlen=2 + strlen(psz_path) + strlen(psz_dirname); + char *psz_newpath = calloc(1, sizeof(char)*i_newlen); + + snprintf(psz_newpath, i_newlen, "%s%s/", psz_path, psz_dirname); + list_files(p_udf, p_udf_dirent2, psz_newpath); + free(psz_newpath); + } + } else { + print_file_info(p_udf_dirent, NULL); + } + } + return p_udf_dirent; +} + +int +main(int argc, const char *argv[]) +{ + udf_t *p_udf; + char const *psz_udf_image; + + if (argc > 1) + psz_udf_image = argv[1]; + else + psz_udf_image = UDF_IMAGE; + + p_udf = udf_open (psz_udf_image); + + if (NULL == p_udf) { + fprintf(stderr, "Sorry, couldn't open %s as something using UDF\n", + psz_udf_image); + return 1; + } else { + udf_dirent_t *p_udf_root = udf_get_root(p_udf, true, 0); + if (NULL == p_udf_root) { + fprintf(stderr, "Sorry, couldn't find / in %s\n", + psz_udf_image); + return 1; + } + + { + char vol_id[UDF_VOLID_SIZE] = ""; + char volset_id[UDF_VOLSET_ID_SIZE+1] = ""; + + if (0 < udf_get_volume_id(p_udf, vol_id, sizeof(vol_id)) ) + printf("volume id: %s\n", vol_id); + + if (0 < udf_get_volume_id(p_udf, volset_id, sizeof(volset_id)) ) { + volset_id[UDF_VOLSET_ID_SIZE]='\0'; + printf("volume set id: %s\n", volset_id); + } + + printf("partition number: %d\n", udf_get_part_number(p_udf)); + + + } + + list_files(p_udf, p_udf_root, ""); + } + + udf_close(p_udf); + return 0; +} + diff --git a/example/udf2.c b/example/udf2.c new file mode 100644 index 00000000..bf9df7a0 --- /dev/null +++ b/example/udf2.c @@ -0,0 +1,134 @@ +/* + $Id: udf2.c,v 1.6 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2005, 2006,2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libudf to extract a file. + + This program can be compiled with either a C or C++ compiler. In + the distribution we prefer C++ just to make sure we haven't broken + things on the C++ side. + */ + +/* This is the UDF image. */ +#define UDF_IMAGE_PATH "../" +#define UDF_IMAGE "/src2/cd-images/udf/test2.iso" +#define UDF_FILENAME "/parse/cue.L" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define CEILING(x, y) ((x+(y-1))/y) + +#define udf_PATH_DELIMITERS "/\\" + +static void +print_file_info(const udf_dirent_t *p_udf_dirent, const char* psz_dirname) +{ + time_t mod_time = udf_get_modification_time(p_udf_dirent); + char psz_mode[11]="invalid"; + const char *psz_fname= psz_dirname + ? psz_dirname : udf_get_filename(p_udf_dirent); + + /* Print directory attributes*/ + printf("%s ", udf_mode_string(udf_get_posix_filemode(p_udf_dirent), + psz_mode)); + printf("%4d ", udf_get_link_count(p_udf_dirent)); + printf("%ld ", (long unsigned int) udf_get_file_length(p_udf_dirent)); + printf("%s %s\n", *psz_fname ? psz_fname : "/", ctime(&mod_time)); +} + +int +main(int argc, const char *argv[]) +{ + udf_t *p_udf; + char const *psz_udf_image; + char const *psz_udf_fname; + + if (argc > 1) + psz_udf_image = argv[1]; + else + psz_udf_image = UDF_IMAGE; + + if (argc > 2) + psz_udf_fname = argv[2]; + else + psz_udf_fname = UDF_FILENAME; + + + p_udf = udf_open (psz_udf_image); + + if (NULL == p_udf) { + fprintf(stderr, "Sorry, couldn't open %s as something using UDF\n", + psz_udf_image); + return 1; + } else { + udf_dirent_t *p_udf_root = udf_get_root(p_udf, true, 0); + udf_dirent_t *p_udf_file = NULL; + if (NULL == p_udf_root) { + fprintf(stderr, "Sorry, couldn't find / in %s\n", + psz_udf_image); + return 1; + } + + p_udf_file = udf_fopen(p_udf_root, psz_udf_fname); + if (!p_udf_file) { + fprintf(stderr, "Sorry, couldn't find %s in %s\n", + psz_udf_fname, psz_udf_image); + return 2; + + } + print_file_info(p_udf_file, udf_get_filename(p_udf_file)); + { + long unsigned int i_file_length = udf_get_file_length(p_udf_file); + char *p_buf; + unsigned int i_blocks = CEILING(i_file_length, UDF_BLOCKSIZE); + char fmt_string[100] = {'\0'}; + snprintf(fmt_string, sizeof(fmt_string), "%%%lus", i_file_length); + + p_buf = (char *) calloc(sizeof(char), UDF_BLOCKSIZE*i_blocks); + udf_read_block(p_udf_file, p_buf, i_blocks); + printf(fmt_string, p_buf); + free(p_buf); + } + udf_dirent_free(p_udf_file); + udf_dirent_free(p_udf_root); + } + + udf_close(p_udf); + return 0; +} + diff --git a/example/udffile.c b/example/udffile.c new file mode 100644 index 00000000..42e823f7 --- /dev/null +++ b/example/udffile.c @@ -0,0 +1,154 @@ +/* + $Id: udffile.c,v 1.3 2008/03/24 15:30:56 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libudf to extract a file. + + This program can be compiled with either a C or C++ compiler. In + the distribution we prefer C++ just to make sure we haven't broken + things on the C++ side. + */ + +/* This is the UDF image. */ +#define UDF_IMAGE_PATH "../" +#define UDF_IMAGE "../test/udf102.iso" +#define UDF_FILENAME "/COPYING" +#define LOCAL_FILENAME "copying" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include + +#include + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define CEILING(x, y) ((x+(y-1))/y) + +#define udf_PATH_DELIMITERS "/\\" + +int +main(int argc, const char *argv[]) +{ + udf_t *p_udf; + FILE *p_outfd; + char const *psz_udf_image; + char const *psz_udf_fname; + char const *psz_local_fname; + + if (argc > 1) + psz_udf_image = argv[1]; + else + psz_udf_image = UDF_IMAGE; + + if (argc > 2) + psz_udf_fname = argv[2]; + else + psz_udf_fname = UDF_FILENAME; + + if (argc > 3) + psz_local_fname = argv[3]; + else + psz_local_fname = LOCAL_FILENAME; + + + p_udf = udf_open (psz_udf_image); + + if (NULL == p_udf) { + fprintf(stderr, "Sorry, couldn't open %s as something using UDF\n", + psz_udf_image); + return 1; + } else { + udf_dirent_t *p_udf_root = udf_get_root(p_udf, true, 0); + udf_dirent_t *p_udf_file = NULL; + if (NULL == p_udf_root) { + fprintf(stderr, "Sorry, couldn't find / in %s\n", + psz_udf_image); + return 1; + } + + p_udf_file = udf_fopen(p_udf_root, psz_udf_fname); + if (!p_udf_file) { + fprintf(stderr, "Sorry, couldn't find %s in %s\n", + psz_udf_fname, psz_udf_image); + return 2; + + } + + if (!(p_outfd = fopen (psz_local_fname, "wb"))) + { + perror ("fopen()"); + return 3; + } + + { + long unsigned int i_file_length = udf_get_file_length(p_udf_file); + const unsigned int i_blocks = CEILING(i_file_length, UDF_BLOCKSIZE); + unsigned int i; + for (i = 0; i < i_blocks ; i++) { + char buf[UDF_BLOCKSIZE] = {'\0',}; + ssize_t i_read = udf_read_block(p_udf_file, buf, 1); + + if ( i_read < 0 ) { + fprintf(stderr, "Error reading UDF file %s at block %u\n", + psz_local_fname, i); + return 4; + } + + fwrite (buf, i_read, 1, p_outfd); + + if (ferror (p_outfd)) { + perror ("fwrite()"); + return 5; + } + } + + fflush (p_outfd); + udf_dirent_free(p_udf_root); + udf_close(p_udf); + /* Make sure the file size has the exact same byte size. Without the + truncate below, the file will a multiple of UDF_BLOCKSIZE. + */ + if (ftruncate (fileno (p_outfd), i_file_length)) + perror ("ftruncate()"); + + printf("Extraction of file '%s' from %s successful.\n", + psz_local_fname, psz_udf_image); + + return 0; + } + } +} + diff --git a/include/.gitignore b/include/.gitignore new file mode 100644 index 00000000..b336cc7c --- /dev/null +++ b/include/.gitignore @@ -0,0 +1,2 @@ +/Makefile +/Makefile.in diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 00000000..8aae7f04 --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,22 @@ +# $Id: Makefile.am,v 1.4 2008/03/20 19:02:37 karl Exp $ +# +# Copyright (C) 2003, 2004, 2006, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +if ENABLE_CXX_BINDINGS +cxxdirs = cdio++ +endif + +SUBDIRS = cdio $(cxxdirs) diff --git a/include/cdio++/.cvsignore b/include/cdio++/.cvsignore new file mode 100644 index 00000000..3dda7298 --- /dev/null +++ b/include/cdio++/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/include/cdio++/.gitignore b/include/cdio++/.gitignore new file mode 100644 index 00000000..b336cc7c --- /dev/null +++ b/include/cdio++/.gitignore @@ -0,0 +1,2 @@ +/Makefile +/Makefile.in diff --git a/include/cdio++/Makefile.am b/include/cdio++/Makefile.am new file mode 100644 index 00000000..076cb4f7 --- /dev/null +++ b/include/cdio++/Makefile.am @@ -0,0 +1,34 @@ +# $Id: Makefile.am,v 1.5 2008/03/20 19:02:38 karl Exp $ +# +# Copyright (C) 2005, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +######################################################## +# Things to make the install (public) libcdio++ headers +######################################################## +# + +libcdioincludedir=$(includedir)/cdio++ +libcdioinclude_HEADERS = \ + cdio.hpp \ + cdtext.hpp \ + device.hpp \ + devices.hpp \ + disc.hpp \ + enum.hpp \ + iso9660.hpp \ + mmc.hpp \ + read.hpp \ + track.hpp diff --git a/include/cdio++/cdio.hpp b/include/cdio++/cdio.hpp new file mode 100644 index 00000000..c31de84f --- /dev/null +++ b/include/cdio++/cdio.hpp @@ -0,0 +1,181 @@ +/* + $Id: cdio.hpp,v 1.13 2008/03/25 15:59:10 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file cdio.hpp + * + * \brief C++ class for libcdio: the CD Input and Control + * library. Applications use this for anything regarding libcdio. + */ + +#ifndef __CDIO_HPP__ +#define __CDIO_HPP__ + +#include +#include +#include +#include + +// Make pre- and post-increment operators for enums in libcdio where it +// makes sense. +#include + +/** Class for driver exceptions. **/ +class DriverOpException +{ +public: + driver_return_code_t driver_return_code; + DriverOpException( void ) { }; + DriverOpException( driver_return_code_t drc ) { + driver_return_code = drc; + }; + driver_return_code_t get_code(void) { + return driver_return_code; + }; + const char *get_msg(void) { + return cdio_driver_errmsg(driver_return_code); + }; +}; + +class DriverOpError: public DriverOpException +{ +public: + DriverOpError(void) { driver_return_code = DRIVER_OP_ERROR; } +}; + +class DriverOpUnsupported: public DriverOpException +{ +public: + DriverOpUnsupported(void) { driver_return_code = DRIVER_OP_UNSUPPORTED; } +}; + +class DriverOpUninit: public DriverOpException +{ +public: + DriverOpUninit(void) { driver_return_code = DRIVER_OP_UNINIT; } +}; + +class DriverOpNotPermitted: public DriverOpException +{ +public: + DriverOpNotPermitted(void) {driver_return_code = DRIVER_OP_NOT_PERMITTED;} +}; + +class DriverOpBadParameter: public DriverOpException +{ +public: + DriverOpBadParameter(void) {driver_return_code = DRIVER_OP_BAD_PARAMETER;} +}; + +class DriverOpBadPointer: public DriverOpException +{ +public: + DriverOpBadPointer(void) {driver_return_code = DRIVER_OP_BAD_POINTER;} +}; + +class DriverOpNoDriver: public DriverOpException +{ +public: + DriverOpNoDriver(void) {driver_return_code = DRIVER_OP_NO_DRIVER;} +}; + +void possible_throw_device_exception(driver_return_code_t drc); + +/** A class relating to CD-Text. Use invalid track number 0 to specify + CD-Text for the CD (as opposed to a specific track). +*/ +class CdioCDText +{ +public: + CdioCDText(cdtext_t *p) + { + p_cdtext = p; + cdtext_init(p); // make sure we're initialized on the C side + } + + ~CdioCDText() + { + cdtext_destroy(p_cdtext); + p_cdtext = (cdtext_t *) NULL; + } + + // Other member functions +#include "cdtext.hpp" + +private: + cdtext_t *p_cdtext; +}; + +/** A class relating to tracks. A track object basically saves device + and track number information so that in track operations these + don't have be specified. +*/ +class CdioTrack +{ + +public: + CdioTrack(CdIo_t *p, track_t t) + { + i_track = t; + p_cdio = p; + } + + // Other member functions +#include "track.hpp" + +private: + track_t i_track; + CdIo_t *p_cdio; +}; + +/** A class relating to a CD-ROM device or pseudo CD-ROM device with + has a particular CD image. A device basically saves the libcdio + "object" (of type CdIo *). +*/ +class CdioDevice +{ + +protected: + + CdIo_t *p_cdio; + +public: + + CdioDevice() + { + p_cdio = (CdIo_t *) NULL; + }; + + ~CdioDevice() + { + cdio_destroy(p_cdio); + p_cdio = (CdIo_t *) NULL; + }; + + // Other member functions +#include "device.hpp" +#include "disc.hpp" +#include "mmc.hpp" +#include "read.hpp" + +}; + +/* Things related to devices. No class or object is needed. */ +#include "devices.hpp" + +#endif /* __CDIO_HPP__ */ diff --git a/include/cdio++/cdtext.hpp b/include/cdio++/cdtext.hpp new file mode 100644 index 00000000..2aeb4b44 --- /dev/null +++ b/include/cdio++/cdtext.hpp @@ -0,0 +1,90 @@ +/* + $Id: cdtext.hpp,v 1.2 2008/03/25 15:59:10 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file cdtext.hpp + * \brief methods relating to CD-Text information. This file + * should not be #included directly. + */ + +/*! Return string representation of the enum values above */ +const char *field2str (cdtext_field_t i) +{ + return cdtext_field2str (i); +} + +/*! returns an allocated string associated with the given field. NULL is + returned if key is CDTEXT_INVALID or the field is not set. + + The user needs to free the string when done with it. + + @see getConst to retrieve a constant string that doesn't + have to be freed. + +*/ +char *get (cdtext_field_t key) +{ + return cdtext_get (key, p_cdtext); +} + +/*! returns the C cdtext_t pointer associated with this object. */ +cdtext_t *get () +{ + return p_cdtext; +} + +/*! returns a const string associated with the given field. NULL is + returned if key is CDTEXT_INVALID or the field is not set. + + Don't use the string when the cdtext object (i.e. the CdIo_t object + you got it from) is no longer valid. + + @see cdio_get to retrieve an allocated string that persists past the + cdtext object. + +*/ +const char *getConst (cdtext_field_t key) +{ + return cdtext_get_const (key, p_cdtext); +} + +/*! + returns enum of keyword if key is a CD-Text keyword, + returns MAX_CDTEXT_FIELDS non-zero otherwise. +*/ +cdtext_field_t isKeyword (const char *key) +{ + return cdtext_is_keyword (key); +} + +/*! + sets cdtext's keyword entry to field +*/ +void set (cdtext_field_t key, const char *value) +{ + cdtext_set (key, value, p_cdtext); +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/cdio++/device.hpp b/include/cdio++/device.hpp new file mode 100644 index 00000000..cbb75ca2 --- /dev/null +++ b/include/cdio++/device.hpp @@ -0,0 +1,261 @@ +/* + $Id: device.hpp,v 1.7 2008/03/25 15:59:10 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file device.hpp + * + * \brief C++ header for driver- or device-related libcdio calls. + * ("device" includes CD-image reading devices.) + */ + +/*! + Free resources associated with CD-ROM Device/Image. After this we + must do another open before any more reading. +*/ +bool +close() +{ + cdio_destroy(p_cdio); + p_cdio = (CdIo_t *) NULL; + return true; +} + +/*! + Eject media in CD drive if there is a routine to do so. + + If the CD is ejected, object is destroyed. +*/ +void +ejectMedia () +{ + driver_return_code_t drc = cdio_eject_media(&p_cdio); + possible_throw_device_exception(drc); +} + +/*! + Free device list returned by GetDevices + + @param device_list list returned by GetDevices + + @see GetDevices + +*/ +void +freeDeviceList (char * device_list[]) +{ + cdio_free_device_list(device_list); +} + +/*! + Get the value associatied with key. + + @param key the key to retrieve + @return the value associatd with "key" or NULL if p_cdio is NULL + or "key" does not exist. + */ +const char * +getArg (const char key[]) +{ + return cdio_get_arg (p_cdio, key); +} + +/*! + Return an opaque CdIo_t pointer for the given track object. +*/ +CdIo_t *getCdIo() +{ + return p_cdio; +} + +/*! + Return an opaque CdIo_t pointer for the given track object. +*/ +cdtext_t *getCdtext(track_t i_track) +{ + return cdio_get_cdtext (p_cdio, i_track); +} + +/*! + Get the CD device name for the object. + + @return a string containing the CD device for this object or NULL is + if we couldn't get a device anme. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. +*/ +char * +getDevice () +{ + return cdio_get_default_device(p_cdio); +} + +/*! + Get the what kind of device we've got. + + @param p_read_cap pointer to return read capabilities + @param p_write_cap pointer to return write capabilities + @param p_misc_cap pointer to return miscellaneous other capabilities + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. +*/ +void +getDriveCap (cdio_drive_read_cap_t &read_cap, + cdio_drive_write_cap_t &write_cap, + cdio_drive_misc_cap_t &misc_cap) +{ + cdio_get_drive_cap(p_cdio, &read_cap, &write_cap, &misc_cap); +} + +/*! + Get a string containing the name of the driver in use. + + @return a string with driver name or NULL if CdIo_t is NULL (we + haven't initialized a specific device. +*/ +const char * +getDriverName () +{ + return cdio_get_driver_name(p_cdio); +} + +/*! + Get the driver id. + if CdIo_t is NULL (we haven't initialized a specific device driver), + then return DRIVER_UNKNOWN. + + @return the driver id.. +*/ +driver_id_t +getDriverId () +{ + return cdio_get_driver_id(p_cdio); +} + +/*! + Get the CD-ROM hardware info via a SCSI MMC INQUIRY command. + False is returned if we had an error getting the information. +*/ +bool +getHWinfo ( /*out*/ cdio_hwinfo_t &hw_info ) +{ + return cdio_get_hwinfo(p_cdio, &hw_info); +} + +/*! Get the LSN of the first track of the last session of + on the CD. + + @param i_last_session pointer to the session number to be returned. +*/ +void +getLastSession (/*out*/ lsn_t &i_last_session) +{ + driver_return_code_t drc = cdio_get_last_session(p_cdio, &i_last_session); + possible_throw_device_exception(drc); +} + +/*! + Find out if media has changed since the last call. + @return 1 if media has changed since last call, 0 if not. Error + return codes are the same as driver_return_code_t +*/ +int +getMediaChanged() +{ + return cdio_get_media_changed(p_cdio); +} + +/*! True if CD-ROM understand ATAPI commands. */ +bool_3way_t +haveATAPI () +{ + return cdio_have_atapi(p_cdio); +} + +/*! + + Sets up to read from the device specified by psz_source. An open + routine should be called before using any read routine. If device + object was previously opened it is closed first. + + @return true if open succeeded or false if error. + +*/ +bool +open(const char *psz_source) +{ + if (p_cdio) cdio_destroy(p_cdio); + p_cdio = cdio_open_cd(psz_source); + return NULL != p_cdio ; +} + +/*! + + Sets up to read from the device specified by psz_source and access + mode. An open routine should be called before using any read + routine. If device object was previously opened it is "closed". + + @return true if open succeeded or false if error. +*/ +bool +open (const char *psz_source, driver_id_t driver_id, + const char *psz_access_mode = (const char *) NULL) +{ + if (p_cdio) cdio_destroy(p_cdio); + if (psz_access_mode) + p_cdio = cdio_open_am(psz_source, driver_id, psz_access_mode); + else + p_cdio = cdio_open(psz_source, driver_id); + return NULL != p_cdio ; +} + +/*! + Set the blocksize for subsequent reads. +*/ +void +setBlocksize ( int i_blocksize ) +{ + driver_return_code_t drc = cdio_set_blocksize ( p_cdio, i_blocksize ); + possible_throw_device_exception(drc); +} + +/*! + Set the drive speed. +*/ +void +setSpeed ( int i_speed ) +{ + driver_return_code_t drc = cdio_set_speed ( p_cdio, i_speed ); + possible_throw_device_exception(drc); +} + +/*! + Set the arg "key" with "value" in "p_cdio". + + @param key the key to set + @param value the value to assocaiate with key +*/ +void +setArg (const char key[], const char value[]) +{ + driver_return_code_t drc = cdio_set_arg (p_cdio, key, value); + possible_throw_device_exception(drc); +} diff --git a/include/cdio++/devices.hpp b/include/cdio++/devices.hpp new file mode 100644 index 00000000..884cd8f6 --- /dev/null +++ b/include/cdio++/devices.hpp @@ -0,0 +1,181 @@ +/* + $Id: devices.hpp,v 1.5 2008/03/25 15:59:10 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file devices.hpp + * + * \brief methods relating to devices. It is *not* part of a class. + * This file should not be #included directly. + */ + + +/*! + Close media tray in CD drive if there is a routine to do so. + + @param psz_drive the name of CD-ROM to be closed. + @param driver_id is the driver to be used or that got used if + it was DRIVER_UNKNOWN or DRIVER_DEVICE; If this is NULL, we won't + report back the driver used. +*/ +void closeTray (const char *psz_drive, /*in/out*/ driver_id_t &driver_id); + +/*! + Close media tray in CD drive if there is a routine to do so. + + @param psz_drive the name of CD-ROM to be closed. If omitted or + NULL, we'll scan for a suitable CD-ROM. +*/ +void closeTray (const char *psz_drive=(const char *)NULL); + +/*! + Get a string decribing driver_id. + + @param driver_id the driver you want the description for + @return a sring of driver description +*/ +const char *driverDescribe (driver_id_t driver_id); + +/*! + Eject media in CD drive if there is a routine to do so. + + If the CD is ejected, object is destroyed. +*/ +void ejectMedia (const char *psz_drive); + +/*! + Free device list returned by GetDevices + + @param device_list list returned by GetDevices + + @see GetDevices + +*/ +void freeDeviceList (char * device_list[]); + +/*! + Return a string containing the default CD device if none is specified. + if p_driver_id is DRIVER_UNKNOWN or DRIVER_DEVICE + then find a suitable one set the default device for that. + + NULL is returned if we couldn't get a default device. +*/ +char * getDefaultDevice(/*in/out*/ driver_id_t &driver_id); + +/*! Return an array of device names. If you want a specific + devices for a driver, give that device. If you want hardware + devices, give DRIVER_DEVICE and if you want all possible devices, + image drivers and hardware drivers give DRIVER_UNKNOWN. + + NULL is returned if we couldn't return a list of devices. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. +*/ +char ** getDevices(driver_id_t driver_id=DRIVER_DEVICE); + +/*! Like GetDevices above, but we may change the p_driver_id if we + were given DRIVER_DEVICE or DRIVER_UNKNOWN. This is because + often one wants to get a drive name and then *open* it + afterwards. Giving the driver back facilitates this, and speeds + things up for libcdio as well. +*/ + +char **getDevices (driver_id_t &driver_id); + +/*! + Get an array of device names in search_devices that have at least + the capabilities listed by the capabities parameter. If + search_devices is NULL, then we'll search all possible CD drives. + + If "b_any" is set false then every capability listed in the + extended portion of capabilities (i.e. not the basic filesystem) + must be satisified. If "any" is set true, then if any of the + capabilities matches, we call that a success. + + To find a CD-drive of any type, use the mask CDIO_FS_MATCH_ALL. + + @return the array of device names or NULL if we couldn't get a + default device. It is also possible to return a non NULL but + after dereferencing the the value is NULL. This also means nothing + was found. +*/ +char ** getDevices(/*in*/ char *ppsz_search_devices[], + cdio_fs_anal_t capabilities, bool b_any=false); + +/*! + Like GetDevices above but we return the driver we found + as well. This is because often one wants to search for kind of drive + and then *open* it afterwards. Giving the driver back facilitates this, + and speeds things up for libcdio as well. +*/ +char ** getDevices(/*in*/ char* ppsz_search_devices[], + cdio_fs_anal_t capabilities, /*out*/ driver_id_t &driver_id, + bool b_any=false); + +/*! Return true if we Have driver for driver_id */ +bool haveDriver (driver_id_t driver_id); + +/*! + +Determine if bin_name is the bin file part of a CDRWIN CD disk image. + +@param bin_name location of presumed CDRWIN bin image file. + @return the corresponding CUE file if bin_name is a BIN file or + NULL if not a BIN file. + */ +char *isBinFile(const char *psz_bin_name); + + +/*! + Determine if cue_name is the cue sheet for a CDRWIN CD disk image. + + @return corresponding BIN file if cue_name is a CDRWIN cue file or + NULL if not a CUE file. + */ +char *isCueFile(const char *psz_cue_name); + +/*! + Determine if psz_source refers to a real hardware CD-ROM. + + @param psz_source location name of object + @param driver_id driver for reading object. Use DRIVER_UNKNOWN if you + don't know what driver to use. + @return true if psz_source is a device; If false is returned we + could have a CD disk image. +*/ +bool isDevice(const char *psz_source, driver_id_t driver_id); + +/*! + Determine if psz_nrg is a Nero CD disk image. + + @param psz_nrg location of presumed NRG image file. + @return true if psz_nrg is a Nero NRG image or false + if not a NRG image. +*/ +bool isNero(const char *psz_nrg); + +/*! + Determine if psz_toc is a TOC file for a cdrdao CD disk image. + + @param psz_toc location of presumed TOC image file. + @return true if toc_name is a cdrdao TOC file or false + if not a TOC file. +*/ +bool isTocFile(const char *psz_toc); + diff --git a/include/cdio++/disc.hpp b/include/cdio++/disc.hpp new file mode 100644 index 00000000..63223874 --- /dev/null +++ b/include/cdio++/disc.hpp @@ -0,0 +1,174 @@ +/* + $Id: disc.hpp,v 1.2 2008/03/25 15:59:10 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file disc.hpp + * \brief methods relating to getting Compact Disc information. This file + * should not be #included directly. + */ + +/*! + Get disc mode - the kind of CD (CD-DA, CD-ROM mode 1, CD-MIXED, etc. + that we've got. The notion of "CD" is extended a little to include + DVD's. +*/ +discmode_t getDiscmode () +{ + return cdio_get_discmode(p_cdio); +} + +/*! + Get the lsn of the end of the CD + + @return the lsn. On error 0 or CDIO_INVALD_LSN. +*/ +lsn_t getDiscLastLsn() +{ + return cdio_get_disc_last_lsn(p_cdio); +} + +/*! + Get the number of the first track. + + @return a track object or NULL; + on error. +*/ +CdioTrack *getFirstTrack() +{ + track_t i_track = cdio_get_first_track_num(p_cdio); + return (CDIO_INVALID_TRACK != i_track) + ? new CdioTrack(p_cdio, i_track) + : (CdioTrack *) NULL; +} + +/*! + Get the number of the first track. + + @return the track number or CDIO_INVALID_TRACK + on error. +*/ +track_t getFirstTrackNum() +{ + return cdio_get_first_track_num(p_cdio); +} + + +/*! + Get the number of the first track. + + @return a track object or NULL; + on error. +*/ +CdioTrack *getLastTrack() +{ + track_t i_track = cdio_get_last_track_num(p_cdio); + return (CDIO_INVALID_TRACK != i_track) + ? new CdioTrack(p_cdio, i_track) + : (CdioTrack *) NULL; +} + +/*! + Get the number of the first track. + + @return the track number or CDIO_INVALID_TRACK + on error. +*/ +track_t getLastTrackNum() +{ + return cdio_get_last_track_num(p_cdio); +} + +/*! + Return the Joliet level recognized for p_cdio. +*/ +uint8_t getJolietLevel() +{ + return cdio_get_joliet_level(p_cdio); +} + +/*! + Get the media catalog number (MCN) from the CD. + + @return the media catalog number r NULL if there is none or we + don't have the ability to get it. + + Note: string is malloc'd so caller has to free() the returned + string when done with it. + +*/ +char * getMcn () +{ + return cdio_get_mcn (p_cdio); +} + +/*! + Get the number of tracks on the CD. + + @return the number of tracks, or CDIO_INVALID_TRACK if there is + an error. +*/ +track_t getNumTracks () +{ + return cdio_get_num_tracks(p_cdio); +} + +/*! Find the track which contans lsn. + CDIO_INVALID_TRACK is returned if the lsn outside of the CD or + if there was some error. + + If the lsn is before the pregap of the first track 0 is returned. + Otherwise we return the track that spans the lsn. +*/ +CdioTrack *getTrackFromNum(track_t i_track) +{ + return new CdioTrack(p_cdio, i_track); +} + +/*! Find the track which contans lsn. + CDIO_INVALID_TRACK is returned if the lsn outside of the CD or + if there was some error. + + If the lsn is before the pregap of the first track 0 is returned. + Otherwise we return the track that spans the lsn. +*/ +CdioTrack *getTrackFromLsn(lsn_t lsn) +{ + track_t i_track = cdio_get_track(p_cdio, lsn); + return (CDIO_INVALID_TRACK != i_track) + ? new CdioTrack(p_cdio, i_track) + : (CdioTrack *) NULL; +} + + +/*! + Return true if discmode is some sort of CD. +*/ +bool isDiscmodeCdrom (discmode_t discmode) { + return cdio_is_discmode_cdrom(discmode); +} + + +/*! + Return true if discmode is some sort of DVD. +*/ +bool isDiscmodeDvd (discmode_t discmode) +{ + return cdio_is_discmode_dvd (discmode) ; +} + + diff --git a/include/cdio++/enum.hpp b/include/cdio++/enum.hpp new file mode 100644 index 00000000..9991be69 --- /dev/null +++ b/include/cdio++/enum.hpp @@ -0,0 +1,49 @@ +/* + $Id: enum.hpp,v 1.2 2008/03/25 15:59:10 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file enum.hpp + * + * \brief C++ header for pre- and post-increment operators for + * enumerations defined in libcdio that it makes sense to iterate over. + */ + +#define ENUM_ITERATE_FNS(type) \ + inline \ + type &operator++(type &t) \ + { \ + return t = type(t + 1); \ + } \ + inline \ + type &operator++(type &t, int) \ + { \ + return t = type(t + 1); \ + } \ + inline \ + type &operator--(type &t) \ + { \ + return t = type(t - 1); \ + } \ + inline \ + type &operator--(type &t, int) \ + { \ + return t = type(t - 1); \ + } + +ENUM_ITERATE_FNS(cdtext_field_t) +ENUM_ITERATE_FNS(driver_id_t) diff --git a/include/cdio++/iso9660.hpp b/include/cdio++/iso9660.hpp new file mode 100644 index 00000000..b62cbca3 --- /dev/null +++ b/include/cdio++/iso9660.hpp @@ -0,0 +1,428 @@ +/* + $Id: iso9660.hpp,v 1.13 2008/06/13 15:58:50 flameeyes Exp $ + + Copyright (C) 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file iso9660.hpp + * + * \brief C++ class for libcdio: the CD Input and Control + * library. Applications use this for anything regarding libcdio. + */ + +#ifndef __ISO9660_HPP__ +#define __ISO9660_HPP__ + +#include +#include +#include // vector class library +#include +#include +using namespace std; + +/** ISO 9660 class. +*/ +class ISO9660 +{ + +public: + + class PVD // Primary Volume ID + { + public: + + iso9660_pvd_t pvd; // Make private? + + PVD() + { + memset(&pvd, 0, sizeof(pvd)); + } + + PVD(iso9660_pvd_t *p_new_pvd) + { + memcpy(&pvd, p_new_pvd, sizeof(pvd)); + }; + + /*! + Return the PVD's application ID. + NULL is returned if there is some problem in getting this. + */ + char * get_application_id(); + + int get_pvd_block_size(); + + /*! + Return the PVD's preparer ID. + NULL is returned if there is some problem in getting this. + */ + char * get_preparer_id(); + + /*! + Return the PVD's publisher ID. + NULL is returned if there is some problem in getting this. + */ + char * get_publisher_id(); + + const char *get_pvd_id(); + + int get_pvd_space_size(); + + uint8_t get_pvd_type(); + + /*! Return the primary volume id version number (of pvd). + If there is an error 0 is returned. + */ + int get_pvd_version(); + + /*! Return the LSN of the root directory for pvd. + If there is an error CDIO_INVALID_LSN is returned. + */ + lsn_t get_root_lsn(); + + /*! + Return the PVD's system ID. + NULL is returned if there is some problem in getting this. + */ + char * get_system_id(); + + /*! + Return the PVD's volume ID. + NULL is returned if there is some problem in getting this. + */ + char * get_volume_id(); + + /*! + Return the PVD's volumeset ID. + NULL is returned if there is some problem in getting this. + */ + char * get_volumeset_id(); + + }; + + class Stat // ISO 9660 file information + { + public: + + iso9660_stat_t *p_stat; + typedef vector< ISO9660::Stat *> stat_vector_t; + + Stat(iso9660_stat_t *p_new_stat) + { + p_stat = p_new_stat; + }; + + Stat(const Stat& copy_in) + { + free(p_stat); + p_stat = (iso9660_stat_t *) + calloc( 1, sizeof(iso9660_stat_t) + + strlen(copy_in.p_stat->filename)+1 ); + p_stat = copy_in.p_stat; + } + + const Stat& operator= (const Stat& right) + { + free(p_stat); + this->p_stat = right.p_stat; + return right; + } + + ~Stat() + { + free(p_stat); + p_stat = NULL; + } + + }; + + class FS : public CdioDevice // ISO 9660 Filesystem on a CD or CD-image + { + public: + + typedef vector< ISO9660::Stat *> stat_vector_t; + + /*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + @return Stat * of entry if we found lsn, or NULL otherwise. + Caller must free return value. + */ + Stat *find_lsn(lsn_t i_lsn); + + /*! Read the Primary Volume Descriptor for a CD. A + PVD object is returned if read, and NULL if there was an error. + */ + PVD *read_pvd (); + + /*! + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental Volume + Descriptor if (Joliet) extensions are acceptable. + */ + bool read_superblock (iso_extension_mask_t iso_extension_mask); + + /*! Read psz_path (a directory) and return a vector of iso9660_stat_t + pointers for the files inside that directory. The caller must free the + returned result. + */ + bool readdir (const char psz_path[], stat_vector_t& stat_vector, + bool b_mode2=false); + + /*! + Return file status for path name psz_path. NULL is returned on + error. + + If translate is true, version numbers in the ISO 9660 name are + dropped, i.e. ;1 is removed and if level 1 ISO-9660 names are + lowercased. + + Mode2 is used only if translate is true and is a hack that + really should go away in libcdio sometime. If set use mode 2 + reading, otherwise use mode 1 reading. + + @return file status object for psz_path. NULL is returned on + error. + */ + Stat * + stat (const char psz_path[], bool b_translate=false, bool b_mode2=false) + { + if (b_translate) + return new Stat(iso9660_fs_stat_translate (p_cdio, psz_path, + b_mode2)); + else + return new Stat(iso9660_fs_stat (p_cdio, psz_path)); + } + }; + + class IFS // ISO 9660 filesystem image + { + public: + + typedef vector< ISO9660::Stat *> stat_vector_t; + + IFS() + { + p_iso9660=NULL; + }; + + ~IFS() + { + iso9660_close(p_iso9660); + p_iso9660 = (iso9660_t *) NULL; + }; + + /*! Close previously opened ISO 9660 image and free resources + associated with the image. Call this when done using using an ISO + 9660 image. + + @return true is unconditionally returned. If there was an error + false would be returned. + */ + bool close(); + + /*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + Returns Stat* of entry if we found lsn, or NULL otherwise. + */ + Stat *find_lsn(lsn_t i_lsn); + + /*! + Get the application ID. psz_app_id is set to NULL if there + is some problem in getting this and false is returned. + */ + bool get_application_id(/*out*/ char * &psz_app_id) + { + return iso9660_ifs_get_application_id(p_iso9660, &psz_app_id); + } + + /*! + Return the Joliet level recognized. + */ + uint8_t get_joliet_level(); + + /*! + Get the preparer ID. psz_preparer_id is set to NULL if there + is some problem in getting this and false is returned. + */ + bool get_preparer_id(/*out*/ char * &psz_preparer_id) + { + return iso9660_ifs_get_preparer_id(p_iso9660, &psz_preparer_id); + } + + /*! + Get the publisher ID. psz_publisher_id is set to NULL if there + is some problem in getting this and false is returned. + */ + bool get_publisher_id(/*out*/ char * &psz_publisher_id) + { + return iso9660_ifs_get_publisher_id(p_iso9660, &psz_publisher_id); + } + + /*! + Get the system ID. psz_system_id is set to NULL if there + is some problem in getting this and false is returned. + */ + bool get_system_id(/*out*/ char * &psz_system_id) + { + return iso9660_ifs_get_system_id(p_iso9660, &psz_system_id); + } + + /*! Return the volume ID in the PVD. psz_volume_id is set to + NULL if there is some problem in getting this and false is + returned. + */ + bool get_volume_id(/*out*/ char * &psz_volume_id) + { + return iso9660_ifs_get_volume_id(p_iso9660, &psz_volume_id); + } + + /*! Return the volumeset ID in the PVD. psz_volumeset_id is set to + NULL if there is some problem in getting this and false is + returned. + */ + bool get_volumeset_id(/*out*/ char * &psz_volumeset_id) + { + return iso9660_ifs_get_volumeset_id(p_iso9660, &psz_volumeset_id); + } + + /*! + Return true if ISO 9660 image has extended attrributes (XA). + */ + bool is_xa (); + + /*! Open an ISO 9660 image for reading. Maybe in the future we will + have a mode. NULL is returned on error. An open routine should be + called before using any read routine. If device object was + previously opened it is closed first. + + @param psz_path location of ISO 9660 image + @param iso_extension_mask the kinds of ISO 9660 extensions will be + considered on access. + + @return true if open succeeded or false if error. + + @see open_fuzzy + */ + bool open(const char *psz_path, + iso_extension_mask_t iso_extension_mask=ISO_EXTENSION_NONE) + { + if (p_iso9660) iso9660_close(p_iso9660); + p_iso9660 = iso9660_open_ext(psz_path, iso_extension_mask); + return NULL != (iso9660_t *) p_iso9660 ; + } + + /*! Open an ISO 9660 image for "fuzzy" reading. This means that we + will try to guess various internal offset based on internal + checks. This may be useful when trying to read an ISO 9660 image + contained in a file format that libiso9660 doesn't know natively + (or knows imperfectly.) + + Maybe in the future we will have a mode. NULL is returned on + error. + + @see open + */ + bool open_fuzzy (const char *psz_path, + iso_extension_mask_t iso_extension_mask + =ISO_EXTENSION_NONE, + uint16_t i_fuzz=20); + + /*! Read the Primary Volume Descriptor for an ISO 9660 image. A + PVD object is returned if read, and NULL if there was an error. + */ + PVD *read_pvd (); + + /*! + Read the Super block of an ISO 9660 image but determine framesize + and datastart and a possible additional offset. Generally here we are + not reading an ISO 9660 image but a CD-Image which contains an ISO 9660 + filesystem. + + @see read_superblock + */ + bool read_superblock (iso_extension_mask_t iso_extension_mask + =ISO_EXTENSION_NONE, + uint16_t i_fuzz=20); + + /*! + Read the Super block of an ISO 9660 image but determine framesize + and datastart and a possible additional offset. Generally here we are + not reading an ISO 9660 image but a CD-Image which contains an ISO 9660 + filesystem. + + @see read_superblock + */ + bool + read_superblock_fuzzy (iso_extension_mask_t iso_extension_mask + =ISO_EXTENSION_NONE, + uint16_t i_fuzz=20); + + /*! Read psz_path (a directory) and return a list of iso9660_stat_t + pointers for the files inside that directory. The caller must free + the returned result. + */ + bool readdir (const char psz_path[], stat_vector_t& stat_vector) + { + CdioList_t *p_stat_list = iso9660_ifs_readdir (p_iso9660, psz_path); + + if (p_stat_list) { + CdioListNode_t *p_entnode; + _CDIO_LIST_FOREACH (p_entnode, p_stat_list) { + iso9660_stat_t *p_statbuf = + (iso9660_stat_t *) _cdio_list_node_data (p_entnode); + stat_vector.push_back(new ISO9660::Stat(p_statbuf)); + } + _cdio_list_free (p_stat_list, false); + return true; + } else { + return false; + } + } + + /*! + Seek to a position and then read n bytes. Size read is returned. + */ + long int + seek_read (void *ptr, lsn_t start, long int i_size=1) + { + return iso9660_iso_seek_read (p_iso9660, ptr, start, i_size); + } + + /*! + Return file status for pathname. NULL is returned on error. + */ + Stat * + stat (const char psz_path[], bool b_translate=false) + { + if (b_translate) + return new Stat(iso9660_ifs_stat_translate (p_iso9660, psz_path)); + else + return new Stat(iso9660_ifs_stat (p_iso9660, psz_path)); + } + + private: + iso9660_t *p_iso9660; + }; + +}; + +typedef vector< ISO9660::Stat *> stat_vector_t; +typedef vector ::iterator stat_vector_iterator_t; + +#endif /* __ISO9660_HPP__ */ diff --git a/include/cdio++/mmc.hpp b/include/cdio++/mmc.hpp new file mode 100644 index 00000000..82af1b09 --- /dev/null +++ b/include/cdio++/mmc.hpp @@ -0,0 +1,425 @@ +/* + $Id: mmc.hpp,v 1.3 2008/03/25 15:59:10 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file mmc.hpp + * \brief methods relating to MMC (Multimedia Commands). This file + * should not be #included directly. + */ + +/*! + Read Audio Subchannel information + + @param p_cdio the CD object to be acted upon. + @param p_subchannel place for returned subchannel information + + A DriverOpException is raised on error. +*/ +void +mmcAudioReadSubchannel (/*out*/ cdio_subchannel_t *p_subchannel) +{ + driver_return_code_t drc = mmc_audio_read_subchannel (p_cdio, p_subchannel); + possible_throw_device_exception(drc); +} + +/*! + Eject using MMC commands. If CD-ROM is "locked" we'll unlock it. + Command is not "immediate" -- we'll wait for the command to complete. + For a more general (and lower-level) routine, @see mmc_start_stop_media. + + A DriverOpException is raised on error. +*/ +void mmcEjectMedia() +{ + driver_return_code_t drc = mmc_eject_media( p_cdio ); + possible_throw_device_exception(drc); +} + +/*! + Get the lsn of the end of the CD + + @return the lsn. On error return CDIO_INVALID_LSN. +*/ +lsn_t mmcGetDiscLastLsn() +{ + return mmc_get_disc_last_lsn( p_cdio ); +} + +/*! + Return the discmode as reported by the MMC Read (FULL) TOC + command. + + Information was obtained from Section 5.1.13 (Read TOC/PMA/ATIP) + pages 56-62 from the MMC draft specification, revision 10a + at http://www.t10.org/ftp/t10/drafts/mmc/mmc-r10a.pdf See + especially tables 72, 73 and 75. +*/ +discmode_t mmcGetDiscmode() +{ + return mmc_get_discmode( p_cdio ); +} + +/*! + Get drive capabilities for a device. + @return the drive capabilities. +*/ +void mmcGetDriveCap ( /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap) +{ + mmc_get_drive_cap ( p_cdio, p_read_cap, p_write_cap, p_misc_cap); +} + +/*! + Get the MMC level supported by the device. +*/ +cdio_mmc_level_t mmcGetDriveMmcCap() +{ + return mmc_get_drive_mmc_cap(p_cdio); +} + +/*! + Get the DVD type associated with cd object. + + @return the DVD discmode. +*/ +discmode_t mmcGetDvdStructPhysical (cdio_dvd_struct_t *s) +{ + return mmc_get_dvd_struct_physical (p_cdio, s); +} + +/*! + Get the CD-ROM hardware info via an MMC INQUIRY command. + + @return true if we were able to get hardware info, false if we had + an error. +*/ +bool mmcGetHwinfo ( /* out*/ cdio_hwinfo_t *p_hw_info ) +{ + return mmc_get_hwinfo ( p_cdio, p_hw_info ); +} + +/*! + Find out if media has changed since the last call. + @param p_cdio the CD object to be acted upon. + @return 1 if media has changed since last call, 0 if not. Error + return codes are the same as driver_return_code_t +*/ +int mmcGetMediaChanged() +{ + return mmc_get_media_changed(p_cdio); +} + +/*! + Get the media catalog number (MCN) from the CD via MMC. + + @return the media catalog number r NULL if there is none or we + don't have the ability to get it. + + Note: string is malloc'd so caller has to free() the returned + string when done with it. + +*/ +char * mmcGetMcn () +{ + return mmc_get_mcn ( p_cdio ); +} + +/** Get the output port volumes and port selections used on AUDIO PLAY + commands via a MMC MODE SENSE command using the CD Audio Control + Page. + + A DriverOpException is raised on error. +*/ +void mmcAudioGetVolume (mmc_audio_volume_t *p_volume) +{ + driver_return_code_t drc = mmc_audio_get_volume (p_cdio, p_volume); + possible_throw_device_exception(drc); +} + +/*! + Report if CD-ROM has a praticular kind of interface (ATAPI, SCSCI, ...) + Is it possible for an interface to have serveral? If not this + routine could probably return the single mmc_feature_interface_t. + @return true if we have the interface and false if not. +*/ +bool_3way_t mmcHaveInterface( cdio_mmc_feature_interface_t e_interface ) +{ + return mmc_have_interface( p_cdio, e_interface ); +} + +/*! Run a MODE_SENSE command (6- or 10-byte version) + and put the results in p_buf + @return DRIVER_OP_SUCCESS if we ran the command ok. +*/ +int mmcModeSense( /*out*/ void *p_buf, int i_size, int page) +{ + return mmc_mode_sense( p_cdio, /*out*/ p_buf, i_size, page); +} + +/*! Run a MODE_SENSE command (10-byte version) + and put the results in p_buf + @return DRIVER_OP_SUCCESS if we ran the command ok. +*/ +int mmcModeSense10( /*out*/ void *p_buf, int i_size, int page) +{ + return mmc_mode_sense_10( p_cdio, /*out*/ p_buf, i_size, page); +} + +/*! Run a MODE_SENSE command (6-byte version) + and put the results in p_buf + @return DRIVER_OP_SUCCESS if we ran the command ok. +*/ +int mmcModeSense6( /*out*/ void *p_buf, int i_size, int page) +{ + return mmc_mode_sense_6( p_cdio, /*out*/ p_buf, i_size, page); +} + +/*! Issue a MMC READ_CD command. + +@param p_cdio object to read from + +@param p_buf Place to store data. The caller should ensure that + p_buf can hold at least i_blocksize * i_blocks bytes. + +@param i_lsn sector to read + +@param expected_sector_type restricts reading to a specific CD + sector type. Only 3 bits with values 1-5 are used: + 0 all sector types + 1 CD-DA sectors only + 2 Mode 1 sectors only + 3 Mode 2 formless sectors only. Note in contrast to all other + values an MMC CD-ROM is not required to support this mode. + 4 Mode 2 Form 1 sectors only + 5 Mode 2 Form 2 sectors only + +@param b_digital_audio_play Control error concealment when the + data being read is CD-DA. If the data being read is not CD-DA, + this parameter is ignored. If the data being read is CD-DA and + DAP is false zero, then the user data returned should not be + modified by flaw obscuring mechanisms such as audio data mute and + interpolate. If the data being read is CD-DA and DAP is true, + then the user data returned should be modified by flaw obscuring + mechanisms such as audio data mute and interpolate. + + b_sync_header return the sync header (which will probably have + the same value as CDIO_SECTOR_SYNC_HEADER of size + CDIO_CD_SYNC_SIZE). + + @param header_codes Header Codes refer to the sector header and + the sub-header that is present in mode 2 formed sectors: + + 0 No header information is returned. + 1 The 4-byte sector header of data sectors is be returned, + 2 The 8-byte sector sub-header of mode 2 formed sectors is + returned. + 3 Both sector header and sub-header (12 bytes) is returned. + The Header preceeds the rest of the bytes (e.g. user-data bytes) + that might get returned. + + @param b_user_data Return user data if true. + + For CD-DA, the User Data is CDIO_CD_FRAMESIZE_RAW bytes. + + For Mode 1, The User Data is ISO_BLOCKSIZE bytes beginning at + offset CDIO_CD_HEADER_SIZE+CDIO_CD_SUBHEADER_SIZE. + + For Mode 2 formless, The User Data is M2RAW_SECTOR_SIZE bytes + beginning at offset CDIO_CD_HEADER_SIZE+CDIO_CD_SUBHEADER_SIZE. + + For data Mode 2, form 1, User Data is ISO_BLOCKSIZE bytes beginning at + offset CDIO_CD_XA_SYNC_HEADER. + + For data Mode 2, form 2, User Data is 2 324 bytes beginning at + offset CDIO_CD_XA_SYNC_HEADER. + + @param b_sync + + @param b_edc_ecc true if we return EDC/ECC error detection/correction bits. + + The presence and size of EDC redundancy or ECC parity is defined + according to sector type: + + CD-DA sectors have neither EDC redundancy nor ECC parity. + + Data Mode 1 sectors have 288 bytes of EDC redundancy, Pad, and + ECC parity beginning at offset 2064. + + Data Mode 2 formless sectors have neither EDC redundancy nor ECC + parity + + Data Mode 2 form 1 sectors have 280 bytes of EDC redundancy and + ECC parity beginning at offset 2072 + + Data Mode 2 form 2 sectors optionally have 4 bytes of EDC + redundancy beginning at offset 2348. + + + @param c2_error_information If true associate a bit with each + sector for C2 error The resulting bit field is ordered exactly as + the main channel bytes. Each 8-bit boundary defines a byte of + flag bits. + + @param subchannel_selection subchannel-selection bits + + 0 No Sub-channel data shall be returned. (0 bytes) + 1 RAW P-W Sub-channel data shall be returned. (96 byte) + 2 Formatted Q sub-channel data shall be transferred (16 bytes) + 3 Reserved + 4 Corrected and de-interleaved R-W sub-channel (96 bytes) + 5-7 Reserved + + @param i_blocksize size of the a block expected to be returned + + @param i_blocks number of blocks expected to be returned. + + A DriverOpException is raised on error. + */ +void +mmcReadCd ( void *p_buf, lsn_t i_lsn, int expected_sector_type, + bool b_digital_audio_play, bool b_sync, uint8_t header_codes, + bool b_user_data, bool b_edc_ecc, uint8_t c2_error_information, + uint8_t subchannel_selection, uint16_t i_blocksize, + uint32_t i_blocks ) +{ + driver_return_code_t drc = + mmc_read_cd ( p_cdio, p_buf, i_lsn, expected_sector_type, + b_digital_audio_play, b_sync, header_codes, + b_user_data, b_edc_ecc, c2_error_information, + subchannel_selection, i_blocksize, i_blocks ); + possible_throw_device_exception(drc); +} + +/*! Read just the user data part of some sort of data sector (via + mmc_read_cd). + + @param p_cdio object to read from + + @param p_buf place to read data into. The caller should make sure + this location can store at least CDIO_CD_FRAMESIZE, + M2RAW_SECTOR_SIZE, or M2F2_SECTOR_SIZE depending on + the kind of sector getting read. If you don't know + whether you have a Mode 1/2, Form 1/ Form 2/Formless + sector best to reserve space for the maximum, + M2RAW_SECTOR_SIZE. + + @param i_lsn sector to read + @param i_blocksize size of each block + @param i_blocks number of blocks to read + + */ +void mmcReadDataSectors ( void *p_buf, lsn_t i_lsn, uint16_t i_blocksize, + uint32_t i_blocks=1) +{ + driver_return_code_t drc = mmc_read_data_sectors ( p_cdio, p_buf, i_lsn, + i_blocksize, i_blocks ); + possible_throw_device_exception(drc); +} + + +/*! Read MMC read mode2 sectors + + A DriverOpException is raised on error. + */ +void mmcReadSectors ( void *p_buf, lsn_t i_lsn, int read_sector_type, + uint32_t i_blocks=1) +{ + driver_return_code_t drc = mmc_read_sectors ( p_cdio, p_buf, i_lsn, + read_sector_type, i_blocks); + possible_throw_device_exception(drc); +} + +/*! + Run an MMC command. + + @param p_cdio CD structure set by cdio_open(). + @param i_timeout_ms time in milliseconds we will wait for the command + to complete. + @param p_cdb CDB bytes. All values that are needed should be set + on input. We'll figure out what the right CDB length + should be. + @param e_direction direction the transfer is to go. + @param i_buf Size of buffer + @param p_buf Buffer for data, both sending and receiving. + + @return 0 if command completed successfully. + */ +int mmcRunCmd( unsigned int i_timeout_ms, const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, unsigned int i_buf, + /*in/out*/ void *p_buf ) +{ + return mmc_run_cmd( p_cdio, i_timeout_ms, p_cdb, e_direction, i_buf, p_buf ); +} + +/*! + Set the block size for subsequent read requests, via MMC. + + @param i_blocksize size to set for subsequent requests + + A DriverOpException is raised on error. +*/ +void mmcSetBlocksize ( uint16_t i_blocksize) +{ + driver_return_code_t drc = mmc_set_blocksize ( p_cdio, i_blocksize); + possible_throw_device_exception(drc); +} + + +/*! + Set the drive speed via MMC. + + @param i_speed speed to set drive to. + + A DriverOpException is raised on error. +*/ +void mmcSetSpeed( int i_speed ) +{ + driver_return_code_t drc = mmc_set_speed( p_cdio, i_speed ); + possible_throw_device_exception(drc); +} + +/*! + Load or Unload media using a MMC START STOP command. + + @param p_cdio the CD object to be acted upon. + @param b_eject eject if true and close tray if false + @param b_immediate wait or don't wait for operation to complete + @param power_condition Set CD-ROM to idle/standby/sleep. If nonzero + eject/load is ignored, so set to 0 if you want to eject or load. + + @see mmc_eject_media or mmc_close_tray + + A DriverOpException is raised on error. +*/ +void mmcStartStopMedia(bool b_eject, bool b_immediate, + uint8_t power_condition) +{ + driver_return_code_t drc = + mmc_start_stop_media(p_cdio, b_eject, b_immediate, power_condition); + possible_throw_device_exception(drc); +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/cdio++/read.hpp b/include/cdio++/read.hpp new file mode 100644 index 00000000..7cce925b --- /dev/null +++ b/include/cdio++/read.hpp @@ -0,0 +1,121 @@ +/* + $Id: read.hpp,v 1.3 2008/03/25 15:59:10 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file read.hpp + * + * \brief methods relating to reading blocks of Compact Discs. This file + * should not be #included directly. + */ + +/*! + Reposition read offset + Similar to (if not the same as) libc's lseek() + + @param offset amount to seek + @param whence like corresponding parameter in libc's lseek, e.g. + SEEK_SET or SEEK_END. + @return (off_t) -1 on error. +*/ + +off_t lseek(off_t offset, int whence) +{ + return cdio_lseek(p_cdio, offset, whence); +} + +/*! + Reads into buf the next size bytes. + Similar to (if not the same as) libc's read() + + @param p_buf place to read data into. The caller should make sure + this location can store at least i_size bytes. + @param i_size number of bytes to read + + @return (ssize_t) -1 on error. +*/ +ssize_t read(void *p_buf, size_t i_size) +{ + return cdio_read(p_cdio, p_buf, i_size); +} + +/*! + Reads a number of sectors (AKA blocks). + + @param p_buf place to read data into. The caller should make sure + this location is large enough. See below for size information. + @param read_mode the kind of "mode" to use in reading. + @param i_lsn sector to read + @param i_blocks number of sectors to read + + If read_mode is CDIO_MODE_AUDIO, + *p_buf should hold at least CDIO_FRAMESIZE_RAW * i_blocks bytes. + + If read_mode is CDIO_MODE_DATA, + *p_buf should hold at least i_blocks times either ISO_BLOCKSIZE, + M1RAW_SECTOR_SIZE or M2F2_SECTOR_SIZE depending on the kind of + sector getting read. If you don't know whether you have a Mode 1/2, + Form 1/ Form 2/Formless sector best to reserve space for the maximum + which is M2RAW_SECTOR_SIZE. + + If read_mode is CDIO_MODE_M2F1, + *p_buf should hold at least M2RAW_SECTOR_SIZE * i_blocks bytes. + + If read_mode is CDIO_MODE_M2F2, + *p_buf should hold at least CDIO_CD_FRAMESIZE * i_blocks bytes. + + A DriverOpException is raised on error. +*/ + +void readSectors(void *p_buf, lsn_t i_lsn, cdio_read_mode_t read_mode, + uint32_t i_blocks=1) +{ + driver_return_code_t drc = cdio_read_sectors(p_cdio, p_buf, i_lsn, read_mode, + i_blocks); + possible_throw_device_exception(drc); +} + +/*! + Reads a number of data sectors (AKA blocks). + + @param p_buf place to read data into. The caller should make sure + this location is large enough. See below for size information. + + *p_buf should hold at least i_blocks times either ISO_BLOCKSIZE, + M1RAW_SECTOR_SIZE or M2F2_SECTOR_SIZE depending on the kind of + sector getting read. If you don't know whether you have a Mode 1/2, + Form 1/ Form 2/Formless sector best to reserve space for the maximum + which is M2RAW_SECTOR_SIZE. + + @param i_lsn sector to read + + @param i_blocksize size of block. Should be either CDIO_CD_FRAMESIZE, + M2RAW_SECTOR_SIZE, or M2F2_SECTOR_SIZE. See comment above under p_buf. + + @param i_blocks number of sectors to read + + A DriverOpException is raised on error. + +*/ + +void readDataBlocks(void *p_buf, lsn_t i_lsn, uint16_t i_blocksize, + uint32_t i_blocks=1) +{ + driver_return_code_t drc = cdio_read_data_sectors (p_cdio, p_buf, i_lsn, + i_blocksize, i_blocks); + possible_throw_device_exception(drc); +} diff --git a/include/cdio++/track.hpp b/include/cdio++/track.hpp new file mode 100644 index 00000000..3e7942fd --- /dev/null +++ b/include/cdio++/track.hpp @@ -0,0 +1,147 @@ +/* + $Id: track.hpp,v 1.2 2008/03/25 15:59:10 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file track.hpp + * \brief methods relating to getting Compact Discs. This file + * should not be #included directly. + */ + +/*! + Return an opaque CdIo_t pointer for the given track object. +*/ +CdIo_t *getCdIo() +{ + return p_cdio; +} + +/*! + Get CD-Text information for a CdIo_t object. + + @return the CD-Text object or NULL if obj is NULL + or CD-Text information does not exist. +*/ +cdtext_t *getCdtext () +{ + return cdio_get_cdtext (p_cdio, i_track); +} + +/*! Return number of channels in track: 2 or 4; -2 if not + implemented or -1 for error. + Not meaningful if track is not an audio track. +*/ +int getChannels() +{ + return cdio_get_track_channels(p_cdio, i_track); +} + +/*! Return copy protection status on a track. Is this meaningful + if not an audio track? +*/ +track_flag_t getCopyPermit() +{ + return cdio_get_track_copy_permit(p_cdio, i_track); +} + +/*! + Get the format (audio, mode2, mode1) of track. +*/ +track_format_t getFormat() +{ + return cdio_get_track_format(p_cdio, i_track); +} + +/*! + 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 getGreen() +{ + return cdio_get_track_green(p_cdio, i_track); +} + +/*! + Return the ending LSN. CDIO_INVALID_LSN is returned on error. +*/ +lsn_t getLastLsn() +{ + return cdio_get_track_last_lsn(p_cdio, i_track); +} + +/*! + Get the starting LBA. + + @return the starting LBA or CDIO_INVALID_LBA on error. +*/ +lba_t getLba() +{ + return cdio_get_track_lba(p_cdio, i_track); +} + +/*! + @return the starting LSN or CDIO_INVALID_LSN on error. +*/ +lsn_t getLsn() +{ + return cdio_get_track_lsn(p_cdio, i_track); +} + + +/*! + Return the starting MSF (minutes/secs/frames) for track number + i_track in p_cdio. + + @return true if things worked or false if there is no track entry. +*/ +bool getMsf(/*out*/ msf_t &msf) +{ + return cdio_get_track_msf(p_cdio, i_track,/*out*/ &msf); +} + +/*! + Return the track number of the track object. +*/ +track_t getTrackNum() +{ + return i_track; +} + +/*! Get linear preemphasis status on an audio track + This is not meaningful if not an audio track? +*/ +track_flag_t getPreemphasis() +{ + return cdio_get_track_preemphasis(p_cdio, i_track); +} + +/*! + Get the number of sectors between this track an the next. This + includes any pregap sectors before the start of the next track. + + @return the number of sectors or 0 if there is an error. +*/ +unsigned int getSecCount() +{ + return cdio_get_track_sec_count(p_cdio, i_track); +} + + diff --git a/include/cdio/.cvsignore b/include/cdio/.cvsignore new file mode 100644 index 00000000..710da8eb --- /dev/null +++ b/include/cdio/.cvsignore @@ -0,0 +1,5 @@ +Makefile +Makefile.in +version.h +cdio_config.h + diff --git a/include/cdio/.gitignore b/include/cdio/.gitignore new file mode 100644 index 00000000..a38874ed --- /dev/null +++ b/include/cdio/.gitignore @@ -0,0 +1,4 @@ +/Makefile +/Makefile.in +/cdio_config.h +/version.h diff --git a/include/cdio/Makefile.am b/include/cdio/Makefile.am new file mode 100644 index 00000000..ea695618 --- /dev/null +++ b/include/cdio/Makefile.am @@ -0,0 +1,62 @@ +# $Id: Makefile.am,v 1.34 2008/03/20 19:02:37 karl Exp $ +# +# Copyright (C) 2003, 2004, 2006, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +######################################################## +# Things to make the install (public) libcdio headers +######################################################## +# + +if BUILD_CD_PARANOIA +paranoiaheaders = cdda.h cdtext.h +endif + +libcdioincludedir=$(includedir)/cdio +libcdioinclude_HEADERS = \ + audio.h \ + bytesex.h \ + bytesex_asm.h \ + cdio.h \ + cdio_config.h \ + cd_types.h \ + device.h \ + disc.h \ + ds.h \ + dvd.h \ + ecma_167.h \ + iso9660.h \ + logging.h \ + mmc.h \ + paranoia.h \ + posix.h \ + read.h \ + rock.h \ + sector.h \ + track.h \ + types.h \ + udf.h \ + udf_file.h \ + udf_time.h \ + utf8.h \ + util.h \ + version.h \ + xa.h \ + $(paranoiaheaders) + +EXTRA_DIST = version.h.in +BUILT_SOURCES = version.h + +DISTCLEANFILES = cdio_config.h diff --git a/include/cdio/audio.h b/include/cdio/audio.h new file mode 100644 index 00000000..880cd541 --- /dev/null +++ b/include/cdio/audio.h @@ -0,0 +1,148 @@ +/* -*- c -*- + $Id: audio.h,v 1.12 2008/03/25 15:59:08 karl Exp $ + + Copyright (C) 2005, 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file audio.h + * + * \brief The top-level header for CD audio-related libcdio + * calls. These control playing of the CD-ROM through its + * line-out jack. + */ +#ifndef __CDIO_AUDIO_H__ +#define __CDIO_AUDIO_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*! This struct is used by the cdio_audio_read_subchannel */ + typedef struct cdio_subchannel_s + { + uint8_t format; + uint8_t audio_status; + uint8_t address: 4; + uint8_t control: 4; + uint8_t track; + uint8_t index; + msf_t abs_addr; + msf_t rel_addr; + } cdio_subchannel_t; + + /*! This struct is used by cdio_audio_get_volume and cdio_audio_set_volume */ + typedef struct cdio_audio_volume_s + { + uint8_t level[4]; + } cdio_audio_volume_t; + + + /*! This struct is used by the CDROMPLAYTRKIND ioctl */ + typedef struct cdio_track_index_s + { + uint8_t i_start_track; /**< start track */ + uint8_t i_start_index; /**< start index */ + uint8_t i_end_track; /**< end track */ + uint8_t i_end_index; /**< end index */ + } cdio_track_index_t; + + /*! + Get volume of an audio CD. + + @param p_cdio the CD object to be acted upon. + @param p_volume place to put the list of volume outputs levels + + p_volume can be NULL in which case we return only whether the driver + has the ability to get the volume or not. + + */ + driver_return_code_t cdio_audio_get_volume (CdIo_t *p_cdio, /*out*/ + cdio_audio_volume_t *p_volume); + + /*! + Return the number of seconds (discarding frame portion) of an MSF + */ + uint32_t cdio_audio_get_msf_seconds(msf_t *p_msf); + + /*! + Pause playing CD through analog output + + @param p_cdio the CD object to be acted upon. + */ + driver_return_code_t cdio_audio_pause (CdIo_t *p_cdio); + + /*! + Playing CD through analog output at the given MSF. + + @param p_cdio the CD object to be acted upon. + @param p_start_msf pointer to staring MSF + @param p_end_msf pointer to ending MSF + */ + driver_return_code_t cdio_audio_play_msf (CdIo_t *p_cdio, + /*in*/msf_t *p_start_msf, + /*in*/ msf_t *p_end_msf); + + /*! + Playing CD through analog output at the desired track and index + + @param p_cdio the CD object to be acted upon. + @param p_track_index location to start/end. + */ + driver_return_code_t cdio_audio_play_track_index + ( CdIo_t *p_cdio, cdio_track_index_t *p_track_index); + + /*! + Get subchannel information. + + @param p_cdio the CD object to be acted upon. + @param p_subchannel place for returned subchannel information + */ + driver_return_code_t cdio_audio_read_subchannel (CdIo_t *p_cdio, + /*out*/ cdio_subchannel_t *p_subchannel); + + /*! + Resume playing an audio CD. + + @param p_cdio the CD object to be acted upon. + + */ + driver_return_code_t cdio_audio_resume (CdIo_t *p_cdio); + + /*! + Set volume of an audio CD. + + @param p_cdio the CD object to be acted upon. + @param p_volume place for returned volume-level information + + */ + driver_return_code_t cdio_audio_set_volume (CdIo_t *p_cdio, /*out*/ + cdio_audio_volume_t *p_volume); + + /*! + Stop playing an audio CD. + + @param p_cdio the CD object to be acted upon. + + */ + driver_return_code_t cdio_audio_stop (CdIo_t *p_cdio); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_AUDIO_H__ */ diff --git a/include/cdio/bytesex.h b/include/cdio/bytesex.h new file mode 100644 index 00000000..e1be483e --- /dev/null +++ b/include/cdio/bytesex.h @@ -0,0 +1,220 @@ +/* + $Id: bytesex.h,v 1.5 2008/03/25 15:59:08 karl Exp $ + + Copyright (C) 2000, 2004 Herbert Valerio Riedel + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file bytesex.h + * \brief Generic Byte-swapping routines. + * + * Note: this header will is slated to get removed and libcdio will + * use glib.h routines instead. +*/ + +#ifndef __CDIO_BYTESEX_H__ +#define __CDIO_BYTESEX_H__ + +#include +#include +#include + +/** 16-bit big-endian to little-endian */ +#define UINT16_SWAP_LE_BE_C(val) ((uint16_t) ( \ + (((uint16_t) (val) & (uint16_t) 0x00ffU) << 8) | \ + (((uint16_t) (val) & (uint16_t) 0xff00U) >> 8))) + +/** 32-bit big-endian to little-endian */ +#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))) + +/** 64-bit big-endian to little-endian */ +#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 field conversion routines */ + +/** Convert from uint8_t to ISO 9660 7.1.1 format */ +#define to_711(i) uint8_to_le(i) + +/** Convert from ISO 9660 7.1.1 format to uint8_t */ +#define from_711(i) uint8_from_le(i) + +/** Convert from uint16_t to ISO 9669 7.2.1 format */ +#define to_721(i) uint16_to_le(i) + +/** Convert from ISO 9660 7.2.1 format to uint16_t */ +#define from_721(i) uint16_from_le(i) + +/** Convert from uint16_t to ISO 9669 7.2.2 format */ +#define to_722(i) uint16_to_be(i) + +/** Convert from ISO 9660 7.2.2 format to uint16_t */ +#define from_722(i) uint16_from_be(i) + +/** Convert from uint16_t to ISO 9669 7.2.3 format */ +static inline uint32_t +to_723(uint16_t i) +{ + return uint32_swap_le_be(i) | i; +} + +/** Convert from ISO 9660 7.2.3 format to uint16_t */ +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); +} + +/** Convert from uint16_t to ISO 9669 7.3.1 format */ +#define to_731(i) uint32_to_le(i) + +/** Convert from ISO 9660 7.3.1 format to uint32_t */ +#define from_731(i) uint32_from_le(i) + +/** Convert from uint32_t to ISO 9669 7.3.2 format */ +#define to_732(i) uint32_to_be(i) + +/** Convert from ISO 9660 7.3.2 format to uint32_t */ +#define from_732(i) uint32_from_be(i) + +/** Convert from uint16_t to ISO 9669 7.3.3 format */ +static inline uint64_t +to_733(uint32_t i) +{ + return uint64_swap_le_be(i) | i; +} + +/** Convert from ISO 9660 7.3.3 format to uint32_t */ +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/include/cdio/bytesex_asm.h b/include/cdio/bytesex_asm.h new file mode 100644 index 00000000..7f1f131a --- /dev/null +++ b/include/cdio/bytesex_asm.h @@ -0,0 +1,130 @@ +/* + $Id: bytesex_asm.h,v 1.3 2008/03/25 15:59:08 karl Exp $ + + Copyright (C) 2008 Rocky Bernstein + 2001, 2004, 2005 Herbert Valerio Riedel + 2001 Sven Ottemann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file bytesex_asm.h + * \brief Assembly code to handle byte-swapping. + + Note: this header will is slated to get removed and libcdio will use + glib.h routines instead. +*/ + +#ifndef __CDIO_BYTESEX_ASM_H__ +#define __CDIO_BYTESEX_ASM_H__ +#if !defined(DISABLE_ASM_OPTIMIZE) + +#include + +#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/include/cdio/cd_types.h b/include/cdio/cd_types.h new file mode 100644 index 00000000..bc1f16c0 --- /dev/null +++ b/include/cdio/cd_types.h @@ -0,0 +1,175 @@ +/* + $Id: cd_types.h,v 1.18 2008/03/25 15:59:08 karl Exp $ + + Copyright (C) 2003, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file cd_types.h + * \brief Header for routines which automatically determine the Compact Disc + * format and possibly filesystem on the CD. + * + */ + +#ifndef __CDIO_CD_TYPES_H__ +#define __CDIO_CD_TYPES_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Filesystem types we understand. The highest-numbered fs type should + * be less than CDIO_FS_MASK defined below. + */ + typedef enum { + CDIO_FS_AUDIO = 1, /**< audio only - not really a + filesystem */ + CDIO_FS_HIGH_SIERRA = 2, /**< High-Sierra Filesystem */ + CDIO_FS_ISO_9660 = 3, /**< ISO 9660 filesystem */ + CDIO_FS_INTERACTIVE = 4, + CDIO_FS_HFS = 5, /**< file system used on the Macintosh + system in MacOS 6 through MacOS 9 + and deprecated in OSX. */ + CDIO_FS_UFS = 6, /**< Generic Unix file system derived + from the Berkeley fast file + system. */ + + /**< + * EXT2 was the GNU/Linux native filesystem for early kernels. Newer + * GNU/Linux OS's may use EXT3 which is EXT2 with a journal. + */ + CDIO_FS_EXT2 = 7, + + CDIO_FS_ISO_HFS = 8, /**< both HFS & ISO-9660 filesystem */ + CDIO_FS_ISO_9660_INTERACTIVE = 9, /**< both CD-RTOS and ISO filesystem */ + + + /**< + * The 3DO is, technically, a set of specifications created by the 3DO + * company. These specs are for making a 3DO Interactive Multiplayer + * which uses a CD-player. Panasonic in the early 90's was the first + * company to manufacture and market a 3DO player. + */ + CDIO_FS_3DO = 10, + + + /**< + Microsoft X-BOX CD. + */ + CDIO_FS_XISO = 11, + CDIO_FS_UDFX = 12, + CDIO_FS_UDF = 13, + CDIO_FS_ISO_UDF = 14 + } cdio_fs_t; + + +/** + * Macro to extract just the FS type portion defined above +*/ +#define CDIO_FSTYPE(fs) (fs & CDIO_FS_MASK) + +/** + * Bit masks for the classes of CD-images. These are generally + * higher-level than the fs-type information above and may be determined + * based of the fs type information. This + */ + typedef enum { + CDIO_FS_MASK = 0x000f, /**< Note: this should be 2**n-1 and + and greater than the highest + CDIO_FS number above */ + CDIO_FS_ANAL_XA = 0x00010, /**< eXtended Architecture format */ + CDIO_FS_ANAL_MULTISESSION = 0x00020, /**< CD has multisesion */ + CDIO_FS_ANAL_PHOTO_CD = 0x00040, /**< Is a Kodak Photo CD */ + CDIO_FS_ANAL_HIDDEN_TRACK = 0x00080, /**< Hidden track at the + beginning of the CD */ + CDIO_FS_ANAL_CDTV = 0x00100, + CDIO_FS_ANAL_BOOTABLE = 0x00200, /**< CD is bootable */ + CDIO_FS_ANAL_VIDEOCD = 0x00400, /**< VCD 1.1 */ + CDIO_FS_ANAL_ROCKRIDGE = 0x00800, /**< Has Rock Ridge Extensions to + ISO 9660, */ + CDIO_FS_ANAL_JOLIET = 0x01000, /**< Microsoft Joliet extensions + to ISO 9660, */ + CDIO_FS_ANAL_SVCD = 0x02000, /**< Super VCD or Choiji Video CD */ + CDIO_FS_ANAL_CVD = 0x04000, /**< Choiji Video CD */ + CDIO_FS_ANAL_XISO = 0x08000, /**< XBOX CD */ + CDIO_FS_ANAL_ISO9660_ANY = 0x10000, /**< Any sort fo ISO9660 FS */ + CDIO_FS_ANAL_VCD_ANY = (CDIO_FS_ANAL_VIDEOCD|CDIO_FS_ANAL_SVCD| + CDIO_FS_ANAL_CVD), + CDIO_FS_MATCH_ALL = ~CDIO_FS_MASK /**< bitmask which can + be used by + cdio_get_devices to + specify matching any + sort of CD. */ + } cdio_fs_cap_t; + + +#define CDIO_FS_UNKNOWN CDIO_FS_MASK + +/** + * + */ +#define CDIO_FS_MATCH_ALL (cdio_fs_anal_t) (~CDIO_FS_MASK) + + +/*! + \brief The type used to return analysis information from + cdio_guess_cd_type. + + These fields make sense only for when an ISO-9660 filesystem is used. + */ +typedef struct +{ + unsigned int joliet_level; /**< If has Joliet extensions, this is the + associated level number (i.e. 1, 2, or 3). */ + char iso_label[33]; /**< This is 32 + 1 for null byte at the end in + formatting the string */ + unsigned int isofs_size; + uint8_t UDFVerMinor; /**< For UDF filesystems only */ + uint8_t UDFVerMajor; /**< For UDF filesystems only */ +} cdio_iso_analysis_t; + +/** + * Try to determine what kind of CD-image and/or filesystem we + * have at track track_num. Return information about the CD image + * is returned in iso_analysis and the return value. + */ +cdio_fs_anal_t cdio_guess_cd_type(const CdIo_t *cdio, int start_session, + track_t track_num, + /*out*/ cdio_iso_analysis_t *iso_analysis); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/** The below variables are trickery to force the above enum symbol + values to be recorded in debug symbol tables. They are used to + allow one to refer to the enumeration value names in the typedefs + above in a debugger and debugger expressions. +*/ +extern cdio_fs_cap_t debug_cdio_fs_cap; +extern cdio_fs_t debug_cdio_fs; + +#endif /* __CDIO_CD_TYPES_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/cdio/cdda.h b/include/cdio/cdda.h new file mode 100644 index 00000000..c30e129d --- /dev/null +++ b/include/cdio/cdda.h @@ -0,0 +1,411 @@ +/* + $Id: cdda.h,v 1.30 2008/03/25 15:59:08 karl Exp $ + + Copyright (C) 2004, 2005, 2006, 2008 Rocky Bernstein + Copyright (C) 2001 Xiph.org and Heiko Eissfeldt heiko@escape.colossus.de + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file cdda.h + * + * \brief The top-level interface header for libcdio_cdda. + * Applications include this for paranoia access. + * + ******************************************************************/ + +#ifndef _CDDA_INTERFACE_H_ +#define _CDDA_INTERFACE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /** cdrom_paranoia is an opaque structure which is used in all of the + library operations. + */ + typedef struct cdrom_paranoia_s cdrom_paranoia_t; + typedef struct cdrom_drive_s cdrom_drive_t; + + /** For compatibility. cdrom_drive_t is deprecated, use cdrom_drive_t + instead. */ + + /** + Flags for simulating jitter used in testing. + + The enumeration type one probably wouldn't really use in a program. + It is here instead of defines to give symbolic names that can be + helpful in debuggers where wants just to say refer to + CDDA_TEST_JITTER_SMALL and get the correct value. + */ + typedef enum { + CDDA_MESSAGE_FORGETIT = 0, + CDDA_MESSAGE_PRINTIT = 1, + CDDA_MESSAGE_LOGIT = 2, + CD_FRAMESAMPLES = CDIO_CD_FRAMESIZE_RAW / 4, + MAXTRK = (CDIO_CD_MAX_TRACKS+1) + } paranoia_cdda_enums_t; + + +#include + +/** We keep MAXTRK since this header is exposed publicly and other + programs may have used this. +*/ +#define MAXTRK (CDIO_CD_MAX_TRACKS+1) + +/** \brief Structure for cdparanoia's CD Table of Contents */ +typedef struct TOC_s { + unsigned char bTrack; + int32_t dwStartSector; +} TOC_t; + +/** For compatibility. TOC is deprecated, use TOC_t instead. */ +#define TOC TOC_t + +/** \brief Structure for cdparanoia's CD-ROM access */ +struct cdrom_drive_s { + + CdIo_t *p_cdio; + int opened; /**< This struct may just represent a candidate for opening */ + + char *cdda_device_name; + + char *drive_model; + int drive_type; + int bigendianp; /**< Whether data returned on the CDDA is bigendian or + not. 1 if big endian, 0 if little endian and -1 if + we don't know. + */ + int nsectors; /**< Number of sectors use in reading. Multiply by + CDIO_CD_FRAMESIZE_RAW to get number of bytes used in + the read buffer. */ + + int cd_extra; /**< -1 if we can't get multisession info, 0 if + there is one session only or the multi-session + LBA is less than or 100 (don't ask me why -- I + don't know), and 1 if the multi-session lba is + greater than 100. */ + + bool b_swap_bytes; /**< Swap bytes if Endian-ness of drive + mismatches the endian-ness of the + computer? */ + track_t tracks; + TOC_t disc_toc[MAXTRK]; /**< info here starts origin 0 rather than the + first track number (usually 1). So to take + a track number and use it here, subtract + off cdio_get_first_track_num() beforehand. + */ + lsn_t audio_first_sector; + lsn_t audio_last_sector; + + int errordest; + int messagedest; + char *errorbuf; + char *messagebuf; + + /* functions specific to particular drives/interfaces */ + + int (*enable_cdda) (cdrom_drive_t *d, int onoff); + int (*read_toc) (cdrom_drive_t *d); + long (*read_audio) (cdrom_drive_t *d, void *p, lsn_t begin, + long sectors); + int (*set_speed) (cdrom_drive_t *d, int speed); + int error_retry; + int report_all; + + int is_atapi; + int is_mmc; + + int i_test_flags; /**< Normally set 0. But if we are testing + paranoia operation this can be set to one of + the flag masks to simulate a particular kind of + failure. */ + +}; + + + /** + Flags for simulating jitter used in testing. + + The enumeration type one probably wouldn't really use in a program. + It is here instead of defines to give symbolic names that can be + helpful in debuggers where wants just to say refer to + CDDA_TEST_JITTER_SMALL and get the correct value. + */ + typedef enum { + CDDA_TEST_JITTER_SMALL = 1, + CDDA_TEST_JITTER_LARGE = 2, + CDDA_TEST_JITTER_MASSIVE = 3, + CDDA_TEST_FRAG_SMALL = (1<<3), + CDDA_TEST_FRAG_LARGE = (2<<3), + CDDA_TEST_FRAG_MASSIVE = (3<<3), + CDDA_TEST_UNDERRUN = 64 + } paranoia_jitter_t; + +/** jitter testing. The first two bits are set to determine the + byte-distance we will jitter the data; 0 is no shifting. + */ + +/**< jitter testing. Set the below bit to always cause jittering on reads. + The below bit only has any effect if the first two (above) bits are + nonzero. If the above bits are set, but the below bit isn't we'll + jitter 90% of the time. + */ +#define CDDA_TEST_ALWAYS_JITTER 4 + +/** fragment testing */ +#define CDDA_TEST_FRAG_SMALL (1<<3) +#define CDDA_TEST_FRAG_LARGE (2<<3) +#define CDDA_TEST_FRAG_MASSIVE (3<<3) + +/**< under-run testing. The below bit is set for testing. */ +#define CDDA_TEST_UNDERRUN 64 + +#if TESTING_IS_FINISHED + + /** scratch testing */ +#define CDDA_TEST_SCRATCH 128 +#undef CDDA_TEST_BOGUS_BYTES 256 +#undef CDDA_TEST_DROPDUPE_BYTES 512 +#endif /* TESTING_IS_FINISHED */ + +/** autosense functions */ + +/** Get a CD-ROM drive with a CD-DA in it. + If mesagedest is 1, then any messages in the process will be stored + in message. +*/ +extern cdrom_drive_t *cdio_cddap_find_a_cdrom(int messagedest, + char **ppsz_message); + +/** Returns a paranoia CD-ROM drive object with a CD-DA in it or NULL + if there was an error. + @see cdio_cddap_identify_cdio + */ +extern cdrom_drive_t *cdio_cddap_identify(const char *psz_device, + int messagedest, + char **ppsz_message); + +/** Returns a paranoia CD-ROM drive object with a CD-DA in it or NULL + if there was an error. In contrast to cdio_cddap_identify, we + start out with an initialized p_cdio object. For example you may + have used that for other purposes such as to get CDDB/CD-Text + information. @see cdio_cddap_identify + */ +cdrom_drive_t *cdio_cddap_identify_cdio(CdIo_t *p_cdio, + int messagedest, char **ppsz_messages); + +/** drive-oriented functions */ + +extern int cdio_cddap_speed_set(cdrom_drive_t *d, int speed); +extern void cdio_cddap_verbose_set(cdrom_drive_t *d, int err_action, + int mes_action); +extern char *cdio_cddap_messages(cdrom_drive_t *d); +extern char *cdio_cddap_errors(cdrom_drive_t *d); + +/*! + Closes d and releases all storage associated with it except + the internal p_cdio pointer. + + @param d cdrom_drive_t object to be closed. + @return 0 if passed a null pointer and 1 if not in which case + some work was probably done. + + @see cdio_cddap_close +*/ +bool cdio_cddap_close_no_free_cdio(cdrom_drive_t *d); + +/*! + Closes d and releases all storage associated with it. + Doubles as "cdrom_drive_free()". + + @param d cdrom_drive_t object to be closed. + @return 0 if passed a null pointer and 1 if not in which case + some work was probably done. + + @see cdio_cddap_close_no_free_cdio +*/ +extern int cdio_cddap_close(cdrom_drive_t *d); + +extern int cdio_cddap_open(cdrom_drive_t *d); + +extern long cdio_cddap_read(cdrom_drive_t *d, void *p_buffer, + lsn_t beginsector, long sectors); + +/*! Return the lsn for the start of track i_track */ +extern lsn_t cdio_cddap_track_firstsector(cdrom_drive_t *d, + track_t i_track); + +/*! Get last lsn of the track. This generally one less than the start + of the next track. -1 is returned on error. */ +extern lsn_t cdio_cddap_track_lastsector(cdrom_drive_t *d, track_t i_track); + +/*! Return the number of tracks on the CD. */ +extern track_t cdio_cddap_tracks(cdrom_drive_t *d); + +/*! Return the track containing the given LSN. If the LSN is before + the first track (in the pregap), 0 is returned. If there was an + error or the LSN after the LEADOUT (beyond the end of the CD), then + CDIO_INVALID_TRACK is returned. + */ +extern int cdio_cddap_sector_gettrack(cdrom_drive_t *d, lsn_t lsn); + +/*! Return the number of channels in track: 2 or 4; -2 if not + implemented or -1 for error. + Not meaningful if track is not an audio track. +*/ +extern int cdio_cddap_track_channels(cdrom_drive_t *d, track_t i_track); + +/*! Return 1 is track is an audio track, 0 otherwise. */ +extern int cdio_cddap_track_audiop(cdrom_drive_t *d, track_t i_track); + +/*! Return 1 is track has copy permit set, 0 otherwise. */ +extern int cdio_cddap_track_copyp(cdrom_drive_t *d, track_t i_track); + +/*! Return 1 is audio track has linear preemphasis set, 0 otherwise. + Only makes sense for audio tracks. + */ +extern int cdio_cddap_track_preemp(cdrom_drive_t *d, track_t i_track); + +/*! Get first lsn of the first audio track. -1 is returned on error. */ +extern lsn_t cdio_cddap_disc_firstsector(cdrom_drive_t *d); + +/*! Get last lsn of the last audio track. The last lsn is generally one + less than the start of the next track after the audio track. -1 is + returned on error. */ +extern lsn_t cdio_cddap_disc_lastsector(cdrom_drive_t *d); + +/*! Determine Endian-ness of the CD-drive based on reading data from + it. Some drives return audio data Big Endian while some (most) + return data Little Endian. Drives known to return data bigendian are + SCSI drives from Kodak, Ricoh, HP, Philips, Plasmon, Grundig + CDR100IPW, and Mitsumi CD-R. ATAPI and MMC drives are little endian. + + rocky: As someone who didn't write the code, I have to say this is + nothing less than brilliant. An FFT is done both ways and the the + transform is looked at to see which has data in the FFT (or audible) + portion. (Or so that's how I understand it.) + + @return 1 if big-endian, 0 if little-endian, -1 if we couldn't + figure things out or some error. + */ +extern int data_bigendianp(cdrom_drive_t *d); + +/** transport errors: */ + +typedef enum { + TR_OK = 0, + TR_EWRITE = 1 /**< Error writing packet command (transport) */, + TR_EREAD = 2 /**< Error reading packet data (transport) */, + TR_UNDERRUN = 3 /**< Read underrun */, + TR_OVERRUN = 4 /**< Read overrun */, + TR_ILLEGAL = 5 /**< Illegal/rejected request */, + TR_MEDIUM = 6 /**< Medium error */, + TR_BUSY = 7 /**< Device busy */, + TR_NOTREADY = 8 /**< Device not ready */, + TR_FAULT = 9 /**< Device failure */, + TR_UNKNOWN = 10 /**< Unspecified error */, + TR_STREAMING = 11 /**< loss of streaming */, +} transport_error_t; + + +#ifdef NEED_STRERROR_TR +const char *strerror_tr[]={ + "Success", + "Error writing packet command to device", + "Error reading command from device", + "SCSI packet data underrun (too little data)", + "SCSI packet data overrun (too much data)", + "Illegal SCSI request (rejected by target)", + "Medium reading data from medium", + "Device busy", + "Device not ready", + "Target hardware fault", + "Unspecified error", + "Drive lost streaming" +}; +#endif /*NEED_STERROR_TR*/ + +/** Errors returned by lib: + +\verbatim +001: Unable to set CDROM to read audio mode +002: Unable to read table of contents lead-out +003: CDROM reporting illegal number of tracks +004: Unable to read table of contents header +005: Unable to read table of contents entry +006: Could not read any data from drive +007: Unknown, unrecoverable error reading data +008: Unable to identify CDROM model +009: CDROM reporting illegal table of contents +010: Unaddressable sector + +100: Interface not supported +101: Drive is neither a CDROM nor a WORM device +102: Permision denied on cdrom (ioctl) device +103: Permision denied on cdrom (data) device + +300: Kernel memory error + +400: Device not open +401: Invalid track number +402: Track not audio data +403: No audio tracks on disc +\endverbatim + +*/ + +#ifndef DO_NOT_WANT_PARANOIA_COMPATIBILITY +/** For compatibility with good ol' paranoia */ +#define cdda_find_a_cdrom cdio_cddap_find_a_cdrom +#define cdda_identify cdio_cddap_identify +#define cdda_speed_set cdio_cddap_speed_set +#define cdda_verbose_set cdio_cddap_verbose_set +#define cdda_messages cdio_cddap_messages +#define cdda_errors cdio_cddap_errors +#define cdda_close cdio_cddap_close +#define cdda_open cdio_cddap_open +#define cdda_read cdio_cddap_read +#define cdda_track_firstsector cdio_cddap_track_firstsector +#define cdda_track_lastsector cdio_cddap_track_lastsector +#define cdda_tracks cdio_cddap_tracks +#define cdda_sector_gettrack cdio_cddap_sector_gettrack +#define cdda_track_channels cdio_cddap_track_channels +#define cdda_track_audiop cdio_cddap_track_audiop +#define cdda_track_copyp cdio_cddap_track_copyp +#define cdda_track_preemp cdio_cddap_track_preemp +#define cdda_disc_firstsector cdio_cddap_disc_firstsector +#define cdda_disc_lastsector cdio_cddap_disc_lastsector +#define cdrom_drive cdrom_drive_t + +#endif /*DO_NOT_WANT_PARANOIA_COMPATIBILITY*/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/** The below variables are trickery to force the above enum symbol + values to be recorded in debug symbol tables. They are used to + allow one to refer to the enumeration value names in the typedefs + above in a debugger and debugger expressions +*/ + +extern paranoia_jitter_t debug_paranoia_jitter; +extern paranoia_cdda_enums_t debug_paranoia_cdda_enums; + +#endif /*_CDDA_INTERFACE_H_*/ + diff --git a/include/cdio/cdio.h b/include/cdio/cdio.h new file mode 100644 index 00000000..84e54b10 --- /dev/null +++ b/include/cdio/cdio.h @@ -0,0 +1,84 @@ +/* -*- c -*- + $Id: cdio.h,v 1.82 2008/03/25 15:59:08 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file cdio.h + * + * \brief The top-level header for libcdio: the CD Input and Control + * library. Applications include this for anything regarding libcdio. + */ + + +#ifndef __CDIO_H__ +#define __CDIO_H__ + +/** Application Interface or Protocol version number. If the public + * interface changes, we increase this number. + */ +#define CDIO_API_VERSION 5 + +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* For compatibility. */ +#define CdIo CdIo_t + + /** This is an opaque structure for the CD object. */ + typedef struct _CdIo CdIo_t; + + /** This is an opaque structure for the CD-Text object. */ + typedef struct cdtext cdtext_t; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/* Drive(r)/Device-related functions. Perhaps we should break out + Driver from device? +*/ +#include + +/* Disc-related functions. */ +#include + +/* Sector (frame, or block)-related functions. Uses driver_return_code_t + from so it should come after that. +*/ +#include + +/* CD-Text-related functions. */ +#include + +/* Track-related functions. */ +#include + +#endif /* __CDIO_H__ */ diff --git a/include/cdio/cdtext.h b/include/cdio/cdtext.h new file mode 100644 index 00000000..daac733f --- /dev/null +++ b/include/cdio/cdtext.h @@ -0,0 +1,125 @@ +/* + $Id: cdtext.h,v 1.14 2008/03/25 15:59:08 karl Exp $ + + Copyright (C) 2004, 2005, 2008 Rocky Bernstein + adapted from cuetools + Copyright (C) 2003 Svend Sanjay Sorensen + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/*! + * \file cdtext.h + * + * \brief The top-level header for CD-Text information. Applications + * include this for CD-Text access. +*/ + + +#ifndef __CDIO_CDTEXT_H__ +#define __CDIO_CDTEXT_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MAX_CDTEXT_FIELDS 13 +#define MIN_CDTEXT_FIELD 0 + + /*! \brief structure for holding CD-Text information + + @see cdtext_init, cdtext_destroy, cdtext_get, and cdtext_set. + */ + struct cdtext { + char *field[MAX_CDTEXT_FIELDS]; + }; + + /*! \brief A list of all of the CD-Text fields. Because + the interval has no gaps, we can use ++ to iterate over fields. + */ + typedef enum { + CDTEXT_ARRANGER = 0, /**< name(s) of the arranger(s) */ + CDTEXT_COMPOSER = 1, /**< name(s) of the composer(s) */ + CDTEXT_DISCID = 2, /**< disc identification information */ + CDTEXT_GENRE = 3, /**< genre identification and genre information */ + CDTEXT_MESSAGE = 4, /**< ISRC code of each track */ + CDTEXT_ISRC = 5, /**< message(s) from the content provider or artist */ + CDTEXT_PERFORMER = 6, /**< name(s) of the performer(s) */ + CDTEXT_SIZE_INFO = 7, /**< size information of the block */ + CDTEXT_SONGWRITER = 8, /**< name(s) of the songwriter(s) */ + CDTEXT_TITLE = 9, /**< title of album name or track titles */ + CDTEXT_TOC_INFO = 10, /**< table of contents information */ + CDTEXT_TOC_INFO2 = 11, /**< second table of contents information */ + CDTEXT_UPC_EAN = 12, + CDTEXT_INVALID = MAX_CDTEXT_FIELDS + } cdtext_field_t; + + /*! Return string representation of the enum values above */ + const char *cdtext_field2str (cdtext_field_t i); + + /*! Initialize a new cdtext structure. + When the structure is no longer needed, release the + resources using cdtext_delete. + */ + void cdtext_init (cdtext_t *cdtext); + + /*! Free memory assocated with cdtext*/ + void cdtext_destroy (cdtext_t *cdtext); + + /*! returns an allocated string associated with the given field. NULL is + returned if key is CDTEXT_INVALID or the field is not set. + + The user needs to free the string when done with it. + + @see cdio_get_const to retrieve a constant string that doesn't + have to be freed. + */ + char *cdtext_get (cdtext_field_t key, const cdtext_t *cdtext); + + /*! returns a const string associated with the given field. NULL is + returned if key is CDTEXT_INVALID or the field is not set. + + Don't use the string when the cdtext object (i.e. the CdIo_t object + you got it from) is no longer valid. + + @see cdio_get to retrieve an allocated string that persists past + the cdtext object. + */ + const char *cdtext_get_const (cdtext_field_t key, const cdtext_t *cdtext); + + /*! + returns enum of keyword if key is a CD-Text keyword, + returns MAX_CDTEXT_FIELDS non-zero otherwise. + */ + cdtext_field_t cdtext_is_keyword (const char *key); + + /*! + sets cdtext's keyword entry to field + */ + void cdtext_set (cdtext_field_t key, const char *value, cdtext_t *cdtext); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_CDTEXT_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/cdio/device.h b/include/cdio/device.h new file mode 100644 index 00000000..e81d2312 --- /dev/null +++ b/include/cdio/device.h @@ -0,0 +1,949 @@ +/* -*- c -*- + $Id: device.h,v 1.39 2008/03/28 01:28:50 rocky Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file device.h + * + * \brief C header for driver- or device-related libcdio + * calls. ("device" includes CD-image reading devices). + */ +#ifndef __CDIO_DEVICE_H__ +#define __CDIO_DEVICE_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*! The type of an drive capability bit mask. See below for values*/ + typedef uint32_t cdio_drive_read_cap_t; + typedef uint32_t cdio_drive_write_cap_t; + typedef uint32_t cdio_drive_misc_cap_t; + + /*! + \brief Drive capability bits returned by cdio_get_drive_cap() + NOTE: Setting a bit here means the presence of a capability. + */ + + /** Miscellaneous capabilities. */ + typedef enum { + CDIO_DRIVE_CAP_ERROR = 0x40000, /**< Error */ + CDIO_DRIVE_CAP_UNKNOWN = 0x80000, /**< Dunno. It can be on if we + have only partial information + or are not completely certain + */ + CDIO_DRIVE_CAP_MISC_CLOSE_TRAY = 0x00001, /**< caddy systems can't + close... */ + CDIO_DRIVE_CAP_MISC_EJECT = 0x00002, /**< but can eject. */ + CDIO_DRIVE_CAP_MISC_LOCK = 0x00004, /**< disable manual eject */ + CDIO_DRIVE_CAP_MISC_SELECT_SPEED = 0x00008, /**< programmable speed */ + CDIO_DRIVE_CAP_MISC_SELECT_DISC = 0x00010, /**< select disc from + juke-box */ + CDIO_DRIVE_CAP_MISC_MULTI_SESSION= 0x00020, /**< read sessions>1 */ + CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED= 0x00080, /**< media changed */ + CDIO_DRIVE_CAP_MISC_RESET = 0x00100, /**< hard reset device */ + CDIO_DRIVE_CAP_MISC_FILE = 0x20000 /**< drive is really a file, + i.e a CD file image */ + } cdio_drive_cap_misc_t; + + /*! Reading masks.. */ + typedef enum { + CDIO_DRIVE_CAP_READ_AUDIO = 0x00001, /**< drive can play CD audio */ + CDIO_DRIVE_CAP_READ_CD_DA = 0x00002, /**< drive can read CD-DA */ + CDIO_DRIVE_CAP_READ_CD_G = 0x00004, /**< drive can read CD+G */ + CDIO_DRIVE_CAP_READ_CD_R = 0x00008, /**< drive can read CD-R */ + CDIO_DRIVE_CAP_READ_CD_RW = 0x00010, /**< drive can read CD-RW */ + CDIO_DRIVE_CAP_READ_DVD_R = 0x00020, /**< drive can read DVD-R */ + CDIO_DRIVE_CAP_READ_DVD_PR = 0x00040, /**< drive can read DVD+R */ + CDIO_DRIVE_CAP_READ_DVD_RAM = 0x00080, /**< drive can read DVD-RAM */ + CDIO_DRIVE_CAP_READ_DVD_ROM = 0x00100, /**< drive can read DVD-ROM */ + CDIO_DRIVE_CAP_READ_DVD_RW = 0x00200, /**< drive can read DVD-RW */ + CDIO_DRIVE_CAP_READ_DVD_RPW = 0x00400, /**< drive can read DVD+RW */ + CDIO_DRIVE_CAP_READ_C2_ERRS = 0x00800, /**< has C2 error correction */ + CDIO_DRIVE_CAP_READ_MODE2_FORM1 = 0x01000, /**< can read mode 2 form 1 */ + CDIO_DRIVE_CAP_READ_MODE2_FORM2 = 0x02000, /**< can read mode 2 form 2 */ + CDIO_DRIVE_CAP_READ_MCN = 0x04000, /**< can read MCN */ + CDIO_DRIVE_CAP_READ_ISRC = 0x08000 /**< can read ISRC */ + } cdio_drive_cap_read_t; + + /*! Writing masks.. */ + typedef enum { + CDIO_DRIVE_CAP_WRITE_CD_R = 0x00001, /**< drive can write CD-R */ + CDIO_DRIVE_CAP_WRITE_CD_RW = 0x00002, /**< drive can write CD-RW */ + CDIO_DRIVE_CAP_WRITE_DVD_R = 0x00004, /**< drive can write DVD-R */ + CDIO_DRIVE_CAP_WRITE_DVD_PR = 0x00008, /**< drive can write DVD+R */ + CDIO_DRIVE_CAP_WRITE_DVD_RAM = 0x00010, /**< drive can write DVD-RAM */ + CDIO_DRIVE_CAP_WRITE_DVD_RW = 0x00020, /**< drive can write DVD-RW */ + CDIO_DRIVE_CAP_WRITE_DVD_RPW = 0x00040, /**< drive can write DVD+RW */ + CDIO_DRIVE_CAP_WRITE_MT_RAINIER = 0x00080, /**< Mount Rainier */ + CDIO_DRIVE_CAP_WRITE_BURN_PROOF = 0x00100, /**< burn proof */ + CDIO_DRIVE_CAP_WRITE_CD = + (CDIO_DRIVE_CAP_WRITE_CD_R | CDIO_DRIVE_CAP_WRITE_CD_RW), + /**< Has some sort of CD writer ability */ + + CDIO_DRIVE_CAP_WRITE_DVD = + (CDIO_DRIVE_CAP_WRITE_DVD_R | CDIO_DRIVE_CAP_WRITE_DVD_PR + | CDIO_DRIVE_CAP_WRITE_DVD_RAM | CDIO_DRIVE_CAP_WRITE_DVD_RW + | CDIO_DRIVE_CAP_WRITE_DVD_RPW ), + /**< Has some sort of DVD writer ability */ + + CDIO_DRIVE_CAP_WRITE = + (CDIO_DRIVE_CAP_WRITE_CD | CDIO_DRIVE_CAP_WRITE_DVD) + /**< Has some sort of DVD or CD writing ability */ + } cdio_drive_cap_write_t; + +/*! Size of fields returned by an INQUIRY command */ + typedef enum { + CDIO_MMC_HW_VENDOR_LEN = 8, /**< length of vendor field */ + CDIO_MMC_HW_MODEL_LEN = 16, /**< length of model field */ + CDIO_MMC_HW_REVISION_LEN = 4 /**< length of revision field */ + } cdio_mmc_hw_len_t; + + + /*! \brief Structure to return CD vendor, model, and revision-level + strings obtained via the INQUIRY command */ + typedef struct cdio_hwinfo + { + char psz_vendor [CDIO_MMC_HW_VENDOR_LEN+1]; + char psz_model [CDIO_MMC_HW_MODEL_LEN+1]; + char psz_revision[CDIO_MMC_HW_REVISION_LEN+1]; + } cdio_hwinfo_t; + + + /** Flags specifying the category of device to open or is opened. */ + typedef enum { + CDIO_SRC_IS_DISK_IMAGE_MASK = 0x0001, /**< Read source is a CD image. */ + CDIO_SRC_IS_DEVICE_MASK = 0x0002, /**< Read source is a CD device. */ + CDIO_SRC_IS_SCSI_MASK = 0x0004, /**< Read source SCSI device. */ + CDIO_SRC_IS_NATIVE_MASK = 0x0008 + } cdio_src_category_mask_t; + + + /** The driver_id_t 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. + * + * Order is a little significant since the order is used in scans. + * Also the enumeration values are assumed not to leave any holes so + * we can use ++ to iterate over values. + * We have to start with DRIVER_UNKNOWN and devices should come before + * disk-image readers. By putting something towards the top (a lower + * enumeration number), in an iterative scan we prefer that to + * something with a higher enumeration number. + * + * NOTE: IF YOU MODIFY ENUM MAKE SURE INITIALIZATION IN CDIO.C AGREES. + * + */ + typedef enum { + DRIVER_UNKNOWN, /**< Used as input when we don't care what kind + of driver to use. */ + DRIVER_AIX, /**< AIX driver */ + DRIVER_BSDI, /**< BSDI driver */ + DRIVER_FREEBSD, /**< FreeBSD driver - includes CAM and ioctl access */ + DRIVER_LINUX, /**< GNU/Linux Driver */ + DRIVER_SOLARIS, /**< Sun Solaris Driver */ + DRIVER_OSX, /**< Apple OSX Driver */ + DRIVER_WIN32, /**< Microsoft Windows Driver. Includes ASPI and + ioctl access. */ + DRIVER_NETBSD, /**< NetBSD Driver. */ + DRIVER_CDRDAO, /**< cdrdao format CD image. This is listed + before BIN/CUE, to make the code prefer cdrdao + over BIN/CUE when both exist. */ + DRIVER_BINCUE, /**< CDRWIN BIN/CUE format CD image. This is + listed before NRG, to make the code prefer + BIN/CUE over NRG when both exist. */ + DRIVER_NRG, /**< Nero NRG format CD image. */ + DRIVER_DEVICE /**< Is really a set of the above; should come last */ + } driver_id_t; + + /** There will generally be only one hardware for a given + build/platform from the list above. You can use the variable + below to determine which you've got. If the build doesn't make an + hardware driver, then the value will be DRIVER_UNKNOWN. + */ + extern const driver_id_t cdio_os_driver; + + +/** Make sure what's listed for CDIO_MIN_DRIVER is the last + enumeration in driver_id_t. Since we have a bogus (but useful) 0th + entry above we don't have to add one. +*/ +#define CDIO_MIN_DRIVER DRIVER_AIX +#define CDIO_MIN_DEVICE_DRIVER CDIO_MIN_DRIVER +#define CDIO_MAX_DRIVER DRIVER_NRG +#define CDIO_MAX_DEVICE_DRIVER DRIVER_NETBSD + + /** The following are status codes for completion of a given cdio + operation. By design 0 is successful completion and -1 is error + completion. This is compatable with ioctl so those routines that + call ioctl can just pass the value the get back (cast as this + enum). Also, by using negative numbers for errors, the + enumeration values below can be used in places where a positive + value is expected when things complete successfully. For example, + get_blocksize returns the blocksize, but on error uses the error + codes below. So note that this enumeration is often cast to an + integer. C seems to tolerate this. + */ + typedef enum { + DRIVER_OP_SUCCESS = 0, /**< in cases where an int is returned, + like cdio_set_speed, more the negative + return codes are for errors and the + positive ones for success. */ + DRIVER_OP_ERROR = -1, /**< operation returned an error */ + DRIVER_OP_UNSUPPORTED = -2, /**< returned when a particular driver + doesn't support a particular operation. + For example an image driver which doesn't + really "eject" a CD. + */ + DRIVER_OP_UNINIT = -3, /**< returned when a particular driver + hasn't been initialized or a null + pointer has been passed. + */ + DRIVER_OP_NOT_PERMITTED = -4, /**< Operation not permitted. + For example might be a permission + problem. + */ + DRIVER_OP_BAD_PARAMETER = -5, /**< Bad parameter passed */ + DRIVER_OP_BAD_POINTER = -6, /**< Bad pointer to memory area */ + DRIVER_OP_NO_DRIVER = -7, /**< Operaton called on a driver + not available on this OS */ + } driver_return_code_t; + + /*! + Close media tray in CD drive if there is a routine to do so. + + @param psz_drive the name of CD-ROM to be closed. If NULL, we will + use the default device. + @param p_driver_id is the driver to be used or that got used if + it was DRIVER_UNKNOWN or DRIVER_DEVICE; If this is NULL, we won't + report back the driver used. + */ + driver_return_code_t cdio_close_tray (const char *psz_drive, + /*in/out*/ driver_id_t *p_driver_id); + + /*! + @param drc the return code you want interpreted. + @return the string information about drc + */ + const char *cdio_driver_errmsg(driver_return_code_t drc); + + /*! + Eject media in CD drive if there is a routine to do so. + + @param p_cdio the CD object to be acted upon. + If the CD is ejected *p_cdio is free'd and p_cdio set to NULL. + */ + driver_return_code_t cdio_eject_media (CdIo_t **p_cdio); + + /*! + Eject media in CD drive if there is a routine to do so. + + @param psz_drive the name of the device to be acted upon. + If NULL is given as the drive, we'll use the default driver device. + */ + driver_return_code_t cdio_eject_media_drive (const char *psz_drive); + + /*! + Free device list returned by cdio_get_devices or + cdio_get_devices_with_cap. + + @param device_list list returned by cdio_get_devices or + cdio_get_devices_with_cap + + @see cdio_get_devices, cdio_get_devices_with_cap + + */ + void cdio_free_device_list (char * device_list[]); + + /*! + Get the default CD device. + if p_cdio is NULL (we haven't initialized a specific device driver), + then find a suitable one and return the default device for that. + + @param p_cdio the CD object queried + @return a string containing the default CD device or NULL is + if we couldn't get a default device. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + char * cdio_get_default_device (const CdIo_t *p_cdio); + + /*! + Return a string containing the default CD device if none is specified. + if p_driver_id is DRIVER_UNKNOWN or DRIVER_DEVICE + then find a suitable one set the default device for that. + + NULL is returned if we couldn't get a default device. + */ + char * cdio_get_default_device_driver (/*in/out*/ driver_id_t *p_driver_id); + + /*! Return an array of device names. If you want a specific + devices for a driver, give that device. If you want hardware + devices, give DRIVER_DEVICE and if you want all possible devices, + image drivers and hardware drivers give DRIVER_UNKNOWN. + + NULL is returned if we couldn't return a list of devices. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + char ** cdio_get_devices (driver_id_t driver_id); + + /*! + Get an array of device names in search_devices that have at least + the capabilities listed by the capabities parameter. If + search_devices is NULL, then we'll search all possible CD drives. + + Capabilities have two parts to them, a "filesystem" part and an + "analysis" part. + + The filesystem part is mutually exclusive. For example either the + filesystem is at most one of the High-Sierra, UFS, or HFS, ISO9660, + fileystems. Valid combinations of say HFS and ISO9660 are + specified as a separate "filesystem". + + Capabilities on the other hand are not mutually exclusive. For example + a filesystem may have none, either, or both of the XA or Rock-Ridge + extension properties. + + If "b_any" is set false then every capability listed in the + analysis portion of capabilities (i.e. not the basic filesystem) + must be satisified. If no analysis capabilities are specified, + that's a match. + + If "b_any" is set true, then if any of the analysis capabilities + matches, we call that a success. + + In either case, in the filesystem portion different filesystem + either specify 0 to match any filesystem or the specific + filesystem type. + + To find a CD-drive of any type, use the mask CDIO_FS_MATCH_ALL. + + @return the array of device names or NULL if we couldn't get a + default device. It is also possible to return a non NULL but + after dereferencing the the value is NULL. This also means nothing + was found. + */ + char ** cdio_get_devices_with_cap (/*in*/ char *ppsz_search_devices[], + cdio_fs_anal_t capabilities, bool b_any); + + /*! + Like cdio_get_devices_with_cap but we return the driver we found + as well. This is because often one wants to search for kind of drive + and then *open* it afterwards. Giving the driver back facilitates this, + and speeds things up for libcdio as well. + */ + char ** cdio_get_devices_with_cap_ret (/*in*/ char* ppsz_search_devices[], + cdio_fs_anal_t capabilities, + bool b_any, + /*out*/ driver_id_t *p_driver_id); + + /*! Like cdio_get_devices, but we may change the p_driver_id if we + were given DRIVER_DEVICE or DRIVER_UNKNOWN. This is because + often one wants to get a drive name and then *open* it + afterwards. Giving the driver back facilitates this, and speeds + things up for libcdio as well. + */ + + char ** cdio_get_devices_ret (/*in/out*/ driver_id_t *p_driver_id); + + /*! + Get the what kind of device we've got. + + @param p_cdio the CD object queried + @param p_read_cap pointer to return read capabilities + @param p_write_cap pointer to return write capabilities + @param p_misc_cap pointer to return miscellaneous other capabilities + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it. In this situation capabilities will show up as + NULL even though there isa hardware CD-ROM. + */ + void cdio_get_drive_cap (const CdIo_t *p_cdio, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap); + + /*! + Get the drive capabilities for a specified device. + + Return a list of device capabilities. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it. In this situation capabilities will show up as + NULL even though there isa hardware CD-ROM. + */ + void cdio_get_drive_cap_dev (const char *device, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap); + + /*! + Get a string containing the name of the driver in use. + + @return a string with driver name or NULL if CdIo_t is NULL (we + haven't initialized a specific device. + */ + const char * cdio_get_driver_name (const CdIo_t *p_cdio); + + /*! + Get the driver id. + if CdIo_t is NULL (we haven't initialized a specific device driver), + then return DRIVER_UNKNOWN. + + @return the driver id.. + */ + driver_id_t cdio_get_driver_id (const CdIo_t *p_cdio); + + /*! + Get the CD-ROM hardware info via a SCSI MMC INQUIRY command. + False is returned if we had an error getting the information. + */ + bool cdio_get_hwinfo ( const CdIo_t *p_cdio, + /*out*/ cdio_hwinfo_t *p_hw_info ); + + + /*! Get the LSN of the first track of the last session of + on the CD. + + @param p_cdio the CD object to be acted upon. + @param i_last_session pointer to the session number to be returned. + */ + driver_return_code_t cdio_get_last_session (CdIo_t *p_cdio, + /*out*/ lsn_t *i_last_session); + + /*! + Find out if media has changed since the last call. + @param p_cdio the CD object to be acted upon. + @return 1 if media has changed since last call, 0 if not. Error + return codes are the same as driver_return_code_t + */ + int cdio_get_media_changed(CdIo_t *p_cdio); + + /*! True if CD-ROM understand ATAPI commands. */ + bool_3way_t cdio_have_atapi (CdIo_t *p_cdio); + + /*! Like cdio_have_xxx but uses an enumeration instead. */ + bool cdio_have_driver (driver_id_t driver_id); + + /* + Free any resources associated with p_cdio. Call this when done using p_cdio + and using CD reading/control operations. + + @param p_cdio the CD object to eliminated. + */ + void cdio_destroy (CdIo_t *p_cdio); + + /*! + Get a string decribing driver_id. + + @param driver_id the driver you want the description for + @return a string of driver description + */ + const char *cdio_driver_describe (driver_id_t driver_id); + + /*! Sets up to read from place specified by psz_source and + driver_id. This or cdio_open_* should be called before using any + other routine, except cdio_init or any routine that accesses the + CD-ROM drive by name. cdio_open will call cdio_init, if that hasn't + been done previously. + + @return the cdio object or NULL on error or no device. If NULL + is given as the source, we'll use the default driver device. + */ + CdIo_t * cdio_open (const char *psz_source, driver_id_t driver_id); + + /*! Sets up to read from place specified by psz_source, driver_id + and access mode. This or cdio_open* should be called before using + any other routine, except cdio_init or any routine that accesses + the CD-ROM drive by name. This will call cdio_init, if that + hasn't been done previously. + + If NULL is given as the source, we'll use the default driver device. + + @return the cdio object or NULL on error or no device. + */ + CdIo_t * cdio_open_am (const char *psz_source, + driver_id_t driver_id, const char *psz_access_mode); + + /*! Set up BIN/CUE CD disk-image for reading. Source is the .bin or + .cue file + + @return the cdio object or NULL on error or no device. + */ + CdIo_t * cdio_open_bincue (const char *psz_cue_name); + + /*! Set up BIN/CUE CD disk-image for reading. Source is the .bin or + .cue file + + @return the cdio object or NULL on error or no device.. + */ + CdIo_t * cdio_open_am_bincue (const char *psz_cue_name, + const char *psz_access_mode); + + /*! Set up cdrdao CD disk-image for reading. Source is the .toc file + + @return the cdio object or NULL on error or no device. + */ + CdIo_t * cdio_open_cdrdao (const char *psz_toc_name); + + /*! Set up cdrdao CD disk-image for reading. Source is the .toc file + + @return the cdio object or NULL on error or no device.. + */ + CdIo_t * cdio_open_am_cdrdao (const char *psz_toc_name, + const char *psz_access_mode); + + /*! Return a string containing the default CUE file that would + be used when none is specified. + + @return the cdio object or NULL on error or no device. + */ + char * cdio_get_default_device_bincue(void); + + char **cdio_get_devices_bincue(void); + + /*! @return string containing the default CUE file that would be + used when none is specified. NULL is returned on error or there + is no device. + */ + char * cdio_get_default_device_cdrdao(void); + + char **cdio_get_devices_cdrdao(void); + + /*! Set up CD-ROM for reading. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no driver for a some sort of hardware CD-ROM. + */ + CdIo_t * cdio_open_cd (const char *device_name); + + /*! Set up CD-ROM for reading. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no driver for a some sort of hardware CD-ROM. + */ + CdIo_t * cdio_open_am_cd (const char *psz_device, + const char *psz_access_mode); + + /*! CDRWIN BIN/CUE CD disc-image routines. Source is the .cue file + + @return the cdio object for subsequent operations. + NULL on error. + */ + CdIo_t * cdio_open_cue (const char *cue_name); + + /*! Set up CD-ROM for reading using the AIX driver. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no AIX driver. + + @see cdio_open + */ + CdIo_t * cdio_open_am_aix (const char *psz_source, + const char *psz_access_mode); + + /*! Set up CD-ROM for reading using the AIX driver. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no AIX driver. + + @see cdio_open + */ + CdIo_t * cdio_open_aix (const char *psz_source); + + /*! Return a string containing the default device name that the + BSDI driver would use when none is specified. + + @return the cdio object for subsequent operations. + NULL on error or there is no AIX driver. + + @see cdio_open_cd, cdio_open + */ + char * cdio_get_default_device_aix(void); + + /*! Return a list of all of the CD-ROM devices that the AIX driver + can find. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + char **cdio_get_devices_aix(void); + + /*! Set up CD-ROM for reading using the BSDI driver. The device_name is + the some sort of device name. + + @param psz_source the name of the device to open + @return the cdio object for subsequent operations. + NULL on error or there is no BSDI driver. + + @see cdio_open + */ + CdIo_t * cdio_open_bsdi (const char *psz_source); + + /*! Set up CD-ROM for reading using the BSDI driver. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no BSDI driver. + + @see cdio_open + */ + CdIo_t * cdio_open_am_bsdi (const char *psz_source, + const char *psz_access_mode); + + /*! Return a string containing the default device name that the + BSDI driver would use when none is specified. + + @return the cdio object for subsequent operations. + NULL on error or there is no BSDI driver. + + @see cdio_open_cd, cdio_open + */ + char * cdio_get_default_device_bsdi(void); + + /*! Return a list of all of the CD-ROM devices that the BSDI driver + can find. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + char **cdio_get_devices_bsdi(void); + + /*! Set up CD-ROM for reading using the FreeBSD driver. The device_name is + the some sort of device name. + + NULL is returned on error or there is no FreeBSD driver. + + @see cdio_open_cd, cdio_open + */ + CdIo_t * cdio_open_freebsd (const char *paz_psz_source); + + /*! Set up CD-ROM for reading using the FreeBSD driver. The device_name is + the some sort of device name. + + NULL is returned on error or there is no FreeBSD driver. + + @see cdio_open_cd, cdio_open + */ + CdIo_t * cdio_open_am_freebsd (const char *psz_source, + const char *psz_access_mode); + + /*! Return a string containing the default device name that the + FreeBSD driver would use when none is specified. + + NULL is returned on error or there is no CD-ROM device. + */ + char * cdio_get_default_device_freebsd(void); + + /*! Return a list of all of the CD-ROM devices that the FreeBSD driver + can find. + */ + char **cdio_get_devices_freebsd(void); + + /*! Set up CD-ROM for reading using the GNU/Linux driver. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no GNU/Linux driver. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + CdIo_t * cdio_open_linux (const char *psz_source); + + /*! Set up CD-ROM for reading using the GNU/Linux driver. The + device_name is the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no GNU/Linux driver. + */ + CdIo_t * cdio_open_am_linux (const char *psz_source, + const char *access_mode); + + /*! Return a string containing the default device name that the + GNU/Linux driver would use when none is specified. A scan is made + for CD-ROM drives with CDs in them. + + NULL is returned on error or there is no CD-ROM device. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + + @see cdio_open_cd, cdio_open + */ + char * cdio_get_default_device_linux(void); + + /*! Return a list of all of the CD-ROM devices that the GNU/Linux driver + can find. + */ + char **cdio_get_devices_linux(void); + + /*! Set up CD-ROM for reading using the Sun Solaris driver. The + device_name is the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no Solaris driver. + */ + CdIo_t * cdio_open_solaris (const char *psz_source); + + /*! Set up CD-ROM for reading using the Sun Solaris driver. The + device_name is the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no Solaris driver. + */ + CdIo_t * cdio_open_am_solaris (const char *psz_source, + const char *psz_access_mode); + + /*! Return a string containing the default device name that the + Solaris driver would use when none is specified. A scan is made + for CD-ROM drives with CDs in them. + + NULL is returned on error or there is no CD-ROM device. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + + @see cdio_open_cd, cdio_open + */ + char * cdio_get_default_device_solaris(void); + + /*! Return a list of all of the CD-ROM devices that the Solaris driver + can find. + */ + char **cdio_get_devices_solaris(void); + + /*! Set up CD-ROM for reading using the Apple OSX driver. The + device_name is the some sort of device name. + + NULL is returned on error or there is no OSX driver. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + + @see cdio_open_cd, cdio_open + */ + CdIo_t * cdio_open_osx (const char *psz_source); + + /*! Set up CD-ROM for reading using the Apple OSX driver. The + device_name is the some sort of device name. + + NULL is returned on error or there is no OSX driver. + + @see cdio_open_cd, cdio_open + */ + CdIo_t * cdio_open_am_osx (const char *psz_source, + const char *psz_access_mode); + + /*! Return a string containing the default device name that the + OSX driver would use when none is specified. A scan is made + for CD-ROM drives with CDs in them. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + char * cdio_get_default_device_osx(void); + + /*! Return a list of all of the CD-ROM devices that the OSX driver + can find. + */ + char **cdio_get_devices_osx(void); + + /*! Set up CD-ROM for reading using the Microsoft Windows driver. The + device_name is the some sort of device name. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + CdIo_t * cdio_open_win32 (const char *psz_source); + + /*! Set up CD-ROM for reading using the Microsoft Windows driver. The + device_name is the some sort of device name. + + NULL is returned on error or there is no Microsof Windows driver. + */ + CdIo_t * cdio_open_am_win32 (const char *psz_source, + const char *psz_access_mode); + + /*! Return a string containing the default device name that the + Win32 driver would use when none is specified. A scan is made + for CD-ROM drives with CDs in them. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + + @see cdio_open_cd, cdio_open + */ + char * cdio_get_default_device_win32(void); + + char **cdio_get_devices_win32(void); + + /*! Set up CD-ROM for reading using the Nero driver. The + device_name is the some sort of device name. + + @return true on success; NULL on error or there is no Nero driver. + */ + CdIo_t * cdio_open_nrg (const char *psz_source); + + /*! Set up CD-ROM for reading using the Nero driver. The + device_name is the some sort of device name. + + @return true on success; NULL on error or there is no Nero driver. + */ + CdIo_t * cdio_open_am_nrg (const char *psz_source, + const char *psz_access_mode); + + /*! Get a string containing the default device name that the NRG + driver would use when none is specified. A scan is made for NRG + disk images in the current directory. + + @return string containing the default device. NULL on error or + there is no CD-ROM device. + */ + char * cdio_get_default_device_nrg(void); + + char **cdio_get_devices_nrg(void); + + /*! + + Determine if bin_name is the bin file part of a CDRWIN CD disk image. + + @param bin_name location of presumed CDRWIN bin image file. + @return the corresponding CUE file if bin_name is a BIN file or + NULL if not a BIN file. + */ + char *cdio_is_binfile(const char *bin_name); + + /*! + Determine if cue_name is the cue sheet for a CDRWIN CD disk image. + + @return corresponding BIN file if cue_name is a CDRWIN cue file or + NULL if not a CUE file. + */ + char *cdio_is_cuefile(const char *cue_name); + + /*! + Determine if psg_nrg is a Nero CD disc image. + + @param psz_nrg location of presumed NRG image file. + @return true if psz_nrg is a Nero NRG image or false + if not a NRG image. + */ + bool cdio_is_nrg(const char *psz_nrg); + + /*! + Determine if psz_toc is a TOC file for a cdrdao CD disc image. + + @param psz_toc location of presumed TOC image file. + @return true if toc_name is a cdrdao TOC file or false + if not a TOC file. + */ + bool cdio_is_tocfile(const char *psz_toc); + + /*! + Determine if psz_source refers to a real hardware CD-ROM. + + @param psz_source location name of object + @param driver_id driver for reading object. Use DRIVER_UNKNOWN if you + don't know what driver to use. + @return true if psz_source is a device; If false is returned we + could have a CD disk image. + */ + bool cdio_is_device(const char *psz_source, driver_id_t driver_id); + + /*! + Set the blocksize for subsequent reads. + */ + driver_return_code_t cdio_set_blocksize ( const CdIo_t *p_cdio, + int i_blocksize ); + + /*! + Set the drive speed. + + @param p_cdio CD structure set by cdio_open(). + @param i_drive_speed speed in CD-ROM speed units. Note this + not Kbs as would be used in the MMC spec or + in mmc_set_speed(). To convert CD-ROM speed units + to Kbs, multiply the number by 176 (for raw data) + and by 150 (for filesystem data). On many CD-ROM + drives, specifying a value too large will result + in using the fastest speed. + + @see mmc_set_speed and mmc_set_drive_speed + */ + driver_return_code_t cdio_set_speed ( const CdIo_t *p_cdio, + int i_drive_speed ); + + /*! + Get the value associatied with key. + + @param p_cdio the CD object queried + @param key the key to retrieve + @return the value associatd with "key" or NULL if p_cdio is NULL + or "key" does not exist. + */ + const char * cdio_get_arg (const CdIo_t *p_cdio, const char key[]); + + /*! + Set the arg "key" with "value" in "p_cdio". + + @param p_cdio the CD object to set + @param key the key to set + @param value the value to assocaiate with key + */ + driver_return_code_t cdio_set_arg (CdIo_t *p_cdio, const char key[], + const char value[]); + + /*! + Initialize CD Reading and control routines. Should be called first. + */ + bool cdio_init(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/** The below variables are trickery to force the above enum symbol + values to be recorded in debug symbol tables. They are used to + allow one to refer to the enumeration value names in the typedefs + above in a debugger and debugger expressions. +*/ +extern cdio_drive_cap_misc_t debug_cdio_drive_cap_misc; +extern cdio_drive_cap_read_t debug_cdio_drive_cap_read_t; +extern cdio_drive_cap_write_t debug_drive_cap_write_t; +extern cdio_mmc_hw_len_t debug_cdio_mmc_hw_len; +extern cdio_src_category_mask_t debug_cdio_src_category_mask; + +#endif /* __CDIO_DEVICE_H__ */ diff --git a/include/cdio/disc.h b/include/cdio/disc.h new file mode 100644 index 00000000..b5ae3c49 --- /dev/null +++ b/include/cdio/disc.h @@ -0,0 +1,108 @@ +/* -*- c -*- + $Id: disc.h,v 1.9 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2004, 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file disc.h + * \brief The top-level header for disc-related libcdio calls. + */ +#ifndef __CDIO_DISC_H__ +#define __CDIO_DISC_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*! disc modes. The first combined from MMC-3 5.29.2.8 (Send CUESHEET) + and GNU/Linux /usr/include/linux/cdrom.h and we've added DVD. + */ + typedef enum { + CDIO_DISC_MODE_CD_DA, /**< CD-DA */ + CDIO_DISC_MODE_CD_DATA, /**< CD-ROM form 1 */ + CDIO_DISC_MODE_CD_XA, /**< CD-ROM XA form2 */ + CDIO_DISC_MODE_CD_MIXED, /**< Some combo of above. */ + CDIO_DISC_MODE_DVD_ROM, /**< DVD ROM (e.g. movies) */ + CDIO_DISC_MODE_DVD_RAM, /**< DVD-RAM */ + CDIO_DISC_MODE_DVD_R, /**< DVD-R */ + CDIO_DISC_MODE_DVD_RW, /**< DVD-RW */ + CDIO_DISC_MODE_DVD_PR, /**< DVD+R */ + CDIO_DISC_MODE_DVD_PRW, /**< DVD+RW */ + CDIO_DISC_MODE_DVD_OTHER, /**< Unknown/unclassified DVD type */ + CDIO_DISC_MODE_NO_INFO, + CDIO_DISC_MODE_ERROR, + CDIO_DISC_MODE_CD_I /**< CD-i. */ + } discmode_t; + + extern const char *discmode2str[]; + + /*! + Get disc mode - the kind of CD (CD-DA, CD-ROM mode 1, CD-MIXED, etc. + that we've got. The notion of "CD" is extended a little to include + DVD's. + */ + discmode_t cdio_get_discmode (CdIo_t *p_cdio); + + /*! + Get the lsn of the end of the CD + + @return the lsn. On error 0 or CDIO_INVALD_LSN. + */ + lsn_t cdio_get_disc_last_lsn(const CdIo_t *p_cdio); + + /*! + Return the Joliet level recognized for p_cdio. + */ + uint8_t cdio_get_joliet_level(const CdIo_t *p_cdio); + + /*! + Get the media catalog number (MCN) from the CD. + + @return the media catalog number or NULL if there is none or we + don't have the ability to get it. + + Note: string is malloc'd so caller has to free() the returned + string when done with it. + + */ + char * cdio_get_mcn (const CdIo_t *p_cdio); + + /*! + Get the number of tracks on the CD. + + @return the number of tracks, or CDIO_INVALID_TRACK if there is + an error. + */ + track_t cdio_get_num_tracks (const CdIo_t *p_cdio); + + /*! + Return true if discmode is some sort of CD. + */ + bool cdio_is_discmode_cdrom (discmode_t discmode); + + /*! + Return true if discmode is some sort of DVD. + */ + bool cdio_is_discmode_dvd (discmode_t discmode); + + /*! cdio_stat_size is deprecated. @see cdio_get_disc_last_lsn */ +#define cdio_stat_size cdio_get_disc_last_lsn + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_DISC_H__ */ diff --git a/include/cdio/ds.h b/include/cdio/ds.h new file mode 100644 index 00000000..447e30df --- /dev/null +++ b/include/cdio/ds.h @@ -0,0 +1,98 @@ +/* + $Id: ds.h,v 1.5 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2005, 2008 Rocky Bernstein + Copyright (C) 2000, 2004 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file ds.h + * \brief The top-level header for list-related data structures. + + Note: this header will is slated to get removed and libcdio will use + glib.h routines instead. +*/ + + +#ifndef __CDIO_DS_H__ +#define __CDIO_DS_H__ + +#include + +/** opaque types... */ +typedef struct _CdioList CdioList_t; +typedef struct _CdioListNode CdioListNode_t; + +typedef int (*_cdio_list_cmp_func_t) (void *p_data1, void *p_data2); +typedef int (*_cdio_list_iterfunc_t) (void *p_data, void *p_user_data); + +/** The below are given compatibility with old code. Please use + the above type names, not these. */ +#define CdioList CdioList_t +#define CdioListNode CdioListNode_t +#define _cdio_list_cmp_func _cdio_list_cmp_func_t +#define _cdio_list_iterfunc _cdio_list_iterfunc_t + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** methods */ +CdioList_t *_cdio_list_new (void); + +void _cdio_list_free (CdioList_t *p_list, int free_data); + +unsigned _cdio_list_length (const CdioList_t *list); + +void _cdio_list_prepend (CdioList_t *p_list, void *p_data); + +void _cdio_list_append (CdioList_t *p_list, void *p_data); + +void _cdio_list_foreach (CdioList_t *p_list, _cdio_list_iterfunc_t func, + void *p_user_data); + +CdioListNode_t *_cdio_list_find (CdioList_t *p_list, + _cdio_list_iterfunc_t cmp_func, + void *p_user_data); + +#define _CDIO_LIST_FOREACH(node, list) \ + for (node = _cdio_list_begin (list); node; node = _cdio_list_node_next (node)) + +/** node operations */ + +CdioListNode_t *_cdio_list_begin (const CdioList_t *p_list); + +CdioListNode_t *_cdio_list_end (CdioList_t *p_list); + +CdioListNode_t *_cdio_list_node_next (CdioListNode_t *p_node); + +void _cdio_list_node_free (CdioListNode_t *p_node, int i_free_data); + +void *_cdio_list_node_data (CdioListNode_t *p_node); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_DS_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ + diff --git a/include/cdio/dvd.h b/include/cdio/dvd.h new file mode 100644 index 00000000..8be8317b --- /dev/null +++ b/include/cdio/dvd.h @@ -0,0 +1,112 @@ +/* + $Id: dvd.h,v 1.5 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2004 Rocky Bernstein + Modeled after GNU/Linux definitions in linux/cdrom.h + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/*! + \file dvd.h + \brief Definitions for DVD access. +*/ + +#ifndef __CDIO_DVD_H__ +#define __CDIO_DVD_H__ + +#include + +/*! Values used in a READ DVD STRUCTURE */ + +#define CDIO_DVD_STRUCT_PHYSICAL 0x00 +#define CDIO_DVD_STRUCT_COPYRIGHT 0x01 +#define CDIO_DVD_STRUCT_DISCKEY 0x02 +#define CDIO_DVD_STRUCT_BCA 0x03 +#define CDIO_DVD_STRUCT_MANUFACT 0x04 + +/*! Media definitions for "Book Type" */ +#define CDIO_DVD_BOOK_DVD_ROM 0 +#define CDIO_DVD_BOOK_DVD_RAM 1 +#define CDIO_DVD_BOOK_DVD_R 2 /**< DVD-R */ +#define CDIO_DVD_BOOK_DVD_RW 3 /**< DVD-RW */ +#define CDIO_DVD_BOOK_DVD_PR 8 /**< DVD+R */ +#define CDIO_DVD_BOOK_DVD_PRW 9 /**< DVD+RW */ + +typedef struct cdio_dvd_layer { + uint8_t book_version : 4; + uint8_t book_type : 4; + uint8_t min_rate : 4; + uint8_t disc_size : 4; + uint8_t layer_type : 4; + uint8_t track_path : 1; + uint8_t nlayers : 2; + uint8_t track_density : 4; + uint8_t linear_density: 4; + uint8_t bca : 1; + uint32_t start_sector; + uint32_t end_sector; + uint32_t end_sector_l0; +} cdio_dvd_layer_t; + +/*! Maximum number of layers in a DVD. */ +#define CDIO_DVD_MAX_LAYERS 4 + +typedef struct cdio_dvd_physical { + uint8_t type; + uint8_t layer_num; + cdio_dvd_layer_t layer[CDIO_DVD_MAX_LAYERS]; +} cdio_dvd_physical_t; + +typedef struct cdio_dvd_copyright { + uint8_t type; + + uint8_t layer_num; + uint8_t cpst; + uint8_t rmi; +} cdio_dvd_copyright_t; + +typedef struct cdio_dvd_disckey { + uint8_t type; + + unsigned agid : 2; + uint8_t value[2048]; +} cdio_dvd_disckey_t; + +typedef struct cdio_dvd_bca { + uint8_t type; + + int len; + uint8_t value[188]; +} cdio_dvd_bca_t; + +typedef struct cdio_dvd_manufact { + uint8_t type; + + uint8_t layer_num; + int len; + uint8_t value[2048]; +} cdio_dvd_manufact_t; + +typedef union { + uint8_t type; + + cdio_dvd_physical_t physical; + cdio_dvd_copyright_t copyright; + cdio_dvd_disckey_t disckey; + cdio_dvd_bca_t bca; + cdio_dvd_manufact_t manufact; +} cdio_dvd_struct_t; + +#endif /* __SCSI_MMC_H__ */ diff --git a/include/cdio/ecma_167.h b/include/cdio/ecma_167.h new file mode 100644 index 00000000..78da7ae0 --- /dev/null +++ b/include/cdio/ecma_167.h @@ -0,0 +1,1006 @@ +/* + Copyright (c) 2005, 2006, 2008 Rocky Bernstein + Copyright (c) 2001-2002 Ben Fennema + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* + * Some portions taken from FreeBSD ecma167-udf.h which states: + * Copyright (c) 2001, 2002 Scott Long + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*! + * \file ecma_167.h + * + * \brief Definitions based on ECMA-167 3rd edition (June 1997) + * See http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-167.pdf +*/ + +#ifndef _ECMA_167_H +#define _ECMA_167_H 1 + +#include + +/** + Imagine the below enum values as \#define'd values rather than + distinct values of an enum. +*/ +typedef enum { + VSD_STD_ID_SIZE = 5, /** Volume Structure Descriptor (ECMA 167r3 + 2/9.1) */ + UDF_REGID_ID_SIZE = 23, /**< See identifier (ECMA 167r3 1/7.4) */ + UDF_VOLID_SIZE = 32, + UDF_FID_SIZE = 38, + UDF_VOLSET_ID_SIZE = 128 +} ecma_167_enum1_t ; + +/** Tag Identifier (ECMA 167r3 3/7.2.1) */ + +typedef enum { + TAGID_PRI_VOL = 0x0001, + TAGID_ANCHOR = 0x0002, + TAGID_VOL = 0x0003, + TAGID_IMP_VOL = 0x0004, + TAGID_PARTITION = 0x0005, + TAGID_LOGVOL = 0x0006, + TAGID_UNALLOC_SPACE = 0x0007, + TAGID_TERM = 0x0008, + TAGID_LOGVOL_INTEGRITY = 0x0009, + TAGID_FSD = 0x0100, + TAGID_FID = 0x0101, + TAGID_AED = 0x0102, + TAGID_IE = 0x0103, + TAGID_TE = 0x0104, + TAGID_FILE_ENTRY = 0x0105, + TAGID_EAHD = 0x0106, + TAGID_USE = 0x0107, + TAGID_SBD = 0x0108, + TAGID_PIE = 0x0109, + TAGID_EFE = 0x010A, +} tag_id_t ; + +/** Character Set Type (ECMA 167r3 1/7.2.1.1) */ +typedef enum { + CHARSPEC_TYPE_CS0 = 0x00, /**< Section 1/7.2.2 */ + CHARSPEC_TYPE_CS1 = 0x01, /**< Section 1/7.2.3 */ + CHARSPEC_TYPE_CS2 = 0x02, /**< Section 1/7.2.4 */ + CHARSPEC_TYPE_CS3 = 0x03, /**< Section 1/7.2.5 */ + CHARSPEC_TYPE_CS4 = 0x04, /**< Section 1/7.2.6 */ + CHARSPEC_TYPE_CS5 = 0x05, /**< Section 1/7.2.7 */ + CHARSPEC_TYPE_CS6 = 0x06, /**< Section 1/7.2.8 */ + CHARSPEC_TYPE_CS7 = 0x07, /**< Section 1/7.2.9 */ + + CHARSPEC_TYPE_CS8 = 0x08, /**< Section 1/7.2.10 */ +} udf_charspec_enum_t; + +typedef uint8_t udf_Uint8_t; /*! Section 1/7/1.1 */ +typedef uint16_t udf_Uint16_t; /*! Section 1/7.1.3 */ +typedef uint32_t udf_Uint32_t; /*! Section 1/7.1.5 */ +typedef uint64_t udf_Uint64_t; /*! Section 1/7.1.7 */ +typedef char udf_dstring; /*! Section 1/7.1.12 */ + +#define UDF_LENGTH_MASK 0x3fffffff + +PRAGMA_BEGIN_PACKED + +/** Character set specification (ECMA 167r3 1/7.2.1) */ +struct udf_charspec_s +{ + udf_Uint8_t charset_type; + udf_Uint8_t charset_info[63]; +} GNUC_PACKED; + +typedef struct udf_charspec_s udf_charspec_t; + +/** Timestamp (ECMA 167r3 1/7.3) */ +struct udf_timestamp_s +{ + udf_Uint16_t type_tz; + udf_Uint16_t year; + udf_Uint8_t month; + udf_Uint8_t day; + udf_Uint8_t hour; + udf_Uint8_t minute; + udf_Uint8_t second; + udf_Uint8_t centiseconds; + udf_Uint8_t hundreds_of_microseconds; + udf_Uint8_t microseconds; +} GNUC_PACKED; + +typedef struct udf_timestamp_s udf_timestamp_t; + +/** Type and Time Zone (ECMA 167r3 1/7.3.1) + + Imagine the below enum values as \#define'd values rather than + distinct values of an enum. +*/ +typedef enum { + TIMESTAMP_TYPE_CUT = 0x0000, + TIMESTAMP_TYPE_LOCAL = 0x1000, + TIMESTAMP_TYPE_AGREEMENT = 0x2000, + TIMESTAMP_TYPE_MASK = 0xF000, + TIMESTAMP_TIMEZONE_MASK = 0x0FFF, +} ecma_167_timezone_enum_t ; + + +#define TIMESTAMP_TYPE_MASK 0xF000 +#define TIMESTAMP_TYPE_CUT 0x0000 +#define TIMESTAMP_TYPE_LOCAL 0x1000 +#define TIMESTAMP_TYPE_AGREEMENT 0x2000 +#define TIMESTAMP_TIMEZONE_MASK 0x0FFF + +struct udf_id_suffix_s +{ + udf_Uint16_t udf_revision; + udf_Uint8_t os_class; + udf_Uint8_t os_identifier; + udf_Uint8_t reserved[4]; +} GNUC_PACKED; + +typedef struct udf_id_suffix_s udf_id_suffix_t; + +/** Entity identifier (ECMA 167r3 1/7.4) */ +struct udf_regid_s +{ + udf_Uint8_t flags; + udf_Uint8_t id[UDF_REGID_ID_SIZE]; + udf_id_suffix_t id_suffix; +} GNUC_PACKED; + +typedef struct udf_regid_s udf_regid_t; + +/** Flags (ECMA 167r3 1/7.4.1) */ +#define ENTITYID_FLAGS_DIRTY 0x00 +#define ENTITYID_FLAGS_PROTECTED 0x01 + +/** Volume Structure Descriptor (ECMA 167r3 2/9.1) */ +struct vol_struct_desc_s +{ + udf_Uint8_t struct_type; + udf_Uint8_t std_id[VSD_STD_ID_SIZE]; + udf_Uint8_t struct_version; + udf_Uint8_t struct_data[2041]; +} GNUC_PACKED; + +/** Standard Identifier (EMCA 167r2 2/9.1.2) */ +#define VSD_STD_ID_NSR02 "NSR02" /* (3/9.1) */ + +/** Standard Identifier (ECMA 167r3 2/9.1.2) */ + +/* The below const definitions are to faciltate debugging of the + values #define'd below. */ +extern const char VSD_STD_ID_BEA01[sizeof("BEA01")-1]; +extern const char VSD_STD_ID_BOOT2[sizeof("BOOT2")-1]; +extern const char VSD_STD_ID_CD001[sizeof("CD001")-1]; +extern const char VSD_STD_ID_CDW01[sizeof("CDW02")-1]; +extern const char VSD_STD_ID_NSR03[sizeof("NSR03")-1]; +extern const char VSD_STD_ID_TEA01[sizeof("TEA01")-1]; + +#define VSD_STD_ID_BEA01 "BEA01" /**< ECMA-167 2/9.2 */ +#define VSD_STD_ID_BOOT2 "BOOT2" /**< ECMA-167 2/9.4 */ +#define VSD_STD_ID_CD001 "CD001" /**< ECMA-119 */ +#define VSD_STD_ID_CDW02 "CDW02" /**< ECMA-168 */ +#define VSD_STD_ID_NSR02 "NSR02" /**< ECMA-167, 3/9.1 + NOTE: ECMA-167, 2nd edition */ +#define VSD_STD_ID_NSR03 "NSR03" /**< ECMA-167 3/9.1 */ +#define VSD_STD_ID_TEA01 "TEA01" /**< ECMA-168 2/9.3 */ + +/** Beginning Extended Area Descriptor (ECMA 167r3 2/9.2) */ +struct beginning_extended_area_desc_s +{ + udf_Uint8_t struct_type; + udf_Uint8_t std_id[VSD_STD_ID_SIZE]; + udf_Uint8_t struct_version; + udf_Uint8_t struct_data[2041]; +} GNUC_PACKED; + +/** Terminating Extended Area Descriptor (ECMA 167r3 2/9.3) */ +struct terminating_extended_area_desc_s +{ + udf_Uint8_t struct_type; + udf_Uint8_t std_id[VSD_STD_ID_SIZE]; + udf_Uint8_t struct_version; + udf_Uint8_t struct_data[2041]; +} GNUC_PACKED; + +/** Boot Descriptor (ECMA 167r3 2/9.4) */ +struct boot_desc_s +{ + udf_Uint8_t struct_type; + udf_Uint8_t std_ident[VSD_STD_ID_SIZE]; + udf_Uint8_t struct_version; + udf_Uint8_t reserved1; + udf_regid_t arch_type; + udf_regid_t boot_ident; + udf_Uint32_t bool_ext_location; + udf_Uint32_t bool_ext_length; + udf_Uint64_t load_address; + udf_Uint64_t start_address; + udf_timestamp_t desc_creation_time; + udf_Uint16_t flags; + udf_Uint8_t reserved2[32]; + udf_Uint8_t boot_use[1906]; +} GNUC_PACKED; + +/** Flags (ECMA 167r3 2/9.4.12) */ +#define BOOT_FLAGS_ERASE 0x01 + +/** Extent Descriptor (ECMA 167r3 3/7.1) */ +struct udf_extent_ad_s +{ + udf_Uint32_t len; + udf_Uint32_t loc; +} GNUC_PACKED; + +typedef struct udf_extent_ad_s udf_extent_ad_t; + +/** Descriptor Tag (ECMA 167r3 3/7.2) */ +struct udf_tag_s +{ + udf_Uint16_t id; + udf_Uint16_t desc_version; + udf_Uint8_t cksum; + udf_Uint8_t reserved; + udf_Uint16_t i_serial; + udf_Uint16_t desc_CRC; + udf_Uint16_t desc_CRC_len; + udf_Uint32_t loc; +} GNUC_PACKED; + +typedef struct udf_tag_s udf_tag_t; + +/** NSR Descriptor (ECMA 167r3 3/9.1) */ +struct NSR_desc_s +{ + udf_Uint8_t struct_type; + udf_Uint8_t std_id[VSD_STD_ID_SIZE]; + udf_Uint8_t struct_version; + udf_Uint8_t reserved; + udf_Uint8_t struct_data[2040]; +} GNUC_PACKED; + +/** Primary Volume Descriptor (ECMA 167r3 3/10.1) */ +struct udf_pvd_s +{ + udf_tag_t tag; + udf_Uint32_t vol_desc_seq_num; + udf_Uint32_t primary_vol_desc_num; + udf_dstring vol_ident[UDF_VOLID_SIZE]; + udf_Uint16_t vol_seq_num; + udf_Uint16_t max_vol_seqnum; + udf_Uint16_t interchange_lvl; + udf_Uint16_t max_interchange_lvl; + udf_Uint32_t charset_list; + udf_Uint32_t max_charset_list; + udf_dstring volset_id[UDF_VOLSET_ID_SIZE]; + udf_charspec_t desc_charset; + udf_charspec_t explanatory_charset; + udf_extent_ad_t vol_abstract; + udf_extent_ad_t vol_copyright; + udf_regid_t app_ident; + udf_timestamp_t recording_time; + udf_regid_t imp_ident; + udf_Uint8_t imp_use[64]; + udf_Uint32_t predecessor_vol_desc_seq_location; + udf_Uint16_t flags; + udf_Uint8_t reserved[22]; +} GNUC_PACKED; + +typedef struct udf_pvd_s udf_pvd_t; + +/** Flags (ECMA 167r3 3/10.1.21) */ +#define PVD_FLAGS_VSID_COMMON 0x0001 + +/** Anchor Volume Descriptor Pointer (ECMA 167r3 3/10.2) */ +struct anchor_vol_desc_ptr_s +{ + udf_tag_t tag; + udf_extent_ad_t main_vol_desc_seq_ext; + udf_extent_ad_t reserve_vol_desc_seq_ext; + udf_Uint8_t reserved[480]; +} GNUC_PACKED; + +typedef struct anchor_vol_desc_ptr_s anchor_vol_desc_ptr_t; + +/** Volume Descriptor Pointer (ECMA 167r3 3/10.3) */ +struct vol_desc_ptr_s +{ + udf_tag_t tag; + udf_Uint32_t vol_desc_seq_num; + udf_extent_ad_t next_vol_desc_set_ext; + udf_Uint8_t reserved[484]; +} GNUC_PACKED; + +/** Implementation Use Volume Descriptor (ECMA 167r3 3/10.4) */ +struct imp_use_vol_desc_s +{ + udf_tag_t tag; + udf_Uint32_t vol_desc_seq_num; + udf_regid_t imp_id; + udf_Uint8_t imp_use[460]; +} GNUC_PACKED; + +/** Partition Descriptor (ECMA 167r3 3/10.5) */ +struct partition_desc_s +{ + udf_tag_t tag; + udf_Uint32_t vol_desc_seq_num; + udf_Uint16_t flags; + udf_Uint16_t number; /**< Partition number */ + udf_regid_t contents; + udf_Uint8_t contents_use[128]; + udf_Uint32_t access_type; + udf_Uint32_t start_loc; + udf_Uint32_t part_len; + udf_regid_t imp_id; + udf_Uint8_t imp_use[128]; + udf_Uint8_t reserved[156]; +} GNUC_PACKED; + +typedef struct partition_desc_s partition_desc_t; + +/** Partition Flags (ECMA 167r3 3/10.5.3) */ +#define PD_PARTITION_FLAGS_ALLOC 0x0001 + +/** Partition Contents (ECMA 167r2 3/10.5.3) */ +#define PD_PARTITION_CONTENTS_NSR02 "+NSR02" + +/** Partition Contents (ECMA 167r3 3/10.5.5) */ +#define PD_PARTITION_CONTENTS_FDC01 "+FDC01" +#define PD_PARTITION_CONTENTS_CD001 "+CD001" +#define PD_PARTITION_CONTENTS_CDW02 "+CDW02" +#define PD_PARTITION_CONTENTS_NSR03 "+NSR03" + +/** Access Type (ECMA 167r3 3/10.5.7) */ +#define PD_ACCESS_TYPE_NONE 0x00000000 +#define PD_ACCESS_TYPE_READ_ONLY 0x00000001 +#define PD_ACCESS_TYPE_WRITE_ONCE 0x00000002 +#define PD_ACCESS_TYPE_REWRITABLE 0x00000003 +#define PD_ACCESS_TYPE_OVERWRITABLE 0x00000004 + +/** Recorded Address (ECMA 167r3 4/7.1) */ +struct udf_lb_addr_s +{ + udf_Uint32_t lba; + udf_Uint16_t partitionReferenceNum; +} GNUC_PACKED; + +typedef struct udf_lb_addr_s udf_lb_addr_t; + +/** Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */ +struct udf_short_ad_s +{ + udf_Uint32_t len; + udf_Uint32_t pos; +} GNUC_PACKED; + +typedef struct udf_short_ad_s udf_short_ad_t; + +/** Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */ +struct udf_long_ad_s +{ + udf_Uint32_t len; + udf_lb_addr_t loc; + udf_Uint8_t imp_use[6]; +} GNUC_PACKED; + +typedef struct udf_long_ad_s udf_long_ad_t; + +/** Logical Volume Descriptor (ECMA 167r3 3/10.6) */ +struct logical_vol_desc_s +{ + udf_tag_t tag; + udf_Uint32_t seq_num; + udf_charspec_t desc_charset; + udf_dstring logvol_id[128]; + udf_Uint32_t logical_blocksize; + udf_regid_t domain_id; + union { + udf_long_ad_t fsd_loc; + udf_Uint8_t logvol_content_use[16]; + } lvd_use; + udf_Uint8_t logvol_contents_use[16]; + udf_Uint32_t maptable_len; + udf_Uint32_t i_partition_maps; + udf_regid_t imp_id; + udf_Uint8_t imp_use[128]; + udf_extent_ad_t integrity_seq_ext; + udf_Uint8_t partition_maps[0]; +} GNUC_PACKED; + +typedef struct logical_vol_desc_s logical_vol_desc_t; + +/** Generic Partition Map (ECMA 167r3 3/10.7.1) */ +struct generic_partition_map +{ + udf_Uint8_t partition_map_type; + udf_Uint8_t partition_map_length; + udf_Uint8_t partition_mapping[0]; +} GNUC_PACKED; + +/** Partition Map Type (ECMA 167r3 3/10.7.1.1) */ +#define GP_PARTITION_MAP_TYPE_UNDEF 0x00 +#define GP_PARTIITON_MAP_TYPE_1 0x01 +#define GP_PARTITION_MAP_TYPE_2 0x02 + +/** Type 1 Partition Map (ECMA 167r3 3/10.7.2) */ +struct generic_partition_map1 +{ + udf_Uint8_t partition_map_type; + udf_Uint8_t partition_map_length; + udf_Uint16_t vol_seq_num; + udf_Uint16_t i_partition; +} GNUC_PACKED; + +/** Type 2 Partition Map (ECMA 167r3 3/10.7.3) */ +struct generic_partition_map2 +{ + udf_Uint8_t partition_map_type; + udf_Uint8_t partition_map_length; + udf_Uint8_t partition_id[62]; +} GNUC_PACKED; + +/** Unallocated Space Descriptor (ECMA 167r3 3/10.8) */ +struct unalloc_space_desc_s +{ + udf_tag_t tag; + udf_Uint32_t vol_desc_seq_num; + udf_Uint32_t i_alloc_descs; + udf_extent_ad_t allocDescs[0]; +} GNUC_PACKED; + +/** Terminating Descriptor (ECMA 167r3 3/10.9) */ +struct terminating_desc_s +{ + udf_tag_t tag; + udf_Uint8_t reserved[496]; +} GNUC_PACKED; + +/** Logical Volume Integrity Descriptor (ECMA 167r3 3/10.10) */ +struct logvol_integrity_desc_s +{ + udf_tag_t tag; + udf_timestamp_t recording_time; + udf_Uint32_t integrity_type; + udf_extent_ad_t next_integrity_ext; + udf_Uint8_t logvol_contents_use[32]; + udf_Uint32_t i_partitions; + udf_Uint32_t imp_use_len; + udf_Uint32_t freespace_table[0]; + udf_Uint32_t size_table[0]; + udf_Uint8_t imp_use[0]; +} GNUC_PACKED; + +/** Integrity Type (ECMA 167r3 3/10.10.3) */ +#define LVID_INTEGRITY_TYPE_OPEN 0x00000000 +#define LVID_INTEGRITY_TYPE_CLOSE 0x00000001 + +/** Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */ +struct udf_ext_ad_s +{ + udf_Uint32_t len; + udf_Uint32_t recorded_len; + udf_Uint32_t information_len; + udf_lb_addr_t ext_loc; +} GNUC_PACKED; + +typedef struct udf_ext_ad_s udf_ext_ad_t; + +/** Descriptor Tag (ECMA 167r3 4/7.2 - See 3/7.2) */ + +/** Tag Identifier (ECMA 167r3 4/7.2.1) */ + +/** File Set Descriptor (ECMA 167r3 4/14.1) */ +struct udf_fsd_s +{ + udf_tag_t tag; + udf_timestamp_t recording_time; + udf_Uint16_t interchange_lvl; + udf_Uint16_t maxInterchange_lvl; + udf_Uint32_t charset_list; + udf_Uint32_t max_charset_list; + udf_Uint32_t fileset_num; + udf_Uint32_t udf_fsd_num; + udf_charspec_t logical_vol_id_charset; + udf_dstring logical_vol_id[128]; + udf_charspec_t fileset_charset; + udf_dstring fileSet_id[32]; + udf_dstring copyright_file_id[32]; + udf_dstring abstract_file_id[32]; + udf_long_ad_t root_icb; + udf_regid_t domain_id; + udf_long_ad_t next_ext; + udf_long_ad_t stream_directory_ICB; + udf_Uint8_t reserved[32]; +} GNUC_PACKED; + +typedef struct udf_fsd_s udf_fsd_t; + +/** Partition Header Descriptor (ECMA 167r3 4/14.3) */ +struct partition_header_desc_s +{ + udf_short_ad_t unalloc_space_table; + udf_short_ad_t unalloc_space_bitmap; + udf_short_ad_t partition_integrity_table; + udf_short_ad_t freed_space_table; + udf_short_ad_t freed_space_bitmap; + udf_Uint8_t reserved[88]; +} GNUC_PACKED; + +typedef struct partition_header_desc_s partition_header_desc_t; + +/** File Identifier Descriptor (ECMA 167r3 4/14.4) */ +struct udf_fileid_desc_s +{ + udf_tag_t tag; + udf_Uint16_t file_version_num; + udf_Uint8_t file_characteristics; + udf_Uint8_t i_file_id; + udf_long_ad_t icb; + udf_Uint16_t i_imp_use; + udf_Uint8_t imp_use[0]; + udf_Uint8_t file_id[0]; + udf_Uint8_t padding[0]; +} GNUC_PACKED; + +typedef struct udf_fileid_desc_s udf_fileid_desc_t; + +/** File Characteristics (ECMA 167r3 4/14.4.3) + + Imagine the below enumeration values are \#defines to be used in a + bitmask rather than distinct values of an enum. +*/ +typedef enum { + UDF_FILE_HIDDEN = (1 << 0), + UDF_FILE_DIRECTORY = (1 << 1), + UDF_FILE_DELETED = (1 << 2), + UDF_FILE_PARENT = (1 << 3), + UDF_FILE_METADATA = (1 << 4) +} file_characteristics_t; + +/** Allocation Ext Descriptor (ECMA 167r3 4/14.5) */ +struct allocExtDesc +{ + udf_tag_t tag; + udf_Uint32_t previous_alloc_ext_loc; + udf_Uint32_t i_alloc_descs; +} GNUC_PACKED; + +/** ICB Tag (ECMA 167r3 4/14.6) */ +struct udf_icbtag_s +{ + udf_Uint32_t prev_num_dirs; + udf_Uint16_t strat_type; + udf_Uint16_t strat_param; + udf_Uint16_t max_num_entries; + udf_Uint8_t reserved; + udf_Uint8_t file_type; + udf_lb_addr_t parent_ICB; + udf_Uint16_t flags; +} GNUC_PACKED; + +typedef struct udf_icbtag_s udf_icbtag_t; + +#define UDF_ICB_TAG_FLAGS_SETUID 0x40 +#define UDF_ICB_TAG_FLAGS_SETGID 0x80 +#define UDF_ICB_TAG_FLAGS_STICKY 0x100 + +/** Strategy Type (ECMA 167r3 4/14.6.2) which helpfully points + largely to 4/A.x */ +#define ICBTAG_STRATEGY_TYPE_UNDEF 0x0000 +#define ICBTAG_STRATEGY_TYPE_1 0x0001 /**< 4/A.2 Direct entries Uint16 */ +#define ICBTAG_STRATEGY_TYPE_2 0x0002 /**< 4/A.3 List of ICB direct entries */ +#define ICBTAG_STRATEGY_TYPE_3 0x0003 /**< 4/A.4 */ +#define ICBTAG_STRATEGY_TYPE_4 0x0004 /**< 4/A.5 Hierarchy having one + single ICB with one direct entry. + This is what's most often used. + */ + +/** File Type (ECMA 167r3 4/14.6.6) + + Imagine the below enum values as \#define'd values rather than + distinct values of an enum. +*/ +typedef enum { + ICBTAG_FILE_TYPE_UNDEF = 0x00, + ICBTAG_FILE_TYPE_USE = 0x01, + ICBTAG_FILE_TYPE_PIE = 0x02, + ICBTAG_FILE_TYPE_IE = 0x03, + ICBTAG_FILE_TYPE_DIRECTORY = 0x04, + ICBTAG_FILE_TYPE_REGULAR = 0x05, + ICBTAG_FILE_TYPE_BLOCK = 0x06, + ICBTAG_FILE_TYPE_CHAR = 0x07, + ICBTAG_FILE_TYPE_EA = 0x08, + ICBTAG_FILE_TYPE_FIFO = 0x09, + ICBTAG_FILE_TYPE_SOCKET = 0x0A, + ICBTAG_FILE_TYPE_TE = 0x0B, + ICBTAG_FILE_TYPE_SYMLINK = 0x0C, + ICBTAG_FILE_TYPE_STREAMDIR = 0x0D +} icbtag_file_type_enum_t; + +/** Flags (ECMA 167r3 4/14.6.8) */ +typedef enum { + ICBTAG_FLAG_AD_MASK = 0x0007, /**< "&" this to get below address + flags */ + ICBTAG_FLAG_AD_SHORT = 0x0000, /**< The allocation descriptor + field is filled with + short_ad's. If the + offset is beyond the + current extent, look for + the next extent. */ + ICBTAG_FLAG_AD_LONG = 0x0001, /**< The allocation descriptor + field is filled with + long_ad's If the offset + is beyond the current + extent, look for the next + extent. */ + ICBTAG_FLAG_AD_EXTENDED = 0x0002, + ICBTAG_FLAG_AD_IN_ICB = 0x0003, /**< This type means that the + file *data* is stored in + the allocation descriptor + field of the file entry. */ + ICBTAG_FLAG_SORTED = 0x0008, + ICBTAG_FLAG_NONRELOCATABLE = 0x0010, + ICBTAG_FLAG_ARCHIVE = 0x0020, + ICBTAG_FLAG_SETUID = 0x0040, + ICBTAG_FLAG_SETGID = 0x0080, + ICBTAG_FLAG_STICKY = 0x0100, + ICBTAG_FLAG_CONTIGUOUS = 0x0200, + ICBTAG_FLAG_SYSTEM = 0x0400, + ICBTAG_FLAG_TRANSFORMED = 0x0800, + ICBTAG_FLAG_MULTIVERSIONS = 0x1000, + ICBTAG_FLAG_STREAM = 0x2000 +} icbtag_flag_enum_t; + +/** Indirect Entry (ECMA 167r3 4/14.7) */ +struct indirect_entry_s +{ + udf_tag_t tag; + udf_icbtag_t icb_tag; + udf_long_ad_t indirect_ICB; +} GNUC_PACKED; + +/** Terminal Entry (ECMA 167r3 4/14.8) */ +struct terminal_entry_s +{ + udf_tag_t tag; + udf_icbtag_t icb_tag; +} GNUC_PACKED; + +/** File Entry (ECMA 167r3 4/14.9) */ +struct udf_file_entry_s +{ + udf_tag_t tag; + udf_icbtag_t icb_tag; /**< 4/14.9.2 */ + udf_Uint32_t uid; /**< 4/14.9.3 */ + udf_Uint32_t gid; /**< 4/14.9.4 */ + udf_Uint32_t permissions; /**< 4/14.9.5 */ + udf_Uint16_t link_count; /**< 4/14.9.6 */ + udf_Uint8_t rec_format; /**< 4/14.9.7 */ + udf_Uint8_t rec_disp_attr; /**< 4/14.9.8 */ + udf_Uint32_t rec_len; /**< 4/14.9.9 */ + udf_Uint64_t info_len; /**< 4/14.9.10 */ + udf_Uint64_t logblks_recorded; /**< 4/14.9.11 */ + udf_timestamp_t access_time; /**< 4/14.9.12 - last access to + any stream of file prior to + recording file entry */ + udf_timestamp_t modification_time; /**< 4/14.9.13 - last access to + modification to any stream of + file */ + udf_timestamp_t attribute_time; + udf_Uint32_t checkpoint; + udf_long_ad_t ext_attr_ICB; + udf_regid_t imp_id; + udf_Uint64_t unique_ID; + udf_Uint32_t i_extended_attr; + udf_Uint32_t i_alloc_descs; + udf_Uint8_t ext_attr[0]; + udf_Uint8_t alloc_descs[0]; +} GNUC_PACKED; + +typedef struct udf_file_entry_s udf_file_entry_t; + +#define UDF_FENTRY_SIZE 176 +#define UDF_FENTRY_PERM_USER_MASK 0x07 +#define UDF_FENTRY_PERM_GRP_MASK 0xE0 +#define UDF_FENTRY_PERM_OWNER_MASK 0x1C00 + +/** Permissions (ECMA 167r3 4/14.9.5) */ +#define FE_PERM_O_EXEC 0x00000001U +#define FE_PERM_O_WRITE 0x00000002U +#define FE_PERM_O_READ 0x00000004U +#define FE_PERM_O_CHATTR 0x00000008U +#define FE_PERM_O_DELETE 0x00000010U +#define FE_PERM_G_EXEC 0x00000020U +#define FE_PERM_G_WRITE 0x00000040U +#define FE_PERM_G_READ 0x00000080U +#define FE_PERM_G_CHATTR 0x00000100U +#define FE_PERM_G_DELETE 0x00000200U +#define FE_PERM_U_EXEC 0x00000400U +#define FE_PERM_U_WRITE 0x00000800U +#define FE_PERM_U_READ 0x00001000U +#define FE_PERM_U_CHATTR 0x00002000U +#define FE_PERM_U_DELETE 0x00004000U + +/** Record Format (ECMA 167r3 4/14.9.7) */ +#define FE_RECORD_FMT_UNDEF 0x00 +#define FE_RECORD_FMT_FIXED_PAD 0x01 +#define FE_RECORD_FMT_FIXED 0x02 +#define FE_RECORD_FMT_VARIABLE8 0x03 +#define FE_RECORD_FMT_VARIABLE16 0x04 +#define FE_RECORD_FMT_VARIABLE16_MSB 0x05 +#define FE_RECORD_FMT_VARIABLE32 0x06 +#define FE_RECORD_FMT_PRINT 0x07 +#define FE_RECORD_FMT_LF 0x08 +#define FE_RECORD_FMT_CR 0x09 +#define FE_RECORD_FMT_CRLF 0x0A +#define FE_RECORD_FMT_LFCR 0x0B + +/** Record Display Attributes (ECMA 167r3 4/14.9.8) */ +#define FE_RECORD_DISPLAY_ATTR_UNDEF 0x00 +#define FE_RECORD_DISPLAY_ATTR_1 0x01 +#define FE_RECORD_DISPLAY_ATTR_2 0x02 +#define FE_RECORD_DISPLAY_ATTR_3 0x03 + +/** Extended Attribute Header Descriptor (ECMA 167r3 4/14.10.1) */ +struct extended_attr_header_desc_s +{ + udf_tag_t tag; + udf_Uint32_t imp_attr_location; + udf_Uint32_t app_attr_location; +} GNUC_PACKED; + +/** Generic Format (ECMA 167r3 4/14.10.2) */ +struct generic_format_s +{ + udf_Uint32_t attr_type; + udf_Uint8_t attr_subtype; + udf_Uint8_t reserved[3]; + udf_Uint32_t attrLength; + udf_Uint8_t attrData[0]; +} GNUC_PACKED; + +/** Character Set Information (ECMA 167r3 4/14.10.3) */ +struct charSet_info_s +{ + udf_Uint32_t attr_type; + udf_Uint8_t attr_subtype; + udf_Uint8_t reserved[3]; + udf_Uint32_t attrLength; + udf_Uint32_t escapeSeqLength; + udf_Uint8_t charSetType; + udf_Uint8_t escapeSeq[0]; +} GNUC_PACKED; + +/* Alternate Permissions (ECMA 167r3 4/14.10.4) */ +struct alt_perms_s +{ + udf_Uint32_t attr_type; + udf_Uint8_t attr_subtype; + udf_Uint8_t reserved[3]; + udf_Uint32_t attrLength; + udf_Uint16_t owner_id; + udf_Uint16_t group_id; + udf_Uint16_t permission; +} GNUC_PACKED; + +/** File Times Extended Attribute (ECMA 167r3 4/14.10.5) */ +struct filetimes_ext_attr_s +{ + udf_Uint32_t attr_type; + udf_Uint8_t attr_subtype; + udf_Uint8_t reserved[3]; + udf_Uint32_t attrLength; + udf_Uint32_t dataLength; + udf_Uint32_t fileTimeExistence; + udf_Uint8_t fileTimes; +} GNUC_PACKED; + +/** FileTimeExistence (ECMA 167r3 4/14.10.5.6) */ +#define FTE_CREATION 0x00000001 +#define FTE_DELETION 0x00000004 +#define FTE_EFFECTIVE 0x00000008 +#define FTE_BACKUP 0x00000002 + +/** Information Times Extended Attribute (ECMA 167r3 4/14.10.6) */ +struct infoTimesExtAttr +{ + udf_Uint32_t attr_type; + udf_Uint8_t attr_subtype; + udf_Uint8_t reserved[3]; + udf_Uint32_t attrLength; + udf_Uint32_t dataLength; + udf_Uint32_t infoTimeExistence; + udf_Uint8_t infoTimes[0]; +} GNUC_PACKED; + +/** Device Specification (ECMA 167r3 4/14.10.7) */ +struct deviceSpec +{ + udf_Uint32_t attr_type; + udf_Uint8_t attr_subtype; + udf_Uint8_t reserved[3]; + udf_Uint32_t attrLength; + udf_Uint32_t imp_useLength; + udf_Uint32_t majorDevice_id; + udf_Uint32_t minorDevice_id; + udf_Uint8_t imp_use[0]; +} GNUC_PACKED; + +/** Implementation Use Extended Attr (ECMA 167r3 4/14.10.8) */ +struct impUseExtAttr +{ + udf_Uint32_t attr_type; + udf_Uint8_t attr_subtype; + udf_Uint8_t reserved[3]; + udf_Uint32_t attrLength; + udf_Uint32_t imp_useLength; + udf_regid_t imp_id; + udf_Uint8_t imp_use[0]; +} GNUC_PACKED; + +/** Application Use Extended Attribute (ECMA 167r3 4/14.10.9) */ +struct appUseExtAttr +{ + udf_Uint32_t attr_type; + udf_Uint8_t attr_subtype; + udf_Uint8_t reserved[3]; + udf_Uint32_t attrLength; + udf_Uint32_t appUseLength; + udf_regid_t app_id; + udf_Uint8_t appUse[0]; +} GNUC_PACKED; + +#define EXTATTR_CHAR_SET 1 +#define EXTATTR_ALT_PERMS 3 +#define EXTATTR_FILE_TIMES 5 +#define EXTATTR_INFO_TIMES 6 +#define EXTATTR_DEV_SPEC 12 +#define EXTATTR_IMP_USE 2048 +#define EXTATTR_APP_USE 65536 + + +/** Unallocated Space Entry (ECMA 167r3 4/14.11) */ +struct unallocSpaceEntry +{ + udf_tag_t tag; + udf_icbtag_t icb_tag; + udf_Uint32_t lengthAllocDescs; + udf_Uint8_t allocDescs[0]; +} GNUC_PACKED; + +/** Space Bitmap Descriptor (ECMA 167r3 4/14.12) */ +struct spaceBitmapDesc +{ + udf_tag_t tag; + udf_Uint32_t i_bits; + udf_Uint32_t i_bytes; + udf_Uint8_t bitmap[0]; +} GNUC_PACKED; + +/** Partition Integrity Entry (ECMA 167r3 4/14.13) */ +struct partitionIntegrityEntry +{ + udf_tag_t tag; + udf_icbtag_t icb_tag; + udf_timestamp_t recording_time; + udf_Uint8_t integrityType; + udf_Uint8_t reserved[175]; + udf_regid_t imp_id; + udf_Uint8_t imp_use[256]; +} GNUC_PACKED; + +/** Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */ + +/** Extent Length (ECMA 167r3 4/14.14.1.1) */ +#define EXT_RECORDED_ALLOCATED 0x00000000 +#define EXT_NOT_RECORDED_ALLOCATED 0x40000000 +#define EXT_NOT_RECORDED_NOT_ALLOCATED 0x80000000 +#define EXT_NEXT_EXTENT_ALLOCDECS 0xC0000000 + +/** Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */ + +/** Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */ + +/** Logical Volume Header Descriptor (ECMA 167r3 4/14.15) */ +struct logical_vol_header_desc_s +{ + udf_Uint64_t uniqueID; + udf_Uint8_t reserved[24]; +} GNUC_PACKED; + +typedef struct logical_vol_header_desc_s logical_vol_header_desc_t; + +/** Path Component (ECMA 167r3 4/14.16.1) */ +struct pathComponent +{ + udf_Uint8_t component_type; + udf_Uint8_t lengthComponent_id; + udf_Uint16_t componentFileVersionNum; + udf_dstring component_id[0]; +} GNUC_PACKED; + +/** File Entry (ECMA 167r3 4/14.17) */ +struct extended_file_entry +{ + udf_tag_t tag; /**< 4/14.17.1 - id = 266 */ + udf_icbtag_t icb_tag; /**< 4/14.17.2 & 4/14.9.2 */ + udf_Uint32_t uid; /**< 4/14.17.3 & 4/14.9.3 */ + udf_Uint32_t gid; /**< 4/14.17.4 & 4/14.9.4 */ + udf_Uint32_t permissions; /**< 4/14.17.5 & 4/14.9.5 */ + udf_Uint16_t link_count; /**< 4/14.17.6 & 4/14.9.6 */ + udf_Uint8_t rec_format; /**< 4/14.17.7 & 4/14.9.7 */ + udf_Uint8_t rec_display_attr; /**< 4/14.17.8 & 4/14.9.8 */ + udf_Uint32_t record_len; /**< 4/14.17.9 & 4/14.9.9 */ + udf_Uint64_t info_len; /**< 4/14.17.10 & 4/14.9.10 */ + udf_Uint64_t object_size; /**< 4/14.17.11 */ + udf_Uint64_t logblks_recorded; /**< 4/14.17.12 & 4/14.9.11 */ + udf_timestamp_t access_time; /**< 4/14.17.13 & 4/14.9.12 - last + access to any stream of file */ + udf_timestamp_t modification_time; /**< 4/14.17.14 & 4/14.9.13 - last + modification to any stream of + file*/ + udf_timestamp_t create_time; /**< 4/14.17.15 */ + udf_timestamp_t attribute_time; /**< 4/14.17.16 & 4/14.9.14 - + most recent create or modify + time */ + udf_Uint32_t checkpoint; + udf_Uint32_t reserved; /**< #00 bytes */ + udf_long_ad_t ext_attr_ICB; + udf_long_ad_t stream_directory_ICB; + udf_regid_t imp_id; + udf_Uint64_t unique_ID; + udf_Uint32_t length_extended_attr; + udf_Uint32_t length_alloc_descs; + udf_Uint8_t ext_attr[0]; + udf_Uint8_t alloc_descs[0]; +} GNUC_PACKED; + +PRAGMA_END_PACKED + +/** The below variables are trickery to force the above enum symbol + values to be recorded in debug symbol tables. They are used to + allow one refer to the enumeration value names in the typedefs + above in a debugger and in debugger expressions. +*/ +extern tag_id_t debug_tagid; +extern file_characteristics_t debug_file_characteristics; +extern icbtag_file_type_enum_t debug_icbtag_file_type_enum; +extern icbtag_flag_enum_t debug_flag_enum; +extern ecma_167_enum1_t debug_ecma_167_enum1; +extern ecma_167_timezone_enum_t debug_ecma_167_timezone_enum; + +#endif /* _ECMA_167_H */ diff --git a/include/cdio/iso9660.h b/include/cdio/iso9660.h new file mode 100644 index 00000000..ffad587a --- /dev/null +++ b/include/cdio/iso9660.h @@ -0,0 +1,1114 @@ +/* + $Id: iso9660.h,v 1.102 2008/07/16 00:28:54 rocky Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 + Rocky Bernstein + Copyright (C) 2000 Herbert Valerio Riedel + + See also iso9660.h by Eric Youngdale (1993). + + Copyright 1993 Yggdrasil Computing, Incorporated + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/*! + * \file iso9660.h + * + * \brief The top-level interface header for libiso9660: the ISO-9660 + * filesystem library; applications include this. + * + * See also the ISO-9660 specification. The freely available European + * equivalant standard is called ECMA-119. +*/ + + +#ifndef __CDIO_ISO9660_H__ +#define __CDIO_ISO9660_H__ + +#include + +#include +#include +#include + +/** \brief ISO 9660 Integer and Character types + +These are described in the section 7 of the ISO 9660 (or ECMA 119) +specification. +*/ + +typedef uint8_t iso711_t; /*! See section 7.1.1 */ +typedef int8_t iso712_t; /*! See section 7.1.2 */ +typedef uint16_t iso721_t; /*! See section 7.2.1 */ +typedef uint16_t iso722_t; /*! See section 7.2.2 */ +typedef uint32_t iso723_t; /*! See section 7.2.3 */ +typedef uint32_t iso731_t; /*! See section 7.3.1 */ +typedef uint32_t iso732_t; /*! See section 7.3.2 */ +typedef uint64_t iso733_t; /*! See section 7.3.3 */ + +typedef char achar_t; /*! See section 7.4.1 */ +typedef char dchar_t; /*! See section 7.4.1 */ + +#ifndef EMPTY_ARRAY_SIZE +#define EMPTY_ARRAY_SIZE 0 +#endif + +#include +#include + +#ifdef ISODCL +#undef ISODCL +#endif +/* This part borrowed from the bsd386 isofs */ +#define ISODCL(from, to) ((to) - (from) + 1) + +#define MIN_TRACK_SIZE 4*75 +#define MIN_ISO_SIZE MIN_TRACK_SIZE + +/*! The below isn't really an enumeration one would really use in a + program; things are done this way so that in a debugger one can to + refer to the enumeration value names such as in a debugger + expression and get something. With the more common a \#define + mechanism, the name/value assocation is lost at run time. + */ +extern enum iso_enum1_s { + ISO_PVD_SECTOR = 16, /**< Sector of Primary Volume Descriptor. */ + ISO_EVD_SECTOR = 17, /**< Sector of End Volume Descriptor. */ + LEN_ISONAME = 31, /**< Size in bytes of the filename + portion + null byte. */ + ISO_MAX_SYSTEM_ID = 32, /**< Maximum number of characters in a system + id. */ + MAX_ISONAME = 37, /**< Size in bytes of the filename + portion + null byte. */ + ISO_MAX_PREPARER_ID = 128, /**< Maximum number of characters in a + preparer id. */ + MAX_ISOPATHNAME = 255, /**< Maximum number of characters in the + entire ISO 9660 filename. */ + ISO_BLOCKSIZE = 2048 /**< Number of bytes in an ISO 9660 block. */ + +} iso_enums1; + +/*! An enumeration for some of the ISO_* #defines below. This isn't + really an enumeration one would really use in a program it is here + to be helpful in debuggers where wants just to refer to the + ISO_*_ names and get something. + */ + +/**! ISO 9660 directory flags. */ +extern enum iso_flag_enum_s { + ISO_FILE = 0, /**< Not really a flag... */ + ISO_EXISTENCE = 1, /**< Do not make existence known (hidden) */ + ISO_DIRECTORY = 2, /**< This file is a directory */ + ISO_ASSOCIATED = 4, /**< This file is an associated file */ + ISO_RECORD = 8, /**< Record format in extended attr. != 0 */ + ISO_PROTECTION = 16, /**< No read/execute perm. in ext. attr. */ + ISO_DRESERVED1 = 32, /**<, Reserved bit 5 */ + ISO_DRESERVED2 = 64, /**<, Reserved bit 6 */ + ISO_MULTIEXTENT = 128, /**< Not final entry of a mult. ext. file */ +} iso_flag_enums; + +/**! Volume descriptor types */ +extern enum iso_vd_enum_s { + ISO_VD_BOOT_RECORD = 0, /**< CD is bootable */ + ISO_VD_PRIMARY = 1, /**< Is in any ISO-9660 */ + ISO_VD_SUPPLEMENTARY = 2, /**< Used by Joliet, for example */ + ISO_VD_PARITION = 3, /**< Indicates a partition of a CD */ + ISO_VD_END = 255 +} iso_vd_enums; + + +/*! + An ISO filename is: "abcde.eee;1" -> '.' ';' + + For ISO-9660 Level 1, the maximum needed string length is: + +@code + 30 chars (filename + ext) + + 2 chars ('.' + ';') + + 5 chars (strlen("32767")) + + 1 null byte + ================================ + = 38 chars +@endcode + +*/ + +/*! \brief Maximum number of characters in a publisher id. */ +#define ISO_MAX_PUBLISHER_ID 128 + +/*! \brief Maximum number of characters in an application id. */ +#define ISO_MAX_APPLICATION_ID 128 + +/*! \brief Maximum number of characters in a volume id. */ +#define ISO_MAX_VOLUME_ID 32 + +/*! \brief Maximum number of characters in a volume-set id. */ +#define ISO_MAX_VOLUMESET_ID 128 + +/*! String inside frame which identifies an ISO 9660 filesystem. This + string is the "id" field of an iso9660_pvd_t or an iso9660_svd_t. +*/ +extern const char ISO_STANDARD_ID[sizeof("CD001")-1]; + +#define ISO_STANDARD_ID "CD001" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef enum strncpy_pad_check { + ISO9660_NOCHECK = 0, + ISO9660_7BIT, + ISO9660_ACHARS, + ISO9660_DCHARS +} strncpy_pad_check_t; + +PRAGMA_BEGIN_PACKED + +/*! + \brief ISO-9660 shorter-format time structure. See ECMA 9.1.5. + + @see iso9660_dtime + */ +struct iso9660_dtime_s { + iso711_t dt_year; /**< Number of years since 1900 */ + iso711_t dt_month; /**< Has value in range 1..12. Note starts + at 1, not 0 like a tm struct. */ + iso711_t dt_day; /**< Day of the month from 1 to 31 */ + iso711_t dt_hour; /**< Hour of the day from 0 to 23 */ + iso711_t dt_minute; /**< Minute of the hour from 0 to 59 */ + iso711_t dt_second; /**< Second of the minute from 0 to 59 */ + iso712_t dt_gmtoff; /**< GMT values -48 .. + 52 in 15 minute + intervals */ +} GNUC_PACKED; + +typedef struct iso9660_dtime_s iso9660_dtime_t; + +/*! + \brief ISO-9660 longer-format time structure. + + Section 8.4.26.1 of ECMA 119. All values are encoded as character + arrays, eg. '1', '9', '5', '5' for the year 1955 (no null terminated + byte). + + @see iso9660_ltime + */ +struct iso9660_ltime_s { + char lt_year [ISODCL( 1, 4)]; /**< Add 1900 to value + for the Julian + year */ + char lt_month [ISODCL( 5, 6)]; /**< Has value in range + 1..12. Note starts + at 1, not 0 like a + tm struct. */ + char lt_day [ISODCL( 7, 8)]; /**< Day of month: 1..31 */ + char lt_hour [ISODCL( 9, 10)]; /**< hour: 0..23 */ + char lt_minute [ISODCL( 11, 12)]; /**< minute: 0..59 */ + char lt_second [ISODCL( 13, 14)]; /**< second: 0..59 */ + char lt_hsecond [ISODCL( 15, 16)]; /**< The value is in + units of 1/100's of + a second */ + iso712_t lt_gmtoff; /**< Offset from Greenwich Mean Time in number + of 15 min intervals from -48 (West) to +52 + (East) recorded according to 7.1.2 numerical + value */ +} GNUC_PACKED; + +typedef struct iso9660_ltime_s iso9660_ltime_t; +typedef struct iso9660_dir_s iso9660_dir_t; +typedef struct iso9660_stat_s iso9660_stat_t; + +#include + +/*! \brief Format of an ISO-9660 directory record + + Section 9.1 of ECMA 119. + + This structure may have an odd length depending on how many + characters there are in the filename! Some compilers (e.g. on + Sun3/mc68020) pad the structures to an even length. For this reason, + we cannot use sizeof (struct iso_path_table) or sizeof (struct + iso_directory_record) to compute on disk sizes. Instead, we use + offsetof(..., name) and add the name size. See mkisofs.h of the + cdrtools package. + + @see iso9660_stat +*/ +struct iso9660_dir_s { + iso711_t length; /*! Length of Directory record (9.1.1) */ + iso711_t xa_length; /*! XA length if XA is used. Otherwise + zero. (9.1.2) */ + iso733_t extent; /*! LBA of first local block allocated + to the extent */ + iso733_t size; /*! data length of File Section. This + does not include the length of + any XA Records. (9.1.2) */ + iso9660_dtime_t recording_time; /*! Recording date and time (9.1.3) */ + uint8_t file_flags; /*! If no XA then zero. If a directory, + then bits 2,3 and 7 are zero. + (9.1.6) */ + iso711_t file_unit_size; /*! File Unit size for the File + Section if the File Section + is recorded in interleaved + mode. Otherwise zero. (9.1.7) */ + iso711_t interleave_gap; /*! Interleave Gap size for the + File Section if the File + Section is interleaved. Otherwise + zero. (9.1.8) */ + iso723_t volume_sequence_number; /*! Ordinal number of the volume + in the Volume Set on which + the Extent described by this + Directory Record is + recorded. (9.1.9) */ + iso711_t filename_len; /*! number of bytes in filename field */ + char filename[EMPTY_ARRAY_SIZE]; +} GNUC_PACKED; + +/*! + \brief ISO-9660 Primary Volume Descriptor. + */ +struct iso9660_pvd_s { + iso711_t type; /**< ISO_VD_PRIMARY - 1 */ + char id[5]; /**< ISO_STANDARD_ID "CD001" + */ + iso711_t version; /**< value 1 for ECMA 119 */ + char unused1[1]; /**< unused - value 0 */ + achar_t system_id[ISO_MAX_SYSTEM_ID]; /**< each char is an achar */ + dchar_t volume_id[ISO_MAX_VOLUME_ID]; /**< each char is a dchar */ + uint8_t unused2[8]; /**< unused - value 0 */ + iso733_t volume_space_size; /**< total number of + sectors */ + uint8_t unused3[32]; /**< unused - value 0 */ + iso723_t volume_set_size; /**< often 1 */ + iso723_t volume_sequence_number; /**< often 1 */ + iso723_t logical_block_size; /**< sector size, e.g. 2048 */ + iso733_t path_table_size; /**< bytes in path table */ + iso731_t type_l_path_table; /**< first sector of L Path + Table */ + iso731_t opt_type_l_path_table; /**< first sector of optional + L Path Table */ + iso732_t type_m_path_table; /**< first sector of M Path + table */ + iso732_t opt_type_m_path_table; /**< first sector of optional + M Path table */ + iso9660_dir_t root_directory_record; /**< See 8.4.18 and + section 9.1 of + ISO 9660 spec. */ + char root_directory_filename; /**< Is '\\0' or root + directory. Also pads previous + field to 34 bytes */ + dchar_t volume_set_id[ISO_MAX_VOLUMESET_ID]; /**< Volume Set of + which the volume is + a member. See + section 8.4.19 */ + achar_t publisher_id[ISO_MAX_PUBLISHER_ID]; /**< Publisher of + volume. If the first + character is '_' 0x5F, + the remaining bytes + specify a file + containing the user. + If all bytes are " " + (0x20) no publisher + is specified. See + section 8.4.20 of + ECMA 119 */ + achar_t preparer_id[ISO_MAX_PREPARER_ID]; /**< preparer of + volume. If the first + character is '_' 0x5F, + the remaining bytes + specify a file + containing the user. + If all bytes are " " + (0x20) no preparer + is specified. + See section 8.4.21 + of ECMA 119 */ + achar_t application_id[ISO_MAX_APPLICATION_ID]; /**< application + use to create the + volume. If the first + character is '_' 0x5F, + the remaining bytes + specify a file + containing the user. + If all bytes are " " + (0x20) no application + is specified. + See section of 8.4.22 + of ECMA 119 */ + dchar_t copyright_file_id[37]; /**< Name of file for + copyright info. If + all bytes are " " + (0x20), then no file + is identified. See + section 8.4.23 of ECMA 119 + 9660 spec. */ + dchar_t abstract_file_id[37]; /**< See section 8.4.24 of + ECMA 119. */ + dchar_t bibliographic_file_id[37]; /**< See section 7.5 of + ISO 9660 spec. */ + iso9660_ltime_t creation_date; /**< date and time of volume + creation. See section 8.4.26.1 + of the ISO 9660 spec. */ + iso9660_ltime_t modification_date; /**< date and time of the most + recent modification. + See section 8.4.27 of the + ISO 9660 spec. */ + iso9660_ltime_t expiration_date; /**< date and time when volume + expires. See section 8.4.28 + of the ISO 9660 spec. */ + iso9660_ltime_t effective_date; /**< date and time when volume + is effective. See section + 8.4.29 of the ISO 9660 + spec. */ + iso711_t file_structure_version; /**< value 1 for ECMA 119 */ + uint8_t unused4[1]; /**< unused - value 0 */ + char application_data[512]; /**< Application can put + whatever it wants here. */ + uint8_t unused5[653]; /**< Unused - value 0 */ +} GNUC_PACKED; + +typedef struct iso9660_pvd_s iso9660_pvd_t; + +/*! + \brief ISO-9660 Supplementary Volume Descriptor. + + This is used for Joliet Extentions and is almost the same as the + the primary descriptor but two unused fields, "unused1" and "unused3 + become "flags and "escape_sequences" respectively. +*/ +struct iso9660_svd_s { + iso711_t type; /**< ISO_VD_SUPPLEMENTARY - 2 + */ + char id[5]; /**< ISO_STANDARD_ID "CD001" + */ + iso711_t version; /**< value 1 */ + char flags; /**< Section 8.5.3 */ + achar_t system_id[ISO_MAX_SYSTEM_ID]; /**< Section 8.5.4; each char + is an achar */ + dchar_t volume_id[ISO_MAX_VOLUME_ID]; /**< Section 8.5.5; each char + is a dchar */ + char unused2[8]; + iso733_t volume_space_size; /**< total number of + sectors */ + char escape_sequences[32]; /**< Section 8.5.6 */ + iso723_t volume_set_size; /**< often 1 */ + iso723_t volume_sequence_number; /**< often 1 */ + iso723_t logical_block_size; /**< sector size, e.g. 2048 */ + iso733_t path_table_size; /**< 8.5.7; bytes in path + table */ + iso731_t type_l_path_table; /**< 8.5.8; first sector of + little-endian path table */ + iso731_t opt_type_l_path_table; /**< 8.5.9; first sector of + optional little-endian + path table */ + iso732_t type_m_path_table; /**< 8.5.10; first sector of + big-endian path table */ + iso732_t opt_type_m_path_table; /**< 8.5.11; first sector of + optional big-endian path + table */ + iso9660_dir_t root_directory_record; /**< See section 8.5.12 and + 9.1 of ISO 9660 spec. */ + char root_directory_filename; /**< Is '\\0' or root + directory. Also pads previous + field to 34 bytes */ + dchar_t volume_set_id[ISO_MAX_VOLUMESET_ID]; /**< 8.5.13; + dchars */ + achar_t publisher_id[ISO_MAX_PUBLISHER_ID]; /**< + Publisher of volume. + If the first char- + aracter is '_' 0x5F, + the remaining bytes + specify a file + containing the user. + If all bytes are " " + (0x20) no publisher + is specified. See + section 8.5.14 of + ECMA 119 */ + achar_t preparer_id[ISO_MAX_PREPARER_ID]; /**< + Data preparer of + volume. If the first + character is '_' 0x5F, + the remaining bytes + specify a file + containing the user. + If all bytes are " " + (0x20) no preparer + is specified. + See section 8.5.15 + of ECMA 119 */ + achar_t application_id[ISO_MAX_APPLICATION_ID]; /**< application + use to create the + volume. If the first + character is '_' 0x5F, + the remaining bytes + specify a file + containing the user. + If all bytes are " " + (0x20) no application + is specified. + See section of 8.5.16 + of ECMA 119 */ + dchar_t copyright_file_id[37]; /**< Name of file for + copyright info. If + all bytes are " " + (0x20), then no file + is identified. See + section 8.5.17 of ECMA 119 + 9660 spec. */ + dchar_t abstract_file_id[37]; /**< See section 8.5.18 of + ECMA 119. */ + dchar_t bibliographic_file_id[37]; /**< See section 8.5.19 of + ECMA 119. */ + iso9660_ltime_t creation_date; /**< date and time of volume + creation. See section 8.4.26.1 + of the ECMA 119 spec. */ + iso9660_ltime_t modification_date; /**< date and time of the most + recent modification. + See section 8.4.27 of the + ECMA 119 spec. */ + iso9660_ltime_t expiration_date; /**< date and time when volume + expires. See section 8.4.28 + of the ECMA 119 spec. */ + iso9660_ltime_t effective_date; /**< date and time when volume + is effective. See section + 8.4.29 of the ECMA 119 + spec. */ + iso711_t file_structure_version; /**< value 1 for ECMA 119 */ + uint8_t unused4[1]; /**< unused - value 0 */ + char application_data[512]; /**< 8.5.20 Application can put + whatever it wants here. */ + uint8_t unused5[653]; /**< Unused - value 0 */ +} GNUC_PACKED; + +typedef struct iso9660_svd_s iso9660_svd_t; + +PRAGMA_END_PACKED + +/*! \brief Unix stat-like version of iso9660_dir + + The iso9660_stat structure is not part of the ISO-9660 + specification. We use it for our to communicate information + in a C-library friendly way, e.g struct tm time structures and + a C-style filename string. + + @see iso9660_dir +*/ +struct iso9660_stat_s { /* big endian!! */ + + iso_rock_statbuf_t rr; /**< Rock Ridge-specific fields */ + + struct tm tm; /**< time on entry - FIXME merge with + one of entries above, like ctime? */ + lsn_t lsn; /**< start logical sector number */ + uint32_t size; /**< total size in bytes */ + uint32_t secsize; /**< number of sectors allocated */ + iso9660_xa_t xa; /**< XA attributes */ + enum { _STAT_FILE = 1, _STAT_DIR = 2 } type; + bool b_xa; + char filename[EMPTY_ARRAY_SIZE]; /**< filename */ +}; + +/** A mask used in iso9660_ifs_read_vd which allows what kinds + of extensions we allow, eg. Joliet, Rock Ridge, etc. */ +typedef uint8_t iso_extension_mask_t; + +/*! An enumeration for some of the ISO_EXTENSION_* \#defines below. This isn't + really an enumeration one would really use in a program it is here + to be helpful in debuggers where wants just to refer to the + ISO_EXTENSION_*_ names and get something. + */ +extern enum iso_extension_enum_s { + ISO_EXTENSION_JOLIET_LEVEL1 = 0x01, + ISO_EXTENSION_JOLIET_LEVEL2 = 0x02, + ISO_EXTENSION_JOLIET_LEVEL3 = 0x04, + ISO_EXTENSION_ROCK_RIDGE = 0x08, + ISO_EXTENSION_HIGH_SIERRA = 0x10 +} iso_extension_enums; + + +#define ISO_EXTENSION_ALL 0xFF +#define ISO_EXTENSION_NONE 0x00 +#define ISO_EXTENSION_JOLIET \ + (ISO_EXTENSION_JOLIET_LEVEL1 | \ + ISO_EXTENSION_JOLIET_LEVEL2 | \ + ISO_EXTENSION_JOLIET_LEVEL3 ) + + +/** This is an opaque structure. */ +typedef struct _iso9660_s iso9660_t; + + /*! Close previously opened ISO 9660 image and free resources + associated with the image. Call this when done using using an ISO + 9660 image. + + @return true is unconditionally returned. If there was an error + false would be returned. + */ + bool iso9660_close (iso9660_t * p_iso); + + + /*! + Open an ISO 9660 image for reading. Maybe in the future we will have + a mode. NULL is returned on error. + */ + iso9660_t *iso9660_open (const char *psz_path /*flags, mode */); + + /*! + Open an ISO 9660 image for reading allowing various ISO 9660 + extensions. Maybe in the future we will have a mode. NULL is + returned on error. + + @see iso9660_open_fuzzy + */ + iso9660_t *iso9660_open_ext (const char *psz_path, + iso_extension_mask_t iso_extension_mask); + + /*! Open an ISO 9660 image for "fuzzy" reading. This means that we + will try to guess various internal offset based on internal + checks. This may be useful when trying to read an ISO 9660 image + contained in a file format that libiso9660 doesn't know natively + (or knows imperfectly.) + + Some tolerence allowed for positioning the ISO 9660 image. We scan + for STANDARD_ID and use that to set the eventual offset to adjust + by (as long as that is <= i_fuzz). + + Maybe in the future we will have a mode. NULL is returned on error. + + @see iso9660_open, @see iso9660_fuzzy_ext + */ + iso9660_t *iso9660_open_fuzzy (const char *psz_path /*flags, mode */, + uint16_t i_fuzz); + + /*! + Open an ISO 9660 image for reading with some tolerence for positioning + of the ISO9660 image. We scan for ISO_STANDARD_ID and use that to set + the eventual offset to adjust by (as long as that is <= i_fuzz). + + Maybe in the future we will have a mode. NULL is returned on error. + + @see iso9660_open_ext @see iso9660_open_fuzzy + */ + iso9660_t *iso9660_open_fuzzy_ext (const char *psz_path, + iso_extension_mask_t iso_extension_mask, + uint16_t i_fuzz + /*flags, mode */); + + /*! + Read the Super block of an ISO 9660 image but determine framesize + and datastart and a possible additional offset. Generally here we are + not reading an ISO 9660 image but a CD-Image which contains an ISO 9660 + filesystem. + */ + bool iso9660_ifs_fuzzy_read_superblock (iso9660_t *p_iso, + iso_extension_mask_t iso_extension_mask, + uint16_t i_fuzz); + + /*! + Seek to a position and then read i_size blocks. + + @param p_iso the ISO-9660 file image to get data from + + @param ptr place to put returned data. It should be able to store + a least i_size bytes + + @param start location to start reading from + + @param i_size number of blocks to read. Each block is ISO_BLOCKSIZE bytes + long. + + @return number of bytes (not blocks) read + + */ + long int iso9660_iso_seek_read (const iso9660_t *p_iso, /*out*/ void *ptr, + lsn_t start, long int i_size); + + /*! + Read the Primary Volume Descriptor for a CD. + True is returned if read, and false if there was an error. + */ + bool iso9660_fs_read_pvd ( const CdIo_t *p_cdio, + /*out*/ iso9660_pvd_t *p_pvd ); + + /*! + Read the Primary Volume Descriptor for an ISO 9660 image. + True is returned if read, and false if there was an error. + */ + bool iso9660_ifs_read_pvd (const iso9660_t *p_iso, + /*out*/ iso9660_pvd_t *p_pvd); + +/*! + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental Volume + Descriptor if (Joliet) extensions are acceptable. +*/ + bool iso9660_fs_read_superblock (CdIo_t *p_cdio, + iso_extension_mask_t iso_extension_mask); + + /*! + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental Volume + Descriptor if (Joliet) extensions are acceptable. + */ + bool iso9660_ifs_read_superblock (iso9660_t *p_iso, + iso_extension_mask_t iso_extension_mask); + + +/*==================================================== + Time conversion + ====================================================*/ +/*! + Set time in format used in ISO 9660 directory index record + from a Unix time structure. */ + void iso9660_set_dtime (const struct tm *tm, + /*out*/ iso9660_dtime_t *idr_date); + + + /*! + Set "long" time in format used in ISO 9660 primary volume descriptor + from a Unix time structure. */ + void iso9660_set_ltime (const struct tm *_tm, + /*out*/ iso9660_ltime_t *p_pvd_date); + + /*! + Get Unix time structure from format use in an ISO 9660 directory index + record. Even though tm_wday and tm_yday fields are not explicitly in + idr_date, they are calculated from the other fields. + + If tm is to reflect the localtime, set "b_localtime" true, otherwise + tm will reported in GMT. + */ + bool iso9660_get_dtime (const iso9660_dtime_t *idr_date, bool b_localtime, + /*out*/ struct tm *tm); + + + /*! + Get "long" time in format used in ISO 9660 primary volume descriptor + from a Unix time structure. + */ + bool iso9660_get_ltime (const iso9660_ltime_t *p_ldate, + /*out*/ struct tm *p_tm); + + /*==================================================== + Character Classification and String Manipulation + ====================================================*/ + /*! + Return true if c is a DCHAR - a character that can appear in an an + ISO-9600 level 1 directory name. These are the ASCII capital + letters A-Z, the digits 0-9 and an underscore. + */ + bool iso9660_is_dchar (int c); + + /*! + Return true if c is an ACHAR - + These are the DCHAR's plus some ASCII symbols including the space + symbol. + */ + bool iso9660_is_achar (int c); + + /*! + Convert an ISO-9660 file name which is in the format usually stored + in a ISO 9660 directory entry into what's usually listed as the + file name in a listing. Lowercase name, and remove trailing ;1's + or .;1's and turn the other ;'s into version numbers. + + @param psz_oldname the ISO-9660 filename to be translated. + @param psz_newname returned string. The caller allocates this and + it should be at least the size of psz_oldname. + @return length of the translated string is returned. + */ + int iso9660_name_translate(const char *psz_oldname, + /*out*/ char *psz_newname); + + /*! + Convert an ISO-9660 file name which is in the format usually stored + in a ISO 9660 directory entry into what's usually listed as the + file name in a listing. Lowercase name if no Joliet Extension + interpretation. Remove trailing ;1's or .;1's and turn the other + ;'s into version numbers. + + @param psz_oldname the ISO-9660 filename to be translated. + @param psz_newname returned string. The caller allocates this and + it should be at least the size of psz_oldname. + @param i_joliet_level 0 if not using Joliet Extension. Otherwise the + Joliet level. + @return length of the translated string is returned. It will be no greater + than the length of psz_oldname. + */ + int iso9660_name_translate_ext(const char *psz_oldname, char *psz_newname, + uint8_t i_joliet_level); + + /*! + Pad string src with spaces to size len and copy this to dst. If + len is less than the length of src, dst will be truncated to the + first len characters of src. + + src can also be scanned to see if it contains only ACHARs, DCHARs, + 7-bit ASCII chars depending on the enumeration _check. + + In addition to getting changed, dst is the return value. + Note: this string might not be NULL terminated. + */ + char *iso9660_strncpy_pad(char dst[], const char src[], size_t len, + enum strncpy_pad_check _check); + + /*===================================================================== + File and Directory Names + ======================================================================*/ + + /*! + Check that psz_path is a valid ISO-9660 directory name. + + A valid directory name should not start out with a slash (/), + dot (.) or null byte, should be less than 37 characters long, + have no more than 8 characters in a directory component + which is separated by a /, and consist of only DCHARs. + + True is returned if psz_path is valid. + */ + bool iso9660_dirname_valid_p (const char psz_path[]); + + /*! + Take psz_path and a version number and turn that into a ISO-9660 + pathname. (That's just the pathname followd by ";" and the version + number. For example, mydir/file.ext -> MYDIR/FILE.EXT;1 for version + 1. The resulting ISO-9660 pathname is returned. + */ + char *iso9660_pathname_isofy (const char psz_path[], uint16_t i_version); + + /*! + Check that psz_path is a valid ISO-9660 pathname. + + A valid pathname contains a valid directory name, if one appears and + the filename portion should be no more than 8 characters for the + file prefix and 3 characters in the extension (or portion after a + dot). There should be exactly one dot somewhere in the filename + portion and the filename should be composed of only DCHARs. + + True is returned if psz_path is valid. + */ + bool iso9660_pathname_valid_p (const char psz_path[]); + +/*===================================================================== + directory tree +======================================================================*/ + +void +iso9660_dir_init_new (void *dir, uint32_t self, uint32_t ssize, + uint32_t parent, uint32_t psize, + const time_t *dir_time); + +void +iso9660_dir_init_new_su (void *dir, uint32_t self, uint32_t ssize, + const void *ssu_data, unsigned int ssu_size, + uint32_t parent, uint32_t psize, + const void *psu_data, unsigned int psu_size, + const time_t *dir_time); + +void +iso9660_dir_add_entry_su (void *dir, const char filename[], uint32_t extent, + uint32_t size, uint8_t file_flags, + const void *su_data, + unsigned int su_size, const time_t *entry_time); + +unsigned int +iso9660_dir_calc_record_size (unsigned int namelen, unsigned int su_len); + +/*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + @return stat_t of entry if we found lsn, or NULL otherwise. + Caller must free return value. + */ +#define iso9660_fs_find_lsn iso9660_find_fs_lsn +iso9660_stat_t *iso9660_fs_find_lsn(CdIo_t *p_cdio, lsn_t i_lsn); + + +/*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + @return stat_t of entry if we found lsn, or NULL otherwise. + Caller must free return value. + */ +iso9660_stat_t *iso9660_fs_find_lsn_with_path(CdIo_t *p_cdio, lsn_t i_lsn, + /*out*/ char **ppsz_path); + +/*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + @return stat_t of entry if we found lsn, or NULL otherwise. + Caller must free return value. + */ +iso9660_stat_t *iso9660_ifs_find_lsn(iso9660_t *p_iso, lsn_t i_lsn); + + +/*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + @param p_iso pointer to iso_t + @param i_lsn LSN to find + @param ppsz_path full path of lsn filename. On entry *ppsz_path should be + NULL. On return it will be allocated an point to the full path of the + file at lsn or NULL if the lsn is not found. You should deallocate + *ppsz_path when you are done using it. + + @return stat_t of entry if we found lsn, or NULL otherwise. + Caller must free return value. + */ +iso9660_stat_t *iso9660_ifs_find_lsn_with_path(iso9660_t *p_iso, + lsn_t i_lsn, + /*out*/ char **ppsz_path); + + +/*! + Return file status for psz_path. NULL is returned on error. + + @param p_cdio the CD object to read from + + @param psz_path filename path to look up and get information about + + @return ISO 9660 file information + + Important note: + + You make get different results looking up "/" versus "/." and the + latter may give more complete information. "/" will take information + from the PVD only, whereas "/." will force a directory read of "/" and + find "." and in that Rock-Ridge information might be found which fills + in more stat information. Ideally iso9660_fs_stat should be fixed. + Patches anyone? + */ +iso9660_stat_t *iso9660_fs_stat (CdIo_t *p_cdio, const char psz_path[]); + + +/*! + Return file status for path name psz_path. NULL is returned on error. + pathname version numbers in the ISO 9660 name are dropped, i.e. ;1 + is removed and if level 1 ISO-9660 names are lowercased. + + b_mode2 is historical. It is not used. + */ +iso9660_stat_t *iso9660_fs_stat_translate (CdIo_t *p_cdio, + const char psz_path[], + bool b_mode2); + +/*! + Return file status for pathname. NULL is returned on error. + */ +iso9660_stat_t *iso9660_ifs_stat (iso9660_t *p_iso, const char psz_path[]); + + +/*! Return file status for path name psz_path. NULL is returned on + error. pathname version numbers in the ISO 9660 name are dropped, + i.e. ;1 is removed and if level 1 ISO-9660 names are lowercased. + */ +iso9660_stat_t *iso9660_ifs_stat_translate (iso9660_t *p_iso, + const char psz_path[]); + +/*! Read psz_path (a directory) and return a list of iso9660_stat_t + pointers for the files inside that directory. The caller must free the + returned result. + + b_mode2 is historical. It is not used. +*/ +CdioList_t * iso9660_fs_readdir (CdIo_t *p_cdio, const char psz_path[], + bool b_mode2); + +/*! Read psz_path (a directory) and return a list of iso9660_stat_t + pointers for the files inside that directory. The caller must free + the returned result. +*/ +CdioList_t * iso9660_ifs_readdir (iso9660_t *p_iso, const char psz_path[]); + +/*! + Return the PVD's application ID. + NULL is returned if there is some problem in getting this. +*/ +char * iso9660_get_application_id(iso9660_pvd_t *p_pvd); + +/*! + Get the application ID. psz_app_id is set to NULL if there + is some problem in getting this and false is returned. +*/ +bool iso9660_ifs_get_application_id(iso9660_t *p_iso, + /*out*/ cdio_utf8_t **p_psz_app_id); + +/*! + Return the Joliet level recognized for p_iso. +*/ +uint8_t iso9660_ifs_get_joliet_level(iso9660_t *p_iso); + +uint8_t iso9660_get_dir_len(const iso9660_dir_t *p_idr); + +#if FIXME +uint8_t iso9660_get_dir_size(const iso9660_dir_t *p_idr); + +lsn_t iso9660_get_dir_extent(const iso9660_dir_t *p_idr); +#endif + + /*! + Return the directory name stored in the iso9660_dir_t + + A string is allocated: the caller must deallocate. This routine + can return NULL if memory allocation fails. + */ + char * iso9660_dir_to_name (const iso9660_dir_t *p_iso9660_dir); + + /*! + Returns a POSIX mode for a given p_iso_dirent. + */ + mode_t iso9660_get_posix_filemode(const iso9660_stat_t *p_iso_dirent); + + /*! + Return a string containing the preparer id with trailing + blanks removed. + */ + char *iso9660_get_preparer_id(const iso9660_pvd_t *p_pvd); + + /*! + Get the preparer ID. psz_preparer_id is set to NULL if there + is some problem in getting this and false is returned. + */ + bool iso9660_ifs_get_preparer_id(iso9660_t *p_iso, + /*out*/ cdio_utf8_t **p_psz_preparer_id); + + /*! + Return a string containing the PVD's publisher id with trailing + blanks removed. + */ + char *iso9660_get_publisher_id(const iso9660_pvd_t *p_pvd); + + /*! + Get the publisher ID. psz_publisher_id is set to NULL if there + is some problem in getting this and false is returned. + */ + bool iso9660_ifs_get_publisher_id(iso9660_t *p_iso, + /*out*/ cdio_utf8_t **p_psz_publisher_id); + + uint8_t iso9660_get_pvd_type(const iso9660_pvd_t *p_pvd); + + const char * iso9660_get_pvd_id(const iso9660_pvd_t *p_pvd); + + int iso9660_get_pvd_space_size(const iso9660_pvd_t *p_pvd); + + int iso9660_get_pvd_block_size(const iso9660_pvd_t *p_pvd) ; + + /*! Return the primary volume id version number (of pvd). + If there is an error 0 is returned. + */ + int iso9660_get_pvd_version(const iso9660_pvd_t *pvd) ; + + /*! + Return a string containing the PVD's system id with trailing + blanks removed. + */ + char *iso9660_get_system_id(const iso9660_pvd_t *p_pvd); + + /*! + Get the system ID. psz_system_id is set to NULL if there + is some problem in getting this and false is returned. + */ + bool iso9660_ifs_get_system_id(iso9660_t *p_iso, + /*out*/ cdio_utf8_t **p_psz_system_id); + + + /*! Return the LSN of the root directory for pvd. + If there is an error CDIO_INVALID_LSN is returned. + */ + lsn_t iso9660_get_root_lsn(const iso9660_pvd_t *p_pvd); + + /*! + Get the volume ID in the PVD. psz_volume_id is set to NULL if there + is some problem in getting this and false is returned. + */ + char *iso9660_get_volume_id(const iso9660_pvd_t *p_pvd); + + /*! + Get the volume ID in the PVD. psz_volume_id is set to NULL if there + is some problem in getting this and false is returned. + */ + bool iso9660_ifs_get_volume_id(iso9660_t *p_iso, + /*out*/ cdio_utf8_t **p_psz_volume_id); + + /*! + Return the volumeset ID in the PVD. + NULL is returned if there is some problem in getting this. + */ + char *iso9660_get_volumeset_id(const iso9660_pvd_t *p_pvd); + + /*! + Get the volumeset ID. psz_systemset_id is set to NULL if there + is some problem in getting this and false is returned. + */ + bool iso9660_ifs_get_volumeset_id(iso9660_t *p_iso, + /*out*/ cdio_utf8_t **p_psz_volumeset_id); + + /* pathtable */ + + /*! Zero's out pathable. Do this first. */ + void iso9660_pathtable_init (void *pt); + + unsigned int iso9660_pathtable_get_size (const void *pt); + + uint16_t iso9660_pathtable_l_add_entry (void *pt, const char name[], + uint32_t extent, uint16_t parent); + + uint16_t iso9660_pathtable_m_add_entry (void *pt, const char name[], + uint32_t extent, uint16_t parent); + + /**===================================================================== + Volume Descriptors + ======================================================================*/ + + void iso9660_set_pvd (void *pd, const char volume_id[], + const char application_id[], + const char publisher_id[], const char preparer_id[], + uint32_t iso_size, const void *root_dir, + uint32_t path_table_l_extent, + uint32_t path_table_m_extent, + uint32_t path_table_size, const time_t *pvd_time); + + void iso9660_set_evd (void *pd); + + /*! + Return true if ISO 9660 image has extended attrributes (XA). + */ + bool iso9660_ifs_is_xa (const iso9660_t * p_iso); + + +#ifndef DO_NOT_WANT_COMPATIBILITY +/** For compatibility with < 0.77 */ +#define iso9660_isdchar iso9660_is_dchar +#define iso9660_isachar iso9660_is_achar +#endif /*DO_NOT_WANT_COMPATIBILITY*/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#undef ISODCL +#endif /* __CDIO_ISO9660_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/cdio/logging.h b/include/cdio/logging.h new file mode 100644 index 00000000..988d11c5 --- /dev/null +++ b/include/cdio/logging.h @@ -0,0 +1,136 @@ +/* + $Id: logging.h,v 1.11 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2003, 2004, 2008 Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file logging.h + * \brief Header to control logging and level of detail of output. + * + */ + +#ifndef __LOGGING_H__ +#define __LOGGING_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The different log levels supported. + */ +typedef enum { + CDIO_LOG_DEBUG = 1, /**< Debug-level messages - helps debug what's up. */ + CDIO_LOG_INFO, /**< Informational - indicates perhaps something of + interest. */ + CDIO_LOG_WARN, /**< Warning conditions - something that looks funny. */ + CDIO_LOG_ERROR, /**< Error conditions - may terminate program. */ + CDIO_LOG_ASSERT /**< Critical conditions - may abort program. */ +} cdio_log_level_t; + +/** + * The place to save the preference concerning how much verbosity + * is desired. This is used by the internal default log handler, but + * it could be use by applications which provide their own log handler. + */ +extern cdio_log_level_t cdio_loglevel_default; + +/** + * This type defines the signature of a log handler. For every + * message being logged, the handler will receive the log level and + * the message string. + * + * @see cdio_log_set_handler + * @see cdio_log_level_t + * + * @param level The log level. + * @param message The log message. + */ +typedef void (*cdio_log_handler_t) (cdio_log_level_t level, + const char message[]); + +/** + * Set a custom log handler for libcdio. The return value is the log + * handler being replaced. If the provided parameter is NULL, then + * the handler will be reset to the default handler. + * + * @see cdio_log_handler_t + * + * @param new_handler The new log handler. + * @return The previous log handler. + */ +cdio_log_handler_t cdio_log_set_handler (cdio_log_handler_t new_handler); + +/** + * Handle an message with the given log level. + * + * @see cdio_debug + * @see cdio_info + * @see cdio_warn + * @see cdio_error + + * @param level The log level. + * @param format printf-style format string + * @param ... remaining arguments needed by format string + */ +void cdio_log (cdio_log_level_t level, + const char format[], ...) GNUC_PRINTF(2, 3); + +/** + * Handle a debugging message. + * + * @see cdio_log for a more generic routine + */ +void cdio_debug (const char format[], ...) GNUC_PRINTF(1,2); + +/** + * Handle an informative message. + * + * @see cdio_log for a more generic routine + */ +void cdio_info (const char format[], ...) GNUC_PRINTF(1,2); + +/** + * Handle a warning message. + * + * @see cdio_log for a more generic routine + */ +void cdio_warn (const char format[], ...) GNUC_PRINTF(1,2); + +/** + * Handle an error message. Execution is terminated. + * + * @see cdio_log for a more generic routine. + */ +void cdio_error (const char format[], ...) GNUC_PRINTF(1,2); + +#ifdef __cplusplus +} +#endif + +#endif /* __LOGGING_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/cdio/mmc.h b/include/cdio/mmc.h new file mode 100644 index 00000000..c7a4eeaf --- /dev/null +++ b/include/cdio/mmc.h @@ -0,0 +1,907 @@ +/* + $Id: mmc.h,v 1.32 2008/05/09 06:13:32 edsdead Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * \file mmc.h + * + * \brief Common definitions for MMC (Multimedia Commands). Applications + * include this for direct MMC access. +*/ + +#ifndef __CDIO_MMC_H__ +#define __CDIO_MMC_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /** Set this to the maximum value in milliseconds that we will + wait on an MMC command. */ + extern uint32_t mmc_timeout_ms; + + /** The default timeout (non-read) is 6 seconds. */ +#define MMC_TIMEOUT_DEFAULT 6000 + + /** Set this to the maximum value in milliseconds that we will + wait on an MMC read command. */ + extern uint32_t mmc_read_timeout_ms; + + /** The default read timeout is 3 minutes. */ +#define MMC_READ_TIMEOUT_DEFAULT 3*60*1000 + + + /** \brief The opcode-portion (generic packet commands) of an MMC command. + + In general, those opcodes that end in 6 take a 6-byte command + descriptor, those that end in 10 take a 10-byte + descriptor and those that in in 12 take a 12-byte descriptor. + + (Not that you need to know that, but it seems to be a + big deal in the MMC specification.) + + */ + typedef enum { + CDIO_MMC_GPCMD_INQUIRY = 0x12, /**< Request drive + information. */ + CDIO_MMC_GPCMD_MODE_SELECT_6 = 0x15, /**< Select medium + (6 bytes). */ + CDIO_MMC_GPCMD_MODE_SENSE_6 = 0x1a, /**< Get medium or device + information. Should be issued + before MODE SELECT to get + mode support or save current + settings. (6 bytes). */ + CDIO_MMC_GPCMD_START_STOP = 0x1b, /**< Enable/disable Disc + operations. (6 bytes). */ + CDIO_MMC_GPCMD_ALLOW_MEDIUM_REMOVAL = 0x1e, /**< Enable/disable Disc + removal. (6 bytes). */ + + /** Group 2 Commands (CDB's here are 10-bytes) + */ + CDIO_MMC_GPCMD_READ_10 = 0x28, /**< Read data from drive + (10 bytes). */ + CDIO_MMC_GPCMD_READ_SUBCHANNEL = 0x42, /**< Read Sub-Channel data. + (10 bytes). */ + CDIO_MMC_GPCMD_READ_TOC = 0x43, /**< READ TOC/PMA/ATIP. + (10 bytes). */ + CDIO_MMC_GPCMD_READ_HEADER = 0x44, + CDIO_MMC_GPCMD_PLAY_AUDIO_10 = 0x45, /**< Begin audio playing at + current position + (10 bytes). */ + CDIO_MMC_GPCMD_GET_CONFIGURATION = 0x46, /**< Get drive Capabilities + (10 bytes) */ + CDIO_MMC_GPCMD_PLAY_AUDIO_MSF = 0x47, /**< Begin audio playing at + specified MSF (10 + bytes). */ + CDIO_MMC_GPCMD_PLAY_AUDIO_TI = 0x48, + CDIO_MMC_GPCMD_PLAY_TRACK_REL_10 = 0x49, /**< Play audio at the track + relative LBA. (10 bytes). + Doesn't seem to be part + of MMC standards but is + handled by Plextor drives. + */ + + CDIO_MMC_GPCMD_GET_EVENT_STATUS = 0x4a, /**< Report events and + Status. */ + CDIO_MMC_GPCMD_PAUSE_RESUME = 0x4b, /**< Stop or restart audio + playback. (10 bytes). + Used with a PLAY command. */ + + CDIO_MMC_GPCMD_READ_DISC_INFO = 0x51, /**< Get CD information. + (10 bytes). */ + CDIO_MMC_GPCMD_MODE_SELECT_10 = 0x55, /**< Select medium + (10-bytes). */ + CDIO_MMC_GPCMD_MODE_SENSE_10 = 0x5a, /**< Get medium or device + information. Should be issued + before MODE SELECT to get + mode support or save current + settings. (6 bytes). */ + + /** Group 5 Commands (CDB's here are 12-bytes) + */ + CDIO_MMC_GPCMD_PLAY_AUDIO_12 = 0xa5, /**< Begin audio playing at + current position + (12 bytes) */ + CDIO_MMC_GPCMD_LOAD_UNLOAD = 0xa6, /**< Load/unload a Disc + (12 bytes) */ + CDIO_MMC_GPCMD_READ_12 = 0xa8, /**< Read data from drive + (12 bytes). */ + CDIO_MMC_GPCMD_PLAY_TRACK_REL_12 = 0xa9, /**< Play audio at the track + relative LBA. (12 bytes). + Doesn't seem to be part + of MMC standards but is + handled by Plextor drives. + */ + CDIO_MMC_GPCMD_READ_DVD_STRUCTURE = 0xad, /**< Get DVD structure info + from media (12 bytes). */ + CDIO_MMC_GPCMD_READ_MSF = 0xb9, /**< Read almost any field + of a CD sector at specified + MSF. (12 bytes). */ + CDIO_MMC_GPCMD_SET_SPEED = 0xbb, /**< Set drive speed + (12 bytes). This is listed + as optional in ATAPI 2.6, + but is (curiously) + missing from Mt. Fuji, + Table 57. It is mentioned + in Mt. Fuji Table 377 as an + MMC command for SCSI + devices though... Most + ATAPI drives support it. */ + CDIO_MMC_GPCMD_READ_CD = 0xbe, /**< Read almost any field + of a CD sector at current + location. (12 bytes). */ + /** Vendor-unique Commands + */ + CDIO_MMC_GPCMD_CD_PLAYBACK_STATUS = 0xc4 /**< SONY unique = command */, + CDIO_MMC_GPCMD_PLAYBACK_CONTROL = 0xc9 /**< SONY unique = command */, + CDIO_MMC_GPCMD_READ_CDDA = 0xd8 /**< Vendor unique = command */, + CDIO_MMC_GPCMD_READ_CDXA = 0xdb /**< Vendor unique = command */, + CDIO_MMC_GPCMD_READ_ALL_SUBCODES = 0xdf /**< Vendor unique = command */ + } cdio_mmc_gpcmd_t; + + + /** Read Subchannel states */ + typedef enum { + CDIO_MMC_READ_SUB_ST_INVALID = 0x00, /**< audio status not supported */ + CDIO_MMC_READ_SUB_ST_PLAY = 0x11, /**< audio play operation in + progress */ + CDIO_MMC_READ_SUB_ST_PAUSED = 0x12, /**< audio play operation paused */ + CDIO_MMC_READ_SUB_ST_COMPLETED = 0x13, /**< audio play successfully + completed */ + CDIO_MMC_READ_SUB_ST_ERROR = 0x14, /**< audio play stopped due to + error */ + CDIO_MMC_READ_SUB_ST_NO_STATUS = 0x15, /**< no current audio status to + return */ + } cdio_mmc_read_sub_state_t; + + /** Level values that can go into READ_CD */ + typedef enum { + CDIO_MMC_READ_TYPE_ANY = 0, /**< All types */ + CDIO_MMC_READ_TYPE_CDDA = 1, /**< Only CD-DA sectors */ + CDIO_MMC_READ_TYPE_MODE1 = 2, /**< mode1 sectors (user data = 2048) */ + CDIO_MMC_READ_TYPE_MODE2 = 3, /**< mode2 sectors form1 or form2 */ + CDIO_MMC_READ_TYPE_M2F1 = 4, /**< mode2 sectors form1 */ + CDIO_MMC_READ_TYPE_M2F2 = 5 /**< mode2 sectors form2 */ + } cdio_mmc_read_cd_type_t; + + /** Format values for READ_TOC */ + typedef enum { + CDIO_MMC_READTOC_FMT_TOC = 0, + CDIO_MMC_READTOC_FMT_SESSION = 1, + CDIO_MMC_READTOC_FMT_FULTOC = 2, + CDIO_MMC_READTOC_FMT_PMA = 3, /**< Q subcode data */ + CDIO_MMC_READTOC_FMT_ATIP = 4, /**< includes media type */ + CDIO_MMC_READTOC_FMT_CDTEXT = 5 /**< CD-TEXT info */ + } cdio_mmc_readtoc_t; + +/** Page codes for MODE SENSE and MODE SET. */ + typedef enum { + CDIO_MMC_R_W_ERROR_PAGE = 0x01, + CDIO_MMC_WRITE_PARMS_PAGE = 0x05, + CDIO_MMC_CDR_PARMS_PAGE = 0x0d, + CDIO_MMC_AUDIO_CTL_PAGE = 0x0e, + CDIO_MMC_POWER_PAGE = 0x1a, + CDIO_MMC_FAULT_FAIL_PAGE = 0x1c, + CDIO_MMC_TO_PROTECT_PAGE = 0x1d, + CDIO_MMC_CAPABILITIES_PAGE = 0x2a, + CDIO_MMC_ALL_PAGES = 0x3f, + } cdio_mmc_mode_page_t; + + +PRAGMA_BEGIN_PACKED + struct mmc_audio_volume_entry_s + { + uint8_t selection; /* Only the lower 4 bits are used. */ + uint8_t volume; + } GNUC_PACKED; + + typedef struct mmc_audio_volume_entry_s mmc_audio_volume_entry_t; + + /** This struct is used by cdio_audio_get_volume and cdio_audio_set_volume */ + struct mmc_audio_volume_s + { + mmc_audio_volume_entry_t port[4]; + } GNUC_PACKED; + + typedef struct mmc_audio_volume_s mmc_audio_volume_t; + +PRAGMA_END_PACKED + + +/** Return type codes for GET_CONFIGURATION. */ +typedef enum { + CDIO_MMC_GET_CONF_ALL_FEATURES = 0, /**< all features without regard + to currency. */ + CDIO_MMC_GET_CONF_CURRENT_FEATURES = 1, /**< features which are currently + in effect (e.g. based on + medium inserted). */ + CDIO_MMC_GET_CONF_NAMED_FEATURE = 2 /**< just the feature named in + the GET_CONFIGURATION cdb. */ +} cdio_mmc_get_conf_t; + + +/** FEATURE codes used in GET CONFIGURATION. */ + +typedef enum { + CDIO_MMC_FEATURE_PROFILE_LIST = 0x000, /**< Profile List Feature */ + CDIO_MMC_FEATURE_CORE = 0x001, + CDIO_MMC_FEATURE_MORPHING = 0x002, /**< Report/prevent operational + changes */ + CDIO_MMC_FEATURE_REMOVABLE_MEDIUM = 0x003, /**< Removable Medium Feature */ + CDIO_MMC_FEATURE_WRITE_PROTECT = 0x004, /**< Write Protect Feature */ + CDIO_MMC_FEATURE_RANDOM_READABLE = 0x010, /**< Random Readable Feature */ + CDIO_MMC_FEATURE_MULTI_READ = 0x01D, /**< Multi-Read Feature */ + CDIO_MMC_FEATURE_CD_READ = 0x01E, /**< CD Read Feature */ + CDIO_MMC_FEATURE_DVD_READ = 0x01F, /**< DVD Read Feature */ + CDIO_MMC_FEATURE_RANDOM_WRITABLE = 0x020, /**< Random Writable Feature */ + CDIO_MMC_FEATURE_INCR_WRITE = 0x021, /**< Incremental Streaming + Writable Feature */ + CDIO_MMC_FEATURE_SECTOR_ERASE = 0x022, /**< Sector Erasable Feature */ + CDIO_MMC_FEATURE_FORMATABLE = 0x023, /**< Formattable Feature */ + CDIO_MMC_FEATURE_DEFECT_MGMT = 0x024, /**< Management Ability of the + Logical Unit/media system to + provide an apparently + defect-free space.*/ + CDIO_MMC_FEATURE_WRITE_ONCE = 0x025, /**< Write Once + Feature */ + CDIO_MMC_FEATURE_RESTRICT_OVERW = 0x026, /**< Restricted Overwrite + Feature */ + CDIO_MMC_FEATURE_CD_RW_CAV = 0x027, /**< CD-RW CAV Write Feature */ + CDIO_MMC_FEATURE_MRW = 0x028, /**< MRW Feature */ + CDIO_MMC_FEATURE_ENHANCED_DEFECT = 0x029, /**< Enhanced Defect Reporting */ + CDIO_MMC_FEATURE_DVD_PRW = 0x02A, /**< DVD+RW Feature */ + CDIO_MMC_FEATURE_DVD_PR = 0x02B, /**< DVD+R Feature */ + CDIO_MMC_FEATURE_RIGID_RES_OVERW = 0x02C, /**< Rigid Restricted Overwrite */ + CDIO_MMC_FEATURE_CD_TAO = 0x02D, /**< CD Track at Once */ + CDIO_MMC_FEATURE_CD_SAO = 0x02E, /**< CD Mastering (Session at + Once) */ + CDIO_MMC_FEATURE_DVD_R_RW_WRITE = 0x02F, /**< DVD-R/RW Write */ + CDIO_MMC_FEATURE_CD_RW_MEDIA_WRITE= 0x037, /**< CD-RW Media Write Support */ + CDIO_MMC_FEATURE_DVD_PR_2_LAYER = 0x03B, /**< DVD+R Double Layer */ + CDIO_MMC_FEATURE_POWER_MGMT = 0x100, /**< Initiator and device directed + power management */ + CDIO_MMC_FEATURE_CDDA_EXT_PLAY = 0x103, /**< Ability to play audio CDs + via the Logical Unit's own + analog output */ + CDIO_MMC_FEATURE_MCODE_UPGRADE = 0x104, /* Ability for the device to + accept new microcode via + the interface */ + CDIO_MMC_FEATURE_TIME_OUT = 0x105, /**< Ability to respond to all + commands within a specific + time */ + CDIO_MMC_FEATURE_DVD_CSS = 0x106, /**< Ability to perform DVD + CSS/CPPM authentication and + RPC */ + CDIO_MMC_FEATURE_RT_STREAMING = 0x107, /**< Ability to read and write + using Initiator requested + performance parameters */ + CDIO_MMC_FEATURE_LU_SN = 0x108, /**< The Logical Unit has a unique + identifier. */ + CDIO_MMC_FEATURE_FIRMWARE_DATE = 0x1FF, /**< Firmware creation date + report */ +} cdio_mmc_feature_t; + +/** Profile profile codes used in GET_CONFIGURATION - PROFILE LIST. */ +typedef enum { + CDIO_MMC_FEATURE_PROF_NON_REMOVABLE = 0x0001, /**< Re-writable disk, capable + of changing behavior */ + CDIO_MMC_FEATURE_PROF_REMOVABLE = 0x0002, /**< disk Re-writable; with + removable media */ + CDIO_MMC_FEATURE_PROF_MO_ERASABLE = 0x0003, /**< Erasable Magneto-Optical + disk with sector erase + capability */ + CDIO_MMC_FEATURE_PROF_MO_WRITE_ONCE = 0x0004, /**< Write Once Magneto-Optical + write once */ + CDIO_MMC_FEATURE_PROF_AS_MO = 0x0005, /**< Advance Storage + Magneto-Optical */ + CDIO_MMC_FEATURE_PROF_CD_ROM = 0x0008, /**< Read only Compact Disc + capable */ + CDIO_MMC_FEATURE_PROF_CD_R = 0x0009, /**< Write once Compact Disc + capable */ + CDIO_MMC_FEATURE_PROF_CD_RW = 0x000A, /**< CD-RW Re-writable + Compact Disc capable */ + CDIO_MMC_FEATURE_PROF_DVD_ROM = 0x0010, /**< Read only DVD */ + CDIO_MMC_FEATURE_PROF_DVD_R_SEQ = 0x0011, /**< Re-recordable DVD using + Sequential recording */ + CDIO_MMC_FEATURE_PROF_DVD_RAM = 0x0012, /**< Re-writable DVD */ + CDIO_MMC_FEATURE_PROF_DVD_RW_RO = 0x0013, /**< Re-recordable DVD using + Restricted Overwrite */ + CDIO_MMC_FEATURE_PROF_DVD_RW_SEQ = 0x0014, /**< Re-recordable DVD using + Sequential recording */ + CDIO_MMC_FEATURE_PROF_DVD_PRW = 0x001A, /**< DVD+RW - DVD ReWritable */ + CDIO_MMC_FEATURE_PROF_DVD_PR = 0x001B, /**< DVD+R - DVD Recordable */ + CDIO_MMC_FEATURE_PROF_DDCD_ROM = 0x0020, /**< Read only DDCD */ + CDIO_MMC_FEATURE_PROF_DDCD_R = 0x0021, /**< DDCD-R Write only DDCD */ + CDIO_MMC_FEATURE_PROF_DDCD_RW = 0x0022, /**< Re-Write only DDCD */ + CDIO_MMC_FEATURE_PROF_DVD_PR2 = 0x002B, /**< DVD+R - DVD Recordable + double layer */ + CDIO_MMC_FEATURE_PROF_NON_CONFORM = 0xFFFF, /**< The Logical Unit does not + conform to any Profile. */ +} cdio_mmc_feature_profile_t; + +typedef enum { + CDIO_MMC_FEATURE_INTERFACE_UNSPECIFIED = 0, + CDIO_MMC_FEATURE_INTERFACE_SCSI = 1, + CDIO_MMC_FEATURE_INTERFACE_ATAPI = 2, + CDIO_MMC_FEATURE_INTERFACE_IEEE_1394 = 3, + CDIO_MMC_FEATURE_INTERFACE_IEEE_1394A = 4, + CDIO_MMC_FEATURE_INTERFACE_FIBRE_CH = 5 +} cdio_mmc_feature_interface_t; + + +/** The largest Command Descriptor Block (CDB) size. + The possible sizes are 6, 10, and 12 bytes. + */ +#define MAX_CDB_LEN 12 + +/** \brief A Command Descriptor Block (CDB) used in sending MMC + commands. + */ +typedef struct mmc_cdb_s { + uint8_t field[MAX_CDB_LEN]; +} mmc_cdb_t; + + /** \brief Format of header block in data returned from an MMC + GET_CONFIGURATION command. + */ + typedef struct mmc_feature_list_header_s { + unsigned char length_msb; + unsigned char length_1sb; + unsigned char length_2sb; + unsigned char length_lsb; + unsigned char reserved1; + unsigned char reserved2; + unsigned char profile_msb; + unsigned char profile_lsb; + } cdio_mmc_feature_list_header_t; + + /** An enumeration indicating whether an MMC command is sending + data or getting data. + */ + typedef enum mmc_direction_s { + SCSI_MMC_DATA_READ, + SCSI_MMC_DATA_WRITE + } cdio_mmc_direction_t; + + typedef struct mmc_subchannel_s + { + uint8_t reserved; + uint8_t audio_status; + uint16_t data_length; /**< Really ISO 9660 7.2.2 */ + uint8_t format; + uint8_t address: 4; + uint8_t control: 4; + uint8_t track; + uint8_t index; + uint8_t abs_addr[4]; + uint8_t rel_addr[4]; + } cdio_mmc_subchannel_t; + +#define CDIO_MMC_SET_COMMAND(cdb, command) \ + cdb[0] = command + +#define CDIO_MMC_SET_READ_TYPE(cdb, sector_type) \ + cdb[1] = (sector_type << 2) + +#define CDIO_MMC_GETPOS_LEN16(p, pos) \ + (p[pos]<<8) + p[pos+1] + +#define CDIO_MMC_GET_LEN16(p) \ + (p[0]<<8) + p[1] + +#define CDIO_MMC_GET_LEN32(p) \ + (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]; + +#define CDIO_MMC_SET_LEN16(cdb, pos, len) \ + cdb[pos ] = (len >> 8) & 0xff; \ + cdb[pos+1] = (len ) & 0xff + +#define CDIO_MMC_SET_READ_LBA(cdb, lba) \ + cdb[2] = (lba >> 24) & 0xff; \ + cdb[3] = (lba >> 16) & 0xff; \ + cdb[4] = (lba >> 8) & 0xff; \ + cdb[5] = (lba ) & 0xff + +#define CDIO_MMC_SET_START_TRACK(cdb, command) \ + cdb[6] = command + +#define CDIO_MMC_SET_READ_LENGTH24(cdb, len) \ + cdb[6] = (len >> 16) & 0xff; \ + cdb[7] = (len >> 8) & 0xff; \ + cdb[8] = (len ) & 0xff + +#define CDIO_MMC_SET_READ_LENGTH16(cdb, len) \ + CDIO_MMC_SET_LEN16(cdb, 7, len) + +#define CDIO_MMC_SET_READ_LENGTH8(cdb, len) \ + cdb[8] = (len ) & 0xff + +#define CDIO_MMC_MCSB_ALL_HEADERS 0xf + +#define CDIO_MMC_SET_MAIN_CHANNEL_SELECTION_BITS(cdb, val) \ + cdb[9] = val << 3; + +/** + Read Audio Subchannel information + + @param p_cdio the CD object to be acted upon. + @param p_subchannel place for returned subchannel information +*/ +driver_return_code_t +mmc_audio_read_subchannel (CdIo_t *p_cdio, + /*out*/ cdio_subchannel_t *p_subchannel); + + /** + Return a string containing the name of the audio state as returned from + the Q_SUBCHANNEL. + */ + const char *mmc_audio_state2str( uint8_t i_audio_state ); + + /** + Eject using MMC commands. If CD-ROM is "locked" we'll unlock it. + Command is not "immediate" -- we'll wait for the command to complete. + For a more general (and lower-level) routine, @see mmc_start_stop_media. + */ + driver_return_code_t mmc_eject_media( const CdIo_t *p_cdio ); + + /** + Return a string containing the name of the given feature + */ + const char *mmc_feature2str( int i_feature ); + + /** + Return a string containing the name of the given feature + */ + const char *mmc_feature_profile2str( int i_feature_profile ); + + /** + Return the length in bytes of the Command Descriptor + Buffer (CDB) for a given MMC command. The length will be + either 6, 10, or 12. + */ + uint8_t mmc_get_cmd_len(uint8_t mmc_cmd); + + /** + Get the block size used in read requests, via MMC. + @return the blocksize if > 0; error if <= 0 + */ + int mmc_get_blocksize ( CdIo_t *p_cdio ); + + /** + * Close tray using a MMC START STOP command. + */ + driver_return_code_t mmc_close_tray( CdIo_t *p_cdio ); + + /** + Get the lsn of the end of the CD + + @return the lsn. On error return CDIO_INVALID_LSN. + */ + lsn_t mmc_get_disc_last_lsn( const CdIo_t *p_cdio ); + + /** + Return the discmode as reported by the MMC Read (FULL) TOC + command. + + Information was obtained from Section 5.1.13 (Read TOC/PMA/ATIP) + pages 56-62 from the MMC draft specification, revision 10a + at http://www.t10.org/ftp/t10/drafts/mmc/mmc-r10a.pdf See + especially tables 72, 73 and 75. + */ + discmode_t mmc_get_discmode( const CdIo_t *p_cdio ); + + + /** + Get drive capabilities for a device. + @return the drive capabilities. + */ + void mmc_get_drive_cap ( CdIo_t *p_cdio, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap); + + typedef enum { + CDIO_MMC_LEVEL_WEIRD, + CDIO_MMC_LEVEL_1, + CDIO_MMC_LEVEL_2, + CDIO_MMC_LEVEL_3, + CDIO_MMC_LEVEL_NONE + } cdio_mmc_level_t; + + /** + Get the MMC level supported by the device. + */ + cdio_mmc_level_t mmc_get_drive_mmc_cap(CdIo_t *p_cdio); + + + /** + Get the DVD type associated with cd object. + + @return the DVD discmode. + */ + discmode_t mmc_get_dvd_struct_physical ( const CdIo_t *p_cdio, + cdio_dvd_struct_t *s); + + /*! + Return results of media status + @param p_cdio the CD object to be acted upon. + @param out_buf media status code from operation + @return DRIVER_OP_SUCCESS (0) if we got the status. + return codes are the same as driver_return_code_t + */ + int mmc_get_event_status(const CdIo_t *p_cdio, uint8_t out_buf[2]); + + /*! + Find out if media tray is open or closed. + @param p_cdio the CD object to be acted upon. + @return 1 if media is open, 0 if closed. Error + return codes are the same as driver_return_code_t + */ + int mmc_get_tray_status ( const CdIo_t *p_cdio ); + + /** + Get the CD-ROM hardware info via an MMC INQUIRY command. + + @return true if we were able to get hardware info, false if we had + an error. + */ + bool mmc_get_hwinfo ( const CdIo_t *p_cdio, + /* out*/ cdio_hwinfo_t *p_hw_info ); + + + /** + Find out if media has changed since the last call. + @param p_cdio the CD object to be acted upon. + @return 1 if media has changed since last call, 0 if not. Error + return codes are the same as driver_return_code_t + */ + int mmc_get_media_changed(const CdIo_t *p_cdio); + + /** + Get the media catalog number (MCN) from the CD via MMC. + + @return the media catalog number r NULL if there is none or we + don't have the ability to get it. + + Note: string is malloc'd so caller has to free() the returned + string when done with it. + + */ + char * mmc_get_mcn ( const CdIo_t *p_cdio ); + + /** Get the output port volumes and port selections used on AUDIO PLAY + commands via a MMC MODE SENSE command using the CD Audio Control + Page. + */ + driver_return_code_t mmc_audio_get_volume (CdIo_t *p_cdio, /*out*/ + mmc_audio_volume_t *p_volume); + + /** + Report if CD-ROM has a praticular kind of interface (ATAPI, SCSCI, ...) + Is it possible for an interface to have serveral? If not this + routine could probably return the single mmc_feature_interface_t. + @return true if we have the interface and false if not. + */ + bool_3way_t mmc_have_interface( CdIo_t *p_cdio, + cdio_mmc_feature_interface_t e_interface ); + + /** Run a MODE_SENSE command (6- or 10-byte version) + and put the results in p_buf + @return DRIVER_OP_SUCCESS if we ran the command ok. + */ + int mmc_mode_sense( CdIo_t *p_cdio, /*out*/ void *p_buf, int i_size, + int page); + + + /** Run a MODE_SENSE command (10-byte version) + and put the results in p_buf + @return DRIVER_OP_SUCCESS if we ran the command ok. + */ + int mmc_mode_sense_10( CdIo_t *p_cdio, /*out*/ void *p_buf, int i_size, + int page); + + /** Run a MODE_SENSE command (6-byte version) + and put the results in p_buf + @return DRIVER_OP_SUCCESS if we ran the command ok. + */ + int mmc_mode_sense_6( CdIo_t *p_cdio, /*out*/ void *p_buf, int i_size, + int page); + + /** Issue a MMC READ_CD command. + + @param p_cdio object to read from + + @param p_buf Place to store data. The caller should ensure that + p_buf can hold at least i_blocksize * i_blocks bytes. + + @param i_lsn sector to read + + @param expected_sector_type restricts reading to a specific CD + sector type. Only 3 bits with values 1-5 are used: + 0 all sector types + 1 CD-DA sectors only + 2 Mode 1 sectors only + 3 Mode 2 formless sectors only. Note in contrast to all other + values an MMC CD-ROM is not required to support this mode. + 4 Mode 2 Form 1 sectors only + 5 Mode 2 Form 2 sectors only + + @param b_digital_audio_play Control error concealment when the + data being read is CD-DA. If the data being read is not CD-DA, + this parameter is ignored. If the data being read is CD-DA and + DAP is false zero, then the user data returned should not be + modified by flaw obscuring mechanisms such as audio data mute and + interpolate. If the data being read is CD-DA and DAP is true, + then the user data returned should be modified by flaw obscuring + mechanisms such as audio data mute and interpolate. + + b_sync_header return the sync header (which will probably have + the same value as CDIO_SECTOR_SYNC_HEADER of size + CDIO_CD_SYNC_SIZE). + + @param header_codes Header Codes refer to the sector header and + the sub-header that is present in mode 2 formed sectors: + + 0 No header information is returned. + 1 The 4-byte sector header of data sectors is be returned, + 2 The 8-byte sector sub-header of mode 2 formed sectors is + returned. + 3 Both sector header and sub-header (12 bytes) is returned. + The Header preceeds the rest of the bytes (e.g. user-data bytes) + that might get returned. + + @param b_user_data Return user data if true. + + For CD-DA, the User Data is CDIO_CD_FRAMESIZE_RAW bytes. + + For Mode 1, The User Data is ISO_BLOCKSIZE bytes beginning at + offset CDIO_CD_HEADER_SIZE+CDIO_CD_SUBHEADER_SIZE. + + For Mode 2 formless, The User Data is M2RAW_SECTOR_SIZE bytes + beginning at offset CDIO_CD_HEADER_SIZE+CDIO_CD_SUBHEADER_SIZE. + + For data Mode 2, form 1, User Data is ISO_BLOCKSIZE bytes beginning at + offset CDIO_CD_XA_SYNC_HEADER. + + For data Mode 2, form 2, User Data is 2 324 bytes beginning at + offset CDIO_CD_XA_SYNC_HEADER. + + @param b_sync + + @param b_edc_ecc true if we return EDC/ECC error detection/correction bits. + + The presence and size of EDC redundancy or ECC parity is defined + according to sector type: + + CD-DA sectors have neither EDC redundancy nor ECC parity. + + Data Mode 1 sectors have 288 bytes of EDC redundancy, Pad, and + ECC parity beginning at offset 2064. + + Data Mode 2 formless sectors have neither EDC redundancy nor ECC + parity + + Data Mode 2 form 1 sectors have 280 bytes of EDC redundancy and + ECC parity beginning at offset 2072 + + Data Mode 2 form 2 sectors optionally have 4 bytes of EDC + redundancy beginning at offset 2348. + + + @param c2_error_information If true associate a bit with each + sector for C2 error The resulting bit field is ordered exactly as + the main channel bytes. Each 8-bit boundary defines a byte of + flag bits. + + @param subchannel_selection subchannel-selection bits + + 0 No Sub-channel data shall be returned. (0 bytes) + 1 RAW P-W Sub-channel data shall be returned. (96 byte) + 2 Formatted Q sub-channel data shall be transferred (16 bytes) + 3 Reserved + 4 Corrected and de-interleaved R-W sub-channel (96 bytes) + 5-7 Reserved + + @param i_blocksize size of the a block expected to be returned + + @param i_blocks number of blocks expected to be returned. + + */ + driver_return_code_t + mmc_read_cd ( const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn, + int expected_sector_type, bool b_digital_audio_play, + bool b_sync, uint8_t header_codes, bool b_user_data, + bool b_edc_ecc, uint8_t c2_error_information, + uint8_t subchannel_selection, uint16_t i_blocksize, + uint32_t i_blocks ); + + /** Read just the user data part of some sort of data sector (via + mmc_read_cd). + + @param p_cdio object to read from + + @param p_buf place to read data into. The caller should make sure + this location can store at least CDIO_CD_FRAMESIZE, + M2RAW_SECTOR_SIZE, or M2F2_SECTOR_SIZE depending on + the kind of sector getting read. If you don't know + whether you have a Mode 1/2, Form 1/ Form 2/Formless + sector best to reserve space for the maximum, + M2RAW_SECTOR_SIZE. + + @param i_lsn sector to read + @param i_blocksize size of each block + @param i_blocks number of blocks to read + + */ + driver_return_code_t mmc_read_data_sectors ( CdIo_t *p_cdio, void *p_buf, + lsn_t i_lsn, + uint16_t i_blocksize, + uint32_t i_blocks ); + + /** Read sectors using SCSI-MMC GPCMD_READ_CD. + Can read only up to 25 blocks. + */ + driver_return_code_t mmc_read_sectors ( const CdIo_t *p_cdio, void *p_buf, + lsn_t i_lsn, int read_sector_type, + uint32_t i_blocks); + + /** + Run a Multimedia command (MMC). + + @param p_cdio CD structure set by cdio_open(). + @param i_timeout_ms time in milliseconds we will wait for the command + to complete. + @param p_cdb CDB bytes. All values that are needed should be set + on input. We'll figure out what the right CDB length + should be. + @param e_direction direction the transfer is to go. + @param i_buf Size of buffer + @param p_buf Buffer for data, both sending and receiving. + + @return 0 if command completed successfully. + */ + driver_return_code_t + mmc_run_cmd( const CdIo_t *p_cdio, unsigned int i_timeout_ms, + const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, unsigned int i_buf, + /*in/out*/ void *p_buf ); + + /** + Run a Multimedia command (MMC) specifying the CDB length. + The motivation here is for example ot use in is an undocumented + debug command for LG drives (namely E7), whose length is being + miscalculated by mmc_get_cmd_len(); it doesn't follow the usual + code number to length conventions. Patch supplied by SukkoPera. + + @param p_cdio CD structure set by cdio_open(). + @param i_timeout_ms time in milliseconds we will wait for the command + to complete. + @param p_cdb CDB bytes. All values that are needed should be set + on input. + @param i_cdb number of CDB bytes. + @param e_direction direction the transfer is to go. + @param i_buf Size of buffer + @param p_buf Buffer for data, both sending and receiving. + + @return 0 if command completed successfully. + */ + driver_return_code_t + mmc_run_cmd_len( const CdIo_t *p_cdio, unsigned int i_timeout_ms, + const mmc_cdb_t *p_cdb, unsigned int i_cdb, + cdio_mmc_direction_t e_direction, unsigned int i_buf, + /*in/out*/ void *p_buf ); + + /** + Set the block size for subsequest read requests, via MMC. + */ + driver_return_code_t mmc_set_blocksize ( const CdIo_t *p_cdio, + uint16_t i_blocksize); + + /** + Set the drive speed in CD-ROM speed units. + + @param p_cdio CD structure set by cdio_open(). + @param i_drive_speed speed in CD-ROM speed units. Note this + not Kbs as would be used in the MMC spec or + in mmc_set_speed(). To convert CD-ROM speed units + to Kbs, multiply the number by 176 (for raw data) + and by 150 (for filesystem data). On many CD-ROM + drives, specifying a value too large will result + in using the fastest speed. + + @return the drive speed if greater than 0. -1 if we had an error. is -2 + returned if this is not implemented for the current driver. + + @see cdio_set_speed and mmc_set_speed + */ + driver_return_code_t mmc_set_drive_speed( const CdIo_t *p_cdio, + int i_drive_speed ); + + /** + Set the drive speed in K bytes per second. + + @param p_cdio CD structure set by cdio_open(). + @param i_Kbs_speed speed in K bytes per second. Note this is + not in standard CD-ROM speed units, e.g. + 1x, 4x, 16x as it is in cdio_set_speed. + To convert CD-ROM speed units to Kbs, + multiply the number by 176 (for raw data) + and by 150 (for filesystem data). + Also note that ATAPI specs say that a value + less than 176 will result in an error. + On many CD-ROM drives, + specifying a value too large will result in using + the fastest speed. + + @return the drive speed if greater than 0. -1 if we had an error. is -2 + returned if this is not implemented for the current driver. + + @see cdio_set_speed and mmc_set_drive_speed + */ + driver_return_code_t mmc_set_speed( const CdIo_t *p_cdio, + int i_Kbs_speed ); + + /** + Load or Unload media using a MMC START STOP command. + + @param p_cdio the CD object to be acted upon. + @param b_eject eject if true and close tray if false + @param b_immediate wait or don't wait for operation to complete + @param power_condition Set CD-ROM to idle/standby/sleep. If nonzero + eject/load is ignored, so set to 0 if you want to eject or load. + + @see mmc_eject_media or mmc_close_tray + */ + driver_return_code_t + mmc_start_stop_media(const CdIo_t *p_cdio, bool b_eject, bool b_immediate, + uint8_t power_condition); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/** The below variables are trickery to force the above enum symbol + values to be recorded in debug symbol tables. They are used to + allow one to refer to the enumeration value names in the typedefs + above in a debugger and debugger expressions +*/ +extern cdio_mmc_feature_t debug_cdio_mmc_feature; +extern cdio_mmc_feature_interface_t debug_cdio_mmc_feature_interface; +extern cdio_mmc_feature_profile_t debug_cdio_mmc_feature_profile; +extern cdio_mmc_get_conf_t debug_cdio_mmc_get_conf; +extern cdio_mmc_gpcmd_t debug_cdio_mmc_gpcmd; +extern cdio_mmc_read_sub_state_t debug_cdio_mmc_read_sub_state; +extern cdio_mmc_read_cd_type_t debug_cdio_mmc_read_cd_type; +extern cdio_mmc_readtoc_t debug_cdio_mmc_readtoc; +extern cdio_mmc_mode_page_t debug_cdio_mmc_mode_page; + +#endif /* __MMC_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/cdio/paranoia.h b/include/cdio/paranoia.h new file mode 100644 index 00000000..dee8dd31 --- /dev/null +++ b/include/cdio/paranoia.h @@ -0,0 +1,201 @@ +/* + $Id: paranoia.h,v 1.15 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2004, 2005, 2006, 2007, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file paranoia.h + * + * \brief The top-level header for libcdda_paranoia: a device- and OS- + * independent library for reading CD-DA with error tolerance and + * repair. Applications include this for paranoia access. + */ + +#ifndef _CDIO_PARANOIA_H_ +#define _CDIO_PARANOIA_H_ + +#include + +/*! Paranoia likes to work with 16-bit numbers rather than + (possibly byte-swapped) bytes. So there are this many + 16-bit numbers block (frame, or sector) read. +*/ +#define CD_FRAMEWORDS (CDIO_CD_FRAMESIZE_RAW/2) + +/** + Flags used in paranoia_modeset. + + The enumeration type one probably wouldn't really use in a program. + It is here instead of defines to give symbolic names that can be + helpful in debuggers where wants just to say refer to + PARANOIA_MODE_DISABLE and get the correct value. +*/ + +typedef enum { + PARANOIA_MODE_DISABLE = 0x00, /**< No fixups */ + PARANOIA_MODE_VERIFY = 0x01, /**< Verify data integrety in overlap area*/ + PARANOIA_MODE_FRAGMENT = 0x02, /**< unsupported */ + PARANOIA_MODE_OVERLAP = 0x04, /**< Perform overlapped reads */ + PARANOIA_MODE_SCRATCH = 0x08, /**< unsupported */ + PARANOIA_MODE_REPAIR = 0x10, /**< unsupported */ + PARANOIA_MODE_NEVERSKIP = 0x20, /**< Do not skip failed reads (retry + maxretries) */ + PARANOIA_MODE_FULL = 0xff, /**< Maximum paranoia - all of the above + (except disable) */ +} paranoia_mode_t; + + +/** + Flags set in a callback. + + The enumeration type one probably wouldn't really use in a program. + It is here instead of defines to give symbolic names that can be + helpful in debuggers where wants just to say refer to + PARANOIA_CB_READ and get the correct value. +*/ +typedef enum { + PARANOIA_CB_READ, /**< Read off adjust ??? */ + PARANOIA_CB_VERIFY, /**< Verifying jitter */ + PARANOIA_CB_FIXUP_EDGE, /**< Fixed edge jitter */ + PARANOIA_CB_FIXUP_ATOM, /**< Fixed atom jitter */ + PARANOIA_CB_SCRATCH, /**< Unsupported */ + PARANOIA_CB_REPAIR, /**< Unsupported */ + PARANOIA_CB_SKIP, /**< Skip exhausted retry */ + PARANOIA_CB_DRIFT, /**< Skip exhausted retry */ + PARANOIA_CB_BACKOFF, /**< Unsupported */ + PARANOIA_CB_OVERLAP, /**< Dynamic overlap adjust */ + PARANOIA_CB_FIXUP_DROPPED, /**< Fixed dropped bytes */ + PARANOIA_CB_FIXUP_DUPED, /**< Fixed duplicate bytes */ + PARANOIA_CB_READERR /**< Hard read error */ +} paranoia_cb_mode_t; + + extern const char *paranoia_cb_mode2str[]; + +#ifdef __cplusplus +extern "C" { +#endif + + /*! + Get and initialize a new cdrom_paranoia object from cdrom_drive. + Run this before calling any of the other paranoia routines below. + + @return new cdrom_paranoia object Call paranoia_free() when you are + done with it + */ + extern cdrom_paranoia_t *cdio_paranoia_init(cdrom_drive_t *d); + + /*! + Free any resources associated with p. + + @param p paranoia object to for which resources are to be freed. + + @see paranoia_init. + */ + extern void cdio_paranoia_free(cdrom_paranoia_t *p); + + /*! + Set the kind of repair you want to on for reading. + The modes are listed above + + @param p paranoia type + @param mode_flags paranoia mode flags built from values in + paranoia_mode_t, e.g. + PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP + */ + extern void cdio_paranoia_modeset(cdrom_paranoia_t *p, int mode_flags); + + /*! + reposition reading offset. + + @param p paranoia type + @param seek byte offset to seek to + @param whence like corresponding parameter in libc's lseek, e.g. + SEEK_SET or SEEK_END. + */ + extern lsn_t cdio_paranoia_seek(cdrom_paranoia_t *p, off_t seek, int whence); + + /*! + Reads the next sector of audio data and returns a pointer to a full + sector of verified samples. + + @param p paranoia object. + + @param callback callback routine which gets called with the status + on each read. + + @return the audio data read, CDIO_CD_FRAMESIZE_RAW (2352) + bytes. This data is not to be freed by the caller. It will persist + only until the next call to paranoia_read() for this p. + */ + extern int16_t *cdio_paranoia_read(cdrom_paranoia_t *p, + void(*callback)(long int, + paranoia_cb_mode_t)); + + /*! The same as cdio_paranoia_read but the number of retries is set. + @param p paranoia object. + + @param callback callback routine which gets called with the status + on each read. + + @param max_retries number of times to try re-reading a block before + failing. + + @return the block of CDIO_FRAMEIZE_RAW bytes (or + CDIO_FRAMESIZE_RAW / 2 16-bit integers). Unless byte-swapping has + been turned off the 16-bit integers Endian independent order. + + @see cdio_paranoia_read. + + */ + extern int16_t *cdio_paranoia_read_limited(cdrom_paranoia_t *p, + void(*callback)(long int, + paranoia_cb_mode_t), + int max_retries); + + +/*! a temporary hack */ + extern void cdio_paranoia_overlapset(cdrom_paranoia_t *p,long overlap); + + extern void cdio_paranoia_set_range(cdrom_paranoia_t *p, long int start, + long int end); + +#ifndef DO_NOT_WANT_PARANOIA_COMPATIBILITY +/** For compatibility with good ol' paranoia */ +#define paranoia_init cdio_paranoia_init +#define paranoia_free cdio_paranoia_free +#define paranoia_modeset cdio_paranoia_modeset +#define paranoia_seek cdio_paranoia_seek +#define paranoia_read cdio_paranoia_read +#define paranoia_read_limited cdio_paranoia_read_limited +#define paranoia_overlapset cdio_paranoia_overlapset +#define paranoia_set_range cdio_paranoia_set_range +#endif /*DO_NOT_WANT_PARANOIA_COMPATIBILITY*/ + +#ifdef __cplusplus +} +#endif + +/** The below variables are trickery to force the above enum symbol + values to be recorded in debug symbol tables. They are used to + allow one to refer to the enumeration value names in the typedefs + above in a debugger and debugger expressions +*/ + +extern paranoia_mode_t debug_paranoia_mode; +extern paranoia_cb_mode_t debug_paranoia_cb_mode; + +#endif /*_CDIO_PARANOIA_H_*/ diff --git a/include/cdio/posix.h b/include/cdio/posix.h new file mode 100644 index 00000000..89e0ad37 --- /dev/null +++ b/include/cdio/posix.h @@ -0,0 +1,43 @@ +/* + $Id: posix.h,v 1.2 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/*! + * \file posix.h + * + * \brief various POSIX definitions. +*/ + +#ifndef __CDIO_POSIX_H__ +#define __CDIO_POSIX_H__ + +typedef uint32_t posix_mode_t; +typedef uint32_t posix_nlink_t; +typedef uint32_t posix_uid_t; +typedef uint32_t posix_gid_t; +typedef uint16_t unicode16_t; + +#endif /* __CDIO_POSIX_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/cdio/read.h b/include/cdio/read.h new file mode 100644 index 00000000..81e5148a --- /dev/null +++ b/include/cdio/read.h @@ -0,0 +1,235 @@ +/* + $Id: read.h,v 1.15 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2005, 2006, 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file read.h + * + * \brief The top-level header for sector (block, frame)-related + * libcdio calls. + */ + +#ifndef __CDIO_READ_H__ +#define __CDIO_READ_H__ + +#ifndef EXTERNAL_LIBCDIO_CONFIG_H +#define EXTERNAL_LIBCDIO_CONFIG_H +/* Need for HAVE_SYS_TYPES_H */ +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +/* Some systems need this for off_t and ssize. */ +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /** All the different ways a block/sector can be read. */ + typedef enum { + CDIO_READ_MODE_AUDIO, /**< CD-DA, audio, Red Book */ + CDIO_READ_MODE_M1F1, /**< Mode 1 Form 1 */ + CDIO_READ_MODE_M1F2, /**< Mode 1 Form 2 */ + CDIO_READ_MODE_M2F1, /**< Mode 2 Form 1 */ + CDIO_READ_MODE_M2F2 /**< Mode 2 Form 2 */ + } cdio_read_mode_t; + + /*! + Reposition read offset + Similar to (if not the same as) libc's fseek() + + @param p_cdio object which gets adjusted + @param offset amount to seek + @param whence like corresponding parameter in libc's fseek, e.g. + SEEK_SET or SEEK_END. + @return (off_t) -1 on error. + */ + + off_t cdio_lseek(const CdIo_t *p_cdio, off_t offset, int whence); + + /*! Reads into buf the next size bytes. Similar to (if not the + same as) libc's read(). This is a "cooked" read, or one handled by + the OS. It probably won't work on audio data. For that use + cdio_read_audio_sector(s). + + @param p_cdio object to read from + @param p_buf place to read data into. The caller should make sure + this location can store at least i_size bytes. + @param i_size number of bytes to read + + @return (ssize_t) -1 on error. + */ + ssize_t cdio_read(const CdIo_t *p_cdio, void *p_buf, size_t i_size); + + /*! + Read an audio sector + + @param p_cdio object to read from + @param p_buf place to read data into. The caller should make sure + this location can store at least CDIO_FRAMESIZE_RAW + bytes. + @param i_lsn sector to read + */ + driver_return_code_t cdio_read_audio_sector (const CdIo_t *p_cdio, + void *p_buf, lsn_t i_lsn); + + /*! + Reads audio sectors + + @param p_cdio object to read from + @param p_buf place to read data into. The caller should make sure + this location can store at least CDIO_FRAMESIZE_RAW + * i_blocks bytes. + @param i_lsn sector to read + @param i_blocks number of sectors to read + */ + driver_return_code_t cdio_read_audio_sectors (const CdIo_t *p_cdio, + void *p_buf, lsn_t i_lsn, + uint32_t i_blocks); + + /*! + Read data sectors + + @param p_cdio object to read from + @param p_buf place to read data into. The caller should make sure + this location can store at least ISO_BLOCKSIZE, + M2RAW_SECTOR_SIZE, or M2F2_SECTOR_SIZE depending + on the kind of sector getting read. If you don't + know whether you have a Mode 1/2, Form 1/ Form 2/Formless + sector best to reserve space for the maximum, + M2RAW_SECTOR_SIZE. + @param i_lsn sector to read + @param i_blocksize size of block. Should be either CDIO_CD_FRAMESIZE, + M2RAW_SECTOR_SIZE, or M2F2_SECTOR_SIZE. See comment above under p_buf. + + @param i_blocks number of blocks to read + */ + driver_return_code_t cdio_read_data_sectors ( const CdIo_t *p_cdio, + void *p_buf, lsn_t i_lsn, + uint16_t i_blocksize, + uint32_t i_blocks ); + /*! + Reads a mode 1 sector + + @param p_cdio object to read from + @param p_buf place to read data into. + @param i_lsn sector to read + @param b_form2 true for reading mode 1 form 2 sectors or false for + mode 1 form 1 sectors. + */ + driver_return_code_t cdio_read_mode1_sector (const CdIo_t *p_cdio, + void *p_buf, lsn_t i_lsn, + bool b_form2); + /*! + Reads mode 1 sectors + + @param p_cdio object to read from + @param p_buf place to read data into + @param i_lsn sector to read + @param b_form2 true for reading mode 1 form 2 sectors or false for + mode 1 form 1 sectors. + @param i_blocks number of sectors to read + */ + driver_return_code_t cdio_read_mode1_sectors (const CdIo_t *p_cdio, + void *p_buf, lsn_t i_lsn, + bool b_form2, + uint32_t i_blocks); + /*! + Reads a mode 2 sector + + @param p_cdio object to read from + @param p_buf place to read data into. The caller should make sure + this location can store at least + M2RAW_SECTOR_SIZE (for form 1) or CDIO_CD_FRAMESIZE (for + form 2) bytes. + @param i_lsn sector to read + @param b_form2 true for reading mode 2 form 2 sectors or false for + mode 2 form 1 sectors. + + @return 0 if no error, nonzero otherwise. + */ + driver_return_code_t cdio_read_mode2_sector (const CdIo_t *p_cdio, + void *p_buf, lsn_t i_lsn, + bool b_form2); + + /** The special case of reading a single block is a common one so we + provide a routine for that as a convenience. + */ + driver_return_code_t cdio_read_sector(const CdIo_t *p_cdio, void *p_buf, + lsn_t i_lsn, + cdio_read_mode_t read_mode); + /*! + Reads mode 2 sectors + + @param p_cdio object to read from + @param p_buf place to read data into. The caller should make sure + this location can store at least + M2RAW_SECTOR_SIZE (for form 1) or CDIO_CD_FRAMESIZE (for + form 2) * i_blocks bytes. + @param i_lsn sector to read + @param b_form2 true for reading mode2 form 2 sectors or false for + mode 2 form 1 sectors. + @param i_blocks number of sectors to read + + @return 0 if no error, nonzero otherwise. + */ + driver_return_code_t cdio_read_mode2_sectors (const CdIo_t *p_cdio, + void *p_buf, lsn_t i_lsn, + bool b_form2, + uint32_t i_blocks); + + /*! + Reads a number of sectors (AKA blocks). + + @param p_cdio cdio object + @param p_buf place to read data into. The caller should make sure + this location is large enough. See below for size information. + @param read_mode the kind of "mode" to use in reading. + @param i_lsn sector to read + @param i_blocks number of sectors to read + @return DRIVER_OP_SUCCESS (0) if no error, other (negative) enumerations + are returned on error. + + If read_mode is CDIO_MODE_AUDIO, + *p_buf should hold at least CDIO_FRAMESIZE_RAW * i_blocks bytes. + + If read_mode is CDIO_MODE_DATA, + *p_buf should hold at least i_blocks times either ISO_BLOCKSIZE, + M1RAW_SECTOR_SIZE or M2F2_SECTOR_SIZE depending on the kind of + sector getting read. If you don't know whether you have a Mode 1/2, + Form 1/ Form 2/Formless sector best to reserve space for the maximum + which is M2RAW_SECTOR_SIZE. + + If read_mode is CDIO_MODE_M2F1, + *p_buf should hold at least M2RAW_SECTOR_SIZE * i_blocks bytes. + + If read_mode is CDIO_MODE_M2F2, + *p_buf should hold at least CDIO_CD_FRAMESIZE * i_blocks bytes. + + */ + driver_return_code_t cdio_read_sectors(const CdIo_t *p_cdio, void *p_buf, + lsn_t i_lsn, + cdio_read_mode_t read_mode, + uint32_t i_blocks); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_TRACK_H__ */ diff --git a/include/cdio/rock.h b/include/cdio/rock.h new file mode 100644 index 00000000..57003276 --- /dev/null +++ b/include/cdio/rock.h @@ -0,0 +1,396 @@ +/* + $Id: rock.h,v 1.15 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2005, 2006 2008 Rocky Bernstein + + See also rock.c by Eric Youngdale (1993) from GNU/Linux + This is Copyright 1993 Yggdrasil Computing, Incorporated + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/*! + \file rock.h + \brief Things related to the Rock Ridge Interchange Protocol (RRIP) + + Applications will probably not include this directly but via + the iso9660.h header. +*/ + + +#ifndef __CDIO_ROCK_H__ +#define __CDIO_ROCK_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* MSYS 1.0.10 with MinGW 3.4.2 (and perhaps others) don't have + S_ISSOCK() or S_ISLNK() macros, so we'll roll our own. */ +#if !defined(HAVE_S_ISSOCK) && !defined(S_ISSOCK) +#define S_ISSOCK(st_mode) ((((st_mode)) & 0170000) == (0140000)) +#endif + +#if !defined(HAVE_S_ISLNK) && !defined(S_ISLNK) +#define S_ISLNK(st_mode) ((((st_mode)) & 0170000) == (0010000)) +#endif + +/*! An enumeration for some of the ISO_ROCK_* \#defines below. This isn't + really an enumeration one would really use in a program it is to + be helpful in debuggers where wants just to refer to the ISO_ROCK_* + names and get something. +*/ +extern enum iso_rock_enums { + ISO_ROCK_IRUSR = 000400, /**< read permission (owner) */ + ISO_ROCK_IWUSR = 000200, /**< write permission (owner) */ + ISO_ROCK_IXUSR = 000100, /**< execute permission (owner) */ + ISO_ROCK_IRGRP = 000040, /**< read permission (group) */ + ISO_ROCK_IWGRP = 000020, /**< write permission (group) */ + ISO_ROCK_IXGRP = 000010, /**< execute permission (group) */ + ISO_ROCK_IROTH = 000004, /**< read permission (other) */ + ISO_ROCK_IWOTH = 000002, /**< write permission (other) */ + ISO_ROCK_IXOTH = 000001, /**< execute permission (other) */ + + ISO_ROCK_ISUID = 004000, /**< set user ID on execution */ + ISO_ROCK_ISGID = 002000, /**< set group ID on execution */ + ISO_ROCK_ISVTX = 001000, /**< save swapped text even after use */ + + ISO_ROCK_ISSOCK = 0140000, /**< socket */ + ISO_ROCK_ISLNK = 0120000, /**< symbolic link */ + ISO_ROCK_ISREG = 0100000, /**< regular */ + ISO_ROCK_ISBLK = 060000, /**< block special */ + ISO_ROCK_ISCHR = 020000, /**< character special */ + ISO_ROCK_ISDIR = 040000, /**< directory */ + ISO_ROCK_ISFIFO = 010000 /**< pipe or FIFO */ +} iso_rock_enums; + +#define ISO_ROCK_IRUSR 000400 /** read permission (owner) */ +#define ISO_ROCK_IWUSR 000200 /** write permission (owner) */ +#define ISO_ROCK_IXUSR 000100 /** execute permission (owner) */ +#define ISO_ROCK_IRGRP 000040 /** read permission (group) */ +#define ISO_ROCK_IWGRP 000020 /** write permission (group) */ +#define ISO_ROCK_IXGRP 000010 /** execute permission (group) */ +#define ISO_ROCK_IROTH 000004 /** read permission (other) */ +#define ISO_ROCK_IWOTH 000002 /** write permission (other) */ +#define ISO_ROCK_IXOTH 000001 /** execute permission (other) */ + +#define ISO_ROCK_ISUID 004000 /** set user ID on execution */ +#define ISO_ROCK_ISGID 002000 /** set group ID on execution */ +#define ISO_ROCK_ISVTX 001000 /** save swapped text even after use */ + +#define ISO_ROCK_ISSOCK 0140000 /** socket */ +#define ISO_ROCK_ISLNK 0120000 /** symbolic link */ +#define ISO_ROCK_ISREG 0100000 /** regular */ +#define ISO_ROCK_ISBLK 060000 /** block special */ +#define ISO_ROCK_ISCHR 020000 /** character special */ +#define ISO_ROCK_ISDIR 040000 /** directory */ +#define ISO_ROCK_ISFIFO 010000 /** pipe or FIFO */ + +/** Enforced file locking (shared w/set group ID) */ +#define ISO_ROCK_ENFMT ISO_ROCK_ISGID + +PRAGMA_BEGIN_PACKED + +/*! The next two structs are used by the system-use-sharing protocol + (SUSP), in which the Rock Ridge extensions are embedded. It is + quite possible that other extensions are present on the disk, and + this is fine as long as they all use SUSP. */ + +/*! system-use-sharing protocol */ +typedef struct iso_su_sp_s{ + unsigned char magic[2]; + uint8_t skip; +} GNUC_PACKED iso_su_sp_t; + +/*! system-use extension record */ +typedef struct iso_su_er_s { + iso711_t len_id; /**< Identifier length. Value 10?. */ + unsigned char len_des; + unsigned char len_src; + iso711_t ext_ver; /**< Extension version. Value 1? */ + char data[EMPTY_ARRAY_SIZE]; +} GNUC_PACKED iso_su_er_t; + +typedef struct iso_su_ce_s { + char extent[8]; + char offset[8]; + char size[8]; +} iso_su_ce_t; + +/*! POSIX file attributes, PX. See Rock Ridge Section 4.1.2 */ +typedef struct iso_rock_px_s { + iso733_t st_mode; /*! file mode permissions; same as st_mode + of POSIX:5.6.1 */ + iso733_t st_nlinks; /*! number of links to file; same as st_nlinks + of POSIX:5.6.1 */ + iso733_t st_uid; /*! user id owner of file; same as st_uid + of POSIX:5.6.1 */ + iso733_t st_gid; /*! group id of file; same as st_gid of + of POSIX:5.6.1 */ +} GNUC_PACKED iso_rock_px_t ; + +/*! POSIX device number, PN. A PN is mandatory if the file type + recorded in the "PX" File Mode field for a Directory Record + indicates a character or block device (ISO_ROCK_ISCHR | + ISO_ROCK_ISBLK). This entry is ignored for other (non-Direcotry) + file types. No more than one "PN" is recorded in the System Use Area + of a Directory Record. + + See Rock Ridge Section 4.1.2 */ +typedef struct iso_rock_pn_s { + iso733_t dev_high; /**< high-order 32 bits of the 64 bit device number. + 7.2.3 encoded */ + iso733_t dev_low; /**< low-order 32 bits of the 64 bit device number. + 7.2.3 encoded */ +} GNUC_PACKED iso_rock_pn_t ; + +/*! These are the bits and their meanings for flags in the SL structure. */ +typedef enum { + ISO_ROCK_SL_CONTINUE = 1, + ISO_ROCK_SL_CURRENT = 2, + ISO_ROCK_SL_PARENT = 4, + ISO_ROCK_SL_ROOT = 8 +} iso_rock_sl_flag_t; + +#define ISO_ROCK_SL_CONTINUE 1 +#define ISO_ROCK_SL_CURRENT 2 +#define ISO_ROCK_SL_PARENT 4 +#define ISO_ROCK_SL_ROOT 8 + +typedef struct iso_rock_sl_part_s { + uint8_t flags; + uint8_t len; + char text[EMPTY_ARRAY_SIZE]; +} GNUC_PACKED iso_rock_sl_part_t ; + +/*! Symbolic link. See Rock Ridge Section 4.1.3 */ +typedef struct iso_rock_sl_s { + unsigned char flags; + iso_rock_sl_part_t link; +} GNUC_PACKED iso_rock_sl_t ; + +/*! Alternate name. See Rock Ridge Section 4.1.4 */ + +/*! These are the bits and their meanings for flags in the NM structure. */ +typedef enum { + ISO_ROCK_NM_CONTINUE = 1, + ISO_ROCK_NM_CURRENT = 2, + ISO_ROCK_NM_PARENT = 4, +} iso_rock_nm_flag_t; + +#define ISO_ROCK_NM_CONTINUE 1 +#define ISO_ROCK_NM_CURRENT 2 +#define ISO_ROCK_NM_PARENT 4 + + +typedef struct iso_rock_nm_s { + unsigned char flags; + char name[EMPTY_ARRAY_SIZE]; +} GNUC_PACKED iso_rock_nm_t ; + +/*! Child link. See Section 4.1.5.1 */ +typedef struct iso_rock_cl_s { + char location[1]; +} GNUC_PACKED iso_rock_cl_t ; + +/*! Parent link. See Section 4.1.5.2 */ +typedef struct iso_rock_pl_s { + char location[1]; +} GNUC_PACKED iso_rock_pl_t ; + +/*! These are the bits and their meanings for flags in the TF structure. */ +typedef enum { + ISO_ROCK_TF_CREATE = 1, + ISO_ROCK_TF_MODIFY = 2, + ISO_ROCK_TF_ACCESS = 4, + ISO_ROCK_TF_ATTRIBUTES = 8, + ISO_ROCK_TF_BACKUP = 16, + ISO_ROCK_TF_EXPIRATION = 32, + ISO_ROCK_TF_EFFECTIVE = 64, + ISO_ROCK_TF_LONG_FORM = 128 +} iso_rock_tf_flag_t; + +/* These are the bits and their meanings for flags in the TF structure. */ +#define ISO_ROCK_TF_CREATE 1 +#define ISO_ROCK_TF_MODIFY 2 +#define ISO_ROCK_TF_ACCESS 4 +#define ISO_ROCK_TF_ATTRIBUTES 8 +#define ISO_ROCK_TF_BACKUP 16 +#define ISO_ROCK_TF_EXPIRATION 32 +#define ISO_ROCK_TF_EFFECTIVE 64 +#define ISO_ROCK_TF_LONG_FORM 128 + +/*! Time stamp(s) for a file. See Rock Ridge Section 4.1.6 */ +typedef struct iso_rock_tf_s { + uint8_t flags; /**< See ISO_ROCK_TF_* bits above. */ + uint8_t time_bytes[EMPTY_ARRAY_SIZE]; /**< A homogenious array of + iso9660_ltime_t or + iso9660_dtime_t entries + depending on flags & + ISO_ROCK_TF_LONG_FORM. Lacking + a better method, we store + this as an array of bytes + and a cast to the + appropriate type will have + to be made before + extraction. */ +} GNUC_PACKED iso_rock_tf_t ; + +/*! File data in sparse format. See Rock Ridge Section 4.1.7 */ +typedef struct iso_rock_sf_s { + iso733_t virtual_size_high; /**< high-order 32 bits of virtual size */ + iso733_t virtual_size_low; /**< low-order 32 bits of virtual size */ + uint8_t table_depth; +} GNUC_PACKED iso_rock_sf_t ; + +typedef struct iso_extension_record_s { + char signature[2]; /**< signature word; either 'SP', 'CE', 'ER', 'RR', + 'PX', 'PN', 'SL', 'NM', 'CL', 'PL', 'TF', or + 'ZF' */ + iso711_t len; /**< length of system-user area - 44 for PX + 20 for PN, 5+strlen(text) for SL, 21 for + SF, etc. */ + iso711_t version; /**< version number - value 1 */ + union { + iso_su_sp_t SP; /**< system-use-sharing protocol - not + strictly part of Rock Ridge */ + iso_su_er_t ER; /**< system-use extension packet - not + strictly part of Rock Ridge */ + iso_su_ce_t CE; /**< system-use - strictly part of Rock Ridge */ + iso_rock_px_t PX; /**< Rock Ridge POSIX file attributes */ + iso_rock_pn_t PN; /**< Rock Ridge POSIX device number */ + iso_rock_sl_t SL; /**< Rock Ridge symbolic link */ + iso_rock_nm_t NM; /**< Rock Ridge alternate name */ + iso_rock_cl_t CL; /**< Rock Ridge child link */ + iso_rock_pl_t PL; /**< Rock Ridge parent link */ + iso_rock_tf_t TF; /**< Rock Ridge timestamp(s) for a file */ + } u; +} GNUC_PACKED iso_extension_record_t; + +typedef struct iso_rock_time_s { + bool b_used; /**< If true, field has been set and is valid. + Otherwise remaning fields are meaningless. */ + bool b_longdate; /**< If true date format is a iso9660_ltime_t. + Otherwise date is iso9660_dtime_t */ + union + { + iso9660_ltime_t ltime; + iso9660_dtime_t dtime; + } t; +} GNUC_PACKED iso_rock_time_t; + +typedef struct iso_rock_statbuf_s { + bool_3way_t b3_rock; /**< has Rock Ridge extension. + If "yep", then the fields + are used. + */ + posix_mode_t st_mode; /**< protection */ + posix_nlink_t st_nlinks; /**< number of hard links */ + posix_uid_t st_uid; /**< user ID of owner */ + posix_gid_t st_gid; /**< group ID of owner */ + uint8_t s_rock_offset; + int i_symlink; /**< size of psz_symlink */ + int i_symlink_max; /**< max allocated to psz_symlink */ + char *psz_symlink; /**< if symbolic link, name + of pointed to file. */ + iso_rock_time_t create; /**< create time See ISO 9660:9.5.4. */ + iso_rock_time_t modify; /**< time of last modification + ISO 9660:9.5.5. st_mtime field of + POSIX:5.6.1. */ + iso_rock_time_t access; /**< time of last file access st_atime + field of POSIX:5.6.1. */ + iso_rock_time_t attributes; /**< time of last attribute change. + st_ctime field of POSIX:5.6.1. */ + iso_rock_time_t backup; /**< time of last backup. */ + iso_rock_time_t expiration; /**< time of expiration; See ISO + 9660:9.5.6. */ + iso_rock_time_t effective; /**< Effective time; See ISO 9660:9.5.7. + */ + uint32_t i_rdev; /**< the upper 16-bits is major device + number, the lower 16-bits is the + minor device number */ + +} iso_rock_statbuf_t; + +PRAGMA_END_PACKED + +/*! return length of name field; 0: not found, -1: to be ignored */ +int get_rock_ridge_filename(iso9660_dir_t * de, /*out*/ char * retname, + /*out*/ iso9660_stat_t *p_stat); + + int parse_rock_ridge_stat(iso9660_dir_t *de, /*out*/ iso9660_stat_t *p_stat); + + /*! + Returns POSIX mode bitstring for a given file. + */ + mode_t + iso9660_get_posix_filemode_from_rock(const iso_rock_statbuf_t *rr); + +/*! + Returns a string which interpreting the POSIX mode st_mode. + For example: + \verbatim + drwxrws--- + -rw---Sr-- + lrwxrwxrwx + \endverbatim + + A description of the characters in the string follows + The 1st character is either "d" if the entry is a directory, "l" is + a symbolic link or "-" if neither. + + The 2nd to 4th characters refer to permissions for a user while the + the 5th to 7th characters refer to permissions for a group while, and + the 8th to 10h characters refer to permissions for everyone. + + In each of these triplets the first character (2, 5, 8) is "r" if + the entry is allowed to be read. + + The second character of a triplet (3, 6, 9) is "w" if the entry is + allowed to be written. + + The third character of a triplet (4, 7, 10) is "x" if the entry is + executable but not user (for character 4) or group (for characters + 6) settable and "s" if the item has the corresponding user/group set. + + For a directory having an executable property on ("x" or "s") means + the directory is allowed to be listed or "searched". If the execute + property is not allowed for a group or user but the corresponding + group/user is set "S" indicates this. If none of these properties + holds the "-" indicates this. +*/ +const char *iso9660_get_rock_attr_str(posix_mode_t st_mode); + +/** These variables are not used, but are defined to facilatate debugging + by letting us use enumerations values (which also correspond to + \#define's inside a debugged program. + */ +extern iso_rock_nm_flag_t iso_rock_nm_flag; +extern iso_rock_sl_flag_t iso_rock_sl_flag; +extern iso_rock_tf_flag_t iso_rock_tf_flag; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __ISO_ROCK_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/cdio/scsi_mmc.h b/include/cdio/scsi_mmc.h new file mode 100644 index 00000000..2c8e5d55 --- /dev/null +++ b/include/cdio/scsi_mmc.h @@ -0,0 +1,26 @@ +/* + $Id: scsi_mmc.h,v 1.46 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/*! + * \file scsi_mmc.h + * + * \brief Obsolete please use instead. +*/ +/* ust a moment while, I transfer your call... */ +#include diff --git a/include/cdio/sector.h b/include/cdio/sector.h new file mode 100644 index 00000000..056433d9 --- /dev/null +++ b/include/cdio/sector.h @@ -0,0 +1,281 @@ +/* + $Id: sector.h,v 1.38 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2008 Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/*! + \file sector.h + \brief Things related to CD-ROM layout: tracks, sector sizes, MSFs, LBAs. + + 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: + +\verbatim + format sector type user data size (bytes) + ----------------------------------------------------------------------------- + 1 (Red Book) CD-DA 2352 (CDIO_CD_FRAMESIZE_RAW) + 2 (Yellow Book) Mode1 Form1 2048 (CDIO_CD_FRAMESIZE) + 3 (Yellow Book) Mode1 Form2 2336 (M2RAW_SECTOR_SIZE) + 4 (Green Book) Mode2 Form1 2048 (CDIO_CD_FRAMESIZE) + 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 | +\endverbatim + + +*/ + +#ifndef _CDIO_SECTOR_H_ +#define _CDIO_SECTOR_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include + + /*! Information that can be obtained through a Read Subchannel + command. + */ +#define CDIO_SUBCHANNEL_SUBQ_DATA 0 +#define CDIO_SUBCHANNEL_CURRENT_POSITION 1 +#define CDIO_SUBCHANNEL_MEDIA_CATALOG 2 +#define CDIO_SUBCHANNEL_TRACK_ISRC 3 + + /*! track flags + * Q Sub-channel Control Field (4.2.3.3) + */ + typedef enum { + NONE = 0x00, /* no flags set */ + PRE_EMPHASIS = 0x01, /* audio track recorded with pre-emphasis */ + COPY_PERMITTED = 0x02, /* digital copy permitted */ + DATA = 0x04, /* data track */ + FOUR_CHANNEL_AUDIO = 0x08, /* 4 audio channels */ + SCMS = 0x10 /* SCMS (5.29.2.7) */ + } flag_t; + +#define CDIO_PREGAP_SECTORS 150 +#define CDIO_POSTGAP_SECTORS 150 + + /*! An enumeration for some of the CDIO_CD \#defines below. This isn't + really an enumeration one would really use in a program it is to + be helpful in debuggers where wants just to refer to the CDIO_CD_ + names and get something. + */ + extern enum cdio_cd_enums { + CDIO_CD_MINS = 74, /**< max. minutes per CD, not really + a limit */ + CDIO_CD_SECS_PER_MIN = 60, /**< seconds per minute */ + CDIO_CD_FRAMES_PER_SEC = 75, /**< frames per second */ + CDIO_CD_SYNC_SIZE = 12, /**< 12 sync bytes per raw data + frame */ + CDIO_CD_CHUNK_SIZE = 24, /**< lowest-level "data bytes + piece" */ + CDIO_CD_NUM_OF_CHUNKS = 98, /**< chunks per frame */ + CDIO_CD_FRAMESIZE_SUB = 96, /**< subchannel data "frame" size */ + CDIO_CD_HEADER_SIZE = 4, /**< header (address) bytes per raw + frame */ + CDIO_CD_SUBHEADER_SIZE = 8, /**< subheader bytes per raw XA data + frame */ + CDIO_CD_ECC_SIZE = 276, /**< bytes ECC per most raw data + frame types */ + CDIO_CD_FRAMESIZE = 2048, /**< bytes per frame, "cooked" + mode */ + CDIO_CD_FRAMESIZE_RAW = 2352, /**< bytes per frame, "raw" mode */ + CDIO_CD_FRAMESIZE_RAWER = 2646, /**< The maximum possible + returned */ + CDIO_CD_FRAMESIZE_RAW1 = 2340, + CDIO_CD_FRAMESIZE_RAW0 = 2336, + CDIO_CD_MAX_SESSIONS = 99, + CDIO_CD_MIN_SESSION_NO = 1, /**<, Smallest CD session number */ + CDIO_CD_MAX_LSN = 450150, /**< Largest LSN in a CD */ + CDIO_CD_MIN_LSN = -450150, /**< Smallest LSN in a CD */ + } cdio_cd_enums; + + /*! + Some generally useful CD-ROM information -- mostly based on the above. + This is from linux.h - not to slight other OS's. This was the first + place I came across such useful stuff. + */ +#define CDIO_CD_MINS 74 /**< max. minutes per CD, not really + a limit */ +#define CDIO_CD_SECS_PER_MIN 60 /**< seconds per minute */ +#define CDIO_CD_FRAMES_PER_SEC 75 /**< frames per second */ +#define CDIO_CD_SYNC_SIZE 12 /**< 12 sync bytes per raw data frame */ +#define CDIO_CD_CHUNK_SIZE 24 /**< lowest-level "data bytes piece" */ +#define CDIO_CD_NUM_OF_CHUNKS 98 /**< chunks per frame */ +#define CDIO_CD_FRAMESIZE_SUB 96 /**< subchannel data "frame" size */ +#define CDIO_CD_HEADER_SIZE 4 /**< header (address) bytes per raw + data frame */ +#define CDIO_CD_SUBHEADER_SIZE 8 /**< subheader bytes per raw XA data + frame */ +#define CDIO_CD_EDC_SIZE 4 /**< bytes EDC per most raw data + frame types */ +#define CDIO_CD_M1F1_ZERO_SIZE 8 /**< bytes zero per yellow book mode + 1 frame */ +#define CDIO_CD_ECC_SIZE 276 /**< bytes ECC per most raw data frame + types */ +#define CDIO_CD_FRAMESIZE 2048 /**< bytes per frame, "cooked" mode */ +#define CDIO_CD_FRAMESIZE_RAW 2352 /**< bytes per frame, "raw" mode */ +#define CDIO_CD_FRAMESIZE_RAWER 2646 /**< The maximum possible returned + bytes */ +#define CDIO_CD_FRAMESIZE_RAW1 (CDIO_CD_CD_FRAMESIZE_RAW-CDIO_CD_SYNC_SIZE) /*2340*/ +#define CDIO_CD_FRAMESIZE_RAW0 (CDIO_CD_FRAMESIZE_RAW-CDIO_CD_SYNC_SIZE-CDIO_CD_HEADER_SIZE) /*2336*/ + + /*! "before data" part of raw XA (green, mode2) frame */ +#define CDIO_CD_XA_HEADER (CDIO_CD_HEADER_SIZE+CDIO_CD_SUBHEADER_SIZE) + + /*! "after data" part of raw XA (green, mode2 form1) frame */ +#define CDIO_CD_XA_TAIL (CDIO_CD_EDC_SIZE+CDIO_CD_ECC_SIZE) + + /*! "before data" sync bytes + header of XA (green, mode2) frame */ +#define CDIO_CD_XA_SYNC_HEADER (CDIO_CD_SYNC_SIZE+CDIO_CD_XA_HEADER) + + /*! String of bytes used to identify the beginning of a Mode 1 or + Mode 2 sector. */ + extern const uint8_t CDIO_SECTOR_SYNC_HEADER[CDIO_CD_SYNC_SIZE]; + /**< + {0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0}; + */ + + /*! An enumeration for some of the M2*_SECTOR_SIZE \#defines + below. This isn't really an enumeration one would really use in a + program it is to be helpful in debuggers where wants just to refer + to the M2*_SECTOR_SIZE names and get something. + */ + extern enum m2_sector_enums { + M2F2_SECTOR_SIZE = 2324, + M2SUB_SECTOR_SIZE = 2332, + M2RAW_SECTOR_SIZE = 2336 + } m2_sector_enums; + +#define M2F2_SECTOR_SIZE 2324 +#define M2SUB_SECTOR_SIZE 2332 +#define M2RAW_SECTOR_SIZE 2336 + + /*! Largest CD session number */ +#define CDIO_CD_MAX_SESSIONS 99 + /*! Smallest CD session number */ +#define CDIO_CD_MIN_SESSION_NO 1 + + /*! Largest LSN in a CD */ +#define CDIO_CD_MAX_LSN 450150 + /*! Smallest LSN in a CD */ +#define CDIO_CD_MIN_LSN -450150 + + +#define CDIO_CD_FRAMES_PER_MIN \ + (CDIO_CD_FRAMES_PER_SEC*CDIO_CD_SECS_PER_MIN) + +#define CDIO_CD_74MIN_SECTORS (UINT32_C(74)*CDIO_CD_FRAMES_PER_MIN) +#define CDIO_CD_80MIN_SECTORS (UINT32_C(80)*CDIO_CD_FRAMES_PER_MIN) +#define CDIO_CD_90MIN_SECTORS (UINT32_C(90)*CDIO_CD_FRAMES_PER_MIN) + +#define CDIO_CD_MAX_SECTORS \ + (UINT32_C(100)*CDIO_CD_FRAMES_PER_MIN-CDIO_PREGAP_SECTORS) + +#define msf_t_SIZEOF 3 + + /*! + Convert an LBA into a string representation of the MSF. + \warning cdio_lba_to_msf_str returns new allocated string */ + char *cdio_lba_to_msf_str (lba_t i_lba); + + /*! + Convert an MSF into a string representation of the MSF. + \warning cdio_msf_to_msf_str returns new allocated string */ + char *cdio_msf_to_str (const msf_t *p_msf); + + /*! + Convert an LBA into the corresponding LSN. + */ + lba_t cdio_lba_to_lsn (lba_t i_lba); + + /*! + Convert an LBA into the corresponding MSF. + */ + void cdio_lba_to_msf(lba_t i_lba, msf_t *p_msf); + + /*! + Convert an LSN into the corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. + */ + lba_t cdio_lsn_to_lba (lsn_t i_lsn); + + /*! + Convert an LSN into the corresponding MSF. + */ + void cdio_lsn_to_msf (lsn_t i_lsn, msf_t *p_msf); + + /*! + Convert a MSF into the corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. + */ + lba_t cdio_msf_to_lba (const msf_t *p_msf); + + /*! + Convert a MSF into the corresponding LSN. + CDIO_INVALID_LSN is returned if there is an error. + */ + lsn_t cdio_msf_to_lsn (const msf_t *p_msf); + + /*! + Convert a MSF - broken out as 3 integer components into the + corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. + */ + lba_t cdio_msf3_to_lba (unsigned int minutes, unsigned int seconds, + unsigned int frames); + + /*! + Convert a string of the form MM:SS:FF into the corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. + */ + lba_t cdio_mmssff_to_lba (const char *psz_mmssff); + +#ifdef __cplusplus + } +#endif + +#endif /* _CDIO_SECTOR_H_ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/cdio/track.h b/include/cdio/track.h new file mode 100644 index 00000000..9deb942b --- /dev/null +++ b/include/cdio/track.h @@ -0,0 +1,269 @@ +/* + $Id: track.h,v 1.14 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file track.h + * \brief The top-level header for track-related libcdio calls. + */ +#ifndef __CDIO_TRACK_H__ +#define __CDIO_TRACK_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*! Printable tags for track_format_t enumeration. */ + extern const char *track_format2str[6]; + + 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_PSX, /**< Playstation CD. Like audio but only 2336 bytes + * of user data. + */ + TRACK_FORMAT_ERROR /**< Dunno what is, or some other error. */ + } track_format_t; + + typedef enum { + CDIO_TRACK_FLAG_FALSE, + CDIO_TRACK_FLAG_TRUE, + CDIO_TRACK_FLAG_ERROR, + CDIO_TRACK_FLAG_UNKNOWN + } track_flag_t; + + /*! \brief Structure containing attributes associated with a track */ + typedef struct { + track_flag_t preemphasis; /**< Linear preemphasis on an audio track */ + track_flag_t copy_permit; /**< Whether copying is permitted */ + int channels; /**< Number of audio channels, 2, 4. -2 if not + implemented or -1 for error. + */ + } track_flags_t; + + /*! The leadout track is always 0xAA, regardless of # of tracks on + disc, or what value may be used internally. For example although + OS X uses a different value for the lead-out track internally than + given below, programmers should use CDIO_CDROM_LEADOUT_TRACK and + not worry about this. + */ + + /*! An enumeration for some of the CDIO_CDROM_* \#defines below. This + isn't really an enumeration one would really use in a program; it + is to be helpful in debuggers where wants just to refer to the + CDIO_CDROM_* names and get something. + */ + extern enum cdio_track_enums { + CDIO_CDROM_LBA = 0x01, /**< "logical block": first frame is #0 */ + CDIO_CDROM_MSF = 0x02, /**< "minute-second-frame": binary, not + BCD here! */ + CDIO_CDROM_DATA_TRACK = 0x04, + CDIO_CDROM_CDI_TRACK = 0x10, + CDIO_CDROM_XA_TRACK = 0x20, + CDIO_CD_MAX_TRACKS = 99, /**< Largest CD track number */ + CDIO_CDROM_LEADOUT_TRACK = 0xAA, /**< Lead-out track number */ + CDIO_INVALID_TRACK = 0xFF, /**< Constant for invalid track number */ + + } cdio_track_enums; + +#define CDIO_CD_MIN_TRACK_NO 1 /**< Smallest CD track number */ + + /*! track modes (Table 350) + reference: MMC-3 draft revsion - 10g + */ + typedef enum { + AUDIO, /**< 2352 byte block length */ + MODE1, /**< 2048 byte block length */ + MODE1_RAW, /**< 2352 byte block length */ + MODE2, /**< 2336 byte block length */ + MODE2_FORM1, /**< 2048 byte block length */ + MODE2_FORM2, /**< 2324 byte block length */ + MODE2_FORM_MIX, /**< 2336 byte block length */ + MODE2_RAW /**< 2352 byte block length */ + } trackmode_t; + + /*! + Get CD-Text information for a CdIo_t object. + + @param p_cdio the CD object that may contain CD-Text information. + @param i_track track for which we are requesting CD-Text information. + @return the CD-Text object or NULL if obj is NULL + or CD-Text information does not exist. + + If i_track is 0 or CDIO_CDROM_LEADOUT_TRACK the track returned + is the information assocated with the CD. + */ + cdtext_t *cdio_get_cdtext (CdIo_t *p_cdio, track_t i_track); + + /*! + Get the number of the first track. + + @return the track number or CDIO_INVALID_TRACK + on error. + */ + track_t cdio_get_first_track_num(const CdIo_t *p_cdio); + + /*! + Return the last track number. + CDIO_INVALID_TRACK is returned on error. + */ + track_t cdio_get_last_track_num (const CdIo_t *p_cdio); + + + /*! Find the track which contains lsn. + CDIO_INVALID_TRACK is returned if the lsn outside of the CD or + if there was some error. + + If the lsn is before the pregap of the first track 0 is returned. + Otherwise we return the track that spans the lsn. + */ + track_t cdio_get_track(const CdIo_t *p_cdio, lsn_t lsn); + + /*! Return number of channels in track: 2 or 4; -2 if not + implemented or -1 for error. + Not meaningful if track is not an audio track. + */ + int cdio_get_track_channels(const CdIo_t *p_cdio, track_t i_track); + + /*! Return copy protection status on a track. Is this meaningful + if not an audio track? + */ + track_flag_t cdio_get_track_copy_permit(const CdIo_t *p_cdio, + track_t i_track); + + /*! + Get the format (audio, mode2, mode1) of track. + */ + track_format_t cdio_get_track_format(const CdIo_t *p_cdio, track_t i_track); + + /*! + 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_t *p_cdio, track_t i_track); + + /*! + Return the ending LSN for track number + i_track in cdio. CDIO_INVALID_LSN is returned on error. + */ + lsn_t cdio_get_track_last_lsn(const CdIo_t *p_cdio, track_t i_track); + + /*! + Get the starting LBA for track number + i_track in p_cdio. Track numbers usually start at something + greater than 0, usually 1. + + The "leadout" track is specified either by + using i_track CDIO_CDROM_LEADOUT_TRACK or the total tracks+1. + + @param p_cdio object to get information from + @param i_track the track number we want the LSN for + @return the starting LBA or CDIO_INVALID_LBA on error. + */ + lba_t cdio_get_track_lba(const CdIo_t *p_cdio, track_t i_track); + + /*! + Return the starting LSN for track number + i_track in p_cdio. Track numbers usually start at something + greater than 0, usually 1. + + The "leadout" track is specified either by + using i_track CDIO_CDROM_LEADOUT_TRACK or the total tracks+1. + + @param p_cdio object to get information from + @param i_track the track number we want the LSN for + @return the starting LSN or CDIO_INVALID_LSN on error. + */ + lsn_t cdio_get_track_lsn(const CdIo_t *p_cdio, track_t i_track); + + /*! + Return the starting LBA for the pregap for track number + i_track in p_cdio. Track numbers usually start at something + greater than 0, usually 1. + + @param p_cdio object to get information from + @param i_track the track number we want the LBA for + @return the starting LBA or CDIO_INVALID_LBA on error. + */ + lba_t cdio_get_track_pregap_lba(const CdIo_t *p_cdio, track_t i_track); + + /*! + Return the starting LSN for the pregap for track number + i_track in p_cdio. Track numbers usually start at something + greater than 0, usually 1. + + @param p_cdio object to get information from + @param i_track the track number we want the LSN for + @return the starting LSN or CDIO_INVALID_LSN on error. + */ + lsn_t cdio_get_track_pregap_lsn(const CdIo_t *p_cdio, track_t i_track); + + /*! + Get the International Standard Recording Code (ISRC) for track number + i_track in p_cdio. Track numbers usually start at something + greater than 0, usually 1. + + @return the International Standard Recording Code (ISRC) or NULL + if there is none or we don't have the ability to get it. + + Note: string is malloc'd so caller has to free() the returned + string when done with it. + + */ + char * cdio_get_track_isrc (const CdIo_t *p_cdio, track_t i_track); + + /*! + Return the starting MSF (minutes/secs/frames) for track number + i_track in p_cdio. Track numbers usually start at something + greater than 0, usually 1. + + The "leadout" track is specified either by + using i_track CDIO_CDROM_LEADOUT_TRACK or the total tracks+1. + + @return true if things worked or false if there is no track entry. + */ + bool cdio_get_track_msf(const CdIo_t *p_cdio, track_t i_track, + /*out*/ msf_t *msf); + + /*! Get linear preemphasis status on an audio track + This is not meaningful if not an audio track? + */ + track_flag_t cdio_get_track_preemphasis(const CdIo_t *p_cdio, + track_t i_track); + + /*! + Get the number of sectors between this track an the next. This + includes any pregap sectors before the start of the next track. + Track numbers usually start at something + greater than 0, usually 1. + + @return the number of sectors or 0 if there is an error. + */ + unsigned int cdio_get_track_sec_count(const CdIo_t *p_cdio, track_t i_track); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_TRACK_H__ */ + diff --git a/include/cdio/types.h b/include/cdio/types.h new file mode 100644 index 00000000..347bf5a7 --- /dev/null +++ b/include/cdio/types.h @@ -0,0 +1,327 @@ +/* + $Id: types.h,v 1.37 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2008 + Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file types.h + * \brief Common type definitions used pervasively in libcdio. + */ + + +#ifndef __CDIO_TYPES_H__ +#define __CDIO_TYPES_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef EXTERNAL_LIBCDIO_CONFIG_H +#define EXTERNAL_LIBCDIO_CONFIG_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + + /* provide some C99 definitions */ + +#if defined(HAVE_SYS_TYPES_H) +#include +#endif + +#if defined(HAVE_STDINT_H) +# include +#elif defined(HAVE_INTTYPES_H) +# include +#elif defined(AMIGA) || defined(__linux__) + 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 */ + +typedef uint8_t ubyte; + + /* 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 + +#ifndef __cplusplus +# if defined(HAVE_STDBOOL_H) +# include +# else + /* ISO/IEC 9899:1999 missing -- enabling workaround */ + +# define false 0 +# define true 1 +# define bool uint8_t +# endif /*HAVE_STDBOOL_H*/ +#endif /*C++*/ + + /* 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) + + /*! + \brief MSF (minute/second/frame) structure + + One CD-ROMs addressing scheme especially used in audio formats + (Red Book) is an address by minute, sector and frame which + BCD-encoded in three bytes. An alternative format is an lba_t. + + Note: the fields in this structure are BCD encoded. Use + cdio_to_bcd8() or cdio_from_bcd8() to convert an integer into or + out of this format. The format specifier %x (not %d) can be used + if you need to format or print values in this structure. + + @see lba_t + */ + PRAGMA_BEGIN_PACKED + struct msf_s { + uint8_t m, s, f; /* BCD encoded! */ + } GNUC_PACKED; + PRAGMA_END_PACKED + + typedef struct msf_s msf_t; + +#define msf_t_SIZEOF 3 + + /*! + \brief UTF-8 char definition + + Type to denote UTF-8 strings. + */ + + typedef char cdio_utf8_t; + + typedef enum { + nope = 0, + yep = 1, + dunno = 2 + } bool_3way_t; + + /* 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. We allow for an lba to be + negative to be consistent with an lba, although I'm not sure this + this is possible. + + */ + typedef int32_t lba_t; + + /*! The type of a Logical Sector Number. Note that an lba can be negative + and the MMC3 specs allow for a conversion of a negative lba. + + @see msf_t + */ + typedef int32_t lsn_t; + + /* Address in either MSF or logical format */ + union cdio_cdrom_addr + { + msf_t msf; + lba_t lba; + }; + + /*! The type of a track number 0..99. */ + typedef uint8_t track_t; + + /*! The type of a session number 0..99. */ + typedef uint8_t session_t; + + /*! + Constant for invalid session number + */ +#define CDIO_INVALID_SESSION 0xFF + + /*! + Constant for invalid LBA. It is 151 less than the most negative + LBA -45150. This provide slack for the 150-frame offset in + LBA to LSN 150 conversions + */ +#define CDIO_INVALID_LBA -45301 + + /*! + Constant for invalid LSN + */ +#define CDIO_INVALID_LSN CDIO_INVALID_LBA + + /*! + Number of ASCII bytes in a media catalog number (MCN). + */ +#define CDIO_MCN_SIZE 13 + + /*! + Type to hold ASCII bytes in a media catalog number (MCN). + We include an extra 0 byte so these can be used as C strings. + */ + typedef char cdio_mcn_t[CDIO_MCN_SIZE+1]; + + + /*! + Number of ASCII bytes in International Standard Recording Codes (ISRC) + */ +#define CDIO_ISRC_SIZE 12 + + /*! + Type to hold ASCII bytes in a media catalog number (MCN). + We include an extra 0 byte so these can be used as C strings. + */ + typedef char cdio_isrc_t[CDIO_ISRC_SIZE+1]; + + typedef int cdio_fs_anal_t; + + /*! + track flags + Q Sub-channel Control Field (4.2.3.3) + */ + typedef enum { + CDIO_TRACK_FLAG_NONE = 0x00, /**< no flags set */ + CDIO_TRACK_FLAG_PRE_EMPHASIS = 0x01, /**< audio track recorded with + pre-emphasis */ + CDIO_TRACK_FLAG_COPY_PERMITTED = 0x02, /**< digital copy permitted */ + CDIO_TRACK_FLAG_DATA = 0x04, /**< data track */ + CDIO_TRACK_FLAG_FOUR_CHANNEL_AUDIO = 0x08, /**< 4 audio channels */ + CDIO_TRACK_FLAG_SCMS = 0x10 /**< SCMS (5.29.2.7) */ +} cdio_track_flag; + +#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/include/cdio/udf.h b/include/cdio/udf.h new file mode 100644 index 00000000..e35d74d1 --- /dev/null +++ b/include/cdio/udf.h @@ -0,0 +1,171 @@ +/* + $Id: udf.h,v 1.22 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/*! + * \file udf.h + * + * \brief The top-level interface header for libudf: UDF filesystem + * library; applications include this. + * +*/ + +#ifndef UDF_H +#define UDF_H + +#include +#include +#include + +typedef uint16_t partition_num_t; + +/** Opaque structures. */ +typedef struct udf_s udf_t; +typedef struct udf_file_s udf_file_t; +typedef struct udf_dirent_s udf_dirent_t; + +/** + Imagine the below a \#define'd value rather than distinct values of + an enum. +*/ +typedef enum { + UDF_BLOCKSIZE = 2048 +} udf_enum1_t; + +/** This variable is trickery to force the above enum symbol value to + be recorded in debug symbol tables. It is used to allow one refer + to above enumeration values in a debugger and debugger + expressions */ +extern udf_enum1_t debug_udf_enum1; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*! + Close UDF and free resources associated with p_udf. + */ + bool udf_close (udf_t *p_udf); + + /*! + Seek to a position i_start and then read i_blocks. Number of + blocks read is returned. One normally expects the return to be + equal to i_blocks. + */ + + driver_return_code_t udf_read_sectors (const udf_t *p_udf, void *ptr, + lsn_t i_start, long int i_blocks); + + /*! + Open an UDF for reading. Maybe in the future we will have + a mode. NULL is returned on error. + + Caller must free result - use udf_close for that. + */ + udf_t *udf_open (const char *psz_path); + + /*! + Return the partition number of the the opened udf handle. -1 + Is returned if we have an error. + */ + int16_t udf_get_part_number(const udf_t *p_udf); + + /*! + Get the root in p_udf. If b_any_partition is false then + the root must be in the given partition. + NULL is returned if the partition is not found or a root is not found or + there is on error. + + Caller must free result - use udf_file_free for that. + */ + udf_dirent_t *udf_get_root (udf_t *p_udf, bool b_any_partition, + partition_num_t i_partition); + + /** + * Gets the Volume Identifier string, in 8bit unicode (latin-1) + * psz_volid, place to put the string + * i_volid_size, size of the buffer volid points to + * returns the size of buffer needed for all data + */ + int udf_get_volume_id(udf_t *p_udf, /*out*/ char *psz_volid, + unsigned int i_volid); + + /** + * Gets the Volume Set Identifier, as a 128-byte dstring (not decoded) + * WARNING This is not a null terminated string + * volsetid, place to put the data + * volsetid_size, size of the buffer volsetid points to + * the buffer should be >=128 bytes to store the whole volumesetidentifier + * returns the size of the available volsetid information (128) + * or 0 on error + */ + int udf_get_volumeset_id(udf_t *p_udf, /*out*/ uint8_t *volsetid, + unsigned int i_volsetid); + + /*! + Return a file pointer matching pzz_name. + */ + udf_dirent_t *udf_fopen(udf_dirent_t *p_udf_root, const char *psz_name); + + /*! udf_mode_string - fill in string PSZ_STR with an ls-style ASCII + representation of the i_mode. PSZ_STR is returned. + + 10 characters are stored in PSZ_STR; a terminating null byte is added. + The characters stored in PSZ_STR are: + + 0 File type. 'd' for directory, 'c' for character + special, 'b' for block special, 'm' for multiplex, + 'l' for symbolic link, 's' for socket, 'p' for fifo, + '-' for regular, '?' for any other file type + + 1 'r' if the owner may read, '-' otherwise. + + 2 'w' if the owner may write, '-' otherwise. + + 3 'x' if the owner may execute, 's' if the file is + set-user-id, '-' otherwise. + 'S' if the file is set-user-id, but the execute + bit isn't set. + + 4 'r' if group members may read, '-' otherwise. + + 5 'w' if group members may write, '-' otherwise. + + 6 'x' if group members may execute, 's' if the file is + set-group-id, '-' otherwise. + 'S' if it is set-group-id but not executable. + + 7 'r' if any user may read, '-' otherwise. + + 8 'w' if any user may write, '-' otherwise. + + 9 'x' if any user may execute, 't' if the file is "sticky" + (will be retained in swap space after execution), '-' + otherwise. + 'T' if the file is sticky but not executable. */ + + char *udf_mode_string (mode_t i_mode, char *psz_str); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#include +#include + +#endif /*UDF_H*/ diff --git a/include/cdio/udf_file.h b/include/cdio/udf_file.h new file mode 100644 index 00000000..0b8fe99e --- /dev/null +++ b/include/cdio/udf_file.h @@ -0,0 +1,117 @@ +/* + $Id: udf_file.h,v 1.12 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * \file udf_file.h + * + * \brief Routines involving UDF file operations + * +*/ + +#ifndef UDF_FILE_H +#define UDF_FILE_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /** + Return the file id descriptor of the given file. + */ + bool udf_get_fileid_descriptor(const udf_dirent_t *p_udf_dirent, + /*out*/ udf_fileid_desc_t *p_udf_fid); + + /** + Return the name of the file + */ + const char *udf_get_filename(const udf_dirent_t *p_udf_dirent); + + /** + Return the name of the file + */ + bool udf_get_file_entry(const udf_dirent_t *p_udf_dirent, + /*out*/ udf_file_entry_t *p_udf_fe); + + /** + Return the number of hard links of the file. Return 0 if error. + */ + uint16_t udf_get_link_count(const udf_dirent_t *p_udf_dirent); + + /** + Return the file length the file. Return 2147483647L if error. + */ + uint64_t udf_get_file_length(const udf_dirent_t *p_udf_dirent); + + /** + Returns a POSIX mode for a given p_udf_dirent. + */ + mode_t udf_get_posix_filemode(const udf_dirent_t *p_udf_dirent); + + /** + Return the next subdirectory. + */ + udf_dirent_t *udf_opendir(const udf_dirent_t *p_udf_dirent); + + /** + Attempts to read up to count bytes from UDF directory entry + p_udf_dirent into the buffer starting at buf. buf should be a + multiple of UDF_BLOCKSIZE bytes. Reading continues after the + point at which we last read or from the beginning the first time. + + If count is zero, read() returns zero and has no other results. If + count is greater than SSIZE_MAX, the result is unspecified. + + If there is an error, cast the result to driver_return_code_t for + the specific error code. + */ + /** + Attempts to read up to count bytes from file descriptor fd into + the buffer starting at buf. + + If count is zero, read() returns zero and has no other results. If + count is greater than SSIZE_MAX, the result is unspecified. + */ + ssize_t udf_read_block(const udf_dirent_t *p_udf_dirent, + void * buf, size_t count); + + /** + Advances p_udf_direct to the the next directory entry in the + pointed to by p_udf_dir. It also returns this as the value. NULL + is returned on reaching the end-of-file or if an error. Also + p_udf_dirent is free'd. If the end of is not reached the caller + must call udf_dirent_free() with p_udf_dirent when done with it to + release resources. + */ + udf_dirent_t *udf_readdir(udf_dirent_t *p_udf_dirent); + + /** + free free resources associated with p_udf_dirent. + */ + bool udf_dirent_free(udf_dirent_t *p_udf_dirent); + + /** + Return true if the file is a directory. + */ + bool udf_is_dir(const udf_dirent_t *p_udf_dirent); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /*UDF_FILE_H*/ diff --git a/include/cdio/udf_time.h b/include/cdio/udf_time.h new file mode 100644 index 00000000..791fcdd9 --- /dev/null +++ b/include/cdio/udf_time.h @@ -0,0 +1,80 @@ +/* + $Id: udf_time.h,v 1.5 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/*! + * \file udf_time.h + * + * \brief UDF time conversion and access files. + * +*/ + +#ifndef UDF_TIME_H +#define UDF_TIME_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*! + Return the access time of the file. + */ + time_t udf_get_access_time(const udf_dirent_t *p_udf_dirent); + + /*! + Return the attribute (most recent create or access) time of the file + */ + time_t udf_get_attribute_time(const udf_dirent_t *p_udf_dirent); + + /*! + Return the modification time of the file. + */ + time_t udf_get_modification_time(const udf_dirent_t *p_udf_dirent); + + /*! + Return the access timestamp of the file + */ + udf_timestamp_t *udf_get_access_timestamp(const udf_dirent_t *p_udf_dirent); + + /*! + Return the modification timestamp of the file + */ + udf_timestamp_t *udf_get_modification_timestamp(const udf_dirent_t + *p_udf_dirent); + + /*! + Return the attr timestamp of the file + */ + udf_timestamp_t *udf_get_attr_timestamp(const udf_dirent_t *p_udf_dirent); + + /*! + Convert a UDF timestamp to a time_t. If microseconds are desired, + use dest_usec. The return value is the same as dest. */ + time_t *udf_stamp_to_time(time_t *dest, long int *dest_usec, + const udf_timestamp_t src); + + udf_timestamp_t *udf_timespec_to_stamp(const struct timespec ts, + udf_timestamp_t *dest); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /*UDF_TIME_H*/ diff --git a/include/cdio/utf8.h b/include/cdio/utf8.h new file mode 100644 index 00000000..236105e9 --- /dev/null +++ b/include/cdio/utf8.h @@ -0,0 +1,92 @@ +/* + $Id: utf8.h,v 1.2 2008/03/25 15:59:09 karl Exp $ + + Copyright (C) 2008 Rocky Bernstein + Copyright (C) 2006 Burkhard Plaum + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* UTF-8 support */ + + +#include + +/** \brief Opaque characterset converter + */ + +typedef struct cdio_charset_coverter_s cdio_charset_coverter_t; + +/** \brief Create a charset converter + * \param src_charset Source charset + * \param dst_charset Destination charset + * \returns A newly allocated charset converter + */ + +cdio_charset_coverter_t * +cdio_charset_converter_create(const char * src_charset, + const char * dst_charset); + +/** \brief Destroy a characterset converter + * \param cnv A characterset converter + */ + +void cdio_charset_converter_destroy(cdio_charset_coverter_t*cnv); + +/** \brief Convert a string from one character set to another + * \param cnv A charset converter + * \param src Source string + * \param src_len Length of source string + * \param dst Returns destination string + * \param dst_len If non NULL, returns the length of the destination string + * \returns true if conversion was sucessful, false else. + * + * The destination string must be freed by the caller with free(). + * If you pass -1 for src_len, strlen() will be used. + */ + +bool cdio_charset_convert(cdio_charset_coverter_t*cnv, + char * src, int src_len, + char ** dst, int * dst_len); + +/** \brief Convert a string from UTF-8 to another charset + * \param src Source string (0 terminated) + * \param dst Returns destination string + * \param dst_len If non NULL, returns the length of the destination string + * \param dst_charset The characterset to convert to + * \returns true if conversion was sucessful, false else. + * + * This is a convenience function, which creates a charset converter, + * converts one string and destroys the charset converter. + */ + + +bool cdio_charset_from_utf8(cdio_utf8_t * src, char ** dst, + int * dst_len, const char * dst_charset); + +/** \brief Convert a string from another charset to UTF-8 + * \param src Source string + * \param src_len Length of the source string + * \param dst Returns destination string (0 terminated) + * \param src_charset The characterset to convert from + * \returns true if conversion was sucessful, false else. + * + * This is a convenience function, which creates a charset converter, + * converts one string and destroys the charset converter. If you pass -1 + * for src_len, strlen() will be used. + */ + + +bool cdio_charset_to_utf8(char *src, size_t src_len, cdio_utf8_t **dst, + const char * src_charset); + diff --git a/include/cdio/util.h b/include/cdio/util.h new file mode 100644 index 00000000..81557dc7 --- /dev/null +++ b/include/cdio/util.h @@ -0,0 +1,117 @@ +/* + $Id: util.h,v 1.12 2008/03/25 15:59:10 karl Exp $ + + Copyright (C) 2004, 2005, 2006, 2008 Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __CDIO_UTIL_H__ +#define __CDIO_UTIL_H__ + +/*! + \file util.h + \brief Miscellaneous utility functions. + + Warning: this will probably get removed/replaced by using glib.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 uint32_t +_cdio_len2blocks (uint32_t i_len, uint16_t i_blocksize) +{ + uint32_t i_blocks; + + i_blocks = i_len / (uint32_t) i_blocksize; + if (i_len % i_blocksize) + i_blocks++; + + return i_blocks; +} + +/* round up to next block boundary */ +static inline unsigned +_cdio_ceil2block (unsigned offset, uint16_t i_blocksize) +{ + return _cdio_len2blocks (offset, i_blocksize) * i_blocksize; +} + +static inline unsigned int +_cdio_ofs_add (unsigned offset, unsigned length, uint16_t i_blocksize) +{ + if (i_blocksize - (offset % i_blocksize) < length) + offset = _cdio_ceil2block (offset, i_blocksize); + + offset += length; + + return offset; +} + +static inline const char * +_cdio_bool_str (bool b) +{ + return b ? "yes" : "no"; +} + +#ifdef __cplusplus +extern "C" { +#endif + +void * +_cdio_memdup (const void *mem, size_t count); + +char * +_cdio_strdup_upper (const char str[]); + +void +_cdio_strfreev(char **strv); + +size_t +_cdio_strlenv(char **str_array); + +char ** +_cdio_strsplit(const char str[], char delim); + +uint8_t cdio_to_bcd8(uint8_t n); +uint8_t cdio_from_bcd8(uint8_t p); + +void cdio_follow_symlink (const char * src, char * dst); + +#ifdef __cplusplus +} +#endif + +#endif /* __CDIO_UTIL_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/cdio/version.h.in b/include/cdio/version.h.in new file mode 100644 index 00000000..17d199e7 --- /dev/null +++ b/include/cdio/version.h.in @@ -0,0 +1,12 @@ +/* $Id: version.h.in,v 1.6 2005/01/29 20:54:20 rocky Exp $ */ +/** \file version.h + * + * \brief A file containing the libcdio package version + * number (@LIBCDIO_VERSION_NUM@) and OS build name. + */ + +/*! CDIO_VERSION can as a string in programs to show what version is used. */ +#define CDIO_VERSION "@VERSION@ @build@" + +/*! LIBCDIO_VERSION_NUM can be used for testing in the C preprocessor */ +#define LIBCDIO_VERSION_NUM @LIBCDIO_VERSION_NUM@ diff --git a/include/cdio/xa.h b/include/cdio/xa.h new file mode 100644 index 00000000..13b21c8f --- /dev/null +++ b/include/cdio/xa.h @@ -0,0 +1,179 @@ +/* + $Id: xa.h,v 1.19 2008/03/25 15:59:10 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2008 Rocky Bernstein + Copyright (C) 2000 Herbert Valerio Riedel + + See also iso9660.h by Eric Youngdale (1993) and in cdrtools. These are + + Copyright 1993 Yggdrasil Computing, Incorporated + Copyright (c) 1999,2000 J. Schilling + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/*! + \file xa.h + \brief Things related to the ISO-9660 XA (Extended Attributes) format + + Applications will probably not include this directly but via + the iso9660.h header. +*/ + + +#ifndef __CDIO_XA_H__ +#define __CDIO_XA_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*! An enumeration for some of the XA_* \#defines below. This isn't + really an enumeration one would really use in a program it is to + be helpful in debuggers where wants just to refer to the XA_* + names and get something. + */ + typedef enum { + ISO_XA_MARKER_OFFSET = 1024, + XA_PERM_RSYS = 0x0001, /**< System Group Read */ + XA_PERM_XSYS = 0x0004, /**< System Group Execute */ + + XA_PERM_RUSR = 0x0010, /**< User (owner) Read */ + XA_PERM_XUSR = 0x0040, /**< User (owner) Execute */ + + XA_PERM_RGRP = 0x0100, /**< Group Read */ + XA_PERM_XGRP = 0x0400, /**< Group Execute */ + + XA_PERM_ROTH = 0x1000, /**< Other (world) Read */ + XA_PERM_XOTH = 0x4000, /**< Other (world) Execute */ + + XA_ATTR_MODE2FORM1 = (1 << 11), + XA_ATTR_MODE2FORM2 = (1 << 12), + XA_ATTR_INTERLEAVED = (1 << 13), + XA_ATTR_CDDA = (1 << 14), + XA_ATTR_DIRECTORY = (1 << 15), + + XA_PERM_ALL_READ = (XA_PERM_RUSR | XA_PERM_RSYS | XA_PERM_RGRP), + XA_PERM_ALL_EXEC = (XA_PERM_XUSR | XA_PERM_XSYS | XA_PERM_XGRP), + XA_PERM_ALL_ALL = (XA_PERM_ALL_READ | XA_PERM_ALL_EXEC), + + XA_FORM1_DIR = (XA_ATTR_DIRECTORY | XA_ATTR_MODE2FORM1 | XA_PERM_ALL_ALL), + XA_FORM1_FILE = (XA_ATTR_MODE2FORM1 | XA_PERM_ALL_ALL), + XA_FORM2_FILE = (XA_ATTR_MODE2FORM2 | XA_PERM_ALL_ALL) + } xa_misc_enum_t; + +extern const char ISO_XA_MARKER_STRING[sizeof("CD-XA001")-1]; + +#define ISO_XA_MARKER_STRING "CD-XA001" + +/*! \brief "Extended Architecture" according to the Philips Yellow Book. + +CD-ROM EXtended Architecture is a modification to the CD-ROM +specification that defines two new types of sectors. CD-ROM XA was +developed jointly by Sony, Philips, and Microsoft, and announced in +August 1988. Its specifications were published in an extension to the +Yellow Book. CD-i, Photo CD, Video CD and CD-EXTRA have all +subsequently been based on CD-ROM XA. + +CD-XA defines another way of formatting sectors on a CD-ROM, including +headers in the sectors that describe the type (audio, video, data) and +some additional info (markers, resolution in case of a video or audio +sector, file numbers, etc). + +The data written on a CD-XA is consistent with and can be in ISO-9660 +file system format and therefore be readable by ISO-9660 file system +translators. But also a CD-I player can also read CD-XA discs even if +its own `Green Book' file system only resembles ISO 9660 and isn't +fully compatible. + + Note structure is big-endian. +*/ +typedef struct iso9660_xa_s +{ + uint16_t group_id; /**< 0 */ + uint16_t user_id; /**< 0 */ + uint16_t attributes; /**< XA_ATTR_ */ + char signature[2]; /**< { 'X', 'A' } */ + uint8_t filenum; /**< file number, see also XA subheader */ + uint8_t reserved[5]; /**< zero */ +} GNUC_PACKED iso9660_xa_t; + + + /*! + Returns POSIX mode bitstring for a given file. + */ + posix_mode_t iso9660_get_posix_filemode_from_xa(uint16_t i_perms); + +/*! + Returns a string interpreting the extended attribute xa_attr. + For example: + \verbatim + d---1xrxrxr + ---2--r-r-r + -a--1xrxrxr + \endverbatim + + A description of the characters in the string follows. + The 1st character is either "d" if the entry is a directory, or "-" if not + The 2nd character is either "a" if the entry is CDDA (audio), or "-" if not + The 3rd character is either "i" if the entry is interleaved, or "-" if not + The 4th character is either "2" if the entry is mode2 form2 or "-" if not + The 5th character is either "1" if the entry is mode2 form1 or "-" if not + Note that an entry will either be in mode2 form1 or mode form2. That + is you will either see "2-" or "-1" in the 4th & 5th positions. + + The 6th and 7th characters refer to permissions for a user while the + the 8th and 9th characters refer to permissions for a group while, and + the 10th and 11th characters refer to permissions for everyone. + + In each of these pairs the first character (6, 8, 10) is "x" if the + entry is executable. For a directory this means the directory is + allowed to be listed or "searched". + The second character of a pair (7, 9, 11) is "r" if the entry is allowed + to be read. +*/ +const char * +iso9660_get_xa_attr_str (uint16_t xa_attr); + +/*! + Allocates and initalizes a new iso9600_xa_t variable and returns + it. The caller should free the returned result. + + @see iso9660_xa +*/ +iso9660_xa_t * +iso9660_xa_init (iso9660_xa_t *_xa, uint16_t uid, uint16_t gid, uint16_t attr, + uint8_t filenum); + +#ifdef __cplusplus +} + +/** The below variables are trickery to force the above enum symbol + values to be recorded in debug symbol tables. They are used to + allow one to refer to the enumeration value names in the typedefs + above in a debugger and debugger expressions. +*/ +extern xa_misc_enum_t debugger_xa_misc_enum; + + +#endif /* __cplusplus */ + +#endif /* __CDIO_XA_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 00000000..b336cc7c --- /dev/null +++ b/lib/.gitignore @@ -0,0 +1,2 @@ +/Makefile +/Makefile.in diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 00000000..cd0203fd --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,30 @@ +# $Id: Makefile.am,v 1.70 2008/03/20 19:02:38 karl Exp $ +# +# Copyright (C) 2003, 2004, 2006, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +######################################################## +# make all libraries +######################################################## + +if BUILD_CD_PARANOIA +paranoiadirs = cdda_interface paranoia +endif + +if ENABLE_CXX_BINDINGS +cxxdirs = cdio++ +endif + +SUBDIRS = driver iso9660 udf $(paranoiadirs) $(cxxdirs) diff --git a/lib/cdda_interface/.cvsignore b/lib/cdda_interface/.cvsignore new file mode 100644 index 00000000..c17b1b9e --- /dev/null +++ b/lib/cdda_interface/.cvsignore @@ -0,0 +1,8 @@ +.deps +.libs +Makefile +Makefile.in +*.o +*.lo +*.la +*.la.ver diff --git a/lib/cdda_interface/.gitignore b/lib/cdda_interface/.gitignore new file mode 100644 index 00000000..f53478a0 --- /dev/null +++ b/lib/cdda_interface/.gitignore @@ -0,0 +1,3 @@ +/.deps +/Makefile +/Makefile.in diff --git a/lib/cdda_interface/Makefile.am b/lib/cdda_interface/Makefile.am new file mode 100644 index 00000000..ad2e6fd5 --- /dev/null +++ b/lib/cdda_interface/Makefile.am @@ -0,0 +1,152 @@ +# $Id: Makefile.am,v 1.16 2008/10/20 01:25:10 rocky Exp $ +# +# Copyright (C) 2004, 2005, 2006, 2007, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +######################################################## +# Things to make the cdda_interface library +######################################################## + +# +# From libtool documentation amended with guidance from N. Boullis: +# +# 1. Start with version information of `0:0:0' for each libtool library. +# +# 2. It is probably not a good idea to update the version information +# several times between public releases, but rather once per public +# release. (This seems to be more an aesthetic consideration than +# a hard technical one.) +# +# 3. If the library source code has changed at all since the last +# update, then increment REVISION (`C:R:A' becomes `C:R+1:A'). +# +# 4. If any interfaces have been added, removed, or changed since the +# last update, increment CURRENT, and set REVISION to 0. +# +# 5. If any interfaces have been added since the last public release, +# then increment AGE. +# +# 6. If any interfaces have been removed or changed since the last +# public release, then set AGE to 0. A changed interface means an +# incompatibility with previous versions. + +EXTRA_DIST = libcdio_cdda.sym + +libcdio_cdda_la_CURRENT = 0 +libcdio_cdda_la_REVISION = 5 +libcdio_cdda_la_AGE = 0 + +noinst_HEADERS = common_interface.h drive_exceptions.h low_interface.h \ + smallft.h utils.h + +libcdio_cdda_sources = common_interface.c cddap_interface.c interface.c \ + scan_devices.c smallft.c toc.c utils.c drive_exceptions.c + +lib_LTLIBRARIES = libcdio_cdda.la + +libcdio_cdda_la_SOURCES = $(libcdio_cdda_sources) +libcdio_cdda_la_ldflags = -version-info $(libcdio_cdda_la_CURRENT):$(libcdio_cdda_la_REVISION):$(libcdio_cdda_la_AGE) @LT_NO_UNDEFINED@ + +INCLUDES = $(LIBCDIO_CFLAGS) + +FLAGS=@LIBCDIO_CFLAGS@ @UCDROM_H@ @TYPESIZES@ @CFLAGS@ + +OPT=$(FLAGS) +DEBUG=$(FLAGS) -DCDDA_TEST + +## test: +## $(MAKE) libcdio_cdda.a CFLAGS="$(DEBUG)" +## $(CC) $(DEBUG) -c test_interface.c +## $(LD) $(DEBUG) test_interface.o $(LDFLAGS) -o cdda_test $(LIBS) libcdio_cdda.a + +LIBS = $(LIBCDIO_LIBS) @COS_LIB@ + + +######################################################## +# Things to version the symbols in the libraries +######################################################## + +# An explanation of the versioning problem from Nicolas Boullis and +# the versioned symbol solution he uses below... +# +# Currently, libvcdinfo uses the cdio_open function from libcdio. +# Let's imagine a program foobar that uses both the vcdinfo_open +# function from libvcdinfo and the cdio_open function from libcdio. + +# Currently, libcdio has SONAME libcdio.so.0, libvcdinfo has SONAME +# libvcdinfo.so.0 and requires libcdio.so.0, and foobar requires both +# libvcdinfo.so.0 and libcdio.so.0. Everything looks fine. +# +# Now, for some reason, you decide to change the cdio_open function. +# That's your right, but you have to bump the CURRENT version and (if I +# understand it correctly, athough this is not that clear in libtool's +# documentation) set the AGE to 0. Anyway, this bumps the SONAME, which is +# sane since the interface changes incompatibly. + +# Now, you have a new libcdio with SONAME libcdio.so.1. But libvcdinfo and +# foobar still require libcdio.so.0. Everything is still fine. + +# Now, after some minor changes, the author of foobar recompiles foobar. +# Then, foobar now requires libvcdinfo.so.0 and libcdio.so.1. And foobar +# now segfaults... + +# What is happening? When you run foobar, if brings both libvcdinfo.so.0 +# and libcdio.so.1, but libvcdinfo.so.0 also brings libcdio.so.0. So you +# have both libcdio.so.0 and libcdio.so.1 that bring their symbols to the +# global namespace. Hence, you have to incompatible versions of the +# cdio_open function in the name space. When foobar calls cdio_open, it +# may choose the wrong function, and segfaults... + +# With versioned symbols, the cdio_open function from libcdio.so.0 may be +# known as (something that looks like) cdio_open@@CDIO_0. An the cdio_open +# function from libcdio.so.1 as cdio_open@@CDIO_1. Both versions of +# libcdio would still be brought in by the most recent foobar, but foobar +# (and libvcdinfo) know which versioned function to use and then use the +# good one. + + +# This is some simple versioning where every symbol is versioned with +# something that looks like the SONAME of the library. More complex (and +# better) versioning is possible; it is for example what is used by glibc. +# But good complex versioning is something that requires much more +# work... + + +# The below is a impliments symbol versioning. First of all, I +# compute MAJOR as CURENT - AGE; that is what is used within libtool +# (at least on GNU/Linux systems) for the number in the SONAME. The +# nm command gives the list of symbols known in each of the object +# files that will be part of the shared library. And the sed command +# extracts from this list those symbols that will be shared. (This sed +# command comes from libtool.) + +libcdio_cdda_la_MAJOR = $(shell expr $(libcdio_cdda_la_CURRENT) - $(libcdio_cdda_la_AGE)) +if BUILD_VERSIONED_LIBS +libcdio_cdda_la_LDFLAGS = $(libcdio_cdda_la_ldflags) -Wl,--version-script=libcdio_cdda.la.ver +libcdio_cdda_la_DEPENDENCIES = libcdio_cdda.la.ver + +libcdio_cdda.la.ver: $(libcdio_cdda_la_OBJECTS) $(srcdir)/libcdio_cdda.sym + echo 'CDIO_CDDA_$(libcdio_cdda_la_MAJOR) { ' > $@ + objs=`for obj in $(libcdio_cdda_la_OBJECTS); do sed -ne "s/^pic_object='\(.*\)'$$/\1/p" $$obj; done`; \ + if test -n "$$objs" ; then \ + nm $${objs} | sed -n -e 's/^.*[ ][ABCDGIRSTW][ABCDGIRSTW]*[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$$/\1/p' | sort -u | { first=true; while read symbol; do if grep -q "^$${symbol}\$$" $(srcdir)/libcdio_cdda.sym; then if test $$first = true; then echo " global:"; first=false; fi; echo " $${symbol};"; fi; done; } >> $@; \ + nm $${objs} | sed -n -e 's/^.*[ ][ABCDGIRSTW][ABCDGIRSTW]*[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$$/\1/p' | sort -u | { first=true; while read symbol; do if grep -q "^$${symbol}\$$" $(srcdir)/libcdio_cdda.sym; then :; else if test $$first = true; then echo " local:"; first=false; fi; echo " $${symbol};"; fi; done; } >> $@; \ + fi + echo '};' >> $@ +else +libcdio_cdda_la_LDFLAGS = $(libcdio_cdda_la_ldflags) +endif + +MOSTLYCLEANFILES = libcdio_cdda.la.ver diff --git a/lib/cdda_interface/cddap_interface.c b/lib/cdda_interface/cddap_interface.c new file mode 100644 index 00000000..7810b3ca --- /dev/null +++ b/lib/cdda_interface/cddap_interface.c @@ -0,0 +1,401 @@ +/* + $Id: cddap_interface.c,v 1.8 2008/06/13 19:26:22 flameeyes Exp $ + + Copyright (C) 2004, 2005, 2007, 2008 Rocky Bernstein + Original interface.c Copyright (C) 1994-1997 + Eissfeldt heiko@colossus.escape.de + Current blenderization Copyright (C) 1998-1999 Monty xiphmont@mit.edu + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/** + + CD-ROM code which interfaces between user-level visible CD paranoia + routines and libddio routines. (There is some GNU/Linux-specific + code here too that should probably be removed. + +**/ + +#include "config.h" +#include "common_interface.h" +#include "low_interface.h" +#include "utils.h" + +/** The below variables are trickery to force the above enum symbol + values to be recorded in debug symbol tables. They are used to + allow one to refer to the enumeration value names in the typedefs + above in a debugger and debugger expressions +*/ + +paranoia_jitter_t debug_paranoia_jitter; +paranoia_cdda_enums_t debug_paranoia_cdda_enums; + +/*! reads TOC via libcdio and returns the number of tracks in the disc. + 0 is returned if there was an error. +*/ +static int +cddap_readtoc (cdrom_drive_t *d) +{ + int i; + track_t i_track; + + /* Save TOC Entries */ + d->tracks = cdio_get_num_tracks(d->p_cdio) ; + + if (CDIO_INVALID_TRACK == d->tracks) return 0; + + i_track = cdio_get_first_track_num(d->p_cdio); + + for ( i=0; i < d->tracks; i++) { + d->disc_toc[i].bTrack = i_track; + d->disc_toc[i].dwStartSector = cdio_get_track_lsn(d->p_cdio, i_track); + i_track++; + } + + d->disc_toc[i].bTrack = i_track; + d->disc_toc[i].dwStartSector = cdio_get_track_lsn(d->p_cdio, + CDIO_CDROM_LEADOUT_TRACK); + + d->cd_extra=FixupTOC(d, i_track); + return --i_track; /* without lead-out */ +} + + +/* Set operating speed */ +static int +cddap_setspeed(cdrom_drive_t *d, int i_speed) +{ + return cdio_set_speed(d->p_cdio, i_speed); +} + +/* read 'i_sector' adjacent audio sectors + * into buffer '*p' beginning at sector 'begin' + */ + +static long int +read_blocks (cdrom_drive_t *d, void *p, lsn_t begin, long i_sectors) +{ + int retry_count = 0; + int err; + char *buffer=(char *)p; + + do { + err = cdio_read_audio_sectors( d->p_cdio, buffer, begin, i_sectors); + + if ( DRIVER_OP_SUCCESS != err ) { + if (!d->error_retry) return -7; + + if (i_sectors==1) { + /* *Could* be I/O or media error. I think. If we're at + 30 retries, we better skip this unhappy little + sector. */ + if (retry_count>MAX_RETRIES-1) { + char b[256]; + snprintf(b, sizeof(b), + "010: Unable to access sector %ld: skipping...\n", + (long int) begin); + cderror(d, b); + return -10; + } + } + + if(retry_count>4) + if(i_sectors>1) + i_sectors=i_sectors*3/4; + retry_count++; + if (retry_count>MAX_RETRIES) { + cderror(d,"007: Unknown, unrecoverable error reading data\n"); + return(-7); + } + } else + break; + } while (err); + + return(i_sectors); +} + +typedef enum { + JITTER_NONE = 0, + JITTER_SMALL= 1, + JITTER_LARGE= 2, + JITTER_MASSIVE=3 +} jitter_baddness_t; + +/* read 'i_sector' adjacent audio sectors + * into buffer '*p' beginning at sector 'begin' + */ + +static long int +jitter_read (cdrom_drive_t *d, void *p, lsn_t begin, long i_sectors, + jitter_baddness_t jitter_badness) +{ + static int i_jitter=0; + int jitter_flag; + long i_sectors_orig = i_sectors; + long i_jitter_offset = 0; + + char *p_buf=malloc(CDIO_CD_FRAMESIZE_RAW*(i_sectors+1)); + + if (d->i_test_flags & CDDA_TEST_ALWAYS_JITTER) + jitter_flag = 1; + else +#ifdef HAVE_DRAND48 + jitter_flag = (drand48() > .9) ? 1 : 0; +#else + jitter_flag = (rand() > .9) ? 1 : 0; +#endif + + if (jitter_flag) { + int i_coeff = 0; + int i_jitter_sectors = 0; + switch(jitter_badness) { + case JITTER_SMALL : i_coeff = 4; break; + case JITTER_LARGE : i_coeff = 32; break; + case JITTER_MASSIVE: i_coeff = 128; break; + case JITTER_NONE : + default : ; + } +#ifdef HAVE_DRAND48 + i_jitter = i_coeff * (int)((drand48()-.5)*CDIO_CD_FRAMESIZE_RAW/8); +#else + i_jitter = i_coeff * (int)((rand()-.5)*CDIO_CD_FRAMESIZE_RAW/8); +#endif + + /* We may need to add another sector to compensate for the bytes that + will be dropped off when jittering, and the begin location may + be a little different. + */ + i_jitter_sectors = i_jitter / CDIO_CD_FRAMESIZE_RAW; + + if (i_jitter >= 0) + i_jitter_offset = i_jitter % CDIO_CD_FRAMESIZE_RAW; + else { + i_jitter_offset = CDIO_CD_FRAMESIZE_RAW - + (-i_jitter % CDIO_CD_FRAMESIZE_RAW); + i_jitter_sectors--; + } + + + if (begin + i_jitter_sectors > 0) { +#if !TRACE_PARANOIA + char buffer[256]; + sprintf(buffer, "jittering by %d, offset %ld\n", i_jitter, + i_jitter_offset); + cdmessage(d,buffer); +#endif + + begin += i_jitter_sectors; + i_sectors ++; + } else + i_jitter_offset = 0; + + } + + i_sectors = read_blocks(d, p_buf, begin, i_sectors); + + if (i_sectors < 0) return i_sectors; + + if (i_sectors < i_sectors_orig) + /* Had to reduce # of sectors due to read errors. So give full amount, + with no jittering. */ + memcpy(p, p_buf, i_sectors*CDIO_CD_FRAMESIZE_RAW); + else { + /* Got full amount, but now adjust size for jittering. */ + memcpy(p, p_buf+i_jitter_offset, i_sectors_orig*CDIO_CD_FRAMESIZE_RAW); + i_sectors = i_sectors_orig; + } + + free(p_buf); + return(i_sectors); +} + +/* read 'i_sector' adjacent audio sectors + * into buffer '*p' beginning at sector 'begin' + */ + +static long int +cddap_read (cdrom_drive_t *d, void *p, lsn_t begin, long i_sectors) +{ + jitter_baddness_t jitter_badness = d->i_test_flags & 0x3; + + /* read d->nsectors at a time, max. */ + i_sectors = ( i_sectors > d->nsectors && d->nsectors > 0 ) + ? d->nsectors : i_sectors; + + /* If we are testing under-run correction, we will deliberately set + what we read a frame short. */ + if (d->i_test_flags & CDDA_TEST_UNDERRUN ) + i_sectors--; + + if (jitter_badness) { + return jitter_read(d, p, begin, i_sectors, jitter_badness); + } else + return read_blocks(d, p, begin, i_sectors); + +} + +static int +verify_read_command(cdrom_drive_t *d) +{ + int i; + int16_t *buff=malloc(CDIO_CD_FRAMESIZE_RAW); + int audioflag=0; + int i_test_flags = d->i_test_flags; + + d->i_test_flags = 0; + + cdmessage(d,"Verifying drive can read CDDA...\n"); + + d->enable_cdda(d,1); + + for(i=1;i<=d->tracks;i++){ + if(cdda_track_audiop(d,i)==1){ + long firstsector=cdda_track_firstsector(d,i); + long lastsector=cdda_track_lastsector(d,i); + long sector=(firstsector+lastsector)>>1; + audioflag=1; + + if(d->read_audio(d,buff,sector,1)>0){ + cdmessage(d,"\tExpected command set reads OK.\n"); + d->enable_cdda(d,0); + free(buff); + d->i_test_flags = i_test_flags; + return(0); + } + } + } + + d->enable_cdda(d,0); + + if(!audioflag){ + cdmessage(d,"\tCould not find any audio tracks on this disk.\n"); + return(-403); + } + + cdmessage(d,"\n\tUnable to read any data; " + "drive probably not CDDA capable.\n"); + + cderror(d,"006: Could not read any data from drive\n"); + + free(buff); + return(-6); +} + +#include "drive_exceptions.h" + +#ifdef HAVE_LINUX_MAJOR_H +static void +check_exceptions(cdrom_drive_t *d, const exception_t *list) +{ + + int i=0; + while(list[i].model){ + if(!strncmp(list[i].model,d->drive_model,strlen(list[i].model))){ + if(list[i].bigendianp!=-1)d->bigendianp=list[i].bigendianp; + return; + } + i++; + } +} +#endif /* HAVE_LINUX_MAJOR_H */ + +/* set function pointers to use the ioctl routines */ +int +cddap_init_drive (cdrom_drive_t *d) +{ + int ret; + +#if HAVE_LINUX_MAJOR_H + switch(d->drive_type){ + case MATSUSHITA_CDROM_MAJOR: /* sbpcd 1 */ + case MATSUSHITA_CDROM2_MAJOR: /* sbpcd 2 */ + case MATSUSHITA_CDROM3_MAJOR: /* sbpcd 3 */ + case MATSUSHITA_CDROM4_MAJOR: /* sbpcd 4 */ + /* don't make the buffer too big; this sucker don't preempt */ + + cdmessage(d,"Attempting to set sbpcd buffer size...\n"); + + d->nsectors=8; + +#if BUFSIZE_DETERMINATION_FIXED + while(1){ + + /* this ioctl returns zero on error; exactly wrong, but that's + what it does. */ + + if (ioctl(d->ioctl_fd, CDROMAUDIOBUFSIZ, d->nsectors)==0) { + d->nsectors>>=1; + if(d->nsectors==0){ + char buffer[256]; + d->nsectors=8; + sprintf(buffer,"\tTrouble setting buffer size. Defaulting to %d sectors.\n", + d->nsectors); + cdmessage(d,buffer); + break; /* Oh, well. Try to read anyway.*/ + } + } else { + char buffer[256]; + sprintf(buffer,"\tSetting read block size at %d sectors (%ld bytes).\n", + d->nsectors,(long)d->nsectors*CDIO_CD_FRAMESIZE_RAW); + cdmessage(d,buffer); + break; + } + } +#endif /* BUFSIZE_DETERMINATION_FIXED */ + + break; + case IDE0_MAJOR: + case IDE1_MAJOR: + case IDE2_MAJOR: + case IDE3_MAJOR: + d->nsectors=8; /* it's a define in the linux kernel; we have no + way of determining other than this guess tho */ + d->bigendianp=0; + d->is_atapi=1; + + check_exceptions(d, atapi_list); + + break; + default: + d->nsectors=25; /* The max for SCSI MMC2 */ + } +#else + { + char buffer[256]; + d->nsectors = 8; + sprintf(buffer,"\tSetting read block size at %d sectors (%ld bytes).\n", + d->nsectors,(long)d->nsectors*CDIO_CD_FRAMESIZE_RAW); + cdmessage(d,buffer); + } +#endif /*HAVE_LINUX_MAJOR_H*/ + + d->enable_cdda = dummy_exception; + d->set_speed = cddap_setspeed; + d->read_toc = cddap_readtoc; + d->read_audio = cddap_read; + + ret = d->tracks = d->read_toc(d); + if(d->tracks<1) + return(ret); + + d->opened=1; + + if( (ret=verify_read_command(d)) ) return(ret); + + d->error_retry=1; + + return(0); +} + diff --git a/lib/cdda_interface/common_interface.c b/lib/cdda_interface/common_interface.c new file mode 100644 index 00000000..33d476f6 --- /dev/null +++ b/lib/cdda_interface/common_interface.c @@ -0,0 +1,271 @@ +/* + $Id: common_interface.c,v 1.17 2008/04/16 17:00:40 karl Exp $ + + Copyright (C) 2004, 2005, 2007, 2008 Rocky Bernstein + Copyright (C) 1998, 2002 Monty monty@xiph.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/****************************************************************** + * + * CDROM communication common to all interface methods is done here + * (mostly ioctl stuff, but not ioctls specific to the 'cooked' + * interface) + * + ******************************************************************/ + +#include +#include "common_interface.h" +#include "utils.h" +#include "smallft.h" + +/* Variables to hold debugger-helping enumerations */ +enum paranoia_cdda_enums; +enum paranoia_jitter_enums; + +/*! Determine Endian-ness of the CD-drive based on reading data from + it. Some drives return audio data Big Endian while some (most) + return data Little Endian. Drives known to return data bigendian are + SCSI drives from Kodak, Ricoh, HP, Philips, Plasmon, Grundig + CDR100IPW, and Mitsumi CD-R. ATAPI and MMC drives are little endian. + + rocky: As someone who didn't write the code, I have to say this is + nothing less than brilliant. An FFT is done both ways and the the + transform is looked at to see which has data in the FFT (or audible) + portion. (Or so that's how I understand it.) + + @return 1 if big-endian, 0 if little-endian, -1 if we couldn't + figure things out or some error. + */ +int +data_bigendianp(cdrom_drive_t *d) +{ + float lsb_votes=0; + float msb_votes=0; + int i,checked; + int endiancache=d->bigendianp; + float *a=calloc(1024,sizeof(float)); + float *b=calloc(1024,sizeof(float)); + long readsectors=5; + int16_t *buff=malloc(readsectors*CDIO_CD_FRAMESIZE_RAW); + memset(buff, 0, readsectors*CDIO_CD_FRAMESIZE_RAW); + + /* look at the starts of the audio tracks */ + /* if real silence, tool in until some static is found */ + + /* Force no swap for now */ + d->bigendianp=-1; + + cdmessage(d,"\nAttempting to determine drive endianness from data..."); + d->enable_cdda(d,1); + for(i=0,checked=0;itracks;i++){ + float lsb_energy=0; + float msb_energy=0; + if(cdda_track_audiop(d,i+1)==1){ + long firstsector=cdda_track_firstsector(d,i+1); + long lastsector=cdda_track_lastsector(d,i+1); + int zeroflag=-1; + long beginsec=0; + + /* find a block with nonzero data */ + + while(firstsector+readsectors<=lastsector){ + int j; + + if(d->read_audio(d,buff,firstsector,readsectors)>0){ + + /* Avoid scanning through jitter at the edges */ + for(beginsec=0;beginsecenable_cdda(d,0); + free(a); + free(b); + free(buff); + return(-1); + } + } + + beginsec*=CDIO_CD_FRAMESIZE_RAW/2; + + /* un-interleave for an FFT */ + if(!zeroflag){ + int j; + + for(j=0;j<128;j++) + a[j] = le16_to_cpu(buff[j*2+beginsec+460]); + for(j=0;j<128;j++) + b[j] = le16_to_cpu(buff[j*2+beginsec+461]); + + fft_forward(128,a,NULL,NULL); + fft_forward(128,b,NULL,NULL); + + for(j=0;j<128;j++) + lsb_energy+=fabs(a[j])+fabs(b[j]); + + for(j=0;j<128;j++) + a[j] = be16_to_cpu(buff[j*2+beginsec+460]); + + for(j=0;j<128;j++) + b[j] = be16_to_cpu(buff[j*2+beginsec+461]); + + fft_forward(128,a,NULL,NULL); + fft_forward(128,b,NULL,NULL); + + for(j=0;j<128;j++) + msb_energy+=fabs(a[j])+fabs(b[j]); + } + } + if(lsb_energymsb_energy){ + msb_votes+=lsb_energy/msb_energy; + checked++; + } + + if(checked==5 && (lsb_votes==0 || msb_votes==0))break; + cdmessage(d,"."); + } + + free(buff); + free(a); + free(b); + d->bigendianp=endiancache; + d->enable_cdda(d,0); + + /* How did we vote? Be potentially noisy */ + if (lsb_votes>msb_votes) { + char buffer[256]; + cdmessage(d,"\n\tData appears to be coming back Little Endian.\n"); + sprintf(buffer,"\tcertainty: %d%%\n",(int) + (100.*lsb_votes/(lsb_votes+msb_votes)+.5)); + cdmessage(d,buffer); + return(0); + } else { + if(msb_votes>lsb_votes){ + char buffer[256]; + cdmessage(d,"\n\tData appears to be coming back Big Endian.\n"); + sprintf(buffer,"\tcertainty: %d%%\n",(int) + (100.*msb_votes/(lsb_votes+msb_votes)+.5)); + cdmessage(d,buffer); + return(1); + } + + cdmessage(d,"\n\tCannot determine CDROM drive endianness.\n"); + return(bigendianp()); + return(-1); + } +} + +/************************************************************************/ + +/*! Here we fix up a couple of things that will never happen. yeah, + right. + + The multisession stuff is from Hannu code; it assumes it knows the + leadout/leadin size. [I think Hannu refers to Hannu Savolainen + from GNU/Linux Kernel code.] + + @return -1 if we can't get multisession info, 0 if there is one + session only or the last session LBA is the same as the first audio + track and 1 if the multi-session lba is higher than first audio track +*/ +int +FixupTOC(cdrom_drive_t *d, track_t i_tracks) +{ + int j; + + /* First off, make sure the 'starting sector' is >=0 */ + + for( j=0; jdisc_toc[j].dwStartSector<0 ) { + cdmessage(d,"\n\tTOC entry claims a negative start offset: massaging" + ".\n"); + d->disc_toc[j].dwStartSector=0; + } + if( jdisc_toc[j].dwStartSector> + d->disc_toc[j+1].dwStartSector ) { + cdmessage(d,"\n\tTOC entry claims an overly large start offset: massaging" + ".\n"); + d->disc_toc[j].dwStartSector=0; + } + + } + /* Make sure the listed 'starting sectors' are actually increasing. + Flag things that are blatant/stupid/wrong */ + { + lsn_t last=d->disc_toc[0].dwStartSector; + for ( j=1; jdisc_toc[j].dwStartSectordisc_toc[j].dwStartSector=last; + + } + last=d->disc_toc[j].dwStartSector; + } + } + + d->audio_last_sector = CDIO_INVALID_LSN; + + { + lsn_t last_ses_lsn; + if (cdio_get_last_session (d->p_cdio, &last_ses_lsn) < 0) + return -1; + + /* A Red Book Disc must have only one session, otherwise this is a + * CD Extra */ + if (last_ses_lsn > d->disc_toc[0].dwStartSector) { + /* CD Extra discs have two session, the first one ending after + * the last audio track + * Thus the need to fix the length of the the audio data portion to + * not cross the lead-out of this session */ + for (j = i_tracks-1; j > 1; j--) { + if (cdio_get_track_format(d->p_cdio, j+1) != TRACK_FORMAT_AUDIO && + cdio_get_track_format(d->p_cdio, j) == TRACK_FORMAT_AUDIO) { + /* First session lead-out is 1:30 + * Lead-ins are 1:00 + * Every session's first track have a 0:02 pregap + * + * That makes a control data section of (90+60+2)*75 sectors in the + * last audio track */ + const int gap = ((90+60+2) * CDIO_CD_FRAMES_PER_SEC); + + if ((last_ses_lsn - gap >= d->disc_toc[j-1].dwStartSector) && + (last_ses_lsn - gap < d->disc_toc[j].dwStartSector)) { + d->audio_last_sector = last_ses_lsn - gap - 1; + break; + } + } + } + return 1; + } + } + + return 0; +} + + diff --git a/lib/cdda_interface/common_interface.h b/lib/cdda_interface/common_interface.h new file mode 100644 index 00000000..f7dad0a4 --- /dev/null +++ b/lib/cdda_interface/common_interface.h @@ -0,0 +1,54 @@ +/* + $Id: common_interface.h,v 1.7 2008/04/16 17:00:40 karl Exp $ + + Copyright (C) 2004, 2005, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _CDDA_COMMON_INTERFACE_H_ +#define _CDDA_COMMON_INTERFACE_H_ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "low_interface.h" + +#if defined(HAVE_LSTAT) && !defined(HAVE_WIN32_CDROM) +/* Define this if the CD-ROM device is a file in the filesystem + that can be lstat'd +*/ +#define DEVICE_IN_FILESYSTEM 1 +#else +#undef DEVICE_IN_FILESYSTEM +#endif + +/** Test for presence of a cdrom by pinging with the 'CDROMVOLREAD' ioctl() */ +extern int ioctl_ping_cdrom(int fd); + +extern char *atapi_drive_info(int fd); + +/*! Here we fix up a couple of things that will never happen. yeah, + right. + + rocky OMITTED FOR NOW: + The multisession stuff is from Hannu's code; it assumes it knows + the leadout/leadin size. +*/ +extern int FixupTOC(cdrom_drive_t *d, track_t tracks); + +#endif /*_CDDA_COMMON_INTERFACE_H_*/ diff --git a/lib/cdda_interface/drive_exceptions.c b/lib/cdda_interface/drive_exceptions.c new file mode 100644 index 00000000..d77dbe12 --- /dev/null +++ b/lib/cdda_interface/drive_exceptions.c @@ -0,0 +1,84 @@ +/* + $Id: drive_exceptions.c,v 1.1 2008/06/14 10:36:49 flameeyes Exp $ + + Copyright (C) 2004, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "common_interface.h" +#include "drive_exceptions.h" + +int dummy_exception (cdrom_drive_t *d,int Switch) +{ + return(0); +} + +#if HAVE_LINUX_MAJOR_H +/* list of drives that affect autosensing in ATAPI specific portions of code + (force drives to detect as ATAPI or SCSI, force ATAPI read command */ + +const exception_t atapi_list[]={ + {"SAMSUNG SCR-830 REV 2.09 2.09 ", 1, 0, dummy_exception,scsi_read_mmc2,0}, + {"Memorex CR-622", 1, 0, dummy_exception, NULL,0}, + {"SONY CD-ROM CDU-561", 0, 0, dummy_exception, NULL,0}, + {"Chinon CD-ROM CDS-525", 0, 0, dummy_exception, NULL,0}, + {NULL,0,0,NULL,NULL,0}}; +#endif /*HAVE_LINUX_MAJOR_H*/ + +/* list of drives that affect MMC default settings */ + +#ifdef NEED_MMC_LIST +static exception_t mmc_list[]={ + {"SAMSUNG SCR-830 REV 2.09 2.09 ", 1, 0, dummy_exception,scsi_read_mmc2,0}, + {"Memorex CR-622", 1, 0, dummy_exception, NULL,0}, + {"SONY CD-ROM CDU-561", 0, 0, dummy_exception, NULL,0}, + {"Chinon CD-ROM CDS-525", 0, 0, dummy_exception, NULL,0}, + {"KENWOOD CD-ROM UCR", -1, 0, NULL,scsi_read_D8, 0}, + {NULL,0,0,NULL,NULL,0}}; +#endif /*NEED_MMC_LIST*/ + +/* list of drives that affect SCSI default settings */ + +#ifdef NEED_SCSI_LIST +static exception_t scsi_list[]={ + {"TOSHIBA", -1,0x82,scsi_enable_cdda,scsi_read_28, 0}, + {"IBM", -1,0x82,scsi_enable_cdda,scsi_read_28, 0}, + {"DEC", -1,0x82,scsi_enable_cdda,scsi_read_28, 0}, + + {"IMS", -1, 0,scsi_enable_cdda,scsi_read_28, 1}, + {"KODAK", -1, 0,scsi_enable_cdda,scsi_read_28, 1}, + {"RICOH", -1, 0,scsi_enable_cdda,scsi_read_28, 1}, + {"HP", -1, 0,scsi_enable_cdda,scsi_read_28, 1}, + {"PHILIPS", -1, 0,scsi_enable_cdda,scsi_read_28, 1}, + {"PLASMON", -1, 0,scsi_enable_cdda,scsi_read_28, 1}, + {"GRUNDIG CDR100IPW", -1, 0,scsi_enable_cdda,scsi_read_28, 1}, + {"MITSUMI CD-R ", -1, 0,scsi_enable_cdda,scsi_read_28, 1}, + {"KENWOOD CD-ROM UCR", -1, 0, NULL,scsi_read_D8, 0}, + + {"YAMAHA", -1, 0,scsi_enable_cdda, NULL, 0}, + + {"PLEXTOR", -1, 0, NULL, NULL, 0}, + {"SONY", -1, 0, NULL, NULL, 0}, + + {"NEC", -1, 0, NULL,scsi_read_D4_10,0}, + + /* the 7501 locks up if hit with the 10 byte version from the + autoprobe first */ + {"MATSHITA CD-R CW-7501", -1, 0, NULL,scsi_read_D4_12,-1}, + + {NULL,0,0,NULL,NULL,0}}; + +#endif /* NEED_SCSI_LIST*/ diff --git a/lib/cdda_interface/drive_exceptions.h b/lib/cdda_interface/drive_exceptions.h new file mode 100644 index 00000000..080f9ea2 --- /dev/null +++ b/lib/cdda_interface/drive_exceptions.h @@ -0,0 +1,58 @@ +/* + $Id: drive_exceptions.h,v 1.6 2008/06/13 19:26:23 flameeyes Exp $ + + Copyright (C) 2004, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +extern int scsi_enable_cdda(cdrom_drive_t *d, int); +extern long scsi_read_mmc(cdrom_drive_t *d, void *,long,long); +extern long scsi_read_D4_10(cdrom_drive_t *, void *,long,long); +extern long scsi_read_D4_12(cdrom_drive_t *, void *,long,long); +extern long scsi_read_D8(cdrom_drive_t *, void *,long,long); +extern long scsi_read_28(cdrom_drive_t *, void *,long,long); +extern long scsi_read_A8(cdrom_drive_t *, void *,long,long); + +typedef struct exception { + const char *model; + int atapi; /* If the ioctl doesn't work */ + unsigned char density; + int (*enable)(cdrom_drive_t *,int); + long (*read)(cdrom_drive_t *,void *, long, long); + int bigendianp; +} exception_t; + +/* specific to general */ + +#ifdef FINISHED_DRIVE_EXCEPTIONS +extern long scsi_read_mmc2(cdrom_drive_t *d, void *,long,long); +#else +#define scsi_read_mmc2 NULL +#endif + +int dummy_exception (cdrom_drive_t *d,int Switch); + +#if HAVE_LINUX_MAJOR_H +extern const exception_t atapi_list[]; +#endif + +#ifdef NEED_MMC_LIST +extern const exception_t mmc_list[]; +#endif + +#ifdef NEED_SCSI_LIST +extern const exception_t scsi_list[]; +#endif diff --git a/lib/cdda_interface/interface.c b/lib/cdda_interface/interface.c new file mode 100644 index 00000000..03af2324 --- /dev/null +++ b/lib/cdda_interface/interface.c @@ -0,0 +1,188 @@ +/* + $Id: interface.c,v 1.26 2008/04/16 17:00:40 karl Exp $ + + Copyright (C) 2005, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/****************************************************************** + * Top-level interface module for cdrom drive access. SCSI, ATAPI, etc + * specific stuff are in other modules. Note that SCSI does use + * specialized ioctls; these appear in common_interface.c where the + * generic_scsi stuff is in scsi_interface.c. + * + ******************************************************************/ + +#include "common_interface.h" +#include "low_interface.h" +#include "utils.h" +#include +#include + +static void _clean_messages(cdrom_drive_t *d) +{ + if(d){ + if(d->messagebuf)free(d->messagebuf); + if(d->errorbuf)free(d->errorbuf); + d->messagebuf=NULL; + d->errorbuf=NULL; + } +} + +/*! + Closes d and releases all storage associated with it except + the internal p_cdio pointer. + + @param d cdrom_drive_t object to be closed. + @return 0 if passed a null pointer and 1 if not in which case + some work was probably done. + + @see cdio_cddap_close +*/ +bool +cdio_cddap_close_no_free_cdio(cdrom_drive_t *d) +{ + if(d){ + if(d->opened) + d->enable_cdda(d,0); + + _clean_messages(d); + if (d->cdda_device_name) free(d->cdda_device_name); + if (d->drive_model) free(d->drive_model); + d->cdda_device_name = d->drive_model = NULL; + free(d); + return true; + } + return false; +} + +/*! + Closes d and releases all storage associated with it. + Doubles as "cdrom_drive_free()". + + @param d cdrom_drive_t object to be closed. + @return 0 if passed a null pointer and 1 if not in which case + some work was probably done. + + @see cdio_cddap_close_no_free_cdio +*/ +int +cdio_cddap_close(cdrom_drive_t *d) +{ + if (d) { + CdIo_t *p_cdio = d->p_cdio; + cdio_cddap_close_no_free_cdio(d); + cdio_destroy (p_cdio); + return 1; + } + return 0; +} + +/* finish initializing the drive! */ +int +cdio_cddap_open(cdrom_drive_t *d) +{ + int ret; + if(d->opened)return(0); + + if ( (ret=cddap_init_drive(d)) ) + return(ret); + + /* Check TOC, enable for CDDA */ + + /* Some drives happily return a TOC even if there is no disc... */ + { + int i; + for(i=0; itracks; i++) + if(d->disc_toc[i].dwStartSector<0 || + d->disc_toc[i+1].dwStartSector==0){ + d->opened=0; + cderror(d,"009: CDROM reporting illegal table of contents\n"); + return(-9); + } + } + + if((ret=d->enable_cdda(d,1))) + return(ret); + + /* d->select_speed(d,d->maxspeed); most drives are full speed by default */ + + if ( -1 == d->bigendianp ) { + d->bigendianp = data_bigendianp(d); + } + + + return(0); +} + +int +cdio_cddap_speed_set(cdrom_drive_t *d, int speed) +{ + return d->set_speed ? d->set_speed(d, speed) : 0; +} + +long +cdio_cddap_read(cdrom_drive_t *d, void *buffer, lsn_t beginsector, + long sectors) +{ + if (d->opened) { + if (sectors>0) { + sectors=d->read_audio(d, buffer, beginsector, sectors); + + if (sectors > 0) { + /* byteswap? */ + if ( d->bigendianp == -1 ) /* not determined yet */ + d->bigendianp = data_bigendianp(d); + + if ( d->b_swap_bytes && d->bigendianp != bigendianp() ) { + int i; + uint16_t *p=(uint16_t *)buffer; + long els=sectors*CDIO_CD_FRAMESIZE_RAW/2; + + for(i=0;imessagedest=mes_action; + d->errordest=err_action; +} + +extern char * +cdio_cddap_messages(cdrom_drive_t *d) +{ + char *ret=d->messagebuf; + d->messagebuf=NULL; + return(ret); +} + +extern char * +cdio_cddap_errors(cdrom_drive_t *d) +{ + char *ret=d->errorbuf; + d->errorbuf=NULL; + return(ret); +} + diff --git a/lib/cdda_interface/libcdio_cdda.sym b/lib/cdda_interface/libcdio_cdda.sym new file mode 100644 index 00000000..a0801b62 --- /dev/null +++ b/lib/cdda_interface/libcdio_cdda.sym @@ -0,0 +1,22 @@ +cdio_cddap_find_a_cdrom +cdio_cddap_identify +cdio_cddap_identify_cdio +cdio_cddap_speed_set +cdio_cddap_verbose_set +cdio_cddap_messages +cdio_cddap_errors +cdio_cddap_close_no_free_cdio +cdio_cddap_close +cdio_cddap_open +cdio_cddap_read +cdio_cddap_track_firstsector +cdio_cddap_track_lastsector +cdio_cddap_tracks +cdio_cddap_sector_gettrack +cdio_cddap_track_channels +cdio_cddap_track_audiop +cdio_cddap_track_copyp +cdio_cddap_track_preemp +cdio_cddap_disc_firstsector +cdio_cddap_disc_lastsector +data_bigendianp diff --git a/lib/cdda_interface/low_interface.h b/lib/cdda_interface/low_interface.h new file mode 100644 index 00000000..e4495ba9 --- /dev/null +++ b/lib/cdda_interface/low_interface.h @@ -0,0 +1,66 @@ +/* + $Id: low_interface.h,v 1.9 2008/04/16 17:00:40 karl Exp $ + + Copyright (C) 2004, 2005, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/** internal include file for cdda interface kit for Linux */ + +#ifndef _CDDA_LOW_INTERFACE_ +#define _CDDA_LOW_INTERFACE_ + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#ifdef HAVE_LINUX_VERSION_H +#include +#endif + +#include +#include + +/* some include file locations have changed with newer kernels */ + +#ifndef CDROMAUDIOBUFSIZ +#define CDROMAUDIOBUFSIZ 0x5382 /* set the audio buffer size */ +#endif + +#ifdef HAVE_LINUX_CDROM_H +#include +#endif + +#ifdef HAVE_LINUX_MAJOR_H +#include +#endif + +#define MAX_RETRIES 8 +#define MAX_BIG_BUFF_SIZE 65536 +#define MIN_BIG_BUFF_SIZE 4096 +#define SG_OFF sizeof(struct sg_header) + +extern int cddap_init_drive (cdrom_drive_t *d); +#endif /*_CDDA_LOW_INTERFACE_*/ + diff --git a/lib/cdda_interface/scan_devices.c b/lib/cdda_interface/scan_devices.c new file mode 100644 index 00000000..a211c449 --- /dev/null +++ b/lib/cdda_interface/scan_devices.c @@ -0,0 +1,335 @@ +/* + $Id: scan_devices.c,v 1.33 2008/06/16 19:45:44 flameeyes Exp $ + + Copyright (C) 2004, 2005, 2007, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/****************************************************************** + * + * Autoscan for or verify presence of a CD-ROM device + * + ******************************************************************/ + +#include "common_interface.h" +#include "low_interface.h" +#include "utils.h" +#include "cdio/mmc.h" +#include +#include + +#ifdef HAVE_PWD_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#define MAX_DEV_LEN 20 /* Safe because strings only come from below */ +/* must be absolute paths! */ +static const char scsi_cdrom_prefixes[][16]={ + "/dev/scd", + "/dev/sr", + ""}; +static const char scsi_generic_prefixes[][16]={ + "/dev/sg", + ""}; + +static const char devfs_scsi_test[]="/dev/scsi/"; +static const char devfs_scsi_cd[]="cd"; +static const char devfs_scsi_generic[]="generic"; + +static const char cdrom_devices[][32]={ + "/dev/cdrom", + "/dev/cdroms/cdrom?", + "/dev/hd?", + "/dev/sg?", + "/dev/cdu31a", + "/dev/cdu535", + "/dev/sbpcd", + "/dev/sbpcd?", + "/dev/sonycd", + "/dev/mcd", + "/dev/sjcd", + /* "/dev/aztcd", timeout is too long */ + "/dev/cm206cd", + "/dev/gscd", + "/dev/optcd", + ""}; + +static cdrom_drive_t * +cdda_identify_device_cdio(CdIo_t *p_cdio, const char *psz_device, + int messagedest, char **ppsz_messages); + +/* Functions here look for a cdrom drive; full init of a drive type + happens in interface.c */ + +cdrom_drive_t * +cdio_cddap_find_a_cdrom(int messagedest, char **ppsz_messages){ + /* Brute force... */ + + int i=0; + cdrom_drive_t *d; + + while(*cdrom_devices[i]!='\0'){ + + /* is it a name or a pattern? */ + char *pos; + if((pos=strchr(cdrom_devices[i],'?'))){ + int j; + /* try first eight of each device */ + for(j=0;j<4;j++){ + char *buffer=strdup(cdrom_devices[i]); + + /* number, then letter */ + + buffer[pos-(cdrom_devices[i])]=j+48; + if((d=cdda_identify(buffer, messagedest, ppsz_messages))) + return(d); + idmessage(messagedest, ppsz_messages, "", NULL); + buffer[pos-(cdrom_devices[i])]=j+97; + if((d=cdda_identify(buffer, messagedest, ppsz_messages))) + return(d); + idmessage(messagedest, ppsz_messages, "", NULL); + } + }else{ + /* Name. Go for it. */ + if((d=cdda_identify(cdrom_devices[i], messagedest, ppsz_messages))) + return(d); + + idmessage(messagedest, ppsz_messages, "", NULL); + } + i++; + } + { +#if defined(HAVE_GETPWUID) && defined(HAVE_GETEUID) + struct passwd *temp; + temp=getpwuid(geteuid()); + idmessage(messagedest, ppsz_messages, + "\n\nNo cdrom drives accessible to %s found.\n", + temp->pw_name); +#else + idmessage(messagedest, ppsz_messages, + "\n\nNo cdrom drives accessible found.\n", NULL); +#endif + } + return(NULL); +} + +#ifdef DEVICE_IN_FILESYSTEM +static char * +test_resolve_symlink(const char *file, int messagedest, char **ppsz_messages) +{ + char resolved[PATH_MAX]; + struct stat st; + if (lstat(file,&st)){ + idperror(messagedest, ppsz_messages, "\t\tCould not stat %s",file); + return(NULL); + } + + if (realpath(file,resolved)) + return(strdup(resolved)); + + idperror(messagedest, ppsz_messages, "\t\tCould not resolve symlink %s", + file); + return(NULL); +} +#endif + +/** Returns a paranoia CD-ROM drive object with a CD-DA in it or NULL + if there was an error. + @see cdio_cddap_identify_cdio + */ +cdrom_drive_t * +cdio_cddap_identify(const char *psz_dev, int messagedest, +char **ppsz_messages) +{ + CdIo_t *p_cdio = NULL; + + if (psz_dev) + idmessage(messagedest, ppsz_messages, "Checking %s for cdrom...", + psz_dev); + else + idmessage(messagedest, ppsz_messages, "Checking for cdrom...", NULL); + +#ifdef DEVICE_IN_FILESYSTEM + if (psz_dev) { + char *psz_device = test_resolve_symlink(psz_dev, messagedest, + ppsz_messages); + if ( psz_device ) { + cdrom_drive_t *d=NULL; + p_cdio = cdio_open(psz_device, DRIVER_UNKNOWN); + d = cdda_identify_device_cdio(p_cdio, psz_device, messagedest, + ppsz_messages); + free(psz_device); + return d; + } + } +#endif + + p_cdio = cdio_open(psz_dev, DRIVER_UNKNOWN); + if (p_cdio) { + if (!psz_dev) { + psz_dev = cdio_get_arg(p_cdio, "source"); + } + return cdda_identify_device_cdio(p_cdio, psz_dev, messagedest, + ppsz_messages); + } + return NULL; +} + +/** Returns a paranoia CD-ROM drive object with a CD-DA in it or NULL + if there was an error. In contrast to cdio_cddap_identify, we + start out with an initialized p_cdio object. For example you may + have used that for other purposes such as to get CDDB/CD-Text + information. @see cdio_cddap_identify + */ +cdrom_drive_t * +cdio_cddap_identify_cdio(CdIo_t *p_cdio, int messagedest, char **ppsz_messages) +{ + if (!p_cdio) return NULL; + { + const char *psz_device = cdio_get_arg(p_cdio, "source"); + idmessage(messagedest, ppsz_messages, "Checking %s for cdrom...", + psz_device); + return cdda_identify_device_cdio(p_cdio, psz_device, messagedest, + ppsz_messages); + } + +} + +static cdrom_drive_t * +cdda_identify_device_cdio(CdIo_t *p_cdio, const char *psz_device, + int messagedest, char **ppsz_messages) +{ + cdrom_drive_t *d=NULL; + int drive_type = 0; + char *description=NULL; +#ifdef HAVE_LINUX_MAJOR_H + struct stat st; +#endif + + if (!p_cdio) { + idperror(messagedest, ppsz_messages, "\t\tUnable to open %s", psz_device); + return NULL; + } + +#ifdef HAVE_LINUX_MAJOR_H + if ( 0 == stat(psz_device, &st) ) { + if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { + drive_type=(int)(st.st_rdev>>8); + switch (drive_type) { + case IDE0_MAJOR: + case IDE1_MAJOR: + case IDE2_MAJOR: + case IDE3_MAJOR: + /* Yay, ATAPI... */ + description=strdup("ATAPI compatible "); + break; + case CDU31A_CDROM_MAJOR: + /* major indicates this is a cdrom; no ping necessary. */ + description=strdup("Sony CDU31A or compatible"); + break; + case CDU535_CDROM_MAJOR: + /* major indicates this is a cdrom; no ping necessary. */ + description=strdup("Sony CDU535 or compatible"); + break; + + case MATSUSHITA_CDROM_MAJOR: + case MATSUSHITA_CDROM2_MAJOR: + case MATSUSHITA_CDROM3_MAJOR: + case MATSUSHITA_CDROM4_MAJOR: + /* major indicates this is a cdrom; no ping necessary. */ + description=strdup("non-ATAPI IDE-style Matsushita/Panasonic CR-5xx or compatible"); + break; + case SANYO_CDROM_MAJOR: + description=strdup("Sanyo proprietary or compatible: NOT CDDA CAPABLE"); + break; + case MITSUMI_CDROM_MAJOR: + case MITSUMI_X_CDROM_MAJOR: + description=strdup("Mitsumi proprietary or compatible: NOT CDDA CAPABLE"); + break; + case OPTICS_CDROM_MAJOR: + description=strdup("Optics Dolphin or compatible: NOT CDDA CAPABLE"); + break; + case AZTECH_CDROM_MAJOR: + description=strdup("Aztech proprietary or compatible: NOT CDDA CAPABLE"); + break; + case GOLDSTAR_CDROM_MAJOR: + description=strdup("Goldstar proprietary: NOT CDDA CAPABLE"); + break; + case CM206_CDROM_MAJOR: + description=strdup("Philips/LMS CM206 proprietary: NOT CDDA CAPABLE"); + break; + + case SCSI_CDROM_MAJOR: + case SCSI_GENERIC_MAJOR: + /* Nope nope nope */ + description=strdup("SCSI CD-ROM"); + break; + default: + /* What the hell is this? */ + idmessage(messagedest, ppsz_messages, + "\t\t%s is not a cooked ioctl CDROM.", + psz_device); + return(NULL); + } + } + } +#endif /*HAVE_LINUX_MAJOR_H*/ + + /* Minimum init */ + + d=calloc(1, sizeof(cdrom_drive_t)); + d->p_cdio = p_cdio; + d->cdda_device_name = strdup(psz_device); + d->drive_type = drive_type; + d->bigendianp = -1; /* We don't know yet... */ + d->nsectors = -1; /* We don't know yet... */ + d->messagedest = messagedest; + d->b_swap_bytes = true; + + { + cdio_hwinfo_t hw_info = { + "UNKNOWN", "Unknown model", "????" + }; + + if ( mmc_get_hwinfo( p_cdio, &hw_info ) ) { + unsigned int i_len = strlen(hw_info.psz_vendor) + + strlen(hw_info.psz_model) + + strlen(hw_info.psz_revision) + 5; + + if (description) { + i_len += strlen(description); + d->drive_model=malloc( i_len ); + snprintf( d->drive_model, i_len, "%s %s %s %s", + hw_info.psz_vendor, hw_info.psz_model, hw_info.psz_revision, + description ); + free(description); + } else { + d->drive_model=malloc( i_len ); + snprintf( d->drive_model, i_len, "%s %s %s", + hw_info.psz_vendor, hw_info.psz_model, hw_info.psz_revision + ); + } + idmessage(messagedest, ppsz_messages, "\t\tCDROM sensed: %s\n", + d->drive_model); + } + } + + return(d); +} diff --git a/lib/cdda_interface/scsi_interface.c b/lib/cdda_interface/scsi_interface.c new file mode 100644 index 00000000..d6bb8007 --- /dev/null +++ b/lib/cdda_interface/scsi_interface.c @@ -0,0 +1,1574 @@ +/* + $Id: scsi_interface.c,v 1.5 2008/06/25 08:01:53 rocky Exp $ + + Copyright (C) 2004, 2008 Rocky Bernstein + Original interface.c Copyright (C) 1994-1997 + Eissfeldt heiko@colossus.escape.de + Current blenderization Copyright (C) 1998-1999 Monty xiphmont@mit.edu + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/****************************************************************** + * + * Generic SCSI interface specific code. + * + ******************************************************************/ + +#define NEED_STRERROR_TR +#define NEED_MMC_LIST +#define NEED_SCSI_LIST + +#include "low_interface.h" +#include "common_interface.h" +#include "utils.h" +#include +#include + +#include "drive_exceptions.h" + +static void +tweak_SG_buffer(cdrom_drive_t *d){ + int table,reserved; + char buffer[256]; + + /* maximum transfer size? */ + if(ioctl(d->cdda_fd,SG_GET_RESERVED_SIZE,&reserved)){ + /* Up, guess not. */ + cdmessage(d,"\tCould not get scatter/gather buffer size.\n"); + return; + } + + if(ioctl(d->cdda_fd,SG_GET_SG_TABLESIZE,&table))table=1; + { + int cur; + + sprintf(buffer,"\tDMA scatter/gather table entries: %d\n\t" + "table entry size: %d bytes\n\t" + "maximum theoretical transfer: %d sectors\n", + table,reserved,table*reserved/CDIO_CD_FRAMESIZE_RAW); + cdmessage(d,buffer); + + cur=table*reserved; + + /* not too much; new kernels have trouble with DMA allocation, so + be more conservative: 32kB max until I test more thoroughly */ + cur=(cur>1024*32?1024*32:cur); + d->nsectors=cur/CDIO_CD_FRAMESIZE_RAW; + d->bigbuff=cur; + + sprintf(buffer,"\tSetting default read size to %d sectors (%d bytes).\n\n", + d->nsectors,d->nsectors*CDIO_CD_FRAMESIZE_RAW); + cdmessage(d,buffer); + } + + /* Disable command queue; we don't need it, no reason to have it on */ + reserved=0; + if(ioctl(d->cdda_fd,SG_SET_COMMAND_Q,&reserved)){ + cdmessage(d,"\tCouldn't disable command queue! Continuing anyway...\n"); + } + +} + +static void +reset_scsi(cdrom_drive_t *d) +{ + int arg; + d->enable_cdda(d,0); + + cdmessage(d,"sending SG SCSI reset... "); + if(ioctl(d->cdda_fd,SG_SCSI_RESET,&arg)) + cdmessage(d,"FAILED: EBUSY\n"); + else + cdmessage(d,"OK\n"); + + d->enable_cdda(d,1); +} + +static void +clear_garbage(cdrom_drive_t *d) +{ + fd_set fdset; + struct timeval tv; + struct sg_header *sg_hd=(struct sg_header *)d->sg; + int flag=0; + + /* clear out any possibly preexisting garbage */ + FD_ZERO(&fdset); + FD_SET(d->cdda_fd,&fdset); + tv.tv_sec=0; + tv.tv_usec=0; + + /* I like select */ + while(select(d->cdda_fd+1,&fdset,NULL,NULL,&tv)==1){ + + sg_hd->twelve_byte = 0; + sg_hd->result = 0; + sg_hd->reply_len = SG_OFF; + read(d->cdda_fd, sg_hd, 1); + + /* reset for select */ + FD_ZERO(&fdset); + FD_SET(d->cdda_fd,&fdset); + tv.tv_sec=0; + tv.tv_usec=0; + if(!flag && d->report_all) + cdmessage(d,"Clearing previously returned data from SCSI buffer\n"); + flag=1; + } +} + +/* process a complete scsi command. */ +static int +handle_scsi_cmd(cdrom_drive_t *d, + unsigned int cmd_len, unsigned int in_size, + unsigned int out_size, unsigned char bytefill, + int bytecheck) +{ + int status = 0; + struct sg_header *sg_hd=(struct sg_header *)d->sg; + long writebytes=SG_OFF+cmd_len+in_size; + + /* generic scsi device services */ + + /* clear out any possibly preexisting garbage */ + clear_garbage(d); + + memset(sg_hd,0,sizeof(sg_hd)); + sg_hd->twelve_byte = cmd_len == 12; + sg_hd->result = 0; + sg_hd->reply_len = SG_OFF + out_size; + + /* The following is one of the scariest hacks I've ever had to use. + The idea is this: We want to know if a command fails. The + generic scsi driver (as of now) won't tell us; it hands back the + uninitialized contents of the preallocated kernel buffer. We + force this buffer to a known value via another bug (nonzero data + length for a command that doesn't take data) such that we can + tell if the command failed. Scared yet? */ + + if(bytecheck && out_size>in_size){ + memset(d->sg_buffer+cmd_len+in_size,bytefill,out_size-in_size); + /* the size does not remove cmd_len due to the way the kernel + driver copies buffers */ + writebytes+=(out_size-in_size); + } + + { + /* Select on write with a 5 second timeout. This is a hack until + a better error reporting layer is in place in alpha 10; right + now, always print a message. */ + + fd_set fdset; + struct timeval tv; + + FD_ZERO(&fdset); + FD_SET(d->cdda_fd,&fdset); + tv.tv_sec=60; /* Increased to 1m for plextor, as the drive will + try to get through rough spots on its own and + this can take time 19991129 */ + tv.tv_usec=0; + + while(1){ + int ret=select(d->cdda_fd+1,NULL,&fdset,NULL,&tv); + if(ret>0)break; + if(ret<0 && errno!=EINTR)break; + if(ret==0){ + fprintf(stderr,"\nSCSI transport error: timeout waiting to write" + " packet\n\n"); + return(TR_EWRITE); + } + } + } + + sigprocmask (SIG_BLOCK, &(d->sigset), NULL ); + errno=0; + status = write(d->cdda_fd, sg_hd, writebytes ); + + if (status<0 || status != writebytes ) { + sigprocmask ( SIG_UNBLOCK, &(d->sigset), NULL ); + if(errno==0)errno=EIO; + return(TR_EWRITE); + } + + { + /* Select on read (and write; this signals an error) with a 5 + second timeout. This is a hack until a better error reporting + layer is in place in alpha 10; right now, always print a + message. */ + + fd_set rset; + struct timeval tv; + + FD_ZERO(&rset); + FD_SET(d->cdda_fd,&rset); + tv.tv_sec=60; /* Increased to 1m for plextor, as the drive will + try to get through rough spots on its own and + this can take time 19991129 */ + tv.tv_usec=0; + + while(1){ + int ret=select(d->cdda_fd+1,&rset,NULL,NULL,&tv); + if(ret<0 && errno!=EINTR)break; + if(ret==0){ + sigprocmask ( SIG_UNBLOCK, &(d->sigset), NULL ); + fprintf(stderr,"\nSCSI transport error: timeout waiting to read" + " packet\n\n"); + return(TR_EREAD); + } + if(ret>0){ + /* is it readable or something else? */ + if(FD_ISSET(d->cdda_fd,&rset))break; + sigprocmask ( SIG_UNBLOCK, &(d->sigset), NULL ); + fprintf(stderr,"\nSCSI transport: error reading packet\n\n"); + return(TR_EREAD); + } + } + } + + errno=0; + status = read(d->cdda_fd, sg_hd, SG_OFF + out_size); + sigprocmask ( SIG_UNBLOCK, &(d->sigset), NULL ); + + if (status<0)return(TR_EREAD); + + if(status != SG_OFF + out_size || sg_hd->result){ + if(errno==0)errno=EIO; + return(TR_EREAD); + } + + if(sg_hd->sense_buffer[0]){ + char key=sg_hd->sense_buffer[2]&0xf; + char ASC=sg_hd->sense_buffer[12]; + char ASCQ=sg_hd->sense_buffer[13]; + switch(key){ + case 0: + if(errno==0)errno=EIO; + return(TR_UNKNOWN); + case 1: + break; + case 2: + if(errno==0)errno=EBUSY; + return(TR_BUSY); + case 3: + if(ASC==0x0C && ASCQ==0x09){ + /* loss of streaming */ + if(errno==0)errno=EIO; + return(TR_STREAMING); + }else{ + if(errno==0)errno=EIO; + return(TR_MEDIUM); + } + case 4: + if(errno==0)errno=EIO; + return(TR_FAULT); + case 5: + if(errno==0)errno=EINVAL; + return(TR_ILLEGAL); + default: + if(errno==0)errno=EIO; + return(TR_UNKNOWN); + } + } + + /* still not foolproof; the following doesn't guarantee that we got + all the data, just that the command was not rejected. */ + + /* Why do this with the above sense stuff? For some reason, + commands still get through. Perhaps no data comes back even + though the target reports success? */ + + if(bytecheck && in_size+cmd_lensg_buffer[i]!=bytefill){ + flag=1; + break; + } + + if(!flag){ + errno=EINVAL; + return(TR_ILLEGAL); + } + } + + errno=0; + return(0); +} + +/* Group 1 (10b) command */ + +static int +mode_sense_atapi(cdrom_drive_t *d, int size, int page) +{ + memcpy(d->sg_buffer, + (char []) {CDIO_MMC_GPCMD_MODE_SENSE_10, + 0x00, /* reserved */ + 0x00, /* page */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* MSB (0) */ + 0, /* sizeof(modesense - SG_OFF) */ + 0}, /* reserved */ + 10); + + d->sg_buffer[1]=d->lun<<5; + d->sg_buffer[2]=0x3F&page; + d->sg_buffer[8]=size+4; + + if (handle_scsi_cmd (d, 10, 0, size+4,'\377',1)) return(1); + + { + char *b=d->sg_buffer; + if(b[0])return(1); /* Handles only up to 256 bytes */ + if(b[6])return(1); /* Handles only up to 256 bytes */ + + b[0]=b[1]-3; + b[1]=b[2]; + b[2]=b[3]; + b[3]=b[7]; + + memmove(b+4,b+8,size); + } + return(0); +} + +/* group 0 (6b) command */ + +static int +mode_sense_scsi(cdrom_drive_t *d, int size, int page) +{ + memcpy(d->sg_buffer, + (char []) {CDIO_MMC_GPCMD_MODE_SENSE, + 0x00, /* return block descriptor/lun */ + 0x00, /* page */ + 0, /* reserved */ + 0, /* sizeof(modesense - SG_OFF) */ + 0}, /* control */ + 6); + + d->sg_buffer[1]=d->lun<<5; + d->sg_buffer[2]=(0x3F&page); + d->sg_buffer[4]=size; + + if (handle_scsi_cmd (d, 6, 0, size, '\377',1)) return(1); + return(0); +} + +static int mode_sense(cdrom_drive_t *d,int size,int page){ + if(d->is_atapi) + return(mode_sense_atapi(d,size,page)); + return(mode_sense_scsi(d,size,page)); +} + +static int mode_select(cdrom_drive_t *d,int density,int secsize){ + /* short circut the way Heiko does it; less flexible, but shorter */ + if(d->is_atapi){ + unsigned char *mode = d->sg_buffer + 18; + + memcpy(d->sg_buffer, + (char []) { CDIO_MMC_GPCMD_MODE_SELECT, + 0x10, /* no save page */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 12, /* sizeof(mode) */ + 0, /* reserved */ + + /* mode parameter header */ + 0, 0, 0, 0, 0, 0, 0, + 8, /* Block Descriptor Length */ + + /* descriptor block */ + 0, /* Density Code */ + 0, 0, 0, /* # of Blocks */ + 0, /* reserved */ + 0, 0, 0},/* Blocklen */ + 26); + + d->sg_buffer[1]|=d->lun<<5; + + /* prepare to read cds in the previous mode */ + mode [0] = density; + mode [6] = secsize >> 8; /* block length "msb" */ + mode [7] = secsize & 0xFF; /* block length lsb */ + + /* do the scsi cmd */ + return(handle_scsi_cmd (d,10, 16, 0,0,0)); + + }else{ + unsigned char *mode = d->sg_buffer + 10; + + memcpy(d->sg_buffer, + (char []) { CDIO_MMC_GPCMD_MODE_SELECT, + 0x10, /* no save page */ + 0, /* reserved */ + 0, /* reserved */ + 12, /* sizeof(mode) */ + 0, /* reserved */ + /* mode section */ + 0, + 0, 0, + 8, /* Block Descriptor Length */ + 0, /* Density Code */ + 0, 0, 0, /* # of Blocks */ + 0, /* reserved */ + 0, 0, 0},/* Blocklen */ + 18); + + /* prepare to read cds in the previous mode */ + mode [0] = density; + mode [6] = secsize >> 8; /* block length "msb" */ + mode [7] = secsize & 0xFF; /* block length lsb */ + + /* do the scsi cmd */ + return(handle_scsi_cmd (d,6, 12, 0,0,0)); + } +} + +/* get current sector size from SCSI cdrom drive */ +static unsigned int +get_orig_sectorsize(cdrom_drive_t *d) +{ + if(mode_sense(d,12,0x01))return(-1); + + d->orgdens = d->sg_buffer[4]; + return(d->orgsize = ((int)(d->sg_buffer[10])<<8)+d->sg_buffer[11]); +} + +/* switch CDROM scsi drives to given sector size */ +static int set_sectorsize (cdrom_drive_t *d,unsigned int secsize) +{ + return(mode_select(d,d->orgdens,secsize)); +} + +/* switch Toshiba/DEC and HP drives from/to cdda density */ +int +scsi_enable_cdda (cdrom_drive_t *d, int fAudioMode) +{ + if (fAudioMode) { + if(mode_select(d,d->density,CDIO_CD_FRAMESIZE_RAW)){ + if(d->error_retry) + cderror(d,"001: Unable to set CDROM to read audio mode\n"); + return(-1); + } + } else { + if(mode_select(d,d->orgdens,d->orgsize)){ + if(d->error_retry) + cderror(d,"001: Unable to set CDROM to read audio mode\n"); + return(-1); + } + } + return(0); +} + +typedef struct scsi_TOC { /* structure of scsi table of contents (cdrom) */ + unsigned char reserved1; + unsigned char bFlags; + unsigned char bTrack; + unsigned char reserved2; + signed char start_MSB; + unsigned char start_1; + unsigned char start_2; + unsigned char start_LSB; +} scsi_TOC; + + +/* read the table of contents from the cd and fill the TOC array */ +/* Do it like the kernel ioctl driver; the 'all at once' approach + fails on at least one Kodak drive. */ + +static int +scsi_read_toc (cdrom_drive_t *d) +{ + int i,first,last; + track_t i_tracks; + + /* READTOC, MSF format flag, res, res, res, res, Start track, len msb, + len lsb, flags */ + + /* read the header first */ + memcpy(d->sg_buffer, (char []){ CDIO_MMC_GPCMD_READ_TOC, + 0, 0, 0, 0, 0, 1, 0, 12, 0}, 10); + d->sg_buffer[1]=d->lun<<5; + + if (handle_scsi_cmd (d,10, 0, 12,'\377',1)){ + cderror(d,"004: Unable to read table of contents header\n"); + return(-4); + } + + first=d->sg_buffer[2]; + last=d->sg_buffer[3]; + i_tracks=last-first+1; + + if (last > MAXTRK || first > MAXTRK || last<0 || first<0) { + cderror(d,"003: CDROM reporting illegal number of tracks\n"); + return(-3); + } + + for (i = first; i <= last; i++){ + memcpy(d->sg_buffer, (char []){ CDIO_MMC_GPCMD_READ_TOC, + 0, 0, 0, 0, 0, 0, 0, 12, 0}, 10); + d->sg_buffer[1]=d->lun<<5; + d->sg_buffer[6]=i; + + if (handle_scsi_cmd (d,10, 0, 12,'\377',1)){ + cderror(d,"005: Unable to read table of contents entry\n"); + return(-5); + } + { + scsi_TOC *toc=(scsi_TOC *)(d->sg_buffer+4); + + d->disc_toc[i-first].bFlags=toc->bFlags; + d->disc_toc[i-first].bTrack=i; + d->disc_toc[i-first].dwStartSector= d->adjust_ssize * + (((int)(toc->start_MSB)<<24) | + (toc->start_1<<16)| + (toc->start_2<<8)| + (toc->start_LSB)); + } + } + + memcpy(d->sg_buffer, (char []){ CDIO_MMC_GPCMD_READ_TOC, + 0, 0, 0, 0, 0, 0, 0, 12, 0}, 10); + d->sg_buffer[1]=d->lun<<5; + d->sg_buffer[6]=0xAA; + + if (handle_scsi_cmd (d,10, 0, 12,'\377',1)){ + cderror(d,"002: Unable to read table of contents lead-out\n"); + return(-2); + } + { + scsi_TOC *toc=(scsi_TOC *)(d->sg_buffer+4); + + d->disc_toc[i-first].bFlags=toc->bFlags; + d->disc_toc[i-first].bTrack=0xAA; + d->disc_toc[i-first].dwStartSector= d->adjust_ssize * + (((int)(toc->start_MSB)<<24) | + (toc->start_1<<16)| + (toc->start_2<<8)| + (toc->start_LSB)); + } + + d->cd_extra = FixupTOC(d,i_tracks+1); /* include lead-out */ + return(i_tracks); +} + +/* a contribution from Boris for IMS cdd 522 */ +/* check this for ACER/Creative/Foo 525,620E,622E, etc? */ +static int +scsi_read_toc2 (cdrom_drive_t *d) +{ + uint32_t foo,bar; + + int i; + track_t i_tracks; + + memcpy(d->sg_buffer, (char[]){ 0xe5, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10); + d->sg_buffer[5]=1; + d->sg_buffer[8]=255; + + if (handle_scsi_cmd (d,10, 0, 256,'\377',1)){ + cderror(d,"004: Unable to read table of contents header\n"); + return(-4); + } + + /* copy to our structure and convert start sector */ + i_tracks = d->sg_buffer[1]; + if (i_tracks > MAXTRK) { + cderror(d,"003: CDROM reporting illegal number of tracks\n"); + return(-3); + } + + for (i = 0; i < i_tracks; i++){ + memcpy(d->sg_buffer, (char[]){ 0xe5, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10); + d->sg_buffer[5]=i+1; + d->sg_buffer[8]=255; + + if (handle_scsi_cmd (d,10, 0, 256,'\377',1)){ + cderror(d,"005: Unable to read table of contents entry\n"); + return(-5); + } + + d->disc_toc[i].bFlags = d->sg_buffer[10]; + d->disc_toc[i].bTrack = i + 1; + + d->disc_toc[i].dwStartSector= d->adjust_ssize * + (((signed char)(d->sg_buffer[2])<<24) | + (d->sg_buffer[3]<<16)| + (d->sg_buffer[4]<<8)| + (d->sg_buffer[5])); + } + + d->disc_toc[i].bFlags = 0; + d->disc_toc[i].bTrack = i + 1; + memcpy (&foo, d->sg_buffer+2, 4); + memcpy (&bar, d->sg_buffer+6, 4); + d->disc_toc[i].dwStartSector = + d->adjust_ssize * (UINT32_FROM_BE(foo) + UINT32_FROM_BE(bar)); + + d->disc_toc[i].dwStartSector= d->adjust_ssize * + ((((signed char)(d->sg_buffer[2])<<24) | + (d->sg_buffer[3]<<16)| + (d->sg_buffer[4]<<8)| + (d->sg_buffer[5]))+ + + ((((signed char)(d->sg_buffer[6])<<24) | + (d->sg_buffer[7]<<16)| + (d->sg_buffer[8]<<8)| + (d->sg_buffer[9])))); + + + d->cd_extra = FixupTOC(d,i_tracks+1); + return(i_tracks); +} + +/* These do one 'extra' copy in the name of clean code */ + +static int +i_read_28 (cdrom_drive_t *d, void *p, long int begin, long int sectors) +{ + int ret; + memcpy(d->sg_buffer,(char []){ + CDIO_MMC_GPCMD_READ_10, + 0, 0, 0, 0, 0, 0, 0, 0, 0}, + 10); + + if(d->fua) + d->sg_buffer[1]=0x08; + + d->sg_buffer[1]|=d->lun<<5; + + d->sg_buffer[3] = (begin >> 16) & 0xFF; + d->sg_buffer[4] = (begin >> 8) & 0xFF; + d->sg_buffer[5] = begin & 0xFF; + d->sg_buffer[8] = sectors; + if((ret=handle_scsi_cmd(d,10,0,sectors * CDIO_CD_FRAMESIZE_RAW,'\177',1))) + return(ret); + if(p)memcpy(p,d->sg_buffer,sectors*CDIO_CD_FRAMESIZE_RAW); + return(0); +} + +static int +i_read_A8 (cdrom_drive_t *d, void *p, long int begin, long int sectors) +{ + int ret; + memcpy(d->sg_buffer,(char []){ + CDIO_MMC_GPCMD_READ_12, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + 12); + + if(d->fua) + d->sg_buffer[1]=0x08; + + d->sg_buffer[1]|=d->lun<<5; + + d->sg_buffer[3] = (begin >> 16) & 0xFF; + d->sg_buffer[4] = (begin >> 8) & 0xFF; + d->sg_buffer[5] = begin & 0xFF; + d->sg_buffer[9] = sectors; + if((ret=handle_scsi_cmd(d,12,0,sectors * CDIO_CD_FRAMESIZE_RAW,'\177',1))) + return(ret); + if(p)memcpy(p,d->sg_buffer,sectors*CDIO_CD_FRAMESIZE_RAW); + return(0); +} + +static int +i_read_D4_10 (cdrom_drive_t *d, void *p, long begin, long sectors) +{ + int ret; + memcpy(d->sg_buffer,(char []){0xd4, 0, 0, 0, 0, 0, 0, 0, 0, 0},10); + + if(d->fua) + d->sg_buffer[1]=0x08; + + d->sg_buffer[1]|=d->lun<<5; + d->sg_buffer[3] = (begin >> 16) & 0xFF; + d->sg_buffer[4] = (begin >> 8) & 0xFF; + d->sg_buffer[5] = begin & 0xFF; + d->sg_buffer[8] = sectors; + if((ret=handle_scsi_cmd(d,10,0,sectors * CDIO_CD_FRAMESIZE_RAW,'\177',1))) + return(ret); + if(p)memcpy(p,d->sg_buffer,sectors*CDIO_CD_FRAMESIZE_RAW); + return(0); +} + +static int i_read_D4_12 (cdrom_drive_t *d, void *p, long begin, long sectors) +{ + int ret; + memcpy(d->sg_buffer,(char []){0xd4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},12); + + if(d->fua) + d->sg_buffer[1]=0x08; + + d->sg_buffer[1]|=d->lun<<5; + d->sg_buffer[3] = (begin >> 16) & 0xFF; + d->sg_buffer[4] = (begin >> 8) & 0xFF; + d->sg_buffer[5] = begin & 0xFF; + d->sg_buffer[9] = sectors; + if((ret=handle_scsi_cmd(d,12,0,sectors * CDIO_CD_FRAMESIZE_RAW,'\177',1))) + return(ret); + if(p)memcpy(p,d->sg_buffer,sectors*CDIO_CD_FRAMESIZE_RAW); + return(0); +} + +static int +i_read_D5 (cdrom_drive_t *d, void *p, long int begin, long int sectors) +{ + int ret; + memcpy(d->sg_buffer,(char []){0xd5, 0, 0, 0, 0, 0, 0, 0, 0, 0},10); + + if(d->fua) + d->sg_buffer[1]=0x08; + + d->sg_buffer[1]|=d->lun<<5; + d->sg_buffer[3] = (begin >> 16) & 0xFF; + d->sg_buffer[4] = (begin >> 8) & 0xFF; + d->sg_buffer[5] = begin & 0xFF; + d->sg_buffer[8] = sectors; + if((ret=handle_scsi_cmd(d,10,0,sectors * CDIO_CD_FRAMESIZE_RAW,'\177',1))) + return(ret); + if(p)memcpy(p,d->sg_buffer,sectors*CDIO_CD_FRAMESIZE_RAW); + return(0); +} + +static int +i_read_D8 (cdrom_drive_t *d, void *p, long int begin, long int sectors) +{ + int ret; + memcpy(d->sg_buffer,(char []){0xd8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},12); + + if(d->fua) + d->sg_buffer[1]=0x08; + + d->sg_buffer[1]|=d->lun<<5; + d->sg_buffer[3] = (begin >> 16) & 0xFF; + d->sg_buffer[4] = (begin >> 8) & 0xFF; + d->sg_buffer[5] = begin & 0xFF; + d->sg_buffer[9] = sectors; + if((ret=handle_scsi_cmd(d,12,0,sectors * CDIO_CD_FRAMESIZE_RAW,'\177',1))) + return(ret); + if(p)memcpy(p,d->sg_buffer,sectors*CDIO_CD_FRAMESIZE_RAW); + return(0); +} + +static int +i_read_mmc (cdrom_drive_t *d, void *p, long int begin, long int sectors) +{ + int ret; + /* if(begin<=12007 && begin+sectors>12000){ + errno=EIO; + return(TR_ILLEGAL); + }*/ + + memcpy(d->sg_buffer,(char []){ + CDIO_MMC_GPCMD_READ_CD, + 0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0, 0},12); + + d->sg_buffer[3] = (begin >> 16) & 0xFF; + d->sg_buffer[4] = (begin >> 8) & 0xFF; + d->sg_buffer[5] = begin & 0xFF; + d->sg_buffer[8] = sectors; + if((ret=handle_scsi_cmd(d,12,0,sectors * CDIO_CD_FRAMESIZE_RAW,'\177',1))) + return(ret); + if(p)memcpy(p,d->sg_buffer,sectors*CDIO_CD_FRAMESIZE_RAW); + return(0); +} + +static int +i_read_mmc2 (cdrom_drive_t *d, void *p, long int begin, long int sectors) +{ + int ret; + memcpy(d->sg_buffer,(char []){ + CDIO_MMC_GPCMD_READ_CD, + 0, 0, 0, 0, 0, 0, 0, 0, 0xf8, 0, 0},12); + + d->sg_buffer[3] = (begin >> 16) & 0xFF; + d->sg_buffer[4] = (begin >> 8) & 0xFF; + d->sg_buffer[5] = begin & 0xFF; + d->sg_buffer[8] = sectors; + if((ret=handle_scsi_cmd(d,12,0,sectors * CDIO_CD_FRAMESIZE_RAW,'\177',1))) + return(ret); + if(p)memcpy(p,d->sg_buffer,sectors*CDIO_CD_FRAMESIZE_RAW); + return(0); +} + +static int +i_read_mmc3 (cdrom_drive_t *d, void *p, long int begin, long int sectors) +{ + int ret; + memcpy(d->sg_buffer,(char []){ + CDIO_MMC_GPCMD_READ_CD, + 4, 0, 0, 0, 0, 0, 0, 0, 0xf8, 0, 0},12); + + d->sg_buffer[3] = (begin >> 16) & 0xFF; + d->sg_buffer[4] = (begin >> 8) & 0xFF; + d->sg_buffer[5] = begin & 0xFF; + d->sg_buffer[8] = sectors; + if((ret=handle_scsi_cmd(d,12,0,sectors * CDIO_CD_FRAMESIZE_RAW,'\177',1))) + return(ret); + if(p)memcpy(p,d->sg_buffer,sectors*CDIO_CD_FRAMESIZE_RAW); + return(0); +} + +/* straight from the MMC3 spec */ +static inline void +LBA_to_MSF(lba_t lba, unsigned char *M, unsigned char *S, unsigned char *F) +{ + if (lba>=-CDIO_PREGAP_SECTORS){ + *M = ( lba+CDIO_PREGAP_SECTORS)/(CDIO_CD_FRAMES_PER_MIN); + lba -= (*M)*(CDIO_CD_FRAMES_PER_MIN); + *S = (lba+CDIO_PREGAP_SECTORS)/CDIO_CD_FRAMES_PER_SEC; + lba -= (*S)*CDIO_CD_FRAMES_PER_SEC; + *F = (lba+CDIO_PREGAP_SECTORS); + } else { + *M = (lba+450150)/(CDIO_CD_FRAMES_PER_MIN); + lba -= (*M)*(CDIO_CD_FRAMES_PER_MIN); + *S = (lba+450150)/CDIO_CD_FRAMES_PER_SEC; + lba -= (*S)*CDIO_CD_FRAMES_PER_SEC; + *F = (lba+450150); + } +} + + +static int +i_read_msf (cdrom_drive_t *d, void *p, long int begin, long int sectors) +{ + int ret; + memcpy(d->sg_buffer,(char []){ + CDIO_MMC_GPCMD_READ_MSF, + 0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0, 0},12); + + LBA_to_MSF(begin,d->sg_buffer+3,d->sg_buffer+4,d->sg_buffer+5); + LBA_to_MSF(begin+sectors,d->sg_buffer+6,d->sg_buffer+7,d->sg_buffer+8); + + if((ret=handle_scsi_cmd(d,12,0,sectors * CDIO_CD_FRAMESIZE_RAW,'\177',1))) + return(ret); + if(p)memcpy(p,d->sg_buffer,sectors*CDIO_CD_FRAMESIZE_RAW); + return(0); +} + +static int +i_read_msf2 (cdrom_drive_t *d, void *p, long int begin, long int sectors) +{ + int ret; + memcpy(d->sg_buffer,(char []){ + CDIO_MMC_GPCMD_READ_MSF, + 0, 0, 0, 0, 0, 0, 0, 0, 0xf8, 0, 0},12); + + LBA_to_MSF(begin,d->sg_buffer+3,d->sg_buffer+4,d->sg_buffer+5); + LBA_to_MSF(begin+sectors,d->sg_buffer+6,d->sg_buffer+7,d->sg_buffer+8); + + if((ret=handle_scsi_cmd(d,12,0,sectors * CDIO_CD_FRAMESIZE_RAW,'\177',1))) + return(ret); + if(p)memcpy(p,d->sg_buffer,sectors*CDIO_CD_FRAMESIZE_RAW); + return(0); +} + +static int +i_read_msf3 (cdrom_drive_t *d, void *p, long int begin, long int sectors) +{ + int ret; + memcpy(d->sg_buffer,(char []){ + CDIO_MMC_GPCMD_READ_MSF, + 4, 0, 0, 0, 0, 0, 0, 0, 0xf8, 0, 0},12); + + LBA_to_MSF(begin,d->sg_buffer+3,d->sg_buffer+4,d->sg_buffer+5); + LBA_to_MSF(begin+sectors,d->sg_buffer+6,d->sg_buffer+7,d->sg_buffer+8); + + if((ret=handle_scsi_cmd(d,12,0,sectors * CDIO_CD_FRAMESIZE_RAW,'\177',1))) + return(ret); + if(p)memcpy(p,d->sg_buffer,sectors*CDIO_CD_FRAMESIZE_RAW); + return(0); +} + +static long +scsi_read_map (cdrom_drive_t *d, void *p, long int begin, long int sectors, + int (*map)(cdrom_drive_t *, void *, long, long)) +{ + int retry_count,err; + char *buffer=(char *)p; + + /* read d->nsectors at a time, max. */ + sectors=(sectors>d->nsectors?d->nsectors:sectors); + sectors=(sectors<1?1:sectors); + + retry_count=0; + + while(1) { + if((err=map(d,(p?buffer:NULL),begin,sectors))){ + if(d->report_all){ + struct sg_header *sg_hd=(struct sg_header *)d->sg; + char b[256]; + + sprintf(b,"scsi_read error: sector=%ld length=%ld retry=%d\n", + begin,sectors,retry_count); + cdmessage(d,b); + sprintf(b," Sense key: %x ASC: %x ASCQ: %x\n", + (int)(sg_hd->sense_buffer[2]&0xf), + (int)(sg_hd->sense_buffer[12]), + (int)(sg_hd->sense_buffer[13])); + cdmessage(d,b); + sprintf(b," Transport error: %s\n",strerror_tr[err]); + cdmessage(d,b); + sprintf(b," System error: %s\n",strerror(errno)); + cdmessage(d,b); + + fprintf(stderr,"scsi_read error: sector=%ld length=%ld retry=%d\n", + begin,sectors,retry_count); + fprintf(stderr," Sense key: %x ASC: %x ASCQ: %x\n", + (int)(sg_hd->sense_buffer[2]&0xf), + (int)(sg_hd->sense_buffer[12]), + (int)(sg_hd->sense_buffer[13])); + fprintf(stderr," Transport error: %s\n",strerror_tr[err]); + fprintf(stderr," System error: %s\n",strerror(errno)); + } + + if(!d->error_retry)return(-7); + switch(errno){ + case EINTR: + usleep(100); + continue; + case ENOMEM: + /* D'oh. Possible kernel error. Keep limping */ + usleep(100); + if(sectors==1){ + /* Nope, can't continue */ + cderror(d,"300: Kernel memory error\n"); + return(-300); + } + if(d->report_all){ + char b[256]; + sprintf(b,"scsi_read: kernel couldn't alloc %ld bytes. " + "backing off...\n",sectors*CDIO_CD_FRAMESIZE_RAW); + + cdmessage(d,b); + } + sectors--; + continue; + default: + if(sectors==1){ + if(errno==EIO) + if(d->fua==-1) /* testing for FUA support */ + return(-7); + + /* *Could* be I/O or media error. I think. If we're at + 30 retries, we better skip this unhappy little + sector. */ + if(retry_count>MAX_RETRIES-1){ + char b[256]; + sprintf(b,"010: Unable to access sector %ld\n", + begin); + cderror(d,b); + return(-10); + + } + break; + } + + /* Hmm. OK, this is just a tad silly. just in case this was + a timeout and a reset happened, we need to set the drive + back to cdda */ + reset_scsi(d); + } + }else{ + + /* Did we get all the bytes we think we did, or did the kernel + suck? */ + if(buffer){ + long i; + for(i=sectors*CDIO_CD_FRAMESIZE_RAW;i>1;i-=2) + if(buffer[i-1]!='\177' || buffer[i-2]!='\177') + break; + + i/=CDIO_CD_FRAMESIZE_RAW; + if(i!=sectors){ + if(d->report_all){ + char b[256]; + sprintf(b,"scsi_read underrun: pos=%ld len=%ld read=%ld retry=%d\n", + begin,sectors,i,retry_count); + + cdmessage(d,b); + } + reset_scsi(d); + } + + if(i>0)return(i); + }else + break; + } + + retry_count++; + if(sectors==1 && retry_count>MAX_RETRIES){ + cderror(d,"007: Unknown, unrecoverable error reading data\n"); + return(-7); + } + if(sectors>1)sectors=sectors/2; + d->enable_cdda(d,0); + d->enable_cdda(d,1); + + } + return(sectors); +} + +long int +scsi_read_28 (cdrom_drive_t *d, void *p, long int begin, long int sectors) +{ + return(scsi_read_map(d,p,begin,sectors,i_read_28)); +} + +long int +scsi_read_A8 (cdrom_drive_t *d, void *p, long int begin, long int sectors) +{ + return(scsi_read_map(d,p,begin,sectors,i_read_A8)); +} + +long int +scsi_read_D4_10 (cdrom_drive_t *d, void *p, long int begin, long int sectors) +{ + return(scsi_read_map(d,p,begin,sectors,i_read_D4_10)); +} + +long int +scsi_read_D4_12 (cdrom_drive_t *d, void *p, long int begin, + long int sectors) +{ + return(scsi_read_map(d,p,begin,sectors,i_read_D4_12)); +} + +static long int +scsi_read_D5 (cdrom_drive_t *d, void *p, long begin, long sectors) +{ + return(scsi_read_map(d,p,begin,sectors,i_read_D5)); +} + +long scsi_read_D8 (cdrom_drive_t *d, void *p, long begin, + long sectors) +{ + return(scsi_read_map(d,p,begin,sectors,i_read_D8)); +} + +long scsi_read_mmc (cdrom_drive_t *d, void *p, long begin, + long sectors) +{ + return(scsi_read_map(d,p,begin,sectors,i_read_mmc)); +} + +long scsi_read_mmc2 (cdrom_drive_t *d, void *p, long begin, + long sectors) +{ + return(scsi_read_map(d,p,begin,sectors,i_read_mmc2)); +} + +static long +scsi_read_mmc3 (cdrom_drive_t *d, void *p, long begin, long sectors) +{ + return(scsi_read_map(d,p,begin,sectors,i_read_mmc3)); +} + +static long +scsi_read_msf (cdrom_drive_t *d, void *p, long begin, long sectors) +{ + return(scsi_read_map(d,p,begin,sectors,i_read_msf)); +} + +static long +scsi_read_msf2 (cdrom_drive_t *d, void *p, long begin, long sectors) +{ + return(scsi_read_map(d,p,begin,sectors,i_read_msf2)); +} + +static long +scsi_read_msf3 (cdrom_drive_t *d, void *p, long begin, long sectors) +{ + return(scsi_read_map(d,p,begin,sectors,i_read_msf3)); +} + + +/* Some drives, given an audio read command, return only 2048 bytes + of data as opposed to 2352 bytes. Look for bytess at the end of the + single sector verification read */ + +static int +count_2352_bytes(cdrom_drive_t *d) +{ + long int i; + for (i=CDIO_CD_FRAMESIZE_RAW-1; i>=0; i--) + if(d->sg_buffer[i]!=(unsigned char)'\177') + return(((i+3)>>2)<<2); + + return(0); +} + +static int +verify_nonzero(cdrom_drive_t *d) +{ + long i,flag=0; + for (i=0; isg_buffer[i]!=0){ + flag=1; + break; + } + + return(flag); +} + +/* So many different read commands, densities, features... + Verify that our selected 'read' command actually reads + nonzero data, else search through other possibilities */ + +static int +verify_read_command(cdrom_drive_t *d) +{ + int i,j,k; + int audioflag=0; + + int (*enablecommand) (cdrom_drive_t *d, int speed); + long (*readcommand) (cdrom_drive_t *d, void *p, long begin, + long int sectors); + unsigned char density; + + int16_t *buff=malloc(CDIO_CD_FRAMESIZE_RAW); + + cdmessage(d,"Verifying CDDA command set...\n"); + + /* try the expected command set; grab the center of each track, look + for data */ + + if(d->enable_cdda(d,1)==0){ + + for(i=1;i<=d->tracks;i++){ + if(cdda_track_audiop(d,i)==1){ + long firstsector=cdda_track_firstsector(d,i); + long lastsector=cdda_track_lastsector(d,i); + long sector=(firstsector+lastsector)>>1; + audioflag=1; + + if(d->read_audio(d,buff,sector,1)>0){ + if(count_2352_bytes(d)==CDIO_CD_FRAMESIZE_RAW){ + cdmessage(d,"\tExpected command set reads OK.\n"); + d->enable_cdda(d,0); + free(buff); + return(0); + } + } + } + } + + d->enable_cdda(d,0); + } + + if(!audioflag){ + cdmessage(d,"\tCould not find any audio tracks on this disk.\n"); + return(-403); + } + + + { + const char *es="",*rs=""; + d->bigendianp=-1; + density=d->density; + readcommand=d->read_audio; + enablecommand=d->enable_cdda; + + + /* No nonzeroes? D'oh. Exhaustive search */ + cdmessage(d,"\tExpected command set FAILED!\n" + "\tPerforming full probe for CDDA command set...\n"); + + /* loops: + density/enable no, 0x0/org, 0x04/org, 0x82/org + read command read_10 read_12 read_nec read_sony read_mmc read_mmc2 */ + + /* NEC test must come before sony; the nec drive expects d8 to be + 10 bytes, and a 12 byte verson (Sony) crashes the drive */ + + for(j=0;j>=0;j++){ + int densitypossible=1; + + switch(j){ + case 0: + d->read_audio=scsi_read_28; + rs="28 0x,00"; + break; + case 1: + d->read_audio=scsi_read_A8; + rs="a8 0x,00"; + break; + case 2: + d->read_audio=scsi_read_mmc; + rs="be 00,10"; + densitypossible=0; + break; + case 3: + d->read_audio=scsi_read_mmc2; + rs="be 00,f8"; + densitypossible=0; + break; + case 4: + d->read_audio=scsi_read_mmc3; + rs="be 04,f8"; + densitypossible=0; + break; + + case 5: + d->read_audio=scsi_read_msf; + rs="b9 00,10"; + densitypossible=0; + break; + case 6: + d->read_audio=scsi_read_msf2; + rs="b9 00,f8"; + densitypossible=0; + break; + case 7: + d->read_audio=scsi_read_msf3; + rs="b9 04,f8"; + densitypossible=0; + break; + + case 8: + d->read_audio=scsi_read_D4_10; + rs="d4(10)0x"; + break; + case 9: + d->read_audio=scsi_read_D4_12; + rs="d4(12)0x"; + break; + case 10: + d->read_audio=scsi_read_D5; + rs="d5 0x,00"; + break; + case 11: + d->read_audio=scsi_read_D8; + rs="d8 0x,00"; + j=-2; + break; + } + + for(i=0;i>=0;i++){ + switch(i){ + case 0: + d->density=0; + d->enable_cdda=dummy_exception; + es="none "; + if(!densitypossible)i=-2; /* short circuit MMC style commands */ + break; + case 1: + d->density=0; + d->enable_cdda=scsi_enable_cdda; + es="yes/0x00"; + break; + case 2: + d->density=0x04; + d->enable_cdda=scsi_enable_cdda; + es="yes/0x04"; + break; + case 3: + d->density=0x82; + d->enable_cdda=scsi_enable_cdda; + es="yes/0x82"; + case 4: + d->density=0x81; + d->enable_cdda=scsi_enable_cdda; + es="yes/0x81"; + i=-2; + break; + } + + cdmessage(d,"\ttest -> density: ["); + cdmessage(d,es); + cdmessage(d,"] command: ["); + cdmessage(d,rs); + cdmessage(d,"]\n"); + + { + int densityflag=0; + int rejectflag=0; + int zeroflag=0; + int lengthflag=0; + + if(d->enable_cdda(d,1)==0){ + for(k=1;k<=d->tracks;k++){ + if(cdda_track_audiop(d,k)==1){ + long firstsector=cdda_track_firstsector(d,k); + long lastsector=cdda_track_lastsector(d,k); + long sector=(firstsector+lastsector)>>1; + + if(d->read_audio(d,buff,sector,1)>0){ + if((lengthflag=count_2352_bytes(d))==CDIO_CD_FRAMESIZE_RAW){ + if(verify_nonzero(d)){ + cdmessage(d,"\t\tCommand set FOUND!\n"); + + free(buff); + d->enable_cdda(d,0); + return(0); + }else{ + zeroflag++; + } + } + }else{ + rejectflag++; + break; + } + } + } + d->enable_cdda(d,0); + }else{ + densityflag++; + } + + if(densityflag) + cdmessage(d,"\t\tDrive rejected density set\n"); + if(rejectflag){ + char buffer[256]; + sprintf(buffer,"\t\tDrive rejected read command packet(s)\n"); + cdmessage(d,buffer); + } + if(lengthflag>0 && lengthflagdensity=density; + d->read_audio=readcommand; + d->enable_cdda=enablecommand; + + cdmessage(d,"\tUnable to find any suitable command set from probe;\n" + "\tdrive probably not CDDA capable.\n"); + + cderror(d,"006: Could not read any data from drive\n"); + + } + free(buff); + return(-6); +} + +static void +check_fua_bit(cdrom_drive_t *d){ + int16_t *buff=malloc(CDIO_CD_FRAMESIZE_RAW); + long i; + + if(d->read_audio==scsi_read_mmc)return; + if(d->read_audio==scsi_read_mmc2)return; + if(d->read_audio==scsi_read_mmc3)return; + + cdmessage(d,"This command set may use a Force Unit Access bit."); + cdmessage(d,"\nChecking drive for FUA bit support...\n"); + + d->enable_cdda(d,1); + d->fua=1; + + for(i=1;i<=d->tracks;i++){ + if(cdda_track_audiop(d,i)==1){ + long firstsector=cdda_track_firstsector(d,i); + long lastsector=cdda_track_lastsector(d,i); + long sector=(firstsector+lastsector)>>1; + + if(d->read_audio(d,buff,sector,1)>0){ + cdmessage(d,"\tDrive accepted FUA bit.\n"); + d->enable_cdda(d,0); + free(buff); + return; + } + } + } + + d->fua=0; + cdmessage(d,"\tDrive rejected FUA bit.\n"); + free(buff); + return; +} + +static int +check_atapi(cdrom_drive_t *d){ + int atapiret=-1; + int fd = d->cdda_fd; /* this is the correct fd (not ioctl_fd), as the + generic device is the device we need to check */ + + cdmessage(d,"\nChecking for SCSI emulation...\n"); + + if (ioctl(fd,SG_EMULATED_HOST,&atapiret)){ + cderror(d,"\tSG_EMULATED_HOST ioctl() failed!\n"); + return(-1); + } else { + if(atapiret==1){ + cdmessage(d,"\tDrive is ATAPI (using SCSI host adaptor emulation)\n"); + /* Disable kernel SCSI command translation layer for access through sg */ + if (ioctl(fd,SG_SET_TRANSFORM,0)) + cderror(d,"\tCouldn't disable kernel command translation layer\n"); + d->is_atapi=1; + }else{ + cdmessage(d,"\tDrive is SCSI\n"); + d->is_atapi=0; + } + + return(d->is_atapi); + } +} + +static int check_mmc(cdrom_drive_t *d){ + char *b; + cdmessage(d,"\nChecking for MMC style command set...\n"); + + d->is_mmc=0; + if(mode_sense(d,22,0x2A)==0){ + + b=d->sg_buffer; + b+=b[3]+4; + + if((b[0]&0x3F)==0x2A){ + /* MMC style drive! */ + d->is_mmc=1; + + if(b[1]>=4){ + if(b[5]&0x1){ + cdmessage(d,"\tDrive is MMC style\n"); + return(1); + }else{ + cdmessage(d,"\tDrive is MMC, but reports CDDA incapable.\n"); + cdmessage(d,"\tIt will likely not be able to read audio data.\n"); + return(1); + } + } + } + } + + cdmessage(d,"\tDrive does not have MMC CDDA support\n"); + return(0); +} + +static void +check_exceptions(cdrom_drive_t *d, exception_t *list) +{ + int i=0; + while(list[i].model){ + if(!strncmp(list[i].model,d->drive_model,strlen(list[i].model))){ + if(list[i].density)d->density=list[i].density; + if(list[i].enable)d->enable_cdda=list[i].enable; + if(list[i].read)d->read_audio=list[i].read; + if(list[i].bigendianp!=-1)d->bigendianp=list[i].bigendianp; + return; + } + i++; + } +} + +/* request vendor brand and model */ +unsigned char * +scsi_inquiry(cdrom_drive_t *d){ + memcpy(d->sg_buffer,(char[]){ 0x12,0,0,0,56,0},6); + + if(handle_scsi_cmd(d,6, 0, 56,'\377',1)) { + cderror(d,"008: Unable to identify CDROM model\n"); + return(NULL); + } + return (d->sg_buffer); +} + + +int +scsi_init_drive(cdrom_drive_t *d){ + int ret; + + check_atapi(d); + check_mmc(d); + + /* generic Sony type defaults; specialize from here */ + d->density = 0x0; + d->enable_cdda = dummy_exception; + d->read_audio = scsi_read_D8; + d->fua=0x0; + if(d->is_atapi)d->lun=0; /* it should already be; just to make sure */ + + if(d->is_mmc){ + + d->read_audio = scsi_read_mmc2; + d->bigendianp=0; + + check_exceptions(d,mmc_list); + + }else{ + + if(d->is_atapi){ + /* Not MMC maybe still uses CDIO_MMC_GPCMD_READ_CD */ + + d->read_audio = scsi_read_mmc2; + d->bigendianp=0; + + check_exceptions(d,atapi_list); + + }else{ + + check_exceptions(d,scsi_list); + + } + } + + /* we really do want the sector size at 2048 to begin.*/ + if (!d->is_atapi) + set_sectorsize(d, CDIO_CD_FRAMESIZE); + + d->enable_cdda(d,0); + + d->read_toc = (!memcmp(d->drive_model, "IMS", 3) && !d->is_atapi) + ? scsi_read_toc2 : scsi_read_toc; + d->set_speed = NULL; + + + if (!d->is_atapi) { + unsigned int sector_size= get_orig_sectorsize(d); + + if ( (sector_size < CDIO_CD_FRAMESIZE) + && set_sectorsize(d, CDIO_CD_FRAMESIZE) ) + d->adjust_ssize = CDIO_CD_FRAMESIZE / sector_size; + else + d->adjust_ssize = 1; + } else + d->adjust_ssize = 1; + + d->tracks=d->read_toc(d); + if(d->tracks<1) + return(d->tracks); + + tweak_SG_buffer(d); + d->opened=1; + + if((ret=verify_read_command(d)))return(ret); + check_fua_bit(d); + + d->error_retry=1; + d->sg=realloc(d->sg,d->nsectors*CDIO_CD_FRAMESIZE_RAW + SG_OFF + 128); + d->sg_buffer=d->sg+SG_OFF; + d->report_all=1; + return(0); +} + diff --git a/lib/cdda_interface/smallft.c b/lib/cdda_interface/smallft.c new file mode 100644 index 00000000..2a9c5316 --- /dev/null +++ b/lib/cdda_interface/smallft.c @@ -0,0 +1,563 @@ +/* + $Id: smallft.c,v 1.2 2008/04/16 17:00:40 karl Exp $ + + Copyright (C) 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/****************************************************************** + * FFT implementation from OggSquish, minus cosine transforms, + * minus all but radix 2/4 case + * + * See OggSquish or NetLib for the version that can do other than just + * power-of-two sized vectors. + ******************************************************************/ + +#include +#include +#include "smallft.h" + +static void drfti1(int n, float *wa, int *ifac){ + static int ntryh[4] = { 4,2,3,5 }; + static float tpi = 6.28318530717958647692528676655900577; + float arg,argh,argld,fi; + int ntry=0,i,j=-1; + int k1, l1, l2, ib; + int ld, ii, ip, is, nq, nr; + int ido, ipm, nfm1; + int nl=n; + int nf=0; + + L101: + j++; + if (j < 4) + ntry=ntryh[j]; + else + ntry+=2; + + L104: + nq=nl/ntry; + nr=nl-ntry*nq; + if (nr!=0) goto L101; + + nf++; + ifac[nf+1]=ntry; + nl=nq; + if(ntry!=2)goto L107; + if(nf==1)goto L107; + + for (i=1;i + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/****************************************************************** + * FFT implementation from OggSquish, minus cosine transforms. + * Only convenience functions exposed + ******************************************************************/ + +extern void fft_forward(int n, float *buf, float *trigcache, int *sp); +extern void fft_backward(int n, float *buf, float *trigcache, int *sp); +extern void fft_i(int n, float **trigcache, int **splitcache); diff --git a/lib/cdda_interface/test_interface.c b/lib/cdda_interface/test_interface.c new file mode 100644 index 00000000..140b2769 --- /dev/null +++ b/lib/cdda_interface/test_interface.c @@ -0,0 +1,235 @@ +/* + $Id: test_interface.c,v 1.4 2008/06/13 19:26:23 flameeyes Exp $ + + Copyright (C) 2004, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/****************************************************************** + * + * Fake interface backend for testing paranoia layer + * NOTE: THIS CODE HAVE BEEN FOLDED INTO THE MAINLINE CODE SO IT IS + * NOT USED ANYMORE. (It's not clear that it had been used + * for a while in the non-libcdio cdparaonoia either.) + * + ******************************************************************/ + +#ifdef CDDA_TEST +#include "low_interface.h" +#include "utils.h" + +/* Build which test model? */ +#define CDDA_TEST_OK +#undef CDDA_TEST_JITTER_SMALL +#undef CDDA_TEST_JITTER_LARGE +#undef CDDA_TEST_JITTER_MASSIVE +#undef CDDA_TEST_FRAG_SMALL +#undef CDDA_TEST_FRAG_LARGE +#undef CDDA_TEST_FRAG_MASSIVE +#undef CDDA_TEST_BOGUS_BYTES +#undef CDDA_TEST_DROPDUPE_BYTES +#undef CDDA_TEST_SCRATCH +#undef CDDA_TEST_UNDERRUN + +#undef CDDA_TEST_ALLJITTER +#undef CDDA_TEST_SOMEJITTER +#undef CDDA_TEST_SEEKJITTER + +static int test_readtoc (cdrom_drive *d){ + int tracks=0; + long bytes; + long sectors; + + /* only one track, as many sectors as the file */ + + bytes=lseek(d->cdda_fd,0,SEEK_END); + lseek(d->cdda_fd,0,SEEK_SET); + sectors=bytes/CDIO_CD_FRAMESIZE_RAW; + + d->disc_toc[0].bFlags = 0; + d->disc_toc[0].bTrack = 1; + d->disc_toc[0].dwStartSector = 37; + + d->disc_toc[1].bFlags = 0x4; + d->disc_toc[1].bTrack = CDROM_LEADOUT; + d->disc_toc[1].dwStartSector = sectors+37; + + tracks=2; + d->cd_extra=0; + return(--tracks); /* without lead-out */ +} + +/* we emulate jitter, scratches, atomic jitter and bogus bytes on + boundaries, etc */ + +static long +test_read(cdrom_drive *d, void *p, long begin, long sectors) +{ + +#if defined(CDDA_TEST_SEEKJITTER) \ + || defined(CDDA_TEST_ALLJITTER) \ + || defined(CDDA_TEST_SOMEJITTER) + int jitter_flag=0; +#endif + + int los_flag=0; + static int jitter=0; + int bytes_so_far=0; + long bytestotal; + static FILE *fd=NULL; + static long lastread=0; + + if(!fd)fd=fdopen(d->cdda_fd,"r"); + +#ifdef CDDA_TEST_UNDERRUN + sectors-=1; +#endif + +#ifdef CDDA_TEST_SEEKJITTER + if(lastread!=begin)jitter_flag=1; +#else +#ifdef CDDA_TEST_ALLJITTER + jitter_flag=1; +#else +#ifdef CDDA_TEST_SOMEJITTER + jitter_flag=(drand48()>.9?1:0); + los_flag=(drand48()>.9?1:0); +#else + los_flag=1; +#endif +#endif +#endif + + lastread=begin+sectors; + bytestotal=sectors*CDIO_CD_FRAMESIZE_RAW; + + begin*=CDIO_CD_FRAMESIZE_RAW; + + while(bytes_so_far.8) + this_jitter=32; + else + this_jitter=0; + +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif + + + if(this_bytes>inner_bytes)this_bytes=inner_bytes; + if(begin+jitter+bytes_so_far<0)jitter=0; + seeki=begin+bytes_so_far+jitter; + + if(fseek(fd,seeki,SEEK_SET)<0){ + return(0); + } + rbytes=fread(inner_buf,1,this_bytes,fd); + bytes_so_far+=rbytes; + if(rbytes==0)break; + +#ifdef CDDA_TEST_SEEKJITTER + jitter_flag=0; + los_flag=0; +#else +#ifdef CDDA_TEST_ALLJITTER + jitter_flag=1; + los_flag=0; +#else +#ifdef CDDA_TEST_SOMEJITTER + jitter_flag=(drand48()>.9?1:0); + los_flag=(drand48()>.9?1:0); +#else + los_flag=1; +#endif +#endif +#endif + + } + +#ifdef CDDA_TEST_SCRATCH + { + long location=300*CDIO_CD_FRAMESIZE_RAW+(drand48()*56)+512; + + if(begin<=location && begin+bytestotal>location){ + memset(p+location-begin,(int)(drand48()*256),1100); + } + } +#endif + + return(sectors); +} + +/* set function pointers to use the ioctl routines */ +int test_init_drive (cdrom_drive *d){ + + d->nsectors=13; + d->enable_cdda = dummy_exception; + d->read_audio = test_read; + d->read_toc = test_readtoc; + d->set_speed = dummy_exception; + d->tracks=d->read_toc(d); + if(d->tracks==-1) + return(d->tracks); + d->opened=1; + srand48(0); + return(0); +} + +#endif + diff --git a/lib/cdda_interface/toc.c b/lib/cdda_interface/toc.c new file mode 100644 index 00000000..70607177 --- /dev/null +++ b/lib/cdda_interface/toc.c @@ -0,0 +1,210 @@ +/* + $Id: toc.c,v 1.7 2008/04/16 17:00:40 karl Exp $ + + Copyright (C) 2005, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + derived from code (C) 1994-1996 Heiko Eissfeldt + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/****************************************************************** + * Table of contents convenience functions + ******************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "low_interface.h" +#include "utils.h" + +/*! Return the lsn for the start of track i_track */ +lsn_t +cdda_track_firstsector(cdrom_drive_t *d, track_t i_track) +{ + if(!d->opened){ + cderror(d,"400: Device not open\n"); + return(-1); + } + + if (i_track == 0) { + if (d->disc_toc[0].dwStartSector == 0) { + /* first track starts at lsn 0 -> no pre-gap */ + cderror(d,"401: Invalid track number\n"); + return(-1); + } + else { + return 0; /* pre-gap of first track always starts at lba 0 */ + } + } + + if( i_track>d->tracks) { + cderror(d,"401: Invalid track number\n"); + return(-1); + } + + { + const track_t i_first_track = cdio_get_first_track_num(d->p_cdio); + return(d->disc_toc[i_track-i_first_track].dwStartSector); + } +} + +/*! Get first lsn of the first audio track. -1 is returned on error. */ +lsn_t +cdda_disc_firstsector(cdrom_drive_t *d) +{ + int i; + if(!d->opened){ + cderror(d,"400: Device not open\n"); + return(-1); + } + + /* look for an audio track */ + for ( i=0; itracks; i++ ) + if( cdda_track_audiop(d, i+1)==1 ) { + if (i == 0) /* disc starts at lba 0 if first track is an audio track */ + return 0; + else + return cdda_track_firstsector(d, i+1); + } + + cderror(d,"403: No audio tracks on disc\n"); + return(-1); +} + +/*! Get last lsn of the track. The lsn is generally one less than the + start of the next track. -1 is returned on error. */ +lsn_t +cdda_track_lastsector(cdrom_drive_t *d, track_t i_track) +{ + if (!d->opened) { + cderror(d,"400: Device not open\n"); + return -1; + } + + if (i_track == 0) { + if (d->disc_toc[0].dwStartSector == 0) { + /* first track starts at lba 0 -> no pre-gap */ + cderror(d,"401: Invalid track number\n"); + return(-1); + } + else { + return d->disc_toc[0].dwStartSector-1; + } + } + + if ( i_track<1 || i_track>d->tracks ) { + cderror(d,"401: Invalid track number\n"); + return -1; + } + + /* CD Extra have their first session ending at the last audio track */ + if (d->cd_extra > 0 && i_track+1 <= d->tracks) { + if (d->audio_last_sector >= d->disc_toc[i_track-1].dwStartSector && + d->audio_last_sector < d->disc_toc[i_track].dwStartSector) { + return d->audio_last_sector; + } + } + + /* Safe, we've always the leadout at disc_toc[tracks] */ + return(d->disc_toc[i_track].dwStartSector-1); +} + +/*! Get last lsn of the last audio track. The last lssn generally one + less than the start of the next track after the audio track. -1 is + returned on error. */ +lsn_t +cdda_disc_lastsector(cdrom_drive_t *d) +{ + if (!d->opened) { + cderror(d,"400: Device not open\n"); + return -1; + } else { + /* look for an audio track */ + const track_t i_first_track = cdio_get_first_track_num(d->p_cdio); + track_t i = cdio_get_last_track_num(d->p_cdio); + for ( ; i >= i_first_track; i-- ) + if ( cdda_track_audiop(d,i) ) + return (cdda_track_lastsector(d,i)); + } + cderror(d,"403: No audio tracks on disc\n"); + return -1; +} + +/*! Return the number of tracks on the CD. */ +track_t +cdda_tracks(cdrom_drive_t *d) +{ + if (!d->opened){ + cderror(d,"400: Device not open\n"); + return -1; + } + return(d->tracks); +} + +/*! Return the track containing the given LSN. If the LSN is before + the first track (in the pregap), 0 is returned. If there was an + error or the LSN after the LEADOUT (beyond the end of the CD), then + CDIO_INVALID_TRACK is returned. + */ +int +cdda_sector_gettrack(cdrom_drive_t *d, lsn_t lsn) +{ + if (!d->opened) { + cderror(d,"400: Device not open\n"); + return -1; + } else { + if (lsn < d->disc_toc[0].dwStartSector) + return 0; /* We're in the pre-gap of first track */ + + return cdio_get_track(d->p_cdio, lsn); + } +} + +/*! Return the number of channels in track: 2 or 4; -2 if not + implemented or -1 for error. + Not meaningful if track is not an audio track. +*/ +int +cdda_track_channels(cdrom_drive_t *d, track_t i_track) +{ + return(cdio_get_track_channels(d->p_cdio, i_track)); +} + +/*! Return 1 is track is an audio track, 0 otherwise. */ +int +cdda_track_audiop(cdrom_drive_t *d, track_t i_track) +{ + track_format_t track_format = cdio_get_track_format(d->p_cdio, i_track); + return TRACK_FORMAT_AUDIO == track_format ? 1 : 0; +} + +/*! Return 1 is track is an audio track, 0 otherwise. */ +int +cdda_track_copyp(cdrom_drive_t *d, track_t i_track) +{ + track_flag_t track_flag = cdio_get_track_copy_permit(d->p_cdio, i_track); + return CDIO_TRACK_FLAG_TRUE == track_flag ? 1 : 0; +} + +/*! Return 1 is audio track has linear preemphasis set, 0 otherwise. + Only makes sense for audio tracks. + */ +int +cdda_track_preemp(cdrom_drive_t *d, track_t i_track) +{ + track_flag_t track_flag = cdio_get_track_preemphasis(d->p_cdio, i_track); + return CDIO_TRACK_FLAG_TRUE == track_flag ? 1 : 0; +} + diff --git a/lib/cdda_interface/utils.c b/lib/cdda_interface/utils.c new file mode 100644 index 00000000..da07ddb2 --- /dev/null +++ b/lib/cdda_interface/utils.c @@ -0,0 +1,152 @@ +/* + $Id: utils.c,v 1.4 2008/04/16 17:00:41 karl Exp $ + + Copyright (C) 2004, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "common_interface.h" +#include "utils.h" +void +cderror(cdrom_drive_t *d,const char *s) +{ + if(s && d){ + switch(d->errordest){ + case CDDA_MESSAGE_PRINTIT: + write(STDERR_FILENO,s,strlen(s)); + break; + case CDDA_MESSAGE_LOGIT: + d->errorbuf=catstring(d->errorbuf,s); + break; + case CDDA_MESSAGE_FORGETIT: + default: ; + } + } +} + +void +cdmessage(cdrom_drive_t *d, const char *s) +{ + if(s && d){ + switch(d->messagedest){ + case CDDA_MESSAGE_PRINTIT: + write(STDERR_FILENO,s,strlen(s)); + break; + case CDDA_MESSAGE_LOGIT: + d->messagebuf=catstring(d->messagebuf,s); + break; + case CDDA_MESSAGE_FORGETIT: + default: ; + } + } +} + +void +idperror(int messagedest,char **messages,const char *f, + const char *s) +{ + + char *buffer; + int malloced=0; + if(!f) + buffer=(char *)s; + else + if(!s) + buffer=(char *)f; + else{ + buffer=malloc(strlen(f)+strlen(s)+9); + sprintf(buffer,f,s); + malloced=1; + } + + if(buffer){ + switch(messagedest){ + case CDDA_MESSAGE_PRINTIT: + write(STDERR_FILENO,buffer,strlen(buffer)); + if(errno){ + write(STDERR_FILENO,": ",2); + write(STDERR_FILENO,strerror(errno),strlen(strerror(errno))); + write(STDERR_FILENO,"\n",1); + } + break; + case CDDA_MESSAGE_LOGIT: + if(messages){ + *messages=catstring(*messages,buffer); + if(errno){ + *messages=catstring(*messages,": "); + *messages=catstring(*messages,strerror(errno)); + *messages=catstring(*messages,"\n"); + } + } + break; + case CDDA_MESSAGE_FORGETIT: + default: ; + } + } + if(malloced)free(buffer); +} + +void +idmessage(int messagedest,char **messages,const char *f, + const char *s) +{ + char *buffer; + int malloced=0; + if(!f) + buffer=(char *)s; + else + if(!s) + buffer=(char *)f; + else{ + const unsigned int i_buffer=strlen(f)+strlen(s)+10; + buffer=malloc(i_buffer); + sprintf(buffer,f,s); + strncat(buffer,"\n", i_buffer); + malloced=1; + } + + if(buffer){ + switch(messagedest){ + case CDDA_MESSAGE_PRINTIT: + write(STDERR_FILENO,buffer,strlen(buffer)); + if(!malloced)write(STDERR_FILENO,"\n",1); + break; + case CDDA_MESSAGE_LOGIT: + if(messages){ + *messages=catstring(*messages,buffer); + if(!malloced)*messages=catstring(*messages,"\n"); + } + break; + case CDDA_MESSAGE_FORGETIT: + default: ; + } + } + if(malloced)free(buffer); +} + +char * +catstring(char *buff, const char *s) { + if (s) { + const unsigned int add_len = strlen(s) + 9; + if(buff) { + buff = realloc(buff, strlen(buff) + add_len); + } else { + buff=calloc(add_len, 1); + } + strncat(buff, s, add_len); + } + return(buff); +} diff --git a/lib/cdda_interface/utils.h b/lib/cdda_interface/utils.h new file mode 100644 index 00000000..e23268cb --- /dev/null +++ b/lib/cdda_interface/utils.h @@ -0,0 +1,79 @@ +/* + $Id: utils.h,v 1.8 2008/04/16 17:00:41 karl Exp $ + + Copyright (C) 2004, 2005, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include + +/* I wonder how many alignment issues this is gonna trip in the + future... it shouldn't trip any... I guess we'll find out :) */ + +static inline int +bigendianp(void) +{ + int test=1; + char *hack=(char *)(&test); + if(hack[0])return(0); + return(1); +} + +extern char *catstring(char *buff, const char *s); + + +/*#if BYTE_ORDER == LITTLE_ENDIAN*/ + +#ifndef WORDS_BIGENDIAN + +static inline int16_t be16_to_cpu(int16_t x){ + return(UINT16_SWAP_LE_BE_C(x)); +} + +static inline int16_t le16_to_cpu(int16_t x){ + return(x); +} + +#else + +static inline int16_t be16_to_cpu(int16_t x){ + return(x); +} + +static inline int16_t le16_to_cpu(int16_t x){ + return(UINT16_SWAP_LE_BE_C(x)); +} + + +#endif + +static inline int16_t cpu_to_be16(int16_t x){ + return(be16_to_cpu(x)); +} + +static inline int16_t cpu_to_le16(int16_t x){ + return(le16_to_cpu(x)); +} + +void cderror(cdrom_drive_t *d, const char *s); + +void cdmessage(cdrom_drive_t *d,const char *s); + +void idperror(int messagedest, char **messages, const char *f, const char *s); + +void idmessage(int messagedest, char **messages, const char *f, const char *s); + diff --git a/lib/cdio++/.gitignore b/lib/cdio++/.gitignore new file mode 100644 index 00000000..f53478a0 --- /dev/null +++ b/lib/cdio++/.gitignore @@ -0,0 +1,3 @@ +/.deps +/Makefile +/Makefile.in diff --git a/lib/cdio++/Makefile.am b/lib/cdio++/Makefile.am new file mode 100644 index 00000000..5e65b981 --- /dev/null +++ b/lib/cdio++/Makefile.am @@ -0,0 +1,65 @@ +# $Id: Makefile.am,v 1.11 2008/10/29 09:53:00 rocky Exp $ +# +# Copyright (C) 2005, 2006, 2007, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +######################################################## +# Things to make the libcdio++ library +######################################################## +# +# From libtool documentation amended with guidance from N. Boullis: +# +# 1. Start with version information of `0:0:0' for each libtool library. +# +# 2. It is probably not a good idea to update the version information +# several times between public releases, but rather once per public +# release. (This seems to be more an aesthetic consideration than +# a hard technical one.) +# +# 3. If the library source code has changed at all since the last +# update, then increment REVISION (`C:R:A' becomes `C:R+1:A'). +# +# 4. If any interfaces have been added, removed, or changed since the +# last update, increment CURRENT, and set REVISION to 0. +# +# 5. If any interfaces have been added since the last public release, +# then increment AGE. +# +# 6. If any interfaces have been removed or changed since the last +# public release, then set AGE to 0. A changed interface means an +# incompatibility with previous versions. + +lib_LTLIBRARIES = libiso9660++.la libcdio++.la + +libcdiopp_la_CURRENT = 0 +libcdiopp_la_REVISION = 2 +libcdiopp_la_AGE = 0 + +libcdiopp_sources = cdio.cpp devices.cpp + +libcdio___la_SOURCES = $(libcdiopp_sources) +libcdio___la_ldflags = -version-info $(libcdiopp_la_CURRENT):$(libcdiopp_la_REVISION):$(libcdiopp_la_AGE) @LT_NO_UNDEFINED@ + +libiso9660pp_la_CURRENT = 0 +libiso9660pp_la_REVISION = 0 +libiso9660pp_la_AGE = 0 + +libiso9660pp_sources = iso9660.cpp + +libiso9660___la_SOURCES = $(libiso9660pp_sources) +libiso9660___la_LIBADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) +libiso9660___la_ldflags = -version-info $(libiso9660pp_la_CURRENT):$(libiso9660pp_la_REVISION):$(libiso9660pp_la_AGE) @LT_NO_UNDEFINED@ + +INCLUDES = -I$(top_srcdir)/include/ -I$(top_builddir)/include diff --git a/lib/cdio++/cdio.cpp b/lib/cdio++/cdio.cpp new file mode 100644 index 00000000..4f8e3084 --- /dev/null +++ b/lib/cdio++/cdio.cpp @@ -0,0 +1,44 @@ +/* -*- C++ -*- + $Id: cdio.cpp,v 1.2 2008/04/20 13:44:31 karl Exp $ + + Copyright (C) 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include +#include + +void possible_throw_device_exception(driver_return_code_t drc) + { + switch (drc) { + case DRIVER_OP_SUCCESS: + return; + case DRIVER_OP_ERROR: + throw DriverOpError(); + case DRIVER_OP_UNSUPPORTED: + throw DriverOpUnsupported(); + case DRIVER_OP_UNINIT: + throw DriverOpUninit(); + case DRIVER_OP_NOT_PERMITTED: + throw DriverOpNotPermitted(); + case DRIVER_OP_BAD_PARAMETER: + throw DriverOpBadParameter(); + case DRIVER_OP_BAD_POINTER: + throw DriverOpBadPointer(); + case DRIVER_OP_NO_DRIVER: + throw DriverOpNoDriver(); + default: + throw DriverOpException(drc); + } + } diff --git a/lib/cdio++/devices.cpp b/lib/cdio++/devices.cpp new file mode 100644 index 00000000..f234f8cc --- /dev/null +++ b/lib/cdio++/devices.cpp @@ -0,0 +1,244 @@ +/* -*- C++ -*- + $Id: devices.cpp,v 1.3 2008/04/20 13:44:31 karl Exp $ + + Copyright (C) 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "config.h" +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include +#include + +/*! + Close media tray in CD drive if there is a routine to do so. + + @param psz_drive the name of CD-ROM to be closed. + @param driver_id is the driver to be used or that got used if + it was DRIVER_UNKNOWN or DRIVER_DEVICE; If this is NULL, we won't + report back the driver used. +*/ +void closeTray (const char *psz_drive, /*in/out*/ driver_id_t &driver_id) +{ + driver_return_code_t drc = cdio_close_tray (psz_drive, &driver_id); + possible_throw_device_exception(drc); +} + +/*! + Close media tray in CD drive if there is a routine to do so. + + @param psz_drive the name of CD-ROM to be closed. If omitted or + NULL, we'll scan for a suitable CD-ROM. +*/ +void closeTray (const char *psz_drive) +{ + driver_id_t driver_id = DRIVER_UNKNOWN; + closeTray(psz_drive, driver_id); +} + +/*! + Get a string decribing driver_id. + + @param driver_id the driver you want the description for + @return a sring of driver description +*/ +const char * +driverDescribe (driver_id_t driver_id) +{ + return cdio_driver_describe(driver_id); +} + +/*! + Eject media in CD drive if there is a routine to do so. + + If the CD is ejected, object is destroyed. +*/ +void +ejectMedia (const char *psz_drive) +{ + driver_return_code_t drc = cdio_eject_media_drive(psz_drive); + possible_throw_device_exception(drc); +} + +/*! + Free device list returned by GetDevices + + @param device_list list returned by GetDevices + + @see GetDevices + +*/ +void +freeDeviceList (char * device_list[]) +{ + cdio_free_device_list(device_list); +} + +/*! + Return a string containing the default CD device if none is specified. + if p_driver_id is DRIVER_UNKNOWN or DRIVER_DEVICE + then find a suitable one set the default device for that. + + NULL is returned if we couldn't get a default device. +*/ +char * +getDefaultDevice(/*in/out*/ driver_id_t &driver_id) +{ + return cdio_get_default_device_driver(&driver_id); +} + +/*! Return an array of device names. If you want a specific + devices for a driver, give that device. If you want hardware + devices, give DRIVER_DEVICE and if you want all possible devices, + image drivers and hardware drivers give DRIVER_UNKNOWN. + + NULL is returned if we couldn't return a list of devices. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. +*/ +char ** +getDevices(driver_id_t driver_id) +{ + return cdio_get_devices(driver_id); +} + +/*! Like GetDevices above, but we may change the p_driver_id if we + were given DRIVER_DEVICE or DRIVER_UNKNOWN. This is because + often one wants to get a drive name and then *open* it + afterwards. Giving the driver back facilitates this, and speeds + things up for libcdio as well. +*/ + +char ** +getDevices (driver_id_t &driver_id) +{ + return cdio_get_devices_ret(&driver_id); +} + +/*! + Get an array of device names in search_devices that have at least + the capabilities listed by the capabities parameter. If + search_devices is NULL, then we'll search all possible CD drives. + + If "b_any" is set false then every capability listed in the + extended portion of capabilities (i.e. not the basic filesystem) + must be satisified. If "any" is set true, then if any of the + capabilities matches, we call that a success. + + To find a CD-drive of any type, use the mask CDIO_FS_MATCH_ALL. + + @return the array of device names or NULL if we couldn't get a + default device. It is also possible to return a non NULL but + after dereferencing the the value is NULL. This also means nothing + was found. +*/ +char ** +getDevices(/*in*/ char *ppsz_search_devices[], + cdio_fs_anal_t capabilities, bool b_any) +{ + return cdio_get_devices_with_cap(ppsz_search_devices, capabilities, b_any); +} + +/*! + Like GetDevices above but we return the driver we found + as well. This is because often one wants to search for kind of drive + and then *open* it afterwards. Giving the driver back facilitates this, + and speeds things up for libcdio as well. +*/ +char ** +getDevices(/*in*/ char* ppsz_search_devices[], + cdio_fs_anal_t capabilities, /*out*/ driver_id_t &driver_id, + bool b_any) +{ + return cdio_get_devices_with_cap_ret(ppsz_search_devices, capabilities, + b_any, &driver_id); +} + +/*! Return true if we Have driver for driver_id */ +bool +haveDriver (driver_id_t driver_id) +{ + return cdio_have_driver(driver_id); +} + +/*! + +Determine if bin_name is the bin file part of a CDRWIN CD disk image. + +@param bin_name location of presumed CDRWIN bin image file. + @return the corresponding CUE file if bin_name is a BIN file or + NULL if not a BIN file. + */ +char *isBinFile(const char *psz_bin_name) +{ + return cdio_is_binfile(psz_bin_name); +} + +/*! + Determine if cue_name is the cue sheet for a CDRWIN CD disk image. + + @return corresponding BIN file if cue_name is a CDRWIN cue file or + NULL if not a CUE file. + */ +char * +isCueFile(const char *psz_cue_name) +{ + return cdio_is_cuefile(psz_cue_name); +} + +/*! + Determine if psz_source refers to a real hardware CD-ROM. + + @param psz_source location name of object + @param driver_id driver for reading object. Use DRIVER_UNKNOWN if you + don't know what driver to use. + @return true if psz_source is a device; If false is returned we + could have a CD disk image. +*/ +bool +isDevice(const char *psz_source, driver_id_t driver_id) +{ + return cdio_is_device(psz_source, driver_id); +} + +/*! + Determine if psz_nrg is a Nero CD disk image. + + @param psz_nrg location of presumed NRG image file. + @return true if psz_nrg is a Nero NRG image or false + if not a NRG image. +*/ +bool +isNero(const char *psz_nrg) +{ + return cdio_is_nrg(psz_nrg); +} + +/*! + Determine if psz_toc is a TOC file for a cdrdao CD disk image. + + @param psz_toc location of presumed TOC image file. + @return true if toc_name is a cdrdao TOC file or false + if not a TOC file. +*/ +bool +isTocFile(const char *psz_toc) +{ + return cdio_is_tocfile(psz_toc); +} diff --git a/lib/cdio++/iso9660.cpp b/lib/cdio++/iso9660.cpp new file mode 100644 index 00000000..846eec94 --- /dev/null +++ b/lib/cdio++/iso9660.cpp @@ -0,0 +1,295 @@ +/* -*- C++ -*- + $Id: iso9660.cpp,v 1.5 2008/04/20 13:44:31 karl Exp $ + + Copyright (C) 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "config.h" +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include + +/*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + @return Stat * of entry if we found lsn, or NULL otherwise. + Caller must free return value. +*/ +ISO9660::Stat * +ISO9660::FS::find_lsn(lsn_t i_lsn) +{ + return new Stat(iso9660_find_fs_lsn(p_cdio, i_lsn)); +} + +/*! + Read the Primary Volume Descriptor for a CD. + True is returned if read, and false if there was an error. +*/ +ISO9660::PVD * +ISO9660::FS::read_pvd () +{ + iso9660_pvd_t pvd; + bool b_okay = iso9660_fs_read_pvd (p_cdio, &pvd); + if (b_okay) { + return new PVD(&pvd); + } + return (PVD *) NULL; +} + +/*! + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental Volume + Descriptor if (Joliet) extensions are acceptable. +*/ +bool +ISO9660::FS::read_superblock (iso_extension_mask_t iso_extension_mask) +{ + return iso9660_fs_read_superblock (p_cdio, iso_extension_mask); +} + +/*! Read psz_path (a directory) and return a list of iso9660_stat_t + pointers for the files inside that directory. The caller must free the + returned result. +*/ +bool +ISO9660::FS::readdir (const char psz_path[], stat_vector_t& stat_vector, + bool b_mode2) +{ + CdioList_t * p_stat_list = iso9660_fs_readdir (p_cdio, psz_path, + b_mode2); + if (p_stat_list) { + CdioListNode_t *p_entnode; + _CDIO_LIST_FOREACH (p_entnode, p_stat_list) { + iso9660_stat_t *p_statbuf = + (iso9660_stat_t *) _cdio_list_node_data (p_entnode); + stat_vector.push_back(new ISO9660::Stat(p_statbuf)); + } + _cdio_list_free (p_stat_list, false); + return true; + } else { + return false; + } +} + +/*! Close previously opened ISO 9660 image and free resources + associated with the image. Call this when done using using an ISO + 9660 image. + + @return true is unconditionally returned. If there was an error + false would be returned. +*/ +bool +ISO9660::IFS::close() +{ + iso9660_close(p_iso9660); + p_iso9660 = (iso9660_t *) NULL; + return true; +} + +/*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + Returns Stat* of entry if we found lsn, or NULL otherwise. +*/ +ISO9660::Stat * +ISO9660::IFS::find_lsn(lsn_t i_lsn) +{ + return new Stat(iso9660_ifs_find_lsn(p_iso9660, i_lsn)); +} + +/*! + Return the Joliet level recognized. +*/ +uint8_t +ISO9660::IFS::get_joliet_level() +{ + return iso9660_ifs_get_joliet_level(p_iso9660); +} + +/*! + Return true if ISO 9660 image has extended attrributes (XA). +*/ +bool +ISO9660::IFS::is_xa () +{ + return iso9660_ifs_is_xa (p_iso9660); +} + +/*! Open an ISO 9660 image for "fuzzy" reading. This means that we + will try to guess various internal offset based on internal + checks. This may be useful when trying to read an ISO 9660 image + contained in a file format that libiso9660 doesn't know natively + (or knows imperfectly.) + + Maybe in the future we will have a mode. NULL is returned on + error. + + @see open +*/ +bool +ISO9660::IFS::open_fuzzy (const char *psz_path, + iso_extension_mask_t iso_extension_mask, + uint16_t i_fuzz) +{ + p_iso9660 = iso9660_open_fuzzy_ext(psz_path, iso_extension_mask, i_fuzz); + //return p_iso9660 != (iso9660_t *) NULL; + return true; +} + +/*! Read the Primary Volume Descriptor for an ISO 9660 image. A + PVD object is returned if read, and NULL if there was an error. +*/ +ISO9660::PVD * +ISO9660::IFS::read_pvd () +{ + iso9660_pvd_t pvd; + bool b_okay = iso9660_ifs_read_pvd (p_iso9660, &pvd); + if (b_okay) { + return new PVD(&pvd); + } + return (PVD *) NULL; +} + +/*! + Read the Super block of an ISO 9660 image but determine framesize + and datastart and a possible additional offset. Generally here we are + not reading an ISO 9660 image but a CD-Image which contains an ISO 9660 + filesystem. + + @see read_superblock +*/ +bool +ISO9660::IFS::read_superblock (iso_extension_mask_t iso_extension_mask, + uint16_t i_fuzz) +{ + return iso9660_ifs_read_superblock (p_iso9660, iso_extension_mask); +} + +/*! + Read the Super block of an ISO 9660 image but determine framesize + and datastart and a possible additional offset. Generally here we are + not reading an ISO 9660 image but a CD-Image which contains an ISO 9660 + filesystem. + + @see read_superblock +*/ +bool +ISO9660::IFS::read_superblock_fuzzy (iso_extension_mask_t iso_extension_mask, + uint16_t i_fuzz) +{ + return iso9660_ifs_fuzzy_read_superblock (p_iso9660, iso_extension_mask, + i_fuzz); +} + +char * +ISO9660::PVD::get_application_id() +{ + return iso9660_get_application_id(&pvd); +} + +int +ISO9660::PVD::get_pvd_block_size() +{ + return iso9660_get_pvd_block_size(&pvd); +} + +/*! + Return the PVD's preparer ID. + NULL is returned if there is some problem in getting this. +*/ +char * +ISO9660::PVD::get_preparer_id() +{ + return iso9660_get_preparer_id(&pvd); +} + +/*! + Return the PVD's publisher ID. + NULL is returned if there is some problem in getting this. +*/ +char * +ISO9660::PVD::get_publisher_id() +{ + return iso9660_get_publisher_id(&pvd); +} + +const char * +ISO9660::PVD::get_pvd_id() +{ + return iso9660_get_pvd_id(&pvd); +} + +int +ISO9660::PVD::get_pvd_space_size() +{ + return iso9660_get_pvd_space_size(&pvd); +} + +uint8_t +ISO9660::PVD::get_pvd_type() { + return iso9660_get_pvd_type(&pvd); +} + +/*! Return the primary volume id version number (of pvd). + If there is an error 0 is returned. +*/ +int +ISO9660::PVD::get_pvd_version() +{ + return iso9660_get_pvd_version(&pvd); +} + +/*! Return the LSN of the root directory for pvd. + If there is an error CDIO_INVALID_LSN is returned. +*/ +lsn_t +ISO9660::PVD::get_root_lsn() +{ + return iso9660_get_root_lsn(&pvd); +} + +/*! + Return the PVD's system ID. + NULL is returned if there is some problem in getting this. +*/ +char * +ISO9660::PVD::get_system_id() +{ + return iso9660_get_system_id(&pvd); +} + +/*! + Return the PVD's volume ID. + NULL is returned if there is some problem in getting this. +*/ +char * +ISO9660::PVD::get_volume_id() +{ + return iso9660_get_volume_id(&pvd); +} + +/*! + Return the PVD's volumeset ID. + NULL is returned if there is some problem in getting this. +*/ +char * +ISO9660::PVD::get_volumeset_id() +{ + return iso9660_get_volumeset_id(&pvd); +} diff --git a/lib/driver/.gitignore b/lib/driver/.gitignore new file mode 100644 index 00000000..f53478a0 --- /dev/null +++ b/lib/driver/.gitignore @@ -0,0 +1,3 @@ +/.deps +/Makefile +/Makefile.in diff --git a/lib/driver/FreeBSD/Makefile b/lib/driver/FreeBSD/Makefile new file mode 100644 index 00000000..44685764 --- /dev/null +++ b/lib/driver/FreeBSD/Makefile @@ -0,0 +1,22 @@ +# $Id: Makefile,v 1.2 2008/04/21 18:30:19 karl Exp $ +# +# Copyright (C) 2004, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# The make is done above. This boilerplate Makefile just transfers the call + + +all install check clean: + cd .. && $(MAKE) $@ diff --git a/lib/driver/FreeBSD/freebsd.c b/lib/driver/FreeBSD/freebsd.c new file mode 100644 index 00000000..9395e053 --- /dev/null +++ b/lib/driver/FreeBSD/freebsd.c @@ -0,0 +1,893 @@ +/* + $Id: freebsd.c,v 1.38 2008/04/21 18:30:20 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* This file contains FreeBSD-specific code and implements low-level + control of the CD drive. Culled initially I think from xine's or + mplayer's FreeBSD code with lots of modifications. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: freebsd.c,v 1.38 2008/04/21 18:30:20 karl Exp $"; + +#include "freebsd.h" + +#ifdef HAVE_FREEBSD_CDROM + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#include + +#include + +static lba_t get_track_lba_freebsd(void *p_user_data, track_t i_track); + +static access_mode_t +str_to_access_mode_freebsd(const char *psz_access_mode) +{ + const access_mode_t default_access_mode = DEFAULT_FREEBSD_AM; + + if (NULL==psz_access_mode) return default_access_mode; + + if (!strcmp(psz_access_mode, "ioctl")) + return _AM_IOCTL; + else if (!strcmp(psz_access_mode, "CAM")) + return _AM_CAM; + else { + cdio_warn ("unknown access type: %s. Default ioctl used.", + psz_access_mode); + return default_access_mode; + } +} + +static void +free_freebsd (void *p_obj) +{ + _img_private_t *p_env = p_obj; + + if (NULL == p_env) return; + + if (NULL != p_env->device) free(p_env->device); + + if (_AM_CAM == p_env->access_mode) + return free_freebsd_cam(p_env); + else + return cdio_generic_free(p_obj); +} + +/* Check a drive to see if it is a CD-ROM + Return 1 if a CD-ROM. 0 if it exists but isn't a CD-ROM drive + and -1 if no device exists . +*/ +static bool +cdio_is_cdrom(char *drive, char *mnttype) +{ + return cdio_is_cdrom_freebsd_ioctl(drive, mnttype); +} + +/*! + Reads i_blocks of audio sectors from cd device into data starting from lsn. + Returns 0 if no error. + */ +static driver_return_code_t +read_audio_sectors_freebsd (void *p_user_data, void *p_buf, lsn_t i_lsn, + unsigned int i_blocks) +{ + _img_private_t *p_env = p_user_data; + if ( p_env->access_mode == _AM_CAM ) { + return mmc_read_sectors( p_env->gen.cdio, p_buf, i_lsn, + CDIO_MMC_READ_TYPE_CDDA, i_blocks); + } else + return read_audio_sectors_freebsd_ioctl(p_user_data, p_buf, i_lsn, + i_blocks); +} + +/*! + Reads a single mode2 sector from cd device into data starting + from i_lsn. Returns 0 if no error. + */ +static driver_return_code_t +read_mode2_sector_freebsd (void *p_user_data, void *data, lsn_t i_lsn, + bool b_form2) +{ + _img_private_t *p_env = p_user_data; + + if ( p_env->access_mode == _AM_CAM ) + return read_mode2_sector_freebsd_cam(p_env, data, i_lsn, b_form2); + else + return read_mode2_sector_freebsd_ioctl(p_env, data, i_lsn, b_form2); +} + +/*! + Reads i_blocks of mode2 sectors from cd device into data starting + from lsn. + */ +static driver_return_code_t +read_mode2_sectors_freebsd (void *p_user_data, void *p_data, lsn_t i_lsn, + bool b_form2, unsigned int i_blocks) +{ + _img_private_t *p_env = p_user_data; + + if ( p_env->access_mode == _AM_CAM && b_form2 ) { + /* We have a routine that covers this case without looping. */ + return read_mode2_sectors_freebsd_cam(p_env, p_data, i_lsn, i_blocks); + } else { + unsigned int i; + uint16_t i_blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + /* For each frame, pick out the data part we need */ + for (i = 0; i < i_blocks; i++) { + int retval = read_mode2_sector_freebsd (p_env, + ((char *)p_data) + + (i_blocksize * i), + i_lsn + i, b_form2); + if (retval) return retval; + } + } + return DRIVER_OP_SUCCESS; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + @return the lsn. On error return CDIO_INVALID_LSN. + */ +static lsn_t +get_disc_last_lsn_freebsd (void *p_obj) +{ + _img_private_t *p_env = p_obj; + + if (!p_env) return CDIO_INVALID_LSN; + + if (_AM_CAM == p_env->access_mode) + return get_disc_last_lsn_mmc(p_env); + else + return get_disc_last_lsn_freebsd_ioctl(p_env); +} + +/*! + Set the arg "key" with "value" in the source device. + Currently "source" and "access-mode" are valid keys. + "source" sets the source device in I/O operations + "access-mode" sets the the method of CD access + + DRIVER_OP_SUCCESS is returned if no error was found, + and nonzero if there as an error. +*/ +static driver_return_code_t +set_arg_freebsd (void *p_user_data, const char key[], const char value[]) +{ + _img_private_t *p_env = p_user_data; + + if (!strcmp (key, "source")) + { + if (!value) return DRIVER_OP_ERROR; + free (p_env->gen.source_name); + p_env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + p_env->access_mode = str_to_access_mode_freebsd(value); + if (p_env->access_mode == _AM_CAM && !p_env->b_cam_init) + return init_freebsd_cam(p_env) + ? DRIVER_OP_SUCCESS : DRIVER_OP_ERROR; + } + else return DRIVER_OP_ERROR; + + return DRIVER_OP_SUCCESS; + +} + +/* Set CD-ROM drive speed */ +static int +set_speed_freebsd (void *p_user_data, int i_speed) +{ + const _img_private_t *p_env = p_user_data; + + if (!p_env) return -1; +#ifdef CDRIOCREADSPEED + i_speed *= 177; + return ioctl(p_env->gen.fd, CDRIOCREADSPEED, &i_speed); +#else + return -2; +#endif +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return false if unsuccessful; +*/ +static bool +read_toc_freebsd (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + track_t i, j; + + /* read TOC header */ + if ( ioctl(p_env->gen.fd, CDIOREADTOCHEADER, &p_env->tochdr) == -1 ) { + cdio_warn("error in ioctl(CDIOREADTOCHEADER): %s\n", strerror(errno)); + return false; + } + + p_env->gen.i_first_track = p_env->tochdr.starting_track; + p_env->gen.i_tracks = p_env->tochdr.ending_track - + p_env->gen.i_first_track + 1; + + j=0; + for (i=p_env->gen.i_first_track; i<=p_env->gen.i_tracks; i++, j++) { + struct ioc_read_toc_single_entry *p_toc = + &(p_env->tocent[i-p_env->gen.i_first_track]); + p_toc->track = i; + p_toc->address_format = CD_LBA_FORMAT; + + if ( ioctl(p_env->gen.fd, CDIOREADTOCENTRY, p_toc) ) { + cdio_warn("%s %d: %s\n", + "error in ioctl CDROMREADTOCENTRY for track", + i, strerror(errno)); + return false; + } + + set_track_flags(&(p_env->gen.track_flags[i]), p_toc->entry.control); + + } + + p_env->tocent[j].track = CDIO_CDROM_LEADOUT_TRACK; + p_env->tocent[j].address_format = CD_LBA_FORMAT; + if ( ioctl(p_env->gen.fd, CDIOREADTOCENTRY, &(p_env->tocent[j]) ) ){ + cdio_warn("%s: %s\n", + "error in ioctl CDROMREADTOCENTRY for leadout track", + strerror(errno)); + return false; + } + + p_env->gen.toc_init = true; + return true; +} + +/*! + Get the volume of an audio CD. + + @param p_cdio the CD object to be acted upon. +*/ +static driver_return_code_t +audio_get_volume_freebsd (void *p_user_data, + /*out*/ cdio_audio_volume_t *p_volume) +{ + + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDIOCGETVOL, p_volume); +} + +/*! + Pause playing CD through analog output + + @param p_cdio the CD object to be acted upon. +*/ +static driver_return_code_t +audio_pause_freebsd (void *p_user_data) +{ + + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDIOCPAUSE); +} + +/*! + Playing starting at given MSF through analog output + + @param p_cdio the CD object to be acted upon. +*/ +static driver_return_code_t +audio_play_msf_freebsd (void *p_user_data, msf_t *p_start_msf, + msf_t *p_end_msf) +{ + const _img_private_t *p_env = p_user_data; + struct ioc_play_msf freebsd_play_msf; + + freebsd_play_msf.start_m = cdio_from_bcd8(p_start_msf->m); + freebsd_play_msf.start_s = cdio_from_bcd8(p_start_msf->s); + freebsd_play_msf.start_f = cdio_from_bcd8(p_start_msf->f); + + freebsd_play_msf.end_m = cdio_from_bcd8(p_end_msf->m); + freebsd_play_msf.end_s = cdio_from_bcd8(p_end_msf->s); + freebsd_play_msf.end_f = cdio_from_bcd8(p_end_msf->f); + + return ioctl(p_env->gen.fd, CDIOCPLAYMSF, &freebsd_play_msf); +} + +/*! + Playing CD through analog output at the desired track and index + + @param p_user_data the CD object to be acted upon. + @param p_track_index location to start/end. +*/ +static driver_return_code_t +audio_play_track_index_freebsd (void *p_user_data, + cdio_track_index_t *p_track_index) +{ + const _img_private_t *p_env = p_user_data; + msf_t start_msf; + msf_t end_msf; + struct ioc_play_msf freebsd_play_msf; + lsn_t i_lsn = get_track_lba_freebsd(p_user_data, + p_track_index->i_start_track); + + cdio_lsn_to_msf(i_lsn, &start_msf); + i_lsn = get_track_lba_freebsd(p_user_data, p_track_index->i_end_track); + cdio_lsn_to_msf(i_lsn, &end_msf); + + freebsd_play_msf.start_m = start_msf.m; + freebsd_play_msf.start_s = start_msf.s; + freebsd_play_msf.start_f = start_msf.f; + + freebsd_play_msf.end_m = end_msf.m; + freebsd_play_msf.end_s = end_msf.s; + freebsd_play_msf.end_f = end_msf.f; + + return ioctl(p_env->gen.fd, CDIOCPLAYMSF, &freebsd_play_msf); + +} + +/*! + Read Audio Subchannel information + + @param p_user_data the CD object to be acted upon. + @param p_subchannel returned information +*/ +#if 1 +static driver_return_code_t +audio_read_subchannel_freebsd (void *p_user_data, + /*out*/ cdio_subchannel_t *p_subchannel) +{ + const _img_private_t *p_env = p_user_data; + int i_rc; + struct cd_sub_channel_info bsdinfo; + struct ioc_read_subchannel read_subchannel; + memset(& bsdinfo, 0, sizeof(struct cd_sub_channel_info)); + read_subchannel.address_format = CD_MSF_FORMAT; + read_subchannel.data_format = CD_CURRENT_POSITION; + read_subchannel.track = 0; + read_subchannel.data_len = sizeof(struct cd_sub_channel_info); + read_subchannel.data = & bsdinfo; + i_rc = ioctl(p_env->gen.fd, CDIOCREADSUBCHANNEL, &read_subchannel); + if (0 == i_rc) { + p_subchannel->audio_status = bsdinfo.header.audio_status; + p_subchannel->address = bsdinfo.what.position.addr_type; + + p_subchannel->control = bsdinfo.what.position.control; + p_subchannel->track = bsdinfo.what.position.track_number; + p_subchannel->index = bsdinfo.what.position.index_number; + + p_subchannel->abs_addr.m = cdio_to_bcd8 (bsdinfo.what.position.absaddr.msf.minute); + p_subchannel->abs_addr.s = cdio_to_bcd8 (bsdinfo.what.position.absaddr.msf.second); + p_subchannel->abs_addr.f = cdio_to_bcd8 (bsdinfo.what.position.absaddr.msf.frame); + p_subchannel->rel_addr.m = cdio_to_bcd8 (bsdinfo.what.position.reladdr.msf.minute); + p_subchannel->rel_addr.s = cdio_to_bcd8 (bsdinfo.what.position.reladdr.msf.second); + p_subchannel->rel_addr.f = cdio_to_bcd8 (bsdinfo.what.position.reladdr.msf.frame); + } + return i_rc; +} +#endif + +/*! + Resume playing an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_resume_freebsd (void *p_user_data) +{ + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDIOCRESUME, 0); +} + +/*! + Set the volume of an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_set_volume_freebsd (void *p_user_data, + cdio_audio_volume_t *p_volume) +{ + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDIOCSETVOL, p_volume); +} + +/*! + Eject media. Return 1 if successful, 0 otherwise. + */ +static int +eject_media_freebsd (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + return (p_env->access_mode == _AM_IOCTL) + ? eject_media_freebsd_ioctl(p_env) + : eject_media_freebsd_cam(p_env); +} + +/*! + Stop playing an audio CD. + + @param p_user_data the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_stop_freebsd (void *p_user_data) +{ + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDIOCSTOP); +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +get_arg_freebsd (void *user_data, const char key[]) +{ + _img_private_t *env = user_data; + + if (!strcmp (key, "source")) { + return env->gen.source_name; + } else if (!strcmp (key, "access-mode")) { + switch (env->access_mode) { + case _AM_IOCTL: + return "ioctl"; + case _AM_CAM: + return "CAM"; + case _AM_NONE: + return "no access method"; + } + } + return NULL; +} + +/*! + Return the media catalog number MCN. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + FIXME: This is just a guess. + + */ +static char * +get_mcn_freebsd (const void *p_user_data) { + const _img_private_t *p_env = p_user_data; + + return (p_env->access_mode == _AM_IOCTL) + ? get_mcn_freebsd_ioctl(p_env) + : mmc_get_mcn(p_env->gen.cdio); + +} + +static void +get_drive_cap_freebsd (const void *p_user_data, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap) +{ + const _img_private_t *p_env = p_user_data; + + if (p_env->access_mode == _AM_CAM) + get_drive_cap_mmc (p_user_data, p_read_cap, p_write_cap, p_misc_cap); + +} + +/*! + Run a SCSI MMC command. + + p_user_data internal CD structure. + i_timeout_ms time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + i_cdb Size of p_cdb + p_cdb CDB bytes. + e_direction direction the transfer is to go. + i_buf Size of buffer + p_buf Buffer for data, both sending and receiving + */ +static driver_return_code_t +run_mmc_cmd_freebsd( void *p_user_data, unsigned int i_timeout_ms, + unsigned int i_cdb, const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + + if (p_env->access_mode == _AM_CAM) + return run_mmc_cmd_freebsd_cam( p_user_data, i_timeout_ms, i_cdb, p_cdb, + e_direction, i_buf, p_buf ); + else + return DRIVER_OP_UNSUPPORTED; +} + +/*! + Get format of track. + + FIXME: We're just guessing this from the GNU/Linux code. + +*/ +static track_format_t +get_track_format_freebsd(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if (!p_env->gen.toc_init) read_toc_freebsd (p_user_data) ; + + if (i_track > TOTAL_TRACKS || i_track == 0) + return TRACK_FORMAT_ERROR; + + i_track -= FIRST_TRACK_NUM; + + /* This is pretty much copied from the "badly broken" cdrom_count_tracks + in linux/cdrom.c. + */ + if (p_env->tocent[i_track].entry.control & CDIO_CDROM_DATA_TRACK) { + if (p_env->tocent[i_track].address_format == CDIO_CDROM_CDI_TRACK) + return TRACK_FORMAT_CDI; + else if (p_env->tocent[i_track].address_format == CDIO_CDROM_XA_TRACK) + 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 +get_track_green_freebsd(void *user_data, track_t i_track) +{ + _img_private_t *p_env = user_data; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = TOTAL_TRACKS+1; + + if (i_track > TOTAL_TRACKS+1 || i_track == 0) + return false; + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cdinfo for a while. + */ + return ((p_env->tocent[i_track-FIRST_TRACK_NUM].entry.control & 2) != 0); +} + +/*! + Return the starting LSN track number + i_track in obj. Track numbers start at 1. + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + CDIO_INVALID_LBA is returned if there is no track entry. +*/ +static lba_t +get_track_lba_freebsd(void *user_data, track_t i_track) +{ + _img_private_t *p_env = user_data; + + if (!p_env->gen.toc_init) read_toc_freebsd (p_env) ; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = TOTAL_TRACKS+1; + + if (i_track > TOTAL_TRACKS+1 || i_track == 0 || !p_env->gen.toc_init) { + return CDIO_INVALID_LBA; + } else { + return cdio_lsn_to_lba(ntohl(p_env->tocent[i_track-FIRST_TRACK_NUM].entry.addr.lba)); + } +} + +#endif /* HAVE_FREEBSD_CDROM */ + +/*! + Return an array of strings giving possible CD devices. + */ +char ** +cdio_get_devices_freebsd (void) +{ +#ifndef HAVE_FREEBSD_CDROM + return NULL; +#else + char drive[40]; + char **drives = NULL; + unsigned int num_drives=0; + bool exists=true; + char c; + + /* Scan the system for CD-ROM drives. + */ + +#ifdef USE_ETC_FSTAB + + struct fstab *fs; + setfsent(); + + /* Check what's in /etc/fstab... */ + while ( (fs = getfsent()) ) + { + if (strncmp(fs->fs_spec, "/dev/sr", 7)) + cdio_add_device_list(&drives, fs->fs_spec, &num_drives); + } + +#endif + + /* Scan the system for CD-ROM drives. + Not always 100% reliable, so use the USE_MNTENT code above first. + */ + + /* Scan SCSI and CAM devices */ + for ( c='0'; exists && c <='9'; c++ ) { + sprintf(drive, "/dev/cd%c%s", c, DEVICE_POSTFIX); + exists = cdio_is_cdrom(drive, NULL); + if ( exists ) { + cdio_add_device_list(&drives, drive, &num_drives); + } + } + + /* Scan are ATAPI devices */ + for ( c='0'; exists && c <='9'; c++ ) { + sprintf(drive, "/dev/acd%c%s", c, DEVICE_POSTFIX); + exists = cdio_is_cdrom(drive, NULL); + if ( exists ) { + cdio_add_device_list(&drives, drive, &num_drives); + } + } + cdio_add_device_list(&drives, NULL, &num_drives); + return drives; +#endif /*HAVE_FREEBSD_CDROM*/ +} + +/*! + Return a string containing the default CD device if none is specified. + */ +char * +cdio_get_default_device_freebsd() +{ +#ifndef HAVE_FREEBSD_CDROM + return NULL; +#else + char drive[40]; + bool exists=true; + char c; + + /* Scan the system for CD-ROM drives. + */ + +#ifdef USE_ETC_FSTAB + + struct fstab *fs; + setfsent(); + + /* Check what's in /etc/fstab... */ + while ( (fs = getfsent()) ) + { + if (strncmp(fs->fs_spec, "/dev/sr", 7)) + return strdup(fs->fs_spec); + } + +#endif + + /* Scan the system for CD-ROM drives. + Not always 100% reliable, so use the USE_MNTENT code above first. + */ + + /* Scan SCSI and CAM devices */ + for ( c='0'; exists && c <='9'; c++ ) { + sprintf(drive, "/dev/cd%c%s", c, DEVICE_POSTFIX); + exists = cdio_is_cdrom(drive, NULL); + if ( exists ) { + return strdup(drive); + } + } + + /* Scan are ATAPI devices */ + for ( c='0'; exists && c <='9'; c++ ) { + sprintf(drive, "/dev/acd%c%s", c, DEVICE_POSTFIX); + exists = cdio_is_cdrom(drive, NULL); + if ( exists ) { + return strdup(drive); + } + } + return NULL; +#endif /*HAVE_FREEBSD_CDROM*/ +} + +/*! + Close tray on CD-ROM. + + @param psz_device the CD-ROM drive to be closed. + +*/ +driver_return_code_t +close_tray_freebsd (const char *psz_device) +{ +#ifdef HAVE_FREEBSD_CDROM + int fd = open (psz_device, O_RDONLY|O_NONBLOCK, 0); + int i_rc; + + if((i_rc = ioctl(fd, CDIOCCLOSE)) != 0) { + cdio_warn ("ioctl CDIOCCLOSE failed: %s\n", strerror(errno)); + return DRIVER_OP_ERROR; + } + close(fd); + return DRIVER_OP_SUCCESS; +#else + return DRIVER_OP_NO_DRIVER; +#endif /*HAVE_FREEBSD_CDROM*/ +} + +#ifdef HAVE_FREEBSD_CDROM +/*! Find out if media has changed since the last call. @param + p_user_data the environment of the CD object to be acted upon. + @return 1 if media has changed since last call, 0 if not. Error + return codes are the same as driver_return_code_t + */ +static int +get_media_changed_freebsd (const void *p_user_data) +{ + const _img_private_t *p_env = p_user_data; + if ( p_env->access_mode == _AM_CAM ) { + return mmc_get_media_changed( p_env->gen.cdio ); + } + else + return DRIVER_OP_UNSUPPORTED; +} +#endif /*HAVE_FREEBSD_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_freebsd (const char *psz_source_name) +{ + return cdio_open_am_freebsd(psz_source_name, NULL); +} + + +/*! + 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_am_freebsd (const char *psz_orig_source_name, + const char *psz_access_mode) +{ + +#ifdef HAVE_FREEBSD_CDROM + CdIo *ret; + _img_private_t *_data; + char *psz_source_name; + + cdio_funcs_t _funcs = { + .audio_get_volume = audio_get_volume_freebsd, + .audio_pause = audio_pause_freebsd, + .audio_play_msf = audio_play_msf_freebsd, + .audio_play_track_index = audio_play_track_index_freebsd, + .audio_read_subchannel = audio_read_subchannel_freebsd, + .audio_resume = audio_resume_freebsd, + .audio_set_volume = audio_set_volume_freebsd, + .audio_stop = audio_stop_freebsd, + .eject_media = eject_media_freebsd, + .free = free_freebsd, + .get_arg = get_arg_freebsd, + .get_blocksize = get_blocksize_mmc, + .get_cdtext = get_cdtext_generic, + .get_default_device = cdio_get_default_device_freebsd, + .get_devices = cdio_get_devices_freebsd, + .get_disc_last_lsn = get_disc_last_lsn_freebsd, + .get_discmode = get_discmode_generic, + .get_drive_cap = get_drive_cap_freebsd, + .get_first_track_num = get_first_track_num_generic, + .get_media_changed = get_media_changed_freebsd, + .get_mcn = get_mcn_freebsd, + .get_num_tracks = get_num_tracks_generic, + .get_track_channels = get_track_channels_generic, + .get_track_copy_permit = get_track_copy_permit_generic, + .get_track_format = get_track_format_freebsd, + .get_track_green = get_track_green_freebsd, + .get_track_lba = get_track_lba_freebsd, + .get_track_preemphasis = get_track_preemphasis_generic, + .get_track_msf = NULL, + .lseek = cdio_generic_lseek, + .read = cdio_generic_read, + .read_audio_sectors = read_audio_sectors_freebsd, + .read_data_sectors = read_data_sectors_mmc, + .read_mode2_sector = read_mode2_sector_freebsd, + .read_mode2_sectors = read_mode2_sectors_freebsd, + .read_toc = read_toc_freebsd, + .run_mmc_cmd = run_mmc_cmd_freebsd, + .set_arg = set_arg_freebsd, + .set_blocksize = set_blocksize_mmc, + .set_speed = set_speed_freebsd, + }; + + _data = calloc(1, sizeof (_img_private_t)); + _data->access_mode = str_to_access_mode_freebsd(psz_access_mode); + _data->gen.init = false; + _data->gen.fd = -1; + _data->gen.toc_init = false; + _data->gen.b_cdtext_init = false; + _data->gen.b_cdtext_error = false; + + if (NULL == psz_orig_source_name) { + psz_source_name=cdio_get_default_device_freebsd(); + if (NULL == psz_source_name) return NULL; + _data->device = psz_source_name; + set_arg_freebsd(_data, "source", psz_source_name); + } else { + if (cdio_is_device_generic(psz_orig_source_name)) { + set_arg_freebsd(_data, "source", psz_orig_source_name); + _data->device = strdup(psz_orig_source_name); + } else { + /* The below would be okay if all device drivers worked this way. */ +#if 0 + cdio_info ("source %s is a not a device", psz_orig_source_name); +#endif + free(_data); + return NULL; + } + } + + ret = cdio_new ((void *)_data, &_funcs); + if (ret == NULL) return NULL; + + if (cdio_generic_init(_data, O_RDONLY)) + if ( _data->access_mode == _AM_IOCTL ) { + return ret; + } else { + if (init_freebsd_cam(_data)) + return ret; + else { + cdio_generic_free (_data); + return NULL; + } + } + else { + cdio_generic_free (_data); + return NULL; + } + +#else + return NULL; +#endif /* HAVE_FREEBSD_CDROM */ + +} + +bool +cdio_have_freebsd (void) +{ +#ifdef HAVE_FREEBSD_CDROM + return true; +#else + return false; +#endif /* HAVE_FREEBSD_CDROM */ +} diff --git a/lib/driver/FreeBSD/freebsd.h b/lib/driver/FreeBSD/freebsd.h new file mode 100644 index 00000000..2bf51662 --- /dev/null +++ b/lib/driver/FreeBSD/freebsd.h @@ -0,0 +1,232 @@ +/* + $Id: freebsd.h,v 1.10 2008/05/11 09:50:54 rocky Exp $ + + Copyright (C) 2003, 2004, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* This file contains FreeBSD-specific code and implements low-level + control of the CD drive. Culled initially I think from xine's or + mplayer's FreeBSD code with lots of modifications. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "cdio_assert.h" +#include "cdio_private.h" + +/*! + For ioctl access /dev/acd0c is preferred over /dev/cd0c. + For cam access /dev/cd0c is preferred. DEFAULT_CDIO_DEVICE and + DEFAULT_FREEBSD_AM should be consistent. + */ + +#ifndef DEFAULT_CDIO_DEVICE +#define DEFAULT_CDIO_DEVICE "/dev/cd0c" +#endif + +#ifndef DEFAULT_FREEBSD_AM +#define DEFAULT_FREEBSD_AM _AM_CAM +#endif + +#include + +#ifdef HAVE_FREEBSD_CDROM + +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_CDIO_H +# include +#endif + +#ifndef CDIOCREADAUDIO +struct ioc_read_audio +{ + u_char address_format; + union msf_lba address; + int nframes; + u_char* buffer; +}; + +#define CDIOCREADAUDIO _IOWR('c',31,struct ioc_read_audio) +#endif + +#include + +#include +#include +#include +#include /* for __FreeBSD_version */ + +#if (__FreeBSD_version < 500000) && (__FreeBSD_kernel_version < 500000) +#define DEVICE_POSTFIX "c" +#else +#define DEVICE_POSTFIX "" +#endif + +#define HAVE_FREEBSD_CAM +#ifdef HAVE_FREEBSD_CAM +#include + +#include +#include +#include +#define ERRCODE(s) ((((s)[2]&0x0F)<<16)|((s)[12]<<8)|((s)[13])) +#define EMEDIUMTYPE EINVAL +#define ENOMEDIUM ENODEV +#define CREAM_ON_ERRNO(s) do { \ + switch ((s)[12]) \ + { case 0x04: errno=EAGAIN; break; \ + case 0x20: errno=ENODEV; break; \ + case 0x21: if ((s)[13]==0) errno=ENOSPC; \ + else errno=EINVAL; \ + break; \ + case 0x30: errno=EMEDIUMTYPE; break; \ + case 0x3A: errno=ENOMEDIUM; break; \ + } \ +} while(0) +#endif /*HAVE_FREEBSD_CAM*/ + +#include + +#define TOTAL_TRACKS ( p_env->tochdr.ending_track \ + - p_env->tochdr.starting_track + 1) +#define FIRST_TRACK_NUM (p_env->tochdr.starting_track) + +typedef enum { + _AM_NONE, + _AM_IOCTL, + _AM_CAM +} access_mode_t; + +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + +#ifdef HAVE_FREEBSD_CAM + char *device; + struct cam_device *cam; + union ccb ccb; +#endif + + access_mode_t access_mode; + + bool b_ioctl_init; + bool b_cam_init; + + /* Track information */ + struct ioc_toc_header tochdr; + + /* Entry info for each track. Add 1 for leadout. */ + struct ioc_read_toc_single_entry tocent[CDIO_CD_MAX_TRACKS+1]; + +} _img_private_t; + +bool cdio_is_cdrom_freebsd_ioctl(char *drive, char *mnttype); + +track_format_t get_track_format_freebsd_ioctl(const _img_private_t *env, + track_t i_track); +bool get_track_green_freebsd_ioctl(const _img_private_t *env, + track_t i_track); + +driver_return_code_t eject_media_freebsd_ioctl (_img_private_t *p_env); +driver_return_code_t eject_media_freebsd_cam (_img_private_t *p_env); + +void get_drive_cap_freebsd_cam (const _img_private_t *p_env, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap); + +static int get_media_changed_freebsd (const void *p_user_data); + +char *get_mcn_freebsd_ioctl (const _img_private_t *p_env); + +void free_freebsd_cam (void *obj); + +/*! + Using the ioctl method, r nblocks of audio sectors from cd device + into data starting from lsn. Returns 0 if no error. + */ +int read_audio_sectors_freebsd_ioctl (_img_private_t *env, void *data, + lsn_t lsn, unsigned int nblocks); +/*! + Using the CAM method, reads nblocks of mode2 sectors from + cd device using into data starting from lsn. Returns 0 if no + error. +*/ +int read_mode2_sector_freebsd_cam (_img_private_t *env, void *data, + lsn_t lsn, bool b_form2); + +/*! + Using the ioctl method, reads nblocks of mode2 sectors from + cd device using into data starting from lsn. Returns 0 if no + error. +*/ +int read_mode2_sector_freebsd_ioctl (_img_private_t *env, void *data, + lsn_t lsn, bool b_form2); + +/*! + Using the CAM method, reads nblocks of mode2 form2 sectors from + cd device using into data starting from lsn. Returns 0 if no + error. + + Note: if you want form1 sectors, the caller has to pick out the + appropriate piece. +*/ +int read_mode2_sectors_freebsd_cam (_img_private_t *env, void *buf, + lsn_t lsn, unsigned int nblocks); + +bool read_toc_freebsd_ioctl (_img_private_t *env); + +/*! + Run a SCSI MMC command. + + p_user_data internal CD structure. + i_timeout time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + i_cdb Size of p_cdb + p_cdb CDB bytes. + e_direction direction the transfer is to go. + i_buf Size of buffer + p_buf Buffer for data, both sending and receiving + + Return 0 if no error. + */ +int run_mmc_cmd_freebsd_cam( const void *p_user_data, + unsigned int i_timeout_ms, + unsigned int i_cdb, + const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, + /*in/out*/ void *p_buf ); + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +lsn_t get_disc_last_lsn_freebsd_ioctl (_img_private_t *_obj); + +bool init_freebsd_cam (_img_private_t *env); +void free_freebsd_cam (void *user_data); + +#endif /*HAVE_FREEBSD_CDROM*/ diff --git a/lib/driver/FreeBSD/freebsd_cam.c b/lib/driver/FreeBSD/freebsd_cam.c new file mode 100644 index 00000000..23fb3954 --- /dev/null +++ b/lib/driver/FreeBSD/freebsd_cam.c @@ -0,0 +1,257 @@ +/* + $Id: freebsd_cam.c,v 1.12 2008/04/21 18:30:20 karl Exp $ + + Copyright (C) 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* This file contains FreeBSD-specific code and implements low-level + control of the CD drive via SCSI emulation. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: freebsd_cam.c,v 1.12 2008/04/21 18:30:20 karl Exp $"; + +#ifdef HAVE_FREEBSD_CDROM + +#include "freebsd.h" +#include + +/* Default value in seconds we will wait for a command to + complete. */ +#define DEFAULT_TIMEOUT_MSECS 10000 + +/*! + Run a SCSI MMC command. + + p_user_data internal CD structure. + i_timeout_ms time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + i_cdb Size of p_cdb + p_cdb CDB bytes. + e_direction direction the transfer is to go. + i_buf Size of buffer + p_buf Buffer for data, both sending and receiving + + Return 0 if no error. + */ +int +run_mmc_cmd_freebsd_cam( const void *p_user_data, unsigned int i_timeout_ms, + unsigned int i_cdb, const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + int i_status; + int direction = CAM_DEV_QFRZDIS; + union ccb ccb; + + if (!p_env || !p_env->cam) return -2; + + memset(&ccb, 0, sizeof(ccb)); + + ccb.ccb_h.path_id = p_env->cam->path_id; + ccb.ccb_h.target_id = p_env->cam->target_id; + ccb.ccb_h.target_lun = p_env->cam->target_lun; + ccb.ccb_h.timeout = i_timeout_ms; + + if (!i_buf) + direction |= CAM_DIR_NONE; + else + direction |= (e_direction == SCSI_MMC_DATA_READ)?CAM_DIR_IN : CAM_DIR_OUT; + + + memcpy(ccb.csio.cdb_io.cdb_bytes, p_cdb->field, i_cdb); + ccb.csio.cdb_len = + mmc_get_cmd_len(ccb.csio.cdb_io.cdb_bytes[0]); + + cam_fill_csio (&(ccb.csio), 1, NULL, + direction | CAM_DEV_QFRZDIS, MSG_SIMPLE_Q_TAG, p_buf, i_buf, + sizeof(ccb.csio.sense_data), ccb.csio.cdb_len, 30*1000); + + if (cam_send_ccb(p_env->cam, &ccb) < 0) + { + cdio_warn ("transport failed: %s", strerror(errno)); + return -1; + } + if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) + { + return 0; + } + errno = EIO; + i_status = ERRCODE(((unsigned char *)&ccb.csio.sense_data)); + if (i_status == 0) + i_status = -1; + else + CREAM_ON_ERRNO(((unsigned char *)&ccb.csio.sense_data)); + cdio_warn ("transport failed: %d", i_status); + return i_status; +} + +bool +init_freebsd_cam (_img_private_t *p_env) +{ + char pass[100]; + + p_env->cam=NULL; + memset (&p_env->ccb, 0, sizeof(p_env->ccb)); + p_env->ccb.ccb_h.func_code = XPT_GDEVLIST; + + if (-1 == p_env->gen.fd) + p_env->gen.fd = open (p_env->device, O_RDONLY, 0); + + if (p_env->gen.fd < 0) + { + cdio_warn ("open (%s): %s", p_env->device, strerror (errno)); + return false; + } + + if (ioctl (p_env->gen.fd, CAMGETPASSTHRU, &p_env->ccb) < 0) + { + cdio_warn ("open: %s", strerror (errno)); + return false; + } + sprintf (pass,"/dev/%.15s%u", + p_env->ccb.cgdl.periph_name, + p_env->ccb.cgdl.unit_number); + p_env->cam = cam_open_pass (pass,O_RDWR,NULL); + if (!p_env->cam) return false; + + p_env->gen.init = true; + p_env->b_cam_init = true; + return true; +} + +void +free_freebsd_cam (void *user_data) +{ + _img_private_t *p_env = user_data; + + if (NULL == p_env) return; + + if (p_env->gen.fd > 0) + close (p_env->gen.fd); + p_env->gen.fd = -1; + + if(p_env->cam) + cam_close_device(p_env->cam); + + free (p_env); +} + +driver_return_code_t +read_mode2_sector_freebsd_cam (_img_private_t *p_env, void *data, lsn_t lsn, + bool b_form2) +{ + if ( b_form2 ) + return read_mode2_sectors_freebsd_cam(p_env, data, lsn, 1); + else { + /* Need to pick out the data portion from a mode2 form2 frame */ + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + int retval = read_mode2_sectors_freebsd_cam(p_env, buf, lsn, 1); + if ( retval ) return retval; + memcpy (((char *)data), buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE); + return DRIVER_OP_SUCCESS; + } +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +int +read_mode2_sectors_freebsd_cam (_img_private_t *p_env, void *p_buf, + lsn_t lsn, unsigned int nblocks) +{ + mmc_cdb_t cdb = {{0, }}; + + bool b_read_10 = false; + + CDIO_MMC_SET_READ_LBA(cdb.field, lsn); + + if (b_read_10) { + int retval; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_10); + CDIO_MMC_SET_READ_LENGTH16(cdb.field, nblocks); + if ((retval = mmc_set_blocksize (p_env->gen.cdio, M2RAW_SECTOR_SIZE))) + return retval; + + if ((retval = run_mmc_cmd_freebsd_cam (p_env, 0, + mmc_get_cmd_len(cdb.field[0]), + &cdb, + SCSI_MMC_DATA_READ, + M2RAW_SECTOR_SIZE * nblocks, + p_buf))) + { + mmc_set_blocksize (p_env->gen.cdio, CDIO_CD_FRAMESIZE); + return retval; + } + + return mmc_set_blocksize (p_env->gen.cdio, CDIO_CD_FRAMESIZE); + } else { + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_CD); + CDIO_MMC_SET_READ_LENGTH24(cdb.field, nblocks); + cdb.field[1] = 0; /* sector size mode2 */ + cdb.field[9] = 0x58; /* 2336 mode2 */ + return run_mmc_cmd_freebsd_cam (p_env, 0, + mmc_get_cmd_len(cdb.field[0]), + &cdb, + SCSI_MMC_DATA_READ, + M2RAW_SECTOR_SIZE * nblocks, p_buf); + + } +} + +/*! + Eject media in CD-ROM drive. Return DRIVER_OP_SUCCESS if successful, + DRIVER_OP_ERROR on error. + */ +driver_return_code_t +eject_media_freebsd_cam (_img_private_t *p_env) +{ + int i_status; + mmc_cdb_t cdb = {{0, }}; + uint8_t buf[1]; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_ALLOW_MEDIUM_REMOVAL); + + i_status = run_mmc_cmd_freebsd_cam (p_env, DEFAULT_TIMEOUT_MSECS, + mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_WRITE, 0, &buf); + if (i_status) return i_status; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_START_STOP); + cdb.field[4] = 1; + i_status = run_mmc_cmd_freebsd_cam (p_env, DEFAULT_TIMEOUT_MSECS, + mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_WRITE, 0, &buf); + if (i_status) return i_status; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_START_STOP); + cdb.field[4] = 2; /* eject */ + + return run_mmc_cmd_freebsd_cam (p_env, DEFAULT_TIMEOUT_MSECS, + mmc_get_cmd_len(cdb.field[0]), + &cdb, + SCSI_MMC_DATA_WRITE, 0, &buf); +} + +#endif /* HAVE_FREEBSD_CDROM */ diff --git a/lib/driver/FreeBSD/freebsd_ioctl.c b/lib/driver/FreeBSD/freebsd_ioctl.c new file mode 100644 index 00000000..0f227a08 --- /dev/null +++ b/lib/driver/FreeBSD/freebsd_ioctl.c @@ -0,0 +1,262 @@ +/* + $Id: freebsd_ioctl.c,v 1.7 2008/04/21 18:30:20 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* This file contains FreeBSD-specific code and implements low-level + control of the CD drive. Culled initially I think from xine's or + mplayer's FreeBSD code with lots of modifications. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: freebsd_ioctl.c,v 1.7 2008/04/21 18:30:20 karl Exp $"; + +#ifdef HAVE_FREEBSD_CDROM + +#include "freebsd.h" + +/* Check a drive to see if it is a CD-ROM + Return 1 if a CD-ROM. 0 if it exists but isn't a CD-ROM drive + and -1 if no device exists . +*/ +bool +cdio_is_cdrom_freebsd_ioctl(char *drive, char *mnttype) +{ + bool is_cd=false; + int cdfd; + struct ioc_toc_header tochdr; + + /* If it doesn't exist, return -1 */ + if ( !cdio_is_device_quiet_generic(drive) ) { + return(false); + } + + /* If it does exist, verify that it's an available CD-ROM */ + cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0); + + /* Should we want to test the condition in more detail: + ENOENT is the error for /dev/xxxxx does not exist; + ENODEV means there's no drive present. */ + + if ( cdfd >= 0 ) { + if ( ioctl(cdfd, CDIOREADTOCHEADER, &tochdr) != -1 ) { + is_cd = true; + } + close(cdfd); + } + /* Even if we can't read it, it might be mounted */ + else if ( mnttype && (strcmp(mnttype, "iso9660") == 0) ) { + is_cd = true; + } + return(is_cd); +} + +/*! + Reads a single mode2 sector from cd device into data starting from lsn. + Returns 0 if no error. + */ +int +read_audio_sectors_freebsd_ioctl (_img_private_t *_obj, void *data, lsn_t lsn, + unsigned int nblocks) +{ + unsigned char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + struct ioc_read_audio cdda; + + cdda.address.lba = lsn; + cdda.buffer = buf; + cdda.nframes = nblocks; + cdda.address_format = CDIO_CDROM_LBA; + + /* read a frame */ + if(ioctl(_obj->gen.fd, CDIOCREADAUDIO, &cdda) < 0) { + perror("CDIOCREADAUDIO"); + return 1; + } + memcpy (data, buf, CDIO_CD_FRAMESIZE_RAW); + + return 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +int +read_mode2_sector_freebsd_ioctl (_img_private_t *p_env, void *data, lsn_t lsn, + bool b_form2) +{ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + int retval; + + if ( !b_form2 ) + return cdio_generic_read_form1_sector (p_env, buf, lsn); + + if ( (retval = read_audio_sectors_freebsd_ioctl (p_env, buf, lsn, 1)) ) + return retval; + + memcpy (data, buf + CDIO_CD_XA_SYNC_HEADER, M2RAW_SECTOR_SIZE); + + return 0; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +lsn_t +get_disc_last_lsn_freebsd_ioctl (_img_private_t *p_obj) +{ + struct ioc_read_toc_single_entry tocent; + uint32_t size; + + tocent.track = CDIO_CDROM_LEADOUT_TRACK; + tocent.address_format = CDIO_CDROM_LBA; + if (ioctl (p_obj->gen.fd, CDIOREADTOCENTRY, &tocent) == -1) + { + perror ("ioctl(CDROMREADTOCENTRY)"); + exit (EXIT_FAILURE); + } + + size = tocent.entry.addr.lba; + + return size; +} + +/*! + Eject media in CD-ROM drive. Return DRIVER_OP_SUCCESS if successful, + DRIVER_OP_ERROR on error. + */ +driver_return_code_t +eject_media_freebsd_ioctl (_img_private_t *p_env) +{ + _img_private_t *p_obj = p_env; + int ret=DRIVER_OP_ERROR; + + if (ioctl(p_obj->gen.fd, CDIOCALLOW) == -1) { + cdio_warn("ioctl(fd, CDIOCALLOW) failed: %s\n", strerror(errno)); + } else if (ioctl(p_obj->gen.fd, CDIOCEJECT) == -1) { + cdio_warn("ioctl(CDIOCEJECT) failed: %s\n", strerror(errno)); + } else { + ret=DRIVER_OP_SUCCESS;; + } + + return ret; +} + +/*! + Return the media catalog number MCN. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + FIXME: This is just a guess. + + */ +char * +get_mcn_freebsd_ioctl (const _img_private_t *p_env) { + + struct ioc_read_subchannel subchannel; + struct cd_sub_channel_info subchannel_info; + + subchannel.address_format = CDIO_CDROM_MSF; + subchannel.data_format = CDIO_SUBCHANNEL_MEDIA_CATALOG; + subchannel.track = 0; + subchannel.data_len = sizeof(subchannel_info); + subchannel.data = &subchannel_info; + + if(ioctl(p_env->gen.fd, CDIOCREADSUBCHANNEL, &subchannel) < 0) { + perror("CDIOCREADSUBCHANNEL"); + return NULL; + } + + /* Probably need a loop over tracks rather than give up if we + can't find in track 0. + */ + if (subchannel_info.what.media_catalog.mc_valid) + return strdup(subchannel_info.what.media_catalog.mc_number); + else + return NULL; +} + +/*! + Get format of track. + + FIXME: We're just guessing this from the GNU/Linux code. + +*/ +track_format_t +get_track_format_freebsd_ioctl(const _img_private_t *p_env, track_t i_track) +{ + struct ioc_read_subchannel subchannel; + struct cd_sub_channel_info subchannel_info; + + subchannel.address_format = CDIO_CDROM_LBA; + subchannel.data_format = CDIO_SUBCHANNEL_CURRENT_POSITION; + subchannel.track = i_track; + subchannel.data_len = 1; + subchannel.data = &subchannel_info; + + if(ioctl(p_env->gen.fd, CDIOCREADSUBCHANNEL, &subchannel) < 0) { + perror("CDIOCREADSUBCHANNEL"); + return 1; + } + + if (subchannel_info.what.position.control == 0x04) { + if (subchannel_info.what.position.data_format == 0x10) + return TRACK_FORMAT_CDI; + else if (subchannel_info.what.position.data_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? +*/ +bool +get_track_green_freebsd_ioctl(const _img_private_t *p_env, track_t i_track) +{ + struct ioc_read_subchannel subchannel; + struct cd_sub_channel_info subchannel_info; + + subchannel.address_format = CDIO_CDROM_LBA; + subchannel.data_format = CDIO_SUBCHANNEL_CURRENT_POSITION; + subchannel.track = i_track; + subchannel.data_len = 1; + subchannel.data = &subchannel_info; + + if(ioctl(p_env->gen.fd, CDIOCREADSUBCHANNEL, &subchannel) < 0) { + perror("CDIOCREADSUBCHANNEL"); + return 1; + } + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cdinfo for a while. + */ + return (subchannel_info.what.position.control & 2) != 0; +} + +#endif /* HAVE_FREEBSD_CDROM */ diff --git a/lib/driver/MSWindows/Makefile b/lib/driver/MSWindows/Makefile new file mode 100644 index 00000000..5ba761ce --- /dev/null +++ b/lib/driver/MSWindows/Makefile @@ -0,0 +1,22 @@ +# $Id: Makefile,v 1.2 2008/04/21 18:30:21 karl Exp $ +# +# Copyright (C) 2004, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# The make is done above. This boilerplate Makefile just transfers the call + + +all install check clean: + cd .. && $(MAKE) $@ diff --git a/lib/driver/MSWindows/aspi32.c b/lib/driver/MSWindows/aspi32.c new file mode 100644 index 00000000..e6544e36 --- /dev/null +++ b/lib/driver/MSWindows/aspi32.c @@ -0,0 +1,808 @@ +/* + $Id: aspi32.c,v 1.11 2008/04/21 18:30:21 karl Exp $ + + Copyright (C) 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* This file contains Win32-specific code and implements low-level + control of the CD drive via the ASPI API. + Inspired by vlc's cdrom.h code +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: aspi32.c,v 1.11 2008/04/21 18:30:21 karl Exp $"; + +#include +#include +#include +#include +#include "cdio_assert.h" + +#include + +#ifdef HAVE_WIN32_CDROM + +#include +#include +#include +#include +#include +#include + +#include +#include "win32.h" + +#include +#include +#include "aspi32.h" +#include "cdtext_private.h" + +/* Amount of time we are willing to wait for an operation to complete. + 10 seconds? +*/ +#define OP_TIMEOUT_MS 10000 + +static const +char *aspierror(int nErrorCode) +{ + switch (nErrorCode) + { + case SS_PENDING: + return "SRB being processed"; + break; + case SS_COMP: + return "SRB completed without error"; + break; + case SS_ABORTED: + return "SRB aborted"; + break; + case SS_ABORT_FAIL: + return "Unable to abort SRB"; + break; + case SS_ERR: + return "SRB completed with error"; + break; + case SS_INVALID_CMD: + return "Invalid ASPI command"; + break; + case SS_INVALID_HA: + return "Invalid host adapter number"; + break; + case SS_NO_DEVICE: + return "SCSI device not installed"; + break; + case SS_INVALID_SRB: + return "Invalid parameter set in SRB"; + break; + case SS_OLD_MANAGER: + return "ASPI manager doesn't support"; + break; + case SS_ILLEGAL_MODE: + return "Unsupported MS Windows mode"; + break; + case SS_NO_ASPI: + return "No ASPI managers"; + break; + case SS_FAILED_INIT: + return "ASPI for windows failed init"; + break; + case SS_ASPI_IS_BUSY: + return "No resources available to execute command."; + break; + case SS_BUFFER_TOO_BIG: + return "Buffer size is too big to handle."; + break; + case SS_MISMATCHED_COMPONENTS: + return "The DLLs/EXEs of ASPI don't version check"; + break; + case SS_NO_ADAPTERS: + return "No host adapters found"; + break; + case SS_INSUFFICIENT_RESOURCES: + return "Couldn't allocate resources needed to init"; + break; + case SS_ASPI_IS_SHUTDOWN: + return "Call came to ASPI after PROCESS_DETACH"; + break; + case SS_BAD_INSTALL: + return "The DLL or other components are installed wrong."; + break; + default: + return "Unknown ASPI error."; + } +} + +/* General ioctl() CD-ROM command function */ +static bool +mciSendCommand_aspi(int id, UINT msg, DWORD flags, void *arg) +{ + MCIERROR mci_error; + + mci_error = mciSendCommand(id, msg, flags, (DWORD)arg); + if ( mci_error ) { + char error[256]; + + mciGetErrorString(mci_error, error, 256); + cdio_warn("mciSendCommand() error: %s", error); + } + return(mci_error == 0); +} + +/* + See if the ASPI DLL is loadable. If so pointers are returned + and we return true. Return false if there was a problem. + */ +static bool +have_aspi( HMODULE *hASPI, + long (**lpGetSupport)( void ), + long (**lpSendCommand)( void* ) ) +{ + /* check if aspi is available */ + *hASPI = LoadLibrary( "wnaspi32.dll" ); + + if( *hASPI == NULL ) { + cdio_debug("Unable to load ASPI DLL"); + return false; + } + + *lpGetSupport = (void *) GetProcAddress( *hASPI, + "GetASPI32SupportInfo" ); + *lpSendCommand = (void *) GetProcAddress( *hASPI, + "SendASPI32Command" ); + + /* make sure that we've got both function addresses */ + if( *lpGetSupport == NULL || *lpSendCommand == NULL ) { + cdio_debug("Unable to get ASPI function pointers"); + FreeLibrary( *hASPI ); + return false; + } + + return true; +} + +/*! + Get disc type associated with cd object. +*/ +discmode_t +get_discmode_aspi (_img_private_t *p_env) +{ + track_t i_track; + discmode_t discmode=CDIO_DISC_MODE_NO_INFO; + + /* See if this is a DVD. */ + cdio_dvd_struct_t dvd; /* DVD READ STRUCT for layer 0. */ + + dvd.physical.type = CDIO_DVD_STRUCT_PHYSICAL; + dvd.physical.layer_num = 0; + if (0 == mmc_get_dvd_struct_physical_private (p_env, &run_mmc_cmd_aspi, + &dvd)) { + switch(dvd.physical.layer[0].book_type) { + case CDIO_DVD_BOOK_DVD_ROM: return CDIO_DISC_MODE_DVD_ROM; + case CDIO_DVD_BOOK_DVD_RAM: return CDIO_DISC_MODE_DVD_RAM; + case CDIO_DVD_BOOK_DVD_R: return CDIO_DISC_MODE_DVD_R; + case CDIO_DVD_BOOK_DVD_RW: return CDIO_DISC_MODE_DVD_RW; + case CDIO_DVD_BOOK_DVD_PR: return CDIO_DISC_MODE_DVD_PR; + case CDIO_DVD_BOOK_DVD_PRW: return CDIO_DISC_MODE_DVD_PRW; + default: return CDIO_DISC_MODE_DVD_OTHER; + } + } + + if (!p_env->gen.toc_init) + read_toc_aspi (p_env); + + if (!p_env->gen.toc_init) + return CDIO_DISC_MODE_NO_INFO; + + for (i_track = p_env->gen.i_first_track; + i_track < p_env->gen.i_first_track + p_env->gen.i_tracks ; + i_track ++) { + track_format_t track_fmt=get_track_format_aspi(p_env, i_track); + + switch(track_fmt) { + case TRACK_FORMAT_AUDIO: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DA; + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_XA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_DATA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DATA; + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_ERROR: + default: + discmode = CDIO_DISC_MODE_ERROR; + } + } + return discmode; +} + +const char * +is_cdrom_aspi(const char drive_letter) +{ + static char psz_win32_drive[7]; + HMODULE hASPI = NULL; + long (*lpGetSupport)( void ) = NULL; + long (*lpSendCommand)( void* ) = NULL; + DWORD dwSupportInfo; + int i_adapter, i_hostadapters; + char c_drive; + int i_rc; + + if ( !have_aspi(&hASPI, &lpGetSupport, &lpSendCommand) ) + return NULL; + + /* ASPI support seems to be there. */ + + dwSupportInfo = lpGetSupport(); + + i_rc = HIBYTE( LOWORD ( dwSupportInfo ) ); + + if( SS_COMP != i_rc ) { + cdio_debug("ASPI: %s", aspierror(i_rc)); + FreeLibrary( hASPI ); + return NULL; + } + + i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) ); + if( i_hostadapters == 0 ) { + FreeLibrary( hASPI ); + return NULL; + } + + c_drive = toupper(drive_letter) - 'A'; + + for( i_adapter = 0; i_adapter < i_hostadapters; i_adapter++ ) { + struct SRB_GetDiskInfo srbDiskInfo; + int i_target; + SRB_HAInquiry srbInquiry; + + srbInquiry.SRB_Cmd = SC_HA_INQUIRY; + srbInquiry.SRB_HaId = i_adapter; + + lpSendCommand( (void*) &srbInquiry ); + + if( srbInquiry.SRB_Status != SS_COMP ) continue; + if( !srbInquiry.HA_Unique[3]) srbInquiry.HA_Unique[3]=8; + + for(i_target=0; i_target < srbInquiry.HA_Unique[3]; i_target++) + { + int i_lun; + for( i_lun=0; i_lun<8; i_lun++) + { + srbDiskInfo.SRB_Cmd = SC_GET_DISK_INFO; + srbDiskInfo.SRB_Flags = 0; + srbDiskInfo.SRB_Hdr_Rsvd = 0; + srbDiskInfo.SRB_HaId = i_adapter; + srbDiskInfo.SRB_Target = i_target; + srbDiskInfo.SRB_Lun = i_lun; + + lpSendCommand( (void*) &srbDiskInfo ); + + if( (srbDiskInfo.SRB_Status == SS_COMP) && + (srbDiskInfo.SRB_Int13HDriveInfo == c_drive) ) { + /* Make sure this is a CD-ROM device. */ + struct SRB_GDEVBlock srbGDEVBlock; + + memset( &srbGDEVBlock, 0, sizeof(struct SRB_GDEVBlock) ); + srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE; + srbDiskInfo.SRB_HaId = i_adapter; + srbGDEVBlock.SRB_Target = i_target; + srbGDEVBlock.SRB_Lun = i_lun; + + lpSendCommand( (void*) &srbGDEVBlock ); + + if( ( srbGDEVBlock.SRB_Status == SS_COMP ) && + ( srbGDEVBlock.SRB_DeviceType == DTYPE_CDROM ) ) { + sprintf( psz_win32_drive, "%c:", drive_letter ); + FreeLibrary( hASPI ); + return(psz_win32_drive); + } + } + } + } + } + FreeLibrary( hASPI ); + return NULL; +} + +/*! + Initialize CD device. + */ +bool +init_aspi (_img_private_t *env) +{ + HMODULE hASPI = NULL; + long (*lpGetSupport)( void ) = NULL; + long (*lpSendCommand)( void* ) = NULL; + DWORD dwSupportInfo; + int i_adapter, i_hostadapters; + char c_drive; + int i_rc; + + if (2 == strlen(env->gen.source_name) && isalpha(env->gen.source_name[0]) ) + { + c_drive = env->gen.source_name[0]; + } else if ( 6 == strlen(env->gen.source_name) + && isalpha(env->gen.source_name[4] )) { + c_drive = env->gen.source_name[4]; + } else { + c_drive = 'C'; + } + + if ( !have_aspi(&hASPI, &lpGetSupport, &lpSendCommand) ) + return false; + + /* ASPI support seems to be there. */ + + dwSupportInfo = lpGetSupport(); + + i_rc = HIBYTE( LOWORD ( dwSupportInfo ) ); + + if( SS_COMP != i_rc ) { + cdio_info("ASPI: %s", aspierror(i_rc)); + FreeLibrary( hASPI ); + return false; + } + + i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) ); + if( i_hostadapters == 0 ) { + FreeLibrary( hASPI ); + return false; + } + + c_drive = toupper(c_drive) - 'A'; + + for( i_adapter = 0; i_adapter < i_hostadapters; i_adapter++ ) { + struct SRB_GetDiskInfo srbDiskInfo; + int i_target; + SRB_HAInquiry srbInquiry; + + srbInquiry.SRB_Cmd = SC_HA_INQUIRY; + srbInquiry.SRB_HaId = i_adapter; + + lpSendCommand( (void*) &srbInquiry ); + + if( srbInquiry.SRB_Status != SS_COMP ) continue; + if( !srbInquiry.HA_Unique[3]) srbInquiry.HA_Unique[3]=8; + + for(i_target=0; i_target < srbInquiry.HA_Unique[3]; i_target++) + { + int i_lun; + for (i_lun = 0; i_lun < 8; i_lun++ ) { + srbDiskInfo.SRB_Cmd = SC_GET_DISK_INFO; + srbDiskInfo.SRB_Flags = 0; + srbDiskInfo.SRB_Hdr_Rsvd = 0; + srbDiskInfo.SRB_HaId = i_adapter; + srbDiskInfo.SRB_Target = i_target; + srbDiskInfo.SRB_Lun = i_lun; + + lpSendCommand( (void*) &srbDiskInfo ); + + if( (srbDiskInfo.SRB_Status == SS_COMP) ) { + + if (srbDiskInfo.SRB_Int13HDriveInfo != c_drive) + { + continue; + } else { + /* Make sure this is a CD-ROM device. */ + struct SRB_GDEVBlock srbGDEVBlock; + + memset( &srbGDEVBlock, 0, sizeof(struct SRB_GDEVBlock) ); + srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE; + srbGDEVBlock.SRB_HaId = i_adapter; + srbGDEVBlock.SRB_Target = i_target; + + lpSendCommand( (void*) &srbGDEVBlock ); + + if( ( srbGDEVBlock.SRB_Status == SS_COMP ) && + ( srbGDEVBlock.SRB_DeviceType == DTYPE_CDROM ) ) { + env->i_sid = MAKEWORD( i_adapter, i_target ); + env->hASPI = (long)hASPI; + env->lpSendCommand = lpSendCommand; + env->b_aspi_init = true; + env->i_lun = i_lun; + cdio_debug("Using ASPI layer"); + + return true; + } else { + FreeLibrary( hASPI ); + cdio_debug( "%c: is not a CD-ROM drive", + env->gen.source_name[0] ); + return false; + } + } + } + } + } + } + + FreeLibrary( hASPI ); + cdio_debug( "Unable to get HaId and target (ASPI)" ); + return false; +} + +/*! + Run a SCSI MMC command. + + env private CD structure + i_timeout_ms time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + p_buf Buffer for data, both sending and receiving + i_buf Size of buffer + e_direction direction the transfer is to go. + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + We return 0 if command completed successfully. + */ +int +run_mmc_cmd_aspi( void *p_user_data, unsigned int i_timeout_ms, + unsigned int i_cdb, const mmc_cdb_t * p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + HANDLE hEvent; + struct SRB_ExecSCSICmd ssc; + + /* Create the transfer completion event */ + hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + if( hEvent == NULL ) { + cdio_info("CreateEvent failed"); + return 1; + } + + memset( &ssc, 0, sizeof( ssc ) ); + + ssc.SRB_Cmd = SC_EXEC_SCSI_CMD; + + ssc.SRB_Flags = SCSI_MMC_DATA_READ == e_direction ? + SRB_DIR_IN | SRB_EVENT_NOTIFY : SRB_DIR_OUT | SRB_EVENT_NOTIFY; + + ssc.SRB_HaId = LOBYTE( p_env->i_sid ); + ssc.SRB_Target = HIBYTE( p_env->i_sid ); + ssc.SRB_Lun = p_env->i_lun; + ssc.SRB_SenseLen = SENSE_LEN; + + ssc.SRB_PostProc = (LPVOID) hEvent; + ssc.SRB_CDBLen = i_cdb; + + /* Result buffer */ + ssc.SRB_BufPointer = p_buf; + ssc.SRB_BufLen = i_buf; + + memcpy( ssc.CDBByte, p_cdb, i_cdb ); + + ResetEvent( hEvent ); + p_env->lpSendCommand( (void*) &ssc ); + + /* If the command has still not been processed, wait until it's + * finished */ + if( ssc.SRB_Status == SS_PENDING ) { + WaitForSingleObject( hEvent, msecs2secs(i_timeout_ms) ); + } + CloseHandle( hEvent ); + + /* check that the transfer went as planned */ + if( ssc.SRB_Status != SS_COMP ) { + cdio_info("ASPI: %s", aspierror(ssc.SRB_Status)); + return 2; + } + + return 0; +} + + +/*! + Reads nblocks sectors from cd device into data starting from lsn. + Returns 0 if no error. + */ +static int +read_sectors_aspi (_img_private_t *p_env, void *data, lsn_t lsn, + int sector_type, unsigned int nblocks) +{ + mmc_cdb_t cdb = {{0, }}; + unsigned int i_buf; + + int sync = 0; + int header_code = 2; + int i_user_data = 1; + int edc_ecc = 0; + int error_field = 0; + +#if 0 + sector_type = 0; /*all types */ +#endif + + /* Set up passthrough command */ + CDIO_MMC_SET_COMMAND (cdb.field, CDIO_MMC_GPCMD_READ_CD); + CDIO_MMC_SET_READ_TYPE (cdb.field, sector_type); + CDIO_MMC_SET_READ_LBA (cdb.field, lsn); + CDIO_MMC_SET_READ_LENGTH24(cdb.field, nblocks); + +#if 1 + cdb.field[ 9 ] = (sync << 7) | + (header_code << 5) | + (i_user_data << 4) | + (edc_ecc << 3) | + (error_field << 1); + /* ssc.CDBByte[ 9 ] = READ_CD_USERDATA_MODE2; */ +#else + CDIO_MMC_SET_MAIN_CHANNEL_SELECTION_BITS(cmd, + CDIO_MMC_MCSB_ALL_HEADERS); +#endif + + switch (sector_type) { + case CDIO_MMC_READ_TYPE_ANY: + case CDIO_MMC_READ_TYPE_CDDA: + i_buf = CDIO_CD_FRAMESIZE_RAW; + break; + case CDIO_MMC_READ_TYPE_M2F1: + i_buf = CDIO_CD_FRAMESIZE; + break; + case CDIO_MMC_READ_TYPE_M2F2: + i_buf = 2324; + break; + case CDIO_MMC_READ_TYPE_MODE1: + i_buf = CDIO_CD_FRAMESIZE; + break; + default: + i_buf = CDIO_CD_FRAMESIZE_RAW; + } + + return run_mmc_cmd_aspi(p_env, OP_TIMEOUT_MS, + mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, i_buf*nblocks, data); +} + +/*! + Reads an audio device into data starting from lsn. + Returns 0 if no error. + */ +int +read_audio_sectors_aspi (_img_private_t *p_env, void *data, lsn_t lsn, + unsigned int i_blocks) +{ + if (read_sectors_aspi(p_env, data, lsn, CDIO_MMC_READ_TYPE_CDDA, i_blocks)) { + return read_sectors_aspi(p_env, data, lsn, + CDIO_MMC_READ_TYPE_ANY, i_blocks); + } + return 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +int +read_mode2_sector_aspi (_img_private_t *p_env, void *data, lsn_t lsn, + bool b_form2) +{ + return read_sectors_aspi(p_env, data, lsn, b_form2 + ? CDIO_MMC_READ_TYPE_M2F2 + : CDIO_MMC_READ_TYPE_M2F1, + 1); +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +int +read_mode1_sector_aspi (_img_private_t *p_env, void *data, lsn_t lsn, + bool b_form2) +{ + return read_sectors_aspi(p_env, data, lsn, CDIO_MMC_READ_TYPE_MODE1, 1); +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return true if successful or false if an error. +*/ +bool +read_toc_aspi (_img_private_t *p_env) +{ + mmc_cdb_t cdb = {{0, }}; + unsigned char tocheader[ 4 ]; + int i_status; + + /* Operation code */ + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC); + + /* Format */ + cdb.field[ 2 ] = CDIO_MMC_READTOC_FMT_TOC; + + /* Starting track */ + CDIO_MMC_SET_START_TRACK(cdb.field, 0); + + CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(tocheader)); + + i_status = run_mmc_cmd_aspi (p_env, OP_TIMEOUT_MS, + mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + sizeof(tocheader), &tocheader); + + if (0 != i_status) return false; + + p_env->gen.i_first_track = tocheader[2]; + p_env->gen.i_tracks = tocheader[3] - tocheader[2] + 1; + + { + int i, j, i_toclength; + unsigned char *p_fulltoc; + + i_toclength = 4 /* header */ + tocheader[0] + + ((unsigned int) tocheader[1] << 8); + + p_fulltoc = malloc( i_toclength ); + + if( p_fulltoc == NULL ) { + cdio_error( "out of memory" ); + return false; + } + + CDIO_MMC_SET_READ_LENGTH16(cdb.field, i_toclength); + + i_status = run_mmc_cmd_aspi (p_env, OP_TIMEOUT_MS, + mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + i_toclength, p_fulltoc); + if( 0 != i_status ) { + p_env->gen.i_tracks = 0; + } + + j = p_env->gen.i_first_track; + + for( i = 0 ; i <= p_env->gen.i_tracks ; i++, j++ ) { + int i_index = 8 + 8 * i; + p_env->tocent[ i ].start_lsn = ((int)p_fulltoc[ i_index ] << 24) + + ((int)p_fulltoc[ i_index+1 ] << 16) + + ((int)p_fulltoc[ i_index+2 ] << 8) + + (int)p_fulltoc[ i_index+3 ]; + p_env->tocent[i].Control = (UCHAR)p_fulltoc[ 1 + 8 * i ]; + + set_track_flags(&(p_env->gen.track_flags[j]), p_env->tocent[i].Control); + + cdio_debug( "p_sectors: %i %lu", + i, (unsigned long int) p_env->tocent[i].start_lsn ); + } + + free( p_fulltoc ); + } + + p_env->gen.toc_init = true; + return true; +} + +/* Eject media will eventually get removed from _cdio_win32.c */ +#if 0 +/*! + Eject media. Return 1 if successful, 0 otherwise. + */ +int +wnaspi32_eject_media (void *user_data) { + + _img_private_t *env = user_data; + + + MCI_OPEN_PARMS op; + MCI_STATUS_PARMS st; + DWORD i_flags; + char psz_drive[4]; + int ret; + + memset( &op, 0, sizeof(MCI_OPEN_PARMS) ); + op.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO; + strcpy( psz_drive, "X:" ); + psz_drive[0] = env->gen.source_name[0]; + op.lpstrElementName = psz_drive; + + /* Set the flags for the device type */ + i_flags = MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID | + MCI_OPEN_ELEMENT | MCI_OPEN_SHAREABLE; + + if( mciSendCommand_aspi( 0, MCI_OPEN, i_flags, &op ) ) { + st.dwItem = MCI_STATUS_READY; + /* Eject disc */ + ret = mciSendCommand_aspi( op.wDeviceID, MCI_SET, + MCI_SET_DOOR_OPEN, 0 ) != 0; + /* Release access to the device */ + mciSendCommand_aspi( op.wDeviceID, MCI_CLOSE, MCI_WAIT, 0 ); + } else + ret = 0; + + return ret; +} +#endif + +/*! + Get format of track. +*/ +track_format_t +get_track_format_aspi(const _img_private_t *p_env, track_t track_num) +{ + MCI_OPEN_PARMS op; + MCI_STATUS_PARMS st; + DWORD i_flags; + int ret; + + memset( &op, 0, sizeof(MCI_OPEN_PARMS) ); + op.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO; + op.lpstrElementName = p_env->gen.source_name; + + /* Set the flags for the device type */ + i_flags = MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID | + MCI_OPEN_ELEMENT | MCI_OPEN_SHAREABLE; + + if( mciSendCommand_aspi( 0, MCI_OPEN, i_flags, &op ) ) { + st.dwItem = MCI_CDA_STATUS_TYPE_TRACK; + st.dwTrack = track_num; + i_flags = MCI_TRACK | MCI_STATUS_ITEM ; + ret = mciSendCommand_aspi( op.wDeviceID, MCI_STATUS, i_flags, &st ); + + /* Release access to the device */ + mciSendCommand_aspi( op.wDeviceID, MCI_CLOSE, MCI_WAIT, 0 ); + + switch(st.dwReturn) { + case MCI_CDA_TRACK_AUDIO: + return TRACK_FORMAT_AUDIO; + case MCI_CDA_TRACK_OTHER: + return TRACK_FORMAT_DATA; + default: + return TRACK_FORMAT_XA; + } + } + return TRACK_FORMAT_ERROR; +} + +#endif /* HAVE_WIN32_CDROM */ diff --git a/lib/driver/MSWindows/aspi32.h b/lib/driver/MSWindows/aspi32.h new file mode 100644 index 00000000..2be044cb --- /dev/null +++ b/lib/driver/MSWindows/aspi32.h @@ -0,0 +1,248 @@ +/* Win32 aspi specific */ +/* + $Id: aspi32.h,v 1.6 2008/04/21 18:30:21 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define ASPI_HAID 0 +#define ASPI_TARGET 0 +#define DTYPE_CDROM 0x05 + +#define SENSE_LEN 0x0E +#define SC_HA_INQUIRY 0x00 +#define SC_GET_DEV_TYPE 0x01 +#define SC_EXEC_SCSI_CMD 0x02 +#define SC_GET_DISK_INFO 0x06 + +//***************************************************************************** +// %%% SRB Status %%% +//***************************************************************************** + +#define SS_PENDING 0x00 // SRB being processed +#define SS_COMP 0x01 // SRB completed without error +#define SS_ABORTED 0x02 // SRB aborted +#define SS_ABORT_FAIL 0x03 // Unable to abort SRB +#define SS_ERR 0x04 // SRB completed with error + +#define SS_INVALID_CMD 0x80 // Invalid ASPI command +#define SS_INVALID_HA 0x81 // Invalid host adapter number +#define SS_NO_DEVICE 0x82 // SCSI device not installed + +#define SS_INVALID_SRB 0xE0 // Invalid parameter set in SRB +#define SS_OLD_MANAGER 0xE1 // ASPI manager doesn't support Windows +#define SS_BUFFER_ALIGN 0xE1 // Buffer not aligned (replaces + // OLD_MANAGER in Win32) +#define SS_ILLEGAL_MODE 0xE2 // Unsupported Windows mode +#define SS_NO_ASPI 0xE3 // No ASPI managers resident +#define SS_FAILED_INIT 0xE4 // ASPI for windows failed init +#define SS_ASPI_IS_BUSY 0xE5 // No resources available to execute + // cmd +#define SS_BUFFER_TOO_BIG 0xE6 // Buffer size to big to handle! +#define SS_MISMATCHED_COMPONENTS 0xE7 // The DLLs/EXEs of ASPI don't version + // check +#define SS_NO_ADAPTERS 0xE8 // No host adapters to manage +#define SS_INSUFFICIENT_RESOURCES 0xE9 // Couldn't allocate resources needed + // to init +#define SS_ASPI_IS_SHUTDOWN 0xEA // Call came to ASPI after + // PROCESS_DETACH +#define SS_BAD_INSTALL 0xEB // The DLL or other components are installed wrong + +//***************************************************************************** +// %%% Host Adapter Status %%% +//***************************************************************************** + +#define HASTAT_OK 0x00 // Host adapter did not detect an + // error +#define HASTAT_SEL_TO 0x11 // Selection Timeout +#define HASTAT_DO_DU 0x12 // Data overrun data underrun +#define HASTAT_BUS_FREE 0x13 // Unexpected bus free +#define HASTAT_PHASE_ERR 0x14 // Target bus phase sequence + // failure +#define HASTAT_TIMEOUT 0x09 // Timed out while SRB was + // waiting to beprocessed. +#define HASTAT_COMMAND_TIMEOUT 0x0B // Adapter timed out processing SRB. +#define HASTAT_MESSAGE_REJECT 0x0D // While processing SRB, the + // adapter received a MESSAGE +#define HASTAT_BUS_RESET 0x0E // A bus reset was detected. +#define HASTAT_PARITY_ERROR 0x0F // A parity error was detected. +#define HASTAT_REQUEST_SENSE_FAILED 0x10 // The adapter failed in issuing +#define SS_NO_ADAPTERS 0xE8 +#define SRB_DIR_IN 0x08 +#define SRB_DIR_OUT 0x10 +#define SRB_EVENT_NOTIFY 0x40 + +#define SECTOR_TYPE_MODE2 0x14 +#define READ_CD_USERDATA_MODE2 0x10 + +#define READ_TOC 0x43 +#define READ_TOC_FORMAT_TOC 0x0 + +#pragma pack(1) + +struct SRB_GetDiskInfo +{ + unsigned char SRB_Cmd; + unsigned char SRB_Status; + unsigned char SRB_HaId; + unsigned char SRB_Flags; + unsigned long SRB_Hdr_Rsvd; + unsigned char SRB_Target; + unsigned char SRB_Lun; + unsigned char SRB_DriveFlags; + unsigned char SRB_Int13HDriveInfo; + unsigned char SRB_Heads; + unsigned char SRB_Sectors; + unsigned char SRB_Rsvd1[22]; +}; + +struct SRB_GDEVBlock +{ + unsigned char SRB_Cmd; + unsigned char SRB_Status; + unsigned char SRB_HaId; + unsigned char SRB_Flags; + unsigned long SRB_Hdr_Rsvd; + unsigned char SRB_Target; + unsigned char SRB_Lun; + unsigned char SRB_DeviceType; + unsigned char SRB_Rsvd1; +}; + +struct SRB_ExecSCSICmd +{ + unsigned char SRB_Cmd; + unsigned char SRB_Status; + unsigned char SRB_HaId; + unsigned char SRB_Flags; + unsigned long SRB_Hdr_Rsvd; + unsigned char SRB_Target; + unsigned char SRB_Lun; + unsigned short SRB_Rsvd1; + unsigned long SRB_BufLen; + unsigned char *SRB_BufPointer; + unsigned char SRB_SenseLen; + unsigned char SRB_CDBLen; + unsigned char SRB_HaStat; + unsigned char SRB_TargStat; + unsigned long *SRB_PostProc; + unsigned char SRB_Rsvd2[20]; + unsigned char CDBByte[16]; + unsigned char SenseArea[SENSE_LEN+2]; +}; + +/***************************************************************************** + %%% SRB - HOST ADAPTER INQUIRY - SC_HA_INQUIRY (0) %%% +*****************************************************************************/ + +typedef struct // Offset +{ // HX/DEC + BYTE SRB_Cmd; // 00/000 ASPI command code = SC_HA_INQUIRY + BYTE SRB_Status; // 01/001 ASPI command status byte + BYTE SRB_HaId; // 02/002 ASPI host adapter number + BYTE SRB_Flags; // 03/003 ASPI request flags + DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0 + BYTE HA_Count; // 08/008 Number of host adapters present + BYTE HA_SCSI_ID; // 09/009 SCSI ID of host adapter + BYTE HA_ManagerId[16]; // 0A/010 String describing the manager + BYTE HA_Identifier[16]; // 1A/026 String describing the host adapter + BYTE HA_Unique[16]; // 2A/042 Host Adapter Unique parameters + WORD HA_Rsvd1; // 3A/058 Reserved, MUST = 0 +} +SRB_HAInquiry; + +/*! + Get disc type associated with cd object. +*/ +discmode_t get_discmode_aspi (_img_private_t *p_env); + +/*! + Return the the kind of drive capabilities of device. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +char * get_mcn_aspi (const _img_private_t *env); + +/*! + Get the format (XA, DATA, AUDIO) of a track. +*/ +track_format_t get_track_format_aspi(const _img_private_t *env, + track_t i_track); + +/*! + Initialize internal structures for CD device. + */ +bool init_aspi (_img_private_t *env); + +/* + Read cdtext information for a CdIo object . + + return true on success, false on error or CD-TEXT information does + not exist. +*/ +bool init_cdtext_aspi (_img_private_t *env); + +const char *is_cdrom_aspi(const char drive_letter); + +/*! + Reads an audio device using the DeviceIoControl method into data + starting from lsn. Returns 0 if no error. + */ +int read_audio_sectors_aspi (_img_private_t *obj, void *data, lsn_t lsn, + unsigned int nblocks); +/*! + Reads a single mode1 sector using the DeviceIoControl method into + data starting from lsn. Returns 0 if no error. + */ +int read_mode1_sector_aspi (_img_private_t *env, void *data, + lsn_t lsn, bool b_form2); +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +int read_mode2_sector_aspi (_img_private_t *env, void *data, lsn_t lsn, + bool b_form2); + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return true if successful or false if an error. +*/ +bool read_toc_aspi (_img_private_t *env); + +/*! + Run a SCSI MMC command. + + env private CD structure + i_timeout time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + p_buf Buffer for data, both sending and receiving + i_buf Size of buffer + e_direction direction the transfer is to go. + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + Return 0 if command completed successfully. + */ +int run_mmc_cmd_aspi( void *p_user_data, + unsigned int i_timeout, + unsigned int i_cdb, + const mmc_cdb_t * p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ); + diff --git a/lib/driver/MSWindows/win32.c b/lib/driver/MSWindows/win32.c new file mode 100644 index 00000000..3f982dce --- /dev/null +++ b/lib/driver/MSWindows/win32.c @@ -0,0 +1,1003 @@ +/* + $Id: win32.c,v 1.37 2008/04/21 18:30:21 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* This file contains Win32-specific code and implements low-level + control of the CD drive. Inspired by vlc's cdrom.h code +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: win32.c,v 1.37 2008/04/21 18:30:21 karl Exp $"; + +#include +#include +#include +#include +#include "cdio_assert.h" +#include "cdio_private.h" /* protoype for cdio_is_device_win32 */ + +#include + +#ifdef HAVE_WIN32_CDROM + +#include +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#include +#include +#include "win32.h" + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#if defined (MSVC) || defined (_XBOX) +#undef IN +#else +#include "aspi32.h" +#endif + +#ifdef _XBOX +#include "stdint.h" +#include +#define WIN_NT 1 +#else +#define WIN_NT ( GetVersion() < 0x80000000 ) +#endif + +/*! + Set the volume of an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_get_volume_win32 ( void *p_user_data, + /*out*/ cdio_audio_volume_t *p_volume) +{ + if ( WIN_NT ) { + return audio_get_volume_win32ioctl (p_user_data, p_volume); + } else { + return DRIVER_OP_UNSUPPORTED; /* not yet, but soon I hope */ + } +} + +/*! + Pause playing CD through analog output + + @param p_cdio the CD object to be acted upon. +*/ +static driver_return_code_t +audio_pause_win32 (void *p_user_data) +{ + if ( WIN_NT ) { + return audio_pause_win32ioctl (p_user_data); + } else { + return DRIVER_OP_UNSUPPORTED; /* not yet, but soon I hope */ + } +} + +/*! + Playing CD through analog output at the given MSF. + + @param p_cdio the CD object to be acted upon. +*/ +static driver_return_code_t +audio_play_msf_win32 (void *p_user_data, msf_t *p_start_msf, msf_t *p_end_msf) +{ + if ( WIN_NT ) { + return audio_play_msf_win32ioctl (p_user_data, p_start_msf, p_end_msf); + } else { + return DRIVER_OP_UNSUPPORTED; /* not yet, but soon I hope */ + } +} + +/*! + Read Audio Subchannel information + + @param p_cdio the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_read_subchannel_win32 (void *p_user_data, + cdio_subchannel_t *p_subchannel) +{ + if ( WIN_NT ) { + return audio_read_subchannel_win32ioctl (p_user_data, p_subchannel); + } else { + return audio_read_subchannel_mmc(p_user_data, p_subchannel); + } +} + + /*! + Resume playing an audio CD. + + @param p_cdio the CD object to be acted upon. + + */ +static driver_return_code_t +audio_resume_win32 (void *p_user_data) +{ + if ( WIN_NT ) { + return audio_resume_win32ioctl (p_user_data); + } else { + return DRIVER_OP_UNSUPPORTED; /* not yet, but soon I hope */ + } +} + +/*! + Set the volume of an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_set_volume_win32 ( void *p_user_data, cdio_audio_volume_t *p_volume) +{ + if ( WIN_NT ) { + return audio_set_volume_win32ioctl (p_user_data, p_volume); + } else { + return DRIVER_OP_UNSUPPORTED; /* not yet, but soon I hope */ + } +} + +static driver_return_code_t +audio_stop_win32 ( void *p_user_data) +{ + if ( WIN_NT ) { + return audio_stop_win32ioctl (p_user_data); + } else { + return DRIVER_OP_UNSUPPORTED; /* not yet, but soon I hope */ + } +} + +/* General ioctl() CD-ROM command function */ +static bool +_cdio_mciSendCommand(int id, UINT msg, DWORD flags, void *arg) +{ +#ifdef _XBOX + return false; +#else + MCIERROR mci_error; + + mci_error = mciSendCommand(id, msg, flags, (DWORD)arg); + if ( mci_error ) { + char error[256]; + + mciGetErrorString(mci_error, error, 256); + cdio_warn("mciSendCommand() error: %s", error); + } + return(mci_error == 0); +#endif +} + +static access_mode_t +str_to_access_mode_win32(const char *psz_access_mode) +{ + const access_mode_t default_access_mode = + WIN_NT ? _AM_IOCTL : _AM_ASPI; + + if (NULL==psz_access_mode) return default_access_mode; + + if (!strcmp(psz_access_mode, "ioctl")) + return _AM_IOCTL; + else if (!strcmp(psz_access_mode, "ASPI")) { +#ifdef _XBOX + return _AM_ASPI; +#else + cdio_warn ("XBOX doesn't support access type: %s. Default used instead.", + psz_access_mode); + return default_access_mode; +#endif + } else { + cdio_warn ("unknown access type: %s. Default used instead.", + psz_access_mode); + return default_access_mode; + } +} + +static discmode_t +get_discmode_win32(void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + if (p_env->hASPI) { + return get_discmode_aspi (p_env); + } else { + return get_discmode_win32ioctl (p_env); + } +} + +static const char * +is_cdrom_win32(const char drive_letter) { + if ( WIN_NT ) { + return is_cdrom_win32ioctl (drive_letter); + } else { + return is_cdrom_aspi(drive_letter); + } +} + +/*! + Run a SCSI MMC command. + + env private CD structure + i_timeout_ms time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + p_buf Buffer for data, both sending and receiving + i_buf Size of buffer + e_direction direction the transfer is to go. + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + Return 0 if command completed successfully. + */ +static int +run_mmc_cmd_win32( void *p_user_data, unsigned int i_timeout_ms, + unsigned int i_cdb, const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + _img_private_t *p_env = p_user_data; + + if (p_env->hASPI) { + return run_mmc_cmd_aspi( p_env, i_timeout_ms, i_cdb, p_cdb, + e_direction, i_buf, p_buf ); + } else { + return run_mmc_cmd_win32ioctl( p_env, i_timeout_ms, i_cdb, p_cdb, + e_direction, i_buf, p_buf ); + } +} + +/*! + Initialize CD device. + */ +static bool +init_win32 (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + bool b_ret; + if (p_env->gen.init) { + cdio_error ("init called more than once"); + return false; + } + + p_env->gen.init = true; + p_env->gen.toc_init = false; + p_env->gen.b_cdtext_init = false; + p_env->gen.b_cdtext_error = false; + p_env->gen.fd = open (p_env->gen.source_name, O_RDONLY, 0); + + /* Initializations */ + p_env->h_device_handle = NULL; + p_env->i_sid = 0; + p_env->hASPI = 0; + p_env->lpSendCommand = 0; + p_env->b_aspi_init = false; + p_env->b_ioctl_init = false; + + + if ( _AM_IOCTL == p_env->access_mode ) { + b_ret = init_win32ioctl(p_env); + } else { + b_ret = init_aspi(p_env); + } + + /* It looks like get_media_changed_mmc will always + return 1 (media changed) on the first call. So + we call it here to clear that flag. We may have + to rethink this if there's a problem doing this + extra work down the line. */ + get_media_changed_mmc(p_user_data); + return b_ret; +} + +/*! + Release and free resources associated with cd. + */ +static void +free_win32 (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + if (NULL == p_env) return; + if (p_env->gen.fd >= 0) close(p_env->gen.fd); + + free (p_env->gen.source_name); + + if( p_env->h_device_handle ) + CloseHandle( p_env->h_device_handle ); + if( p_env->hASPI ) + FreeLibrary( (HMODULE)p_env->hASPI ); + + free (p_env); +} + +/*! + Reads an audio device into data starting from lsn. + Returns 0 if no error. + */ +static int +read_audio_sectors (void *p_user_data, void *p_buf, lsn_t i_lsn, + unsigned int i_blocks) +{ + _img_private_t *p_env = p_user_data; + if ( p_env->hASPI ) { + return read_audio_sectors_aspi( p_env, p_buf, i_lsn, i_blocks ); + } else { +#if 0 + return read_audio_sectors_win32ioctl( p_env, p_buf, i_lsn, i_blocks ); +#else + return mmc_read_sectors( p_env->gen.cdio, p_buf, i_lsn, + CDIO_MMC_READ_TYPE_CDDA, i_blocks); +#endif + } +} + +/*! + Reads an audio device into data starting from lsn. + Returns 0 if no error. + */ +static driver_return_code_t +read_data_sectors_win32 (void *p_user_data, void *p_buf, lsn_t i_lsn, + uint16_t i_blocksize, uint32_t i_blocks) +{ + driver_return_code_t rc = + read_data_sectors_mmc(p_user_data, p_buf, i_lsn, i_blocksize, i_blocks); + if ( DRIVER_OP_SUCCESS != rc ) { + /* Try using generic "cooked" mode. */ + return read_data_sectors_generic( p_user_data, p_buf, i_lsn, + i_blocksize, i_blocks ); + } + return DRIVER_OP_SUCCESS; +} + +/*! + Reads a single mode1 sector from cd device into data starting from + lsn. Returns 0 if no error. + */ +static int +read_mode1_sector_win32 (void *p_user_data, void *p_buf, lsn_t lsn, + bool b_form2) +{ + _img_private_t *p_env = p_user_data; + + if (p_env->gen.ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged < 75 + || (p_env->gen.ioctls_debugged < (30 * 75) + && p_env->gen.ioctls_debugged % 75 == 0) + || p_env->gen.ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %lu", (unsigned long int) lsn); + + p_env->gen.ioctls_debugged++; + + if ( p_env->hASPI ) { + return read_mode1_sector_aspi( p_env, p_buf, lsn, b_form2 ); + } else { + return read_mode1_sector_win32ioctl( p_env, p_buf, lsn, b_form2 ); + } +} + +/*! + Reads nblocks of mode1 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +read_mode1_sectors_win32 (void *p_user_data, void *p_buf, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *p_env = p_user_data; + int i; + int retval; + + for (i = 0; i < nblocks; i++) { + if (b_form2) { + retval = read_mode1_sector_win32 (p_env, ((char *)p_buf) + + (M2RAW_SECTOR_SIZE * i), + lsn + i, true); + if ( retval ) return retval; + } else { + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + if ( (retval = read_mode1_sector_win32 (p_env, buf, lsn + i, false)) ) + return retval; + + memcpy (((char *)p_buf) + (CDIO_CD_FRAMESIZE * i), + buf, CDIO_CD_FRAMESIZE); + } + } + return 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +read_mode2_sector_win32 (void *p_user_data, void *data, lsn_t lsn, + bool b_form2) +{ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + _img_private_t *p_env = p_user_data; + + if (p_env->gen.ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged < 75 + || (p_env->gen.ioctls_debugged < (30 * 75) + && p_env->gen.ioctls_debugged % 75 == 0) + || p_env->gen.ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %lu", (unsigned long int) lsn); + + p_env->gen.ioctls_debugged++; + + if ( p_env->hASPI ) { + int ret; + ret = read_mode2_sector_aspi(p_user_data, buf, lsn, 1); + if( ret != 0 ) return ret; + if (b_form2) + memcpy (data, buf, M2RAW_SECTOR_SIZE); + else + memcpy (((char *)data), buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE); + return 0; + } else { + return read_mode2_sector_win32ioctl( p_env, data, lsn, b_form2 ); + } +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +read_mode2_sectors_win32 (void *p_user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int i_blocks) +{ + int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < i_blocks; i++) { + if ( (retval = read_mode2_sector_win32 (p_user_data, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static lsn_t +get_disc_last_lsn_win32 (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + return p_env->tocent[p_env->gen.i_tracks].start_lsn; +} + +/*! + Set the key "arg" to "value" in source device. +*/ +static int +set_arg_win32 (void *p_user_data, const char key[], const char value[]) +{ + _img_private_t *p_env = p_user_data; + + if (!strcmp (key, "source")) + { + if (!value) + return -2; + + free (p_env->gen.source_name); + + p_env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + p_env->access_mode = str_to_access_mode_win32(value); + if (p_env->access_mode == _AM_ASPI && !p_env->b_aspi_init) + return init_aspi(p_env) ? 1 : -3; + else if (p_env->access_mode == _AM_IOCTL && !p_env->b_ioctl_init) + return init_win32ioctl(p_env) ? 1 : -3; + else + return -4; + return 0; + } + 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 +read_toc_win32 (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + bool ret; + if( p_env->hASPI ) { + ret = read_toc_aspi( p_env ); + } else { + ret = read_toc_win32ioctl( p_env ); + } + if (ret) p_env->gen.toc_init = true ; + return ret; +} + +/*! + Close media tray. + */ +static driver_return_code_t +open_close_media_win32 (const char *psz_win32_drive, DWORD command_flags) +{ +#ifdef _XBOX + return DRIVER_OP_UNSUPPORTED; +#else + MCI_OPEN_PARMS op; + MCI_STATUS_PARMS st; + DWORD i_flags; + int ret; + + memset( &op, 0, sizeof(MCI_OPEN_PARMS) ); + op.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO; + op.lpstrElementName = psz_win32_drive; + + /* Set the flags for the device type */ + i_flags = MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID | + MCI_OPEN_ELEMENT | MCI_OPEN_SHAREABLE; + + if( _cdio_mciSendCommand( 0, MCI_OPEN, i_flags, &op ) ) { + st.dwItem = MCI_STATUS_READY; + /* Eject disc */ + ret = _cdio_mciSendCommand( op.wDeviceID, MCI_SET, command_flags, 0 ) == 0; + /* Release access to the device */ + _cdio_mciSendCommand( op.wDeviceID, MCI_CLOSE, MCI_WAIT, 0 ); + } else + ret = DRIVER_OP_ERROR; + + return ret; +#endif +} + +/*! + Eject media. + */ +static driver_return_code_t +eject_media_win32 (void *p_user_data) +{ + const _img_private_t *p_env = p_user_data; + char psz_drive[4]; + unsigned int i_device = strlen(p_env->gen.source_name); + + strcpy( psz_drive, "X:" ); + if (6 == i_device) { + psz_drive[0] = p_env->gen.source_name[4]; + } else if (2 == i_device) { + psz_drive[0] = p_env->gen.source_name[0]; + } else { + cdio_info ("Can't pick out drive letter from device %s", + p_env->gen.source_name); + return DRIVER_OP_ERROR; + } + + return open_close_media_win32(psz_drive, MCI_SET_DOOR_OPEN); +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +get_arg_win32 (void *p_user_data, const char key[]) +{ + _img_private_t *p_env = p_user_data; + + if (!strcmp (key, "source")) { + return p_env->gen.source_name; + } else if (!strcmp (key, "access-mode")) { + if (p_env->hASPI) + return "ASPI"; + else + return "ioctl"; + } + return NULL; +} + +/*! + Return the media catalog number MCN. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +static char * +_cdio_get_mcn (const void *p_user_data) { + const _img_private_t *p_env = p_user_data; + + if( p_env->hASPI ) { + return mmc_get_mcn( p_env->gen.cdio ); + } else { + return get_mcn_win32ioctl(p_env); + } +} + +/*! + Get format of track. +*/ +static track_format_t +_cdio_get_track_format(void *p_obj, track_t i_track) +{ + _img_private_t *p_env = p_obj; + + if ( !p_env ) return TRACK_FORMAT_ERROR; + + if (!p_env->gen.toc_init) + if (!read_toc_win32 (p_env)) return TRACK_FORMAT_ERROR; + + if ( i_track < p_env->gen.i_first_track + || i_track >= p_env->gen.i_tracks + p_env->gen.i_first_track ) + return TRACK_FORMAT_ERROR; + + if( p_env->hASPI ) { + return get_track_format_aspi(p_env, i_track); + } else { + return get_track_format_win32ioctl(p_env, i_track); + } +} + +/*! + 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 *p_obj, track_t i_track) +{ + _img_private_t *p_env = p_obj; + + switch (_cdio_get_track_format(p_env, i_track)) { + case TRACK_FORMAT_XA: + return true; + case TRACK_FORMAT_ERROR: + case TRACK_FORMAT_CDI: + case TRACK_FORMAT_AUDIO: + return false; + case TRACK_FORMAT_DATA: + if (_AM_ASPI == p_env->access_mode ) + return ((p_env->tocent[i_track-p_env->gen.i_first_track].Control & 8) != 0); + default: + break; + } + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cd-info for a while. + */ + return ((p_env->tocent[i_track-p_env->gen.i_first_track].Control & 2) != 0); +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + i_tracks in obj. Track numbers start at 1. + The "leadout" track is specified either by + using i_tracks LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +static bool +_cdio_get_track_msf(void *p_user_data, track_t i_tracks, msf_t *p_msf) +{ + _img_private_t *p_env = p_user_data; + + if (!p_msf) return false; + + if (!p_env->gen.toc_init) + if (!read_toc_win32 (p_env)) return false; + + if (i_tracks == CDIO_CDROM_LEADOUT_TRACK) i_tracks = p_env->gen.i_tracks+1; + + if (i_tracks > p_env->gen.i_tracks+1 || i_tracks == 0) { + return false; + } else { + cdio_lsn_to_msf(p_env->tocent[i_tracks-1].start_lsn, p_msf); + return true; + } +} + +#endif /* HAVE_WIN32_CDROM */ + +/*! + Return an array of strings giving possible CD devices. + */ +char ** +cdio_get_devices_win32 (void) +{ +#ifndef HAVE_WIN32_CDROM + return NULL; +#else + char **drives = NULL; + unsigned int num_drives=0; + char drive_letter; + + /* Scan the system for CD-ROM drives. + */ + +#if FINISHED + /* Now check the currently mounted CD drives */ + if (NULL != (ret_drive = cdio_check_mounts("/etc/mtab"))) { + cdio_add_device_list(&drives, drive, &num_drives); + } + + /* Finally check possible mountable drives in /etc/fstab */ + if (NULL != (ret_drive = cdio_check_mounts("/etc/fstab"))) { + cdio_add_device_list(&drives, drive, &num_drives); + } +#endif + + /* Scan the system for CD-ROM drives. + Not always 100% reliable, so use the USE_MNTENT code above first. + */ + for (drive_letter='A'; drive_letter <= 'Z'; drive_letter++) { + const char *drive_str=is_cdrom_win32(drive_letter); + if (drive_str != NULL) { + cdio_add_device_list(&drives, drive_str, &num_drives); + } + } + cdio_add_device_list(&drives, NULL, &num_drives); + return drives; +#endif /*HAVE_WIN32_CDROM*/ +} + +/*! + Return a string containing the default CD device if none is specified. + if CdIo is NULL (we haven't initialized a specific device driver), + then find a suitable one and return the default device for that. + + NULL is returned if we couldn't get a default device. +*/ +char * +cdio_get_default_device_win32(void) +{ + +#ifdef HAVE_WIN32_CDROM + char drive_letter; + + for (drive_letter='A'; drive_letter <= 'Z'; drive_letter++) { + const char *drive_str=is_cdrom_win32(drive_letter); + if (drive_str != NULL) { + return strdup(drive_str); + } + } +#endif + return NULL; +} + +/*! + Return true if source_name could be a device containing a CD-ROM. +*/ +bool +cdio_is_device_win32(const char *source_name) +{ + unsigned int len; + + if (NULL == source_name) return false; + len = strlen(source_name); + +#ifdef HAVE_WIN32_CDROM + if ((len == 2) && isalpha(source_name[0]) + && (source_name[len-1] == ':')) + return true; + + if ( ! WIN_NT ) return false; + + /* Test to see if of form: \\.\x: */ + return ( (len == 6) + && source_name[0] == '\\' && source_name[1] == '\\' + && source_name[2] == '.' && source_name[3] == '\\' + && isalpha(source_name[len-2]) + && (source_name[len-1] == ':') ); +#else + return false; +#endif +} + +driver_return_code_t +close_tray_win32 (const char *psz_win32_drive) +{ +#ifdef HAVE_WIN32_CDROM +#if 1 + return open_close_media_win32(psz_win32_drive, MCI_SET_DOOR_CLOSED|MCI_WAIT); +#else + if ( WIN_NT ) { + return close_tray_win32ioctl (psz_win32_drive); + } else { + return DRIVER_OP_UNSUPPORTED; /* not yet, but soon I hope */ + } +#endif +#else + return DRIVER_OP_UNSUPPORTED; +#endif /* HAVE_WIN32_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_t * +cdio_open_win32 (const char *psz_source_name) +{ +#ifdef HAVE_WIN32_CDROM + if ( WIN_NT ) { + return cdio_open_am_win32(psz_source_name, "ioctl"); + } else { + return cdio_open_am_win32(psz_source_name, "ASPI"); + } +#else + return NULL; +#endif /* HAVE_WIN32_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_t * +cdio_open_am_win32 (const char *psz_orig_source, const char *psz_access_mode) +{ + +#ifdef HAVE_WIN32_CDROM + CdIo_t *ret; + _img_private_t *_data; + char *psz_source; + + cdio_funcs_t _funcs; + + memset( &_funcs, 0, sizeof(_funcs) ); + + _funcs.audio_get_volume = audio_get_volume_win32; + _funcs.audio_pause = audio_pause_win32; + _funcs.audio_play_msf = audio_play_msf_win32; +#if 0 + _funcs.audio_play_track_index = audio_play_track_index_win32; +#endif + _funcs.audio_read_subchannel = audio_read_subchannel_win32; + _funcs.audio_resume = audio_resume_win32; + _funcs.audio_set_volume = audio_set_volume_win32; + _funcs.audio_stop = audio_stop_win32; + _funcs.eject_media = eject_media_win32; + _funcs.free = free_win32; + _funcs.get_arg = get_arg_win32; + _funcs.get_cdtext = get_cdtext_generic; + _funcs.get_default_device = cdio_get_default_device_win32; + _funcs.get_devices = cdio_get_devices_win32; + _funcs.get_disc_last_lsn = get_disc_last_lsn_win32; + _funcs.get_discmode = get_discmode_win32; + _funcs.get_drive_cap = get_drive_cap_mmc; + _funcs.get_first_track_num = get_first_track_num_generic; + _funcs.get_hwinfo = NULL; + _funcs.get_media_changed = get_media_changed_mmc; + _funcs.get_mcn = _cdio_get_mcn; + _funcs.get_num_tracks = get_num_tracks_generic; + _funcs.get_track_channels = get_track_channels_generic, + _funcs.get_track_copy_permit = get_track_copy_permit_generic, + _funcs.get_track_format = _cdio_get_track_format; + _funcs.get_track_green = _cdio_get_track_green; + _funcs.get_track_lba = NULL; /* This could be done if need be. */ + _funcs.get_track_msf = _cdio_get_track_msf; + _funcs.get_track_preemphasis = get_track_preemphasis_generic, + _funcs.lseek = NULL; + _funcs.read = NULL; + _funcs.read_audio_sectors = read_audio_sectors; + _funcs.read_data_sectors = read_data_sectors_win32; + _funcs.read_mode1_sector = read_mode1_sector_win32; + _funcs.read_mode1_sectors = read_mode1_sectors_win32; + _funcs.read_mode2_sector = read_mode2_sector_win32; + _funcs.read_mode2_sectors = read_mode2_sectors_win32; + _funcs.read_toc = read_toc_win32; + _funcs.run_mmc_cmd = run_mmc_cmd_win32; + _funcs.set_arg = set_arg_win32; + _funcs.set_blocksize = set_blocksize_mmc; + _funcs.set_speed = set_drive_speed_mmc; + + _data = calloc(1, sizeof (_img_private_t)); + _data->access_mode = str_to_access_mode_win32(psz_access_mode); + _data->gen.init = false; + _data->gen.fd = -1; + + if (NULL == psz_orig_source) { + psz_source=cdio_get_default_device_win32(); + if (NULL == psz_source) return NULL; + set_arg_win32(_data, "source", psz_source); + free(psz_source); + } else { + if (cdio_is_device_win32(psz_orig_source)) + set_arg_win32(_data, "source", psz_orig_source); + else { + /* The below would be okay if all device drivers worked this way. */ +#if 0 + cdio_info ("source %s is a not a device", psz_orig_source); +#endif + free(_data); + return NULL; + } + } + + ret = cdio_new ((void *)_data, &_funcs); + if (ret == NULL) return NULL; + + if (init_win32(_data)) + return ret; + else { + free_win32 (_data); + return NULL; + } + +#else + return NULL; +#endif /* HAVE_WIN32_CDROM */ + +} + +bool +cdio_have_win32 (void) +{ +#ifdef HAVE_WIN32_CDROM + return true; +#else + return false; +#endif /* HAVE_WIN32_CDROM */ +} diff --git a/lib/driver/MSWindows/win32.h b/lib/driver/MSWindows/win32.h new file mode 100644 index 00000000..e06c1136 --- /dev/null +++ b/lib/driver/MSWindows/win32.h @@ -0,0 +1,239 @@ +/* + $Id: win32.h,v 1.11 2008/04/21 18:30:21 karl Exp $ + + Copyright (C) 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "cdio_private.h" + +#pragma pack() + +typedef struct { + lsn_t start_lsn; + UCHAR Control : 4; + UCHAR Format; + cdtext_t cdtext; /* CD-TEXT */ +} track_info_t; + +typedef enum { + _AM_NONE, + _AM_IOCTL, + _AM_ASPI, +} access_mode_t; + +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + + access_mode_t access_mode; + + /* Some of the more OS specific things. */ + /* Entry info for each track, add 1 for leadout. */ + track_info_t tocent[CDIO_CD_MAX_TRACKS+1]; + + HANDLE h_device_handle; /* device descriptor */ + long hASPI; + short i_sid; + short i_lun; + long (*lpSendCommand)( void* ); + + bool b_ioctl_init; + bool b_aspi_init; + +} _img_private_t; + +/*! + Pause playing CD through analog output + + @param p_cdio the CD object to be acted upon. +*/ +driver_return_code_t audio_pause_win32ioctl (void *p_user_data); + +/*! + Playing starting at given MSF through analog output + + @param p_cdio the CD object to be acted upon. +*/ +driver_return_code_t audio_play_msf_win32ioctl (void *p_user_data, + msf_t *p_start_msf, + msf_t *p_end_msf); +/*! + Resume playing an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +driver_return_code_t audio_resume_win32ioctl (void *p_user_data); + +/*! + Get disc type associated with cd object. +*/ +discmode_t get_discmode_win32ioctl (_img_private_t *p_env); + +/*! + Get the volume settings of an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +driver_return_code_t +audio_get_volume_win32ioctl ( void *p_user_data, + /*out*/ cdio_audio_volume_t *p_volume); + +/*! + Read Audio Subchannel information + + @param p_cdio the CD object to be acted upon. + +*/ +driver_return_code_t +audio_read_subchannel_win32ioctl (void *p_user_data, + cdio_subchannel_t *p_subchannel); + +/*! + Set the volume of an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +driver_return_code_t +audio_stop_win32ioctl ( void *p_user_data ); + +/*! + Set the volume of an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +driver_return_code_t +audio_set_volume_win32ioctl ( void *p_user_data, + cdio_audio_volume_t *p_volume); + +/*! + Close the tray of a CD-ROM + + @param p_user_data the CD object to be acted upon. + +*/ +driver_return_code_t close_tray_win32ioctl (const char *psz_win32_drive); + +/*! + Reads an audio device using the DeviceIoControl method into data + starting from lsn. Returns 0 if no error. +*/ +int read_audio_sectors_win32ioctl (_img_private_t *p_obj, void *p_data, lsn_t lsn, + unsigned int nblocks); +/*! + Reads a single mode2 sector using the DeviceIoControl method into + data starting from lsn. Returns 0 if no error. + */ +int read_mode2_sector_win32ioctl (_img_private_t *p_env, void *p_data, + lsn_t lsn, bool b_form2); + +/*! + Reads a single mode1 sector using the DeviceIoControl method into + data starting from lsn. Returns 0 if no error. + */ +int read_mode1_sector_win32ioctl (_img_private_t *p_env, void *p_data, + lsn_t lsn, bool b_form2); + +const char *is_cdrom_win32ioctl (const char drive_letter); + +/*! + Run a SCSI MMC command. + + env private CD structure + i_timeout_ms time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + p_buf Buffer for data, both sending and receiving + i_buf Size of buffer + e_direction direction the transfer is to go. + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + Return 0 if command completed successfully. + */ +int run_mmc_cmd_win32ioctl( void *p_user_data, + unsigned int i_timeout, + unsigned int i_cdb, + const mmc_cdb_t * p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ); + +/*! + Initialize internal structures for CD device. + */ +bool init_win32ioctl (_img_private_t *p_env); + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return true if successful or false if an error. +*/ +bool read_toc_win32ioctl (_img_private_t *p_env); + +/*! + Return the media catalog number MCN. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +char *get_mcn_win32ioctl (const _img_private_t *p_env); + +/* + Read cdtext information for a CdIo object . + + return true on success, false on error or CD-TEXT information does + not exist. +*/ +bool init_cdtext_win32ioctl (_img_private_t *p_env); + +/*! + Return the the kind of drive capabilities of device. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +void get_drive_cap_aspi (const _img_private_t *p_env, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap); + +/*! + Return the the kind of drive capabilities of device. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +void get_drive_cap_win32ioctl (const _img_private_t *p_env, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap); + +/*! + Get the format (XA, DATA, AUDIO) of a track. +*/ +track_format_t get_track_format_win32ioctl(const _img_private_t *p_env, + track_t i_track); + +void set_cdtext_field_win32(void *user_data, track_t i_track, + track_t i_first_track, + cdtext_field_t e_field, const char *psz_value); + diff --git a/lib/driver/MSWindows/win32_ioctl.c b/lib/driver/MSWindows/win32_ioctl.c new file mode 100644 index 00000000..378661e6 --- /dev/null +++ b/lib/driver/MSWindows/win32_ioctl.c @@ -0,0 +1,1067 @@ +/* + $Id: win32_ioctl.c,v 1.30 2008/04/21 18:30:21 karl Exp $ + + Copyright (C) 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* This file contains Win32-specific code using the DeviceIoControl + access method. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: win32_ioctl.c,v 1.30 2008/04/21 18:30:21 karl Exp $"; + +#ifdef HAVE_WIN32_CDROM + +#if defined (_XBOX) +#include "inttypes.h" +#include "NtScsi.h" +#include "undocumented.h" +#define FORMAT_ERROR(i_err, psz_msg) \ + psz_msg=(char *)LocalAlloc(LMEM_ZEROINIT, 255); \ + sprintf(psz_msg, "error file %s: line %d (%s) %d\n", + _FILE__, __LINE__, __PRETTY_FUNCTION__, i_err) +#else +#include +#include +#include +#define FORMAT_ERROR(i_err, psz_msg) \ + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, \ + NULL, i_err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), \ + (LPSTR) psz_msg, 0, NULL) +#endif + +#ifdef WIN32 +#include +#endif + +#include +#include /* offsetof() macro */ +#include +#include +#include + +#include +#include +#include +#include "cdio_assert.h" +#include +#include "cdtext_private.h" +#include "cdio/logging.h" + +typedef struct _TRACK_DATA_FULL { + UCHAR SessionNumber; + UCHAR Control : 4; + UCHAR Adr : 4; + UCHAR TNO; + UCHAR POINT; /* Tracknumber (of session?) or lead-out/in (0xA0, 0xA1, 0xA2) */ + UCHAR Min; /* Only valid if disctype is CDDA ? */ + UCHAR Sec; /* Only valid if disctype is CDDA ? */ + UCHAR Frame; /* Only valid if disctype is CDDA ? */ + UCHAR Zero; /* Always zero */ + UCHAR PMIN; /* start min, if POINT is a track; if lead-out/in 0xA0: First Track */ + UCHAR PSEC; + UCHAR PFRAME; +} TRACK_DATA_FULL, *PTRACK_DATA_FULL; + +typedef struct _CDROM_TOC_FULL { + UCHAR Length[2]; + UCHAR FirstSession; + UCHAR LastSession; + TRACK_DATA_FULL TrackData[CDIO_CD_MAX_TRACKS+3]; +} CDROM_TOC_FULL, *PCDROM_TOC_FULL; + +typedef struct { + SCSI_PASS_THROUGH Spt; + ULONG Filler; + UCHAR SenseBuf[32]; + UCHAR DataBuf[512]; +} SCSI_PASS_THROUGH_WITH_BUFFERS; + +#include "win32.h" + +#define OP_TIMEOUT_MS 60 + +/*! + Pause playing CD through analog output + + @param p_cdio the CD object to be acted upon. +*/ +driver_return_code_t +audio_pause_win32ioctl (void *p_user_data) +{ + const _img_private_t *p_env = p_user_data; + DWORD dw_bytes_returned; + + bool b_success = + DeviceIoControl(p_env->h_device_handle, IOCTL_CDROM_PAUSE_AUDIO, + NULL, (DWORD) 0, NULL, 0, &dw_bytes_returned, NULL); + + if ( ! b_success ) { + char *psz_msg = NULL; + long int i_err = GetLastError(); + FORMAT_ERROR(i_err, psz_msg); + if (psz_msg) + cdio_info("Error: %s", psz_msg); + else + cdio_info("Error: %ld", i_err); + LocalFree(psz_msg); + return DRIVER_OP_ERROR; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Playing starting at given MSF through analog output + + @param p_cdio the CD object to be acted upon. +*/ +driver_return_code_t +audio_play_msf_win32ioctl (void *p_user_data, msf_t *p_start_msf, + msf_t *p_end_msf) +{ + const _img_private_t *p_env = p_user_data; + CDROM_PLAY_AUDIO_MSF play; + DWORD dw_bytes_returned; + + play.StartingM = cdio_from_bcd8(p_start_msf->m); + play.StartingS = cdio_from_bcd8(p_start_msf->s); + play.StartingF = cdio_from_bcd8(p_start_msf->f); + + play.EndingM = cdio_from_bcd8(p_end_msf->m); + play.EndingS = cdio_from_bcd8(p_end_msf->s); + play.EndingF = cdio_from_bcd8(p_end_msf->f); + + bool b_success = + DeviceIoControl(p_env->h_device_handle, IOCTL_CDROM_PLAY_AUDIO_MSF, + &play, sizeof(play), NULL, 0, &dw_bytes_returned, NULL); + + if ( ! b_success ) { + char *psz_msg = NULL; + long int i_err = GetLastError(); + FORMAT_ERROR(i_err, psz_msg); + if (psz_msg) + cdio_info("Error: %s", psz_msg); + else + cdio_info("Error: %ld", i_err); + LocalFree(psz_msg); + return DRIVER_OP_ERROR; + } + return DRIVER_OP_SUCCESS; + +} + +/*! + Read Audio Subchannel information + + @param p_cdio the CD object to be acted upon. + +*/ +driver_return_code_t +audio_read_subchannel_win32ioctl (void *p_user_data, + cdio_subchannel_t *p_subchannel) +{ + const _img_private_t *p_env = p_user_data; + DWORD dw_bytes_returned; + CDROM_SUB_Q_DATA_FORMAT q_data_format; + SUB_Q_CHANNEL_DATA q_subchannel_data; + + q_data_format.Format = CDIO_SUBCHANNEL_CURRENT_POSITION; + q_data_format.Track=0; /* Not sure if this has to be set or if so what + it should be. */ + + if( ! DeviceIoControl( p_env->h_device_handle, + IOCTL_CDROM_READ_Q_CHANNEL, + &q_data_format, sizeof(q_data_format), + &q_subchannel_data, sizeof(q_subchannel_data), + &dw_bytes_returned, NULL ) ) { + char *psz_msg = NULL; + long int i_err = GetLastError(); + FORMAT_ERROR(i_err, psz_msg); + if (psz_msg) + cdio_info("Error: %s", psz_msg); + else + cdio_info("Error: %ld", i_err); + LocalFree(psz_msg); + return DRIVER_OP_ERROR; + } + p_subchannel->audio_status = + q_subchannel_data.CurrentPosition.Header.AudioStatus; + p_subchannel->track = + q_subchannel_data.CurrentPosition.TrackNumber; + p_subchannel->index = + q_subchannel_data.CurrentPosition.IndexNumber; + p_subchannel->index = + q_subchannel_data.CurrentPosition.IndexNumber; + p_subchannel->address = q_subchannel_data.CurrentPosition.ADR; + p_subchannel->control = q_subchannel_data.CurrentPosition.Control; + + { + const UCHAR *abs_addr = + q_subchannel_data.CurrentPosition.AbsoluteAddress; + const UCHAR *rel_addr = + q_subchannel_data.CurrentPosition.TrackRelativeAddress; + + p_subchannel->abs_addr.m = cdio_to_bcd8(abs_addr[1]); + p_subchannel->abs_addr.s = cdio_to_bcd8(abs_addr[2]); + p_subchannel->abs_addr.f = cdio_to_bcd8(abs_addr[3]); + p_subchannel->rel_addr.m = cdio_to_bcd8(rel_addr[1]); + p_subchannel->rel_addr.s = cdio_to_bcd8(rel_addr[2]); + p_subchannel->rel_addr.f = cdio_to_bcd8(rel_addr[3]); + } + + return DRIVER_OP_SUCCESS; +} + + +/*! + Resume playing an audio CD. + + @param p_user_data the CD object to be acted upon. + +*/ +driver_return_code_t +audio_resume_win32ioctl (void *p_user_data) +{ + const _img_private_t *p_env = p_user_data; + DWORD dw_bytes_returned; + + bool b_success = + DeviceIoControl(p_env->h_device_handle, IOCTL_CDROM_RESUME_AUDIO, + NULL, (DWORD) 0, NULL, 0, &dw_bytes_returned, NULL); + + if ( ! b_success ) { + char *psz_msg = NULL; + long int i_err = GetLastError(); + FORMAT_ERROR(i_err, psz_msg); + if (psz_msg) + cdio_info("Error: %s", psz_msg); + else + cdio_info("Error: %ld", i_err); + LocalFree(psz_msg); + return DRIVER_OP_ERROR; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Set the volume of an audio CD. + + @param p_user_data pointer to the CD object to be acted upon. + @param p_volume pointer to the volume levels + +*/ +driver_return_code_t +audio_set_volume_win32ioctl (void *p_user_data, + /*in*/ cdio_audio_volume_t *p_volume) +{ + const _img_private_t *p_env = p_user_data; + DWORD dw_bytes_returned; + + bool b_success = + DeviceIoControl(p_env->h_device_handle, IOCTL_CDROM_SET_VOLUME, + p_volume, (DWORD) sizeof(cdio_audio_volume_t), + NULL, 0, &dw_bytes_returned, NULL); + + if ( ! b_success ) { + char *psz_msg = NULL; + long int i_err = GetLastError(); + FORMAT_ERROR(i_err, psz_msg); + if (psz_msg) + cdio_info("Error: %s", psz_msg); + else + cdio_info("Error: %ld", i_err); + LocalFree(psz_msg); + return DRIVER_OP_ERROR; + } + return DRIVER_OP_SUCCESS; +} + +driver_return_code_t +audio_get_volume_win32ioctl (void *p_user_data, + /*out*/ cdio_audio_volume_t *p_volume) +{ + const _img_private_t *p_env = p_user_data; + DWORD dw_bytes_returned; + + bool b_success = + DeviceIoControl(p_env->h_device_handle, IOCTL_CDROM_GET_VOLUME, + NULL, 0, + p_volume, (DWORD) sizeof(cdio_audio_volume_t), + &dw_bytes_returned, NULL); + + if ( ! b_success ) { + char *psz_msg = NULL; + long int i_err = GetLastError(); + FORMAT_ERROR(i_err, psz_msg); + if (psz_msg) + cdio_info("Error: %s", psz_msg); + else + cdio_info("Error: %ld", i_err); + LocalFree(psz_msg); + return DRIVER_OP_ERROR; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Stop playing an audio CD. + + @param p_user_data the CD object to be acted upon. + +*/ +driver_return_code_t +audio_stop_win32ioctl (void *p_user_data) +{ + const _img_private_t *p_env = p_user_data; + DWORD dw_bytes_returned; + + bool b_success = + DeviceIoControl(p_env->h_device_handle, IOCTL_CDROM_STOP_AUDIO, + NULL, (DWORD) 0, NULL, 0, &dw_bytes_returned, NULL); + + if ( ! b_success ) { + char *psz_msg = NULL; + long int i_err = GetLastError(); + FORMAT_ERROR(i_err, psz_msg); + if (psz_msg) + cdio_info("Error: %s", psz_msg); + else + cdio_info("Error: %ld", i_err); + LocalFree(psz_msg); + return DRIVER_OP_ERROR; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Close the tray of a CD-ROM + + @param p_user_data the CD object to be acted upon. + +*/ +driver_return_code_t +close_tray_win32ioctl (const char *psz_win32_drive) +{ +#ifdef WIN32 + DWORD dw_bytes_returned; + DWORD dw_access_flags; + + OSVERSIONINFO ov; + HANDLE h_device_handle; + bool b_success; + + memset(&ov,0,sizeof(OSVERSIONINFO)); + ov.dwOSVersionInfoSize=sizeof(OSVERSIONINFO); + GetVersionEx(&ov); + + if((ov.dwPlatformId==VER_PLATFORM_WIN32_NT) && + (ov.dwMajorVersion>4)) + dw_access_flags = GENERIC_READ|GENERIC_WRITE; /* add gen write on W2k/XP */ + else dw_access_flags = GENERIC_READ; + + h_device_handle = CreateFile( psz_win32_drive, + dw_access_flags, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if( h_device_handle == INVALID_HANDLE_VALUE ) { + return DRIVER_OP_ERROR; + } + + b_success = + DeviceIoControl(h_device_handle, IOCTL_STORAGE_LOAD_MEDIA2, + NULL, (DWORD) 0, NULL, 0, &dw_bytes_returned, NULL); + + + CloseHandle(h_device_handle); + + if ( ! b_success ) { + char *psz_msg = NULL; + long int i_err = GetLastError(); + FORMAT_ERROR(i_err, psz_msg); + if (psz_msg) + cdio_info("Error: %s", psz_msg); + else + cdio_info("Error: %ld", i_err); + LocalFree(psz_msg); + return DRIVER_OP_ERROR; + } + return DRIVER_OP_SUCCESS; +#else + return DRIVER_OP_UNSUPPORTED; +#endif +} + +/*! + Run a SCSI MMC command. + + env private CD structure + i_timeout time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + p_buf Buffer for data, both sending and receiving + i_buf Size of buffer + e_direction direction the transfer is to go. + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + Return 0 if command completed successfully. + */ +int +run_mmc_cmd_win32ioctl( void *p_user_data, + unsigned int i_timeout_ms, + unsigned int i_cdb, const mmc_cdb_t * p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + SCSI_PASS_THROUGH_DIRECT sptd; + bool b_success; + DWORD dw_bytes_returned; + + sptd.Length = sizeof(sptd); + sptd.PathId = 0; /* SCSI card ID will be filled in automatically */ + sptd.TargetId= 0; /* SCSI target ID will also be filled in */ + sptd.Lun=0; /* SCSI lun ID will also be filled in */ + sptd.CdbLength = i_cdb; + sptd.SenseInfoLength = 0; /* Don't return any sense data */ + sptd.DataIn = SCSI_MMC_DATA_READ == e_direction ? + SCSI_IOCTL_DATA_IN : SCSI_IOCTL_DATA_OUT; + sptd.DataTransferLength= i_buf; + sptd.TimeOutValue = msecs2secs(i_timeout_ms); + sptd.DataBuffer = (void *) p_buf; + sptd.SenseInfoOffset = 0; + + memcpy(sptd.Cdb, p_cdb, i_cdb); + + /* Send the command to drive */ + b_success = DeviceIoControl(p_env->h_device_handle, + IOCTL_SCSI_PASS_THROUGH_DIRECT, + (void *)&sptd, + (DWORD)sizeof(SCSI_PASS_THROUGH_DIRECT), + NULL, 0, + &dw_bytes_returned, + NULL); + + if ( !b_success ) { + char *psz_msg = NULL; + long int i_err = GetLastError(); + FORMAT_ERROR(i_err, psz_msg); + if (psz_msg) + cdio_info("Error: %s", psz_msg); + else + cdio_info("Error: %ld", i_err); + LocalFree(psz_msg); + return DRIVER_OP_ERROR; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Get disc type associated with cd object. +*/ +static discmode_t +dvd_discmode_win32ioctl (_img_private_t *p_env) +{ + discmode_t discmode=CDIO_DISC_MODE_NO_INFO; + driver_return_code_t rc; + + /* See if this is a DVD. */ + cdio_dvd_struct_t dvd; /* DVD READ STRUCT for layer 0. */ + + dvd.physical.type = CDIO_DVD_STRUCT_PHYSICAL; + dvd.physical.layer_num = 0; + + rc = mmc_get_dvd_struct_physical_private (p_env, &run_mmc_cmd_win32ioctl, + &dvd); + + if (DRIVER_OP_SUCCESS == rc) { + switch(dvd.physical.layer[0].book_type) { + case CDIO_DVD_BOOK_DVD_ROM: return CDIO_DISC_MODE_DVD_ROM; + case CDIO_DVD_BOOK_DVD_RAM: return CDIO_DISC_MODE_DVD_RAM; + case CDIO_DVD_BOOK_DVD_R: return CDIO_DISC_MODE_DVD_R; + case CDIO_DVD_BOOK_DVD_RW: return CDIO_DISC_MODE_DVD_RW; + case CDIO_DVD_BOOK_DVD_PR: return CDIO_DISC_MODE_DVD_PR; + case CDIO_DVD_BOOK_DVD_PRW: return CDIO_DISC_MODE_DVD_PRW; + default: return CDIO_DISC_MODE_DVD_OTHER; + } + } + return discmode; +} + + +/*! + Get disc type associated with cd object. +*/ +discmode_t +get_discmode_win32ioctl (_img_private_t *p_env) +{ + track_t i_track; + discmode_t discmode; + + if (!p_env) return CDIO_DISC_MODE_ERROR; + + discmode = dvd_discmode_win32ioctl(p_env); + + if (CDIO_DISC_MODE_NO_INFO != discmode) return discmode; + + if (!p_env->gen.toc_init) read_toc_win32ioctl (p_env); + + if (!p_env->gen.toc_init) return CDIO_DISC_MODE_ERROR; + + for (i_track = p_env->gen.i_first_track; + i_track < p_env->gen.i_first_track + p_env->gen.i_tracks ; + i_track ++) { + track_format_t track_fmt=get_track_format_win32ioctl(p_env, i_track); + + switch(track_fmt) { + case TRACK_FORMAT_AUDIO: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DA; + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_XA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_DATA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DATA; + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_ERROR: + default: + discmode = CDIO_DISC_MODE_ERROR; + } + } + return discmode; +} + +/* + Returns a string that can be used in a CreateFile call if + c_drive letter is a character. If not NULL is returned. + */ + +const char * +is_cdrom_win32ioctl(const char c_drive_letter) +{ +#ifdef _XBOX + char sz_win32_drive_full[] = "\\\\.\\X:"; + sz_win32_drive_full[4] = c_drive_letter; + return strdup(sz_win32_drive_full); +#else + UINT uDriveType; + char sz_win32_drive[4]; + + sz_win32_drive[0]= c_drive_letter; + sz_win32_drive[1]=':'; + sz_win32_drive[2]='\\'; + sz_win32_drive[3]='\0'; + + uDriveType = GetDriveType(sz_win32_drive); + + switch(uDriveType) { + case DRIVE_CDROM: { + char sz_win32_drive_full[] = "\\\\.\\X:"; + sz_win32_drive_full[4] = c_drive_letter; + return strdup(sz_win32_drive_full); + } + default: + cdio_debug("Drive %c is not a CD-ROM", c_drive_letter); + return NULL; + } +#endif +} + +/*! + Reads an audio device using the DeviceIoControl method into data + starting from lsn. Returns 0 if no error. + */ +driver_return_code_t +read_audio_sectors_win32ioctl (_img_private_t *p_env, void *data, lsn_t lsn, + unsigned int nblocks) +{ + DWORD dw_bytes_returned; + RAW_READ_INFO cdrom_raw; + + /* Initialize CDROM_RAW_READ structure */ + cdrom_raw.DiskOffset.QuadPart = (long long) CDIO_CD_FRAMESIZE_RAW * lsn; + cdrom_raw.SectorCount = nblocks; + cdrom_raw.TrackMode = CDDA; + + if( DeviceIoControl( p_env->h_device_handle, + IOCTL_CDROM_RAW_READ, &cdrom_raw, + sizeof(RAW_READ_INFO), data, + CDIO_CD_FRAMESIZE_RAW * nblocks, + &dw_bytes_returned, NULL ) == 0 ) { + char *psz_msg = NULL; + long int i_err = GetLastError(); + FORMAT_ERROR(i_err, psz_msg); + if (psz_msg) { + cdio_info("Error reading audio-mode lsn %lu\n%s (%ld))", + (long unsigned int) lsn, psz_msg, i_err); + } else { + cdio_info("Error reading audio-mode lsn %lu\n (%ld))", + (long unsigned int) lsn, i_err); + } + LocalFree(psz_msg); + return DRIVER_OP_ERROR; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Reads a single raw sector using the DeviceIoControl method into + data starting from lsn. Returns 0 if no error. + */ +static int +read_raw_sector (_img_private_t *p_env, void *p_buf, lsn_t lsn) +{ + mmc_cdb_t cdb = {{0, }}; + + /* ReadCD CDB12 command. The values were taken from MMC1 draft paper. */ + CDIO_MMC_SET_COMMAND (cdb.field, CDIO_MMC_GPCMD_READ_CD); + CDIO_MMC_SET_READ_LBA (cdb.field, lsn); + CDIO_MMC_SET_READ_LENGTH24(cdb.field, 1); + + cdb.field[9]=0xF8; /* Raw read, 2352 bytes per sector */ + + return run_mmc_cmd_win32ioctl(p_env, OP_TIMEOUT_MS, + mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + CDIO_CD_FRAMESIZE_RAW, p_buf); +} + +/*! + Reads a single mode2 sector using the DeviceIoControl method into + data starting from lsn. Returns 0 if no error. + */ +int +read_mode2_sector_win32ioctl (_img_private_t *p_env, void *p_data, + lsn_t lsn, bool b_form2) +{ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + int ret = read_raw_sector (p_env, buf, lsn); + + if ( 0 != ret) return ret; + + memcpy (p_data, + buf + CDIO_CD_SYNC_SIZE + CDIO_CD_XA_HEADER, + b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE); + + return 0; + +} + +/*! + Reads a single mode2 sector using the DeviceIoControl method into + data starting from lsn. Returns 0 if no error. + */ +int +read_mode1_sector_win32ioctl (_img_private_t *env, void *data, + lsn_t lsn, bool b_form2) +{ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + int ret = read_raw_sector (env, buf, lsn); + + if ( 0 != ret) return ret; + + memcpy (data, + buf + CDIO_CD_SYNC_SIZE+CDIO_CD_HEADER_SIZE, + b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE); + + return 0; + +} + +/*! + Initialize internal structures for CD device. + */ +bool +init_win32ioctl (_img_private_t *env) +{ +#ifdef WIN32 + OSVERSIONINFO ov; +#endif + +#ifdef _XBOX + ANSI_STRING filename; + OBJECT_ATTRIBUTES attributes; + IO_STATUS_BLOCK status; + HANDLE hDevice; + NTSTATUS error; +#else + unsigned int len=strlen(env->gen.source_name); + char psz_win32_drive[7]; + DWORD dw_access_flags; +#endif + + cdio_debug("using winNT/2K/XP ioctl layer"); + +#ifdef WIN32 + memset(&ov,0,sizeof(OSVERSIONINFO)); + ov.dwOSVersionInfoSize=sizeof(OSVERSIONINFO); + GetVersionEx(&ov); + + if((ov.dwPlatformId==VER_PLATFORM_WIN32_NT) && + (ov.dwMajorVersion>4)) + dw_access_flags = GENERIC_READ|GENERIC_WRITE; /* add gen write on W2k/XP */ + else dw_access_flags = GENERIC_READ; +#endif + + if (cdio_is_device_win32(env->gen.source_name)) + { +#ifdef _XBOX + // Use XBOX cdrom, no matter what drive letter is given. + RtlInitAnsiString(&filename,"\\Device\\Cdrom0"); + InitializeObjectAttributes(&attributes, &filename, OBJ_CASE_INSENSITIVE, + NULL); + error = NtCreateFile( &hDevice, + GENERIC_READ |SYNCHRONIZE | FILE_READ_ATTRIBUTES, + &attributes, + &status, + NULL, + 0, + FILE_SHARE_READ, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE + | FILE_SYNCHRONOUS_IO_NONALERT ); + + if (!NT_SUCCESS(error)) + { + return false; + } + env->h_device_handle = hDevice; +#else + sprintf( psz_win32_drive, "\\\\.\\%c:", env->gen.source_name[len-2] ); + + env->h_device_handle = CreateFile( psz_win32_drive, + dw_access_flags, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if( env->h_device_handle == INVALID_HANDLE_VALUE ) + { + /* No good. try toggle write. */ + dw_access_flags ^= GENERIC_WRITE; + env->h_device_handle = CreateFile( psz_win32_drive, + dw_access_flags, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + if (env->h_device_handle == NULL) + return false; + } +#endif + env->b_ioctl_init = true; + return true; + } + return false; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + via a SCSI MMC READ_TOC (FULTOC). Return true if successful or + false if an error. +*/ +static bool +read_fulltoc_win32mmc (_img_private_t *p_env) +{ + mmc_cdb_t cdb = {{0, }}; + CDROM_TOC_FULL cdrom_toc_full; + int i_status, i, j; + int i_track_format = 0; + int i_seen_flag; + + /* Operation code */ + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC); + + cdb.field[1] = 0x00; + + /* Format */ + cdb.field[2] = CDIO_MMC_READTOC_FMT_FULTOC; + + memset(&cdrom_toc_full, 0, sizeof(cdrom_toc_full)); + + /* Setup to read header, to get length of data */ + CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(cdrom_toc_full)); + + i_status = run_mmc_cmd_win32ioctl (p_env, 1000*60*3, + mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + sizeof(cdrom_toc_full), &cdrom_toc_full); + + if ( 0 != i_status ) { + cdio_debug ("SCSI MMC READ_TOC failed\n"); + return false; + } + + i_seen_flag=0; + for( i = 0 ; i <= CDIO_CD_MAX_TRACKS+3; i++ ) { + + if ( 0xA0 == cdrom_toc_full.TrackData[i].POINT ) { + /* First track number */ + p_env->gen.i_first_track = cdrom_toc_full.TrackData[i].PMIN; + i_track_format = cdrom_toc_full.TrackData[i].PSEC; + i_seen_flag|=0x01; + } + + if ( 0xA1 == cdrom_toc_full.TrackData[i].POINT ) { + /* Last track number */ + p_env->gen.i_tracks = + cdrom_toc_full.TrackData[i].PMIN - p_env->gen.i_first_track + 1; + i_seen_flag|=0x02; + } + + j = cdrom_toc_full.TrackData[i].POINT; + if ( 0xA2 == j ) { + /* Start position of the lead out */ + p_env->tocent[ p_env->gen.i_tracks ].start_lsn = + cdio_lba_to_lsn( + cdio_msf3_to_lba( + cdrom_toc_full.TrackData[i].PMIN, + cdrom_toc_full.TrackData[i].PSEC, + cdrom_toc_full.TrackData[i].PFRAME + ) + ); + p_env->tocent[ p_env->gen.i_tracks ].Control + = cdrom_toc_full.TrackData[i].Control; + p_env->tocent[ p_env->gen.i_tracks ].Format = i_track_format; + i_seen_flag|=0x04; + } + + if (cdrom_toc_full.TrackData[i].POINT > 0 + && cdrom_toc_full.TrackData[i].POINT <= p_env->gen.i_tracks) { + p_env->tocent[j-1].start_lsn = + cdio_lba_to_lsn( + cdio_msf3_to_lba( + cdrom_toc_full.TrackData[i].PMIN, + cdrom_toc_full.TrackData[i].PSEC, + cdrom_toc_full.TrackData[i].PFRAME + ) + ); + p_env->tocent[j-1].Control = + cdrom_toc_full.TrackData[i].Control; + p_env->tocent[j-1].Format = i_track_format; + + set_track_flags(&(p_env->gen.track_flags[j]), + p_env->tocent[j-1].Control); + + cdio_debug("p_sectors: %i, %lu", i, + (unsigned long int) (p_env->tocent[i].start_lsn)); + + if (cdrom_toc_full.TrackData[i].POINT == p_env->gen.i_tracks) + i_seen_flag|=0x08; + } + + if ( 0x0F == i_seen_flag ) break; + } + if ( 0x0F == i_seen_flag ) { + p_env->gen.toc_init = true; + return true; + } + return false; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return true if successful or false if an error. +*/ +bool +read_toc_win32ioctl (_img_private_t *p_env) +{ + CDROM_TOC cdrom_toc; + DWORD dw_bytes_returned; + unsigned int i, j; + bool b_fulltoc_first; /* Do we do fulltoc or DeviceIoControl + first? */ + if ( ! p_env ) return false; + + /* + The MMC5 spec says: + For media other than CD, information may be fabricated in order + ^^^ ^^ + to emulate a CD structure for the specific media. + + There is no requirement though that it *has* to and some DVD + drives like one by Thompson for XBOX don't support a + IOCTL_CDROM_READ_TOC for DVD's. So if we have a DVD we should not + prefer getting the TOC via MMC. + + But on the other hand in GNU/Linux it is reported that using the + TOC via MMC gives better information such as for CD DATA Form 2 (used + in SVCDs). So if we *don't* have a DVD I think we want to try MMC + first. + + Is this complicated enough? I could be wrong... + + */ + b_fulltoc_first = (CDIO_DISC_MODE_NO_INFO == dvd_discmode_win32ioctl(p_env)); + + if ( b_fulltoc_first && read_fulltoc_win32mmc(p_env) ) return true; + + /* SCSI-MMC READ_TOC (FULTOC) read failed or we don't want to try it + initiaily. Try reading TOC via DeviceIoControl... */ + + if( DeviceIoControl( p_env->h_device_handle, + IOCTL_CDROM_READ_TOC, + NULL, 0, &cdrom_toc, sizeof(CDROM_TOC), + &dw_bytes_returned, NULL ) == 0 ) { + char *psz_msg = NULL; + long int i_err = GetLastError(); + cdio_log_level_t loglevel = b_fulltoc_first + ? CDIO_LOG_WARN : CDIO_LOG_DEBUG; + + FORMAT_ERROR(i_err, psz_msg); + if (psz_msg) { + cdio_log(loglevel, "could not read TOC (%ld): %s", i_err, psz_msg); + LocalFree(psz_msg); + } else + cdio_log(loglevel, "could not read TOC (%ld)", i_err); + + if ( !b_fulltoc_first && read_fulltoc_win32mmc(p_env) ) return true; + return false; + } + + p_env->gen.i_first_track = cdrom_toc.FirstTrack; + p_env->gen.i_tracks = cdrom_toc.LastTrack - cdrom_toc.FirstTrack + 1; + + j = p_env->gen.i_first_track; + for( i = 0 ; i <= p_env->gen.i_tracks ; i++, j++ ) { + p_env->tocent[ i ].start_lsn = + cdio_lba_to_lsn( + cdio_msf3_to_lba( cdrom_toc.TrackData[i].Address[1], + cdrom_toc.TrackData[i].Address[2], + cdrom_toc.TrackData[i].Address[3] ) + ); + p_env->tocent[i].Control = cdrom_toc.TrackData[i].Control; + p_env->tocent[i].Format = cdrom_toc.TrackData[i].Adr; + + p_env->gen.track_flags[j].preemphasis = + p_env->tocent[i].Control & 0x1 + ? CDIO_TRACK_FLAG_TRUE : CDIO_TRACK_FLAG_FALSE; + + p_env->gen.track_flags[j].copy_permit = + p_env->tocent[i].Control & 0x2 + ? CDIO_TRACK_FLAG_TRUE : CDIO_TRACK_FLAG_FALSE; + + p_env->gen.track_flags[j].channels = + p_env->tocent[i].Control & 0x8 ? 4 : 2; + + + cdio_debug("p_sectors: %i, %lu", i, + (unsigned long int) (p_env->tocent[i].start_lsn)); + } + p_env->gen.toc_init = true; + return true; +} + +/*! + Return the media catalog number MCN. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +char * +get_mcn_win32ioctl (const _img_private_t *p_env) { + + DWORD dw_bytes_returned; + SUB_Q_MEDIA_CATALOG_NUMBER mcn; + CDROM_SUB_Q_DATA_FORMAT q_data_format; + + memset( &mcn, 0, sizeof(mcn) ); + + q_data_format.Format = CDIO_SUBCHANNEL_MEDIA_CATALOG; + + /* MSDN info on CDROM_SUB_Q_DATA_FORMAT says if Format is set to + get MCN, track must be set 0. + */ + q_data_format.Track=0; + + if( ! DeviceIoControl( p_env->h_device_handle, + IOCTL_CDROM_READ_Q_CHANNEL, + &q_data_format, sizeof(q_data_format), + &mcn, sizeof(mcn), + &dw_bytes_returned, NULL ) ) { + cdio_warn( "could not read Q Channel at track %d", 1); + } else if (mcn.Mcval) + return strdup(mcn.MediaCatalog); + return NULL; +} + +/*! + Get the format (XA, DATA, AUDIO) of a track. +*/ +track_format_t +get_track_format_win32ioctl(const _img_private_t *env, track_t i_track) +{ + /* This is pretty much copied from the "badly broken" cdrom_count_tracks + in linux/cdrom.c. + */ + + if (env->tocent[i_track - env->gen.i_first_track].Control & 0x04) { + if (env->tocent[i_track - env->gen.i_first_track].Format == 0x10) + return TRACK_FORMAT_CDI; + else if (env->tocent[i_track - env->gen.i_first_track].Format == 0x20) + return TRACK_FORMAT_XA; + else + return TRACK_FORMAT_DATA; + } else + return TRACK_FORMAT_AUDIO; +} + +#endif /*HAVE_WIN32_CDROM*/ diff --git a/lib/driver/Makefile.am b/lib/driver/Makefile.am new file mode 100644 index 00000000..5f07c3a9 --- /dev/null +++ b/lib/driver/Makefile.am @@ -0,0 +1,183 @@ +# $Id: Makefile.am,v 1.28 2008/10/20 01:25:15 rocky Exp $ +# +# Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 +# 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +######################################################## +# Things to make the libcdio library +######################################################## +# +# From libtool documentation amended with guidance from N. Boullis: +# +# 1. Start with version information of `0:0:0' for each libtool library. +# +# 2. It is probably not a good idea to update the version information +# several times between public releases, but rather once per public +# release. (This seems to be more an aesthetic consideration than +# a hard technical one.) +# +# 3. If the library source code has changed at all since the last +# update, then increment REVISION (`C:R:A' becomes `C:R+1:A'). +# +# 4. If any interfaces have been added, removed, or changed since the +# last update, increment CURRENT, and set REVISION to 0. +# +# 5. If any interfaces have been added since the last public release, +# then increment AGE. +# +# 6. If any interfaces have been removed or changed since the last +# public release, then set AGE to 0. A changed interface means an +# incompatibility with previous versions. + +libcdio_la_CURRENT = 10 +libcdio_la_REVISION = 0 +libcdio_la_AGE = 0 + +EXTRA_DIST = image/Makefile FreeBSD/Makefile MSWindows/Makefile \ + libcdio.sym + +noinst_HEADERS = cdio_assert.h cdio_private.h portable.h + +libcdio_sources = \ + _cdio_generic.c \ + _cdio_stdio.c \ + _cdio_stdio.h \ + _cdio_stream.c \ + _cdio_stream.h \ + aix.c \ + bsdi.c \ + audio.c \ + cd_types.c \ + cdio.c \ + cdtext.c \ + cdtext_private.h \ + device.c \ + disc.c \ + ds.c \ + FreeBSD/freebsd.c \ + FreeBSD/freebsd.h \ + FreeBSD/freebsd_cam.c \ + FreeBSD/freebsd_ioctl.c \ + generic.h \ + gnu_linux.c \ + image.h \ + image/bincue.c \ + image/cdrdao.c \ + image_common.c \ + image_common.h \ + image/nrg.c \ + image/nrg.h \ + logging.c \ + mmc.c \ + mmc_private.h \ + MSWindows/aspi32.c \ + MSWindows/aspi32.h \ + MSWindows/win32_ioctl.c \ + MSWindows/win32.c \ + MSWindows/win32.h \ + netbsd.c \ + osx.c \ + read.c \ + sector.c \ + solaris.c \ + track.c \ + utf8.c \ + util.c + +lib_LTLIBRARIES = libcdio.la +libcdio_la_LIBADD = $(LTLIBICONV) + +libcdio_la_SOURCES = $(libcdio_sources) +libcdio_la_ldflags = -version-info $(libcdio_la_CURRENT):$(libcdio_la_REVISION):$(libcdio_la_AGE) @LT_NO_UNDEFINED@ + +INCLUDES = $(LIBCDIO_CFLAGS) + +######################################################## +# Things to version the symbols in the libraries +######################################################## + +# An explanation of the versioning problem from Nicolas Boullis and +# the versioned symbol solution he uses below... +# +# Currently, libvcdinfo uses the cdio_open function from libcdio. +# Let's imagine a program foobar that uses both the vcdinfo_open +# function from libvcdinfo and the cdio_open function from libcdio. + +# Currently, libcdio has SONAME libcdio.so.0, libvcdinfo has SONAME +# libvcdinfo.so.0 and requires libcdio.so.0, and foobar requires both +# libvcdinfo.so.0 and libcdio.so.0. Everything looks fine. +# +# Now, for some reason, you decide to change the cdio_open function. +# That's your right, but you have to bump the CURRENT version and (if I +# understand it correctly, athough this is not that clear in libtool's +# documentation) set the AGE to 0. Anyway, this bumps the SONAME, which is +# sane since the interface changes incompatibly. + +# Now, you have a new libcdio with SONAME libcdio.so.1. But libvcdinfo and +# foobar still require libcdio.so.0. Everything is still fine. + +# Now, after some minor changes, the author of foobar recompiles foobar. +# Then, foobar now requires libvcdinfo.so.0 and libcdio.so.1. And foobar +# now segfaults... + +# What is happening? When you run foobar, if brings both libvcdinfo.so.0 +# and libcdio.so.1, but libvcdinfo.so.0 also brings libcdio.so.0. So you +# have both libcdio.so.0 and libcdio.so.1 that bring their symbols to the +# global namespace. Hence, you have to incompatible versions of the +# cdio_open function in the name space. When foobar calls cdio_open, it +# may choose the wrong function, and segfaults... + +# With versioned symbols, the cdio_open function from libcdio.so.0 may be +# known as (something that looks like) cdio_open@@CDIO_0. An the cdio_open +# function from libcdio.so.1 as cdio_open@@CDIO_1. Both versions of +# libcdio would still be brought in by the most recent foobar, but foobar +# (and libvcdinfo) know which versioned function to use and then use the +# good one. + + +# This is some simple versioning where every symbol is versioned with +# something that looks like the SONAME of the library. More complex (and +# better) versioning is possible; it is for example what is used by glibc. +# But good complex versioning is something that requires much more +# work... + + +# The below is a impliments symbol versioning. First of all, I +# compute MAJOR as CURENT - AGE; that is what is used within libtool +# (at least on GNU/Linux systems) for the number in the SONAME. The +# nm command gives the list of symbols known in each of the object +# files that will be part of the shared library. And the sed command +# extracts from this list those symbols that will be shared. (This sed +# command comes from libtool.) + +libcdio_la_MAJOR = $(shell expr $(libcdio_la_CURRENT) - $(libcdio_la_AGE)) +if BUILD_VERSIONED_LIBS +libcdio_la_LDFLAGS = $(libcdio_la_ldflags) -Wl,--version-script=libcdio.la.ver +libcdio_la_DEPENDENCIES = libcdio.la.ver + +libcdio.la.ver: $(libcdio_la_OBJECTS) $(srcdir)/libcdio.sym + echo 'CDIO_$(libcdio_la_MAJOR) { ' > $@ + objs=`for obj in $(libcdio_la_OBJECTS); do sed -ne "s/^pic_object='\(.*\)'$$/\1/p" $$obj; done`; \ + if test -n "$${objs}" ; then \ + nm $${objs} | sed -n -e 's/^.*[ ][ABCDGIRSTW][ABCDGIRSTW]*[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$$/\1/p' | sort -u | { first=true; while read symbol; do if grep -q "^$${symbol}\$$" $(srcdir)/libcdio.sym; then if test $$first = true; then echo " global:"; first=false; fi; echo " $${symbol};"; fi; done; } >> $@; \ + nm $${objs} | sed -n -e 's/^.*[ ][ABCDGIRSTW][ABCDGIRSTW]*[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$$/\1/p' | sort -u | { first=true; while read symbol; do if grep -q "^$${symbol}\$$" $(srcdir)/libcdio.sym; then :; else if test $$first = true; then echo " local:"; first=false; fi; echo " $${symbol};"; fi; done; } >> $@; \ + fi + echo '};' >> $@ +else +libcdio_la_LDFLAGS = $(libcdio_la_ldflags) +endif + +MOSTLYCLEANFILES = libcdio.la.ver diff --git a/lib/driver/_cdio_generic.c b/lib/driver/_cdio_generic.c new file mode 100644 index 00000000..1508c736 --- /dev/null +++ b/lib/driver/_cdio_generic.c @@ -0,0 +1,513 @@ +/* + $Id: _cdio_generic.c,v 1.27 2008/04/22 15:29:11 karl Exp $ + + Copyright (C) 2004, 2005, 2006, 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* This file contains generic implementations of device-driver routines. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: _cdio_generic.c,v 1.27 2008/04/22 15:29:11 karl Exp $"; + +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ + +#include +#include + +#include +#include + +#include +#include +#include +#include "cdio_assert.h" +#include "cdio_private.h" +#include "_cdio_stdio.h" +#include "portable.h" + +/*! + Eject media -- there's nothing to do here. We always return -2. + Should we also free resources? + */ +int +cdio_generic_unimplemented_eject_media (void *p_user_data) { + /* Sort of a stub here. Perhaps log a message? */ + return DRIVER_OP_UNSUPPORTED; +} + +/*! + Set the blocksize for subsequent reads. +*/ +int +cdio_generic_unimplemented_set_blocksize (void *p_user_data, + uint16_t i_blocksize) { + /* Sort of a stub here. Perhaps log a message? */ + return DRIVER_OP_UNSUPPORTED; +} + +/*! + Set the drive speed. +*/ +int +cdio_generic_unimplemented_set_speed (void *p_user_data, int i_speed) { + /* Sort of a stub here. Perhaps log a message? */ + return DRIVER_OP_UNSUPPORTED; +} + + +/*! + Release and free resources associated with cd. + */ +void +cdio_generic_free (void *p_user_data) +{ + generic_img_private_t *p_env = p_user_data; + track_t i_track; + + if (NULL == p_env) return; + if (p_env->source_name) free (p_env->source_name); + + if (p_env->b_cdtext_init) { + for (i_track=0; i_track < p_env->i_tracks; i_track++) { + cdtext_destroy(&(p_env->cdtext_track[i_track])); + } + } + + if (p_env->fd >= 0) + close (p_env->fd); + + free (p_env); +} + +/*! + Initialize CD device. + */ +bool +cdio_generic_init (void *user_data, int open_flags) +{ + generic_img_private_t *p_env = user_data; + if (p_env->init) { + cdio_warn ("init called more than once"); + return false; + } + + p_env->fd = open (p_env->source_name, open_flags, 0); + + if (p_env->fd < 0) + { + cdio_warn ("open (%s): %s", p_env->source_name, strerror (errno)); + return false; + } + + p_env->init = true; + p_env->toc_init = false; + p_env->b_cdtext_init = false; + p_env->b_cdtext_error = false; + p_env->i_joliet_level = 0; /* Assume no Joliet extensions initally */ + return true; +} + +/*! + Reads a single form1 sector from cd device into data starting + from lsn. + */ +driver_return_code_t +cdio_generic_read_form1_sector (void * user_data, void *data, lsn_t lsn) +{ + if (0 > cdio_generic_lseek(user_data, CDIO_CD_FRAMESIZE*lsn, SEEK_SET)) + return DRIVER_OP_ERROR; + return cdio_generic_read(user_data, data, CDIO_CD_FRAMESIZE); +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + Is in fact libc's lseek(). +*/ +off_t +cdio_generic_lseek (void *user_data, off_t offset, int whence) +{ + generic_img_private_t *p_env = user_data; + return lseek(p_env->fd, offset, whence); +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + Is in fact libc's read(). +*/ +ssize_t +cdio_generic_read (void *user_data, void *buf, size_t size) +{ + generic_img_private_t *p_env = user_data; + return read(p_env->fd, buf, size); +} + +/*! + Release and free resources associated with stream or disk image. +*/ +void +cdio_generic_stdio_free (void *p_user_data) +{ + generic_img_private_t *p_env = p_user_data; + + if (NULL == p_env) return; + if (NULL != p_env->source_name) + free (p_env->source_name); + + if (p_env->data_source) + cdio_stdio_destroy (p_env->data_source); +} + + +/*! + Return true if source_name could be a device containing a CD-ROM. +*/ +bool +cdio_is_device_generic(const char *source_name) +{ + struct stat buf; + if (0 != stat(source_name, &buf)) { + cdio_warn ("Can't get file status for %s:\n%s", source_name, + strerror(errno)); + return false; + } + return (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode)); +} + +/*! + Like above, but don't give a warning device doesn't exist. +*/ +bool +cdio_is_device_quiet_generic(const char *source_name) +{ + struct stat buf; + if (0 != stat(source_name, &buf)) { + return false; + } + return (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode)); +} + +/*! + Add/allocate a drive to the end of drives. + Use cdio_free_device_list() to free this device_list. +*/ +void +cdio_add_device_list(char **device_list[], const char *drive, + unsigned int *num_drives) +{ + if (NULL != drive) { + unsigned int j; + char real_device_1[PATH_MAX]; + char real_device_2[PATH_MAX]; + cdio_follow_symlink(drive, real_device_1); + /* Check if drive is already in list. */ + for (j=0; j<*num_drives; j++) { + cdio_follow_symlink((*device_list)[j], real_device_2); + if (strcmp(real_device_1, real_device_2) == 0) break; + } + + if (j==*num_drives) { + /* Drive not in list. Add it. */ + (*num_drives)++; + *device_list = realloc(*device_list, (*num_drives) * sizeof(char *)); + (*device_list)[*num_drives-1] = strdup(drive); + } + + } else { + (*num_drives)++; + if (*device_list) { + *device_list = realloc(*device_list, (*num_drives) * sizeof(char *)); + } else { + *device_list = malloc((*num_drives) * sizeof(char *)); + } + (*device_list)[*num_drives-1] = NULL; + } +} + +/* + Get cdtext information in p_user_data for track i_track. + For disc information i_track is 0. + + Return the CD-TEXT or NULL if obj is NULL, CD-TEXT information does + not exist, or we don't know how to get this implemented. +*/ +cdtext_t * +get_cdtext_generic (void *p_user_data, track_t i_track) +{ + generic_img_private_t *p_env = p_user_data; + + if (!p_env) return NULL; + if (!p_env->toc_init) + p_env->cdio->op.read_toc (p_user_data); + + if ( (0 != i_track + && i_track >= p_env->i_tracks+p_env->i_first_track ) ) + return NULL; + + if (!p_env->b_cdtext_init) + init_cdtext_generic(p_env); + if (!p_env->b_cdtext_init) return NULL; + + if (0 == i_track) + return &(p_env->cdtext); + else + return &(p_env->cdtext_track[i_track-p_env->i_first_track]); + +} + +/*! + Get disc type associated with cd object. +*/ +discmode_t +get_discmode_generic (void *p_user_data ) +{ + generic_img_private_t *p_env = p_user_data; + + /* See if this is a DVD. */ + cdio_dvd_struct_t dvd; /* DVD READ STRUCT for layer 0. */ + + dvd.physical.type = CDIO_DVD_STRUCT_PHYSICAL; + dvd.physical.layer_num = 0; + if (0 == mmc_get_dvd_struct_physical (p_env->cdio, &dvd)) { + switch(dvd.physical.layer[0].book_type) { + case CDIO_DVD_BOOK_DVD_ROM: return CDIO_DISC_MODE_DVD_ROM; + case CDIO_DVD_BOOK_DVD_RAM: return CDIO_DISC_MODE_DVD_RAM; + case CDIO_DVD_BOOK_DVD_R: return CDIO_DISC_MODE_DVD_R; + case CDIO_DVD_BOOK_DVD_RW: return CDIO_DISC_MODE_DVD_RW; + case CDIO_DVD_BOOK_DVD_PR: return CDIO_DISC_MODE_DVD_PR; + case CDIO_DVD_BOOK_DVD_PRW: return CDIO_DISC_MODE_DVD_PRW; + default: return CDIO_DISC_MODE_DVD_OTHER; + } + } + + return get_discmode_cd_generic(p_user_data); +} + +/*! + Get disc type associated with cd object. +*/ +discmode_t +get_discmode_cd_generic (void *p_user_data ) +{ + generic_img_private_t *p_env = p_user_data; + track_t i_track; + discmode_t discmode=CDIO_DISC_MODE_NO_INFO; + + if (!p_env->toc_init) + p_env->cdio->op.read_toc (p_user_data); + + if (!p_env->toc_init) + return CDIO_DISC_MODE_NO_INFO; + + for (i_track = p_env->i_first_track; + i_track < p_env->i_first_track + p_env->i_tracks ; + i_track ++) { + track_format_t track_fmt = + p_env->cdio->op.get_track_format(p_env, i_track); + + switch(track_fmt) { + case TRACK_FORMAT_AUDIO: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DA; + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_XA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_CDI: + case TRACK_FORMAT_DATA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DATA; + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_ERROR: + default: + discmode = CDIO_DISC_MODE_ERROR; + } + } + return discmode; +} + +/*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. +*/ +track_t +get_first_track_num_generic(void *p_user_data) +{ + const generic_img_private_t *p_env = p_user_data; + + if (!p_env->toc_init) + p_env->cdio->op.read_toc (p_user_data); + + return p_env->toc_init ? p_env->i_first_track : CDIO_INVALID_TRACK; +} + + +/*! + Return the number of tracks in the current medium. +*/ +track_t +get_num_tracks_generic(void *p_user_data) +{ + generic_img_private_t *p_env = p_user_data; + + if (!p_env->toc_init) + p_env->cdio->op.read_toc (p_user_data); + + return p_env->toc_init ? p_env->i_tracks : CDIO_INVALID_TRACK; +} + +void +set_cdtext_field_generic(void *p_user_data, track_t i_track, + track_t i_first_track, + cdtext_field_t e_field, const char *psz_value) +{ + char **pp_field; + generic_img_private_t *p_env = p_user_data; + + if( i_track == 0 ) + pp_field = &(p_env->cdtext.field[e_field]); + + else + pp_field = &(p_env->cdtext_track[i_track-i_first_track].field[e_field]); + + if (*pp_field) free(*pp_field); + *pp_field = (psz_value) ? strdup(psz_value) : NULL; +} + +/*! + Read CD-Text information for a CdIo_t object . + + return true on success, false on error or CD-Text information does + not exist. +*/ +bool +init_cdtext_generic (generic_img_private_t *p_env) +{ + return mmc_init_cdtext_private( p_env, + p_env->cdio->op.run_mmc_cmd, + set_cdtext_field_generic + ); +} + +/*! Return number of channels in track: 2 or 4; -2 if not + implemented or -1 for error. + Not meaningful if track is not an audio track. +*/ +int +get_track_channels_generic(const void *p_user_data, track_t i_track) +{ + const generic_img_private_t *p_env = p_user_data; + return p_env->track_flags[i_track].channels; +} + +/*! Return 1 if copy is permitted on the track, 0 if not, or -1 for error. + Is this meaningful if not an audio track? +*/ +track_flag_t +get_track_copy_permit_generic(void *p_user_data, track_t i_track) +{ + const generic_img_private_t *p_env = p_user_data; + return p_env->track_flags[i_track].copy_permit; +} + +/*! Return 1 if track has pre-emphasis, 0 if not, or -1 for error. + Is this meaningful if not an audio track? + + pre-emphasis is a non linear frequency response. +*/ +track_flag_t +get_track_preemphasis_generic(const void *p_user_data, track_t i_track) +{ + const generic_img_private_t *p_env = p_user_data; + return p_env->track_flags[i_track].preemphasis; +} + +void +set_track_flags(track_flags_t *p_track_flag, uint8_t i_flag) +{ + p_track_flag->preemphasis = ( i_flag & CDIO_TRACK_FLAG_PRE_EMPHASIS ) + ? CDIO_TRACK_FLAG_TRUE : CDIO_TRACK_FLAG_FALSE; + + p_track_flag->copy_permit = ( i_flag & CDIO_TRACK_FLAG_COPY_PERMITTED ) + ? CDIO_TRACK_FLAG_TRUE : CDIO_TRACK_FLAG_FALSE; + + p_track_flag->channels = ( i_flag & CDIO_TRACK_FLAG_FOUR_CHANNEL_AUDIO ) + ? 4 : 2; +} + +driver_return_code_t +read_data_sectors_generic (void *p_user_data, void *p_buf, lsn_t i_lsn, + uint16_t i_blocksize, uint32_t i_blocks) +{ + int rc; + if (0 > cdio_generic_lseek(p_user_data, i_blocksize*i_lsn, SEEK_SET)) + return DRIVER_OP_ERROR; + rc = cdio_generic_read(p_user_data, p_buf, i_blocksize*i_blocks); + if (rc > 0) return DRIVER_OP_SUCCESS; + return DRIVER_OP_ERROR; +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/driver/_cdio_stdio.c b/lib/driver/_cdio_stdio.c new file mode 100644 index 00000000..fec8538e --- /dev/null +++ b/lib/driver/_cdio_stdio.c @@ -0,0 +1,224 @@ +/* + $Id: _cdio_stdio.c,v 1.6 2008/04/22 15:29:11 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2008 Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ + +#include +#include + +#include +#include +#include "_cdio_stream.h" +#include "_cdio_stdio.h" + +static const char _rcsid[] = "$Id: _cdio_stdio.c,v 1.6 2008/04/22 15:29:11 karl 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 = calloc (1, 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); +} + +/*! + Like fseek(3) and in fact may be the same. + + This function sets the file position indicator for the stream + pointed to by stream. The new position, measured in bytes, is obtained + by adding offset bytes to the position specified by whence. If whence + is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is relative to + the start of the file, the current position indicator, or end-of-file, + respectively. A successful call to the fseek function clears the end- + of-file indicator for the stream and undoes any effects of the + ungetc(3) function on the same stream. + + @return upon successful completion, DRIVER_OP_SUCCESS, else, + DRIVER_OP_ERROR is returned and the global variable errno is set to + indicate the error. +*/ +static driver_return_code_t +_stdio_seek(void *p_user_data, long i_offset, int whence) +{ + _UserData *const ud = p_user_data; + + if ( (i_offset=fseek (ud->fd, i_offset, whence)) ) { + cdio_error ("fseek (): %s", strerror (errno)); + } + + return i_offset; +} + +static long int +_stdio_stat(void *p_user_data) +{ + const _UserData *const ud = p_user_data; + + return ud->st_size; +} + +/*! + Like fread(3) and in fact is about the same. + + DESCRIPTION: + The function fread reads nmemb elements of data, each size bytes long, + from the stream pointed to by stream, storing them at the location + given by ptr. + + RETURN VALUE: + return the number of items successfully read or written (i.e., + not the number of characters). If an error occurs, or the + end-of-file is reached, the return value is a short item count + (or zero). + + We do not distinguish between end-of-file and error, and callers + must use feof(3) and ferror(3) to determine which occurred. + */ +static long +_stdio_read(void *user_data, void *buf, long int 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; +} + +/*! + Deallocate resources assocaited with obj. After this obj is unusable. +*/ +void +cdio_stdio_destroy(CdioDataSource_t *p_obj) +{ + cdio_stream_destroy(p_obj); +} + +CdioDataSource_t * +cdio_stdio_new(const char pathname[]) +{ + CdioDataSource_t *new_obj = NULL; + cdio_stream_io_functions funcs = { NULL, NULL, NULL, NULL, NULL, NULL }; + _UserData *ud = NULL; + struct stat statbuf; + + if (stat (pathname, &statbuf) == -1) + { + cdio_warn ("could not retrieve file info for `%s': %s", + pathname, strerror (errno)); + return NULL; + } + + ud = calloc (1, 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/driver/_cdio_stdio.h b/lib/driver/_cdio_stdio.h new file mode 100644 index 00000000..a2cd4c02 --- /dev/null +++ b/lib/driver/_cdio_stdio.h @@ -0,0 +1,51 @@ +/* + $Id: _cdio_stdio.h,v 1.3 2008/04/22 15:29:11 karl Exp $ + + Copyright (C) 2003, 2008 Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifndef __CDIO_STDIO_H__ +#define __CDIO_STDIO_H__ + +#include "_cdio_stream.h" + +/*! + Initialize a new stdio stream reading from pathname. + A pointer to the stream is returned or NULL if there was an error. + + cdio_stream_free should be called on the returned value when you + don't need the stream any more. No other finalization is needed. + */ +CdioDataSource_t * cdio_stdio_new(const char psz_path[]); + +/*! + Deallocate resources assocaited with obj. After this obj is unusable. +*/ +void cdio_stdio_destroy(CdioDataSource_t *p_obj); + + +#endif /* __CDIO_STREAM_STDIO_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/driver/_cdio_stream.c b/lib/driver/_cdio_stream.c new file mode 100644 index 00000000..149e30bb --- /dev/null +++ b/lib/driver/_cdio_stream.c @@ -0,0 +1,214 @@ +/* + $Id: _cdio_stream.c,v 1.9 2008/04/22 15:29:11 karl Exp $ + + Copyright (C) 2005, 2006, 2008 Rocky Bernstein + Copyright (C) 2000, 2004, 2005 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include "cdio_assert.h" + +/* #define STREAM_DEBUG */ + +#include +#include +#include "_cdio_stream.h" + +static const char _rcsid[] = "$Id: _cdio_stream.c,v 1.9 2008/04/22 15:29:11 karl Exp $"; + +/* + * DataSource implementations + */ + +struct _CdioDataSource { + void* user_data; + cdio_stream_io_functions op; + int is_open; + long position; +}; + +void +cdio_stream_close(CdioDataSource_t *p_obj) +{ + if (!p_obj) return; + + if (p_obj->is_open) { + cdio_debug ("closed source..."); + p_obj->op.close(p_obj->user_data); + p_obj->is_open = 0; + p_obj->position = 0; + } +} + +void +cdio_stream_destroy(CdioDataSource_t *p_obj) +{ + if (!p_obj) return; + + cdio_stream_close(p_obj); + + p_obj->op.free(p_obj->user_data); + + free(p_obj); +} + +/** + Like 3 fgetpos. + + This function gets the current file position indicator for the stream + pointed to by stream. + + @return unpon successful completion, return value is positive, else, + the global variable errno is set to indicate the error. +*/ +ssize_t +cdio_stream_getpos(CdioDataSource_t* p_obj, /*out*/ ssize_t *i_offset) +{ + if (!p_obj || !p_obj->is_open) return DRIVER_OP_UNINIT; + return *i_offset = p_obj->position; +} + +CdioDataSource_t * +cdio_stream_new(void *user_data, const cdio_stream_io_functions *funcs) +{ + CdioDataSource_t *new_obj; + + new_obj = calloc (1, sizeof (CdioDataSource_t)); + + new_obj->user_data = user_data; + memcpy(&(new_obj->op), funcs, sizeof(cdio_stream_io_functions)); + + return new_obj; +} + +/* + Open if not already open. + Return false if we hit an error. Errno should be set for that error. +*/ +static bool +_cdio_stream_open_if_necessary(CdioDataSource_t *p_obj) +{ + if (!p_obj) return false; + + if (!p_obj->is_open) { + if (p_obj->op.open(p_obj->user_data)) { + cdio_warn ("could not open input stream..."); + return false; + } else { + cdio_debug ("opened source..."); + p_obj->is_open = 1; + p_obj->position = 0; + } + } + return true; +} + +/** + Like fread(3) and in fact may be the same. + + DESCRIPTION: + The function fread reads nmemb elements of data, each size bytes long, + from the stream pointed to by stream, storing them at the location + given by ptr. + + RETURN VALUE: + return the number of items successfully read or written (i.e., + not the number of characters). If an error occurs, or the + end-of-file is reached, the return value is a short item count + (or zero). + + We do not distinguish between end-of-file and error, and callers + must use feof(3) and ferror(3) to determine which occurred. +*/ +ssize_t +cdio_stream_read(CdioDataSource_t* p_obj, void *ptr, long size, long nmemb) +{ + long read_bytes; + + if (!p_obj) return 0; + if (!_cdio_stream_open_if_necessary(p_obj)) return 0; + + read_bytes = (p_obj->op.read)(p_obj->user_data, ptr, size*nmemb); + p_obj->position += read_bytes; + + return read_bytes; +} + +/** + Like 3 fseek and in fact may be the same. + + This function sets the file position indicator for the stream + pointed to by stream. The new position, measured in bytes, is obtained + by adding offset bytes to the position specified by whence. If whence + is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is relative to + the start of the file, the current position indicator, or end-of-file, + respectively. A successful call to the fseek function clears the end- + of-file indicator for the stream and undoes any effects of the + ungetc(3) function on the same stream. + + @return unpon successful completion, return value is positive, else, + the global variable errno is set to indicate the error. +*/ +ssize_t +cdio_stream_seek(CdioDataSource_t* p_obj, ssize_t offset, int whence) +{ + if (!p_obj) return DRIVER_OP_UNINIT; + + if (!_cdio_stream_open_if_necessary(p_obj)) + /* errno is set by _cdio_stream_open_if necessary. */ + return DRIVER_OP_ERROR; + + if (offset < 0) return DRIVER_OP_ERROR; + + if (p_obj->position != offset) { +#ifdef STREAM_DEBUG + cdio_warn("had to reposition DataSource from %ld to %ld!", obj->position, offset); +#endif + p_obj->position = offset; + return p_obj->op.seek(p_obj->user_data, offset, whence); + } + + return 0; +} + +/** + Return whatever size of stream reports, I guess unit size is bytes. + On error return -1; + */ +ssize_t +cdio_stream_stat(CdioDataSource_t *p_obj) +{ + if (!p_obj) return -1; + if (!_cdio_stream_open_if_necessary(p_obj)) return -1; + + return p_obj->op.stat(p_obj->user_data); +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/driver/_cdio_stream.h b/lib/driver/_cdio_stream.h new file mode 100644 index 00000000..7147a0b9 --- /dev/null +++ b/lib/driver/_cdio_stream.h @@ -0,0 +1,139 @@ +/* + $Id: _cdio_stream.h,v 1.5 2008/04/22 15:29:11 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2008 Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifndef __CDIO_STREAM_H__ +#define __CDIO_STREAM_H__ + +#include +#include "cdio_private.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 driver_return_code_t(*cdio_data_seek_t)(void *user_data, long offset, + int whence); + + 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 { + 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; + + /** + Like 3 fgetpos. + + This function gets the current file position indicator for the stream + pointed to by stream. + + @return unpon successful completion, return value is positive, else, + the global variable errno is set to indicate the error. + */ + ssize_t cdio_stream_getpos(CdioDataSource_t* p_obj, + /*out*/ ssize_t *i_offset); + + CdioDataSource_t * + cdio_stream_new(void *user_data, const cdio_stream_io_functions *funcs); + + /** + Like fread(3) and in fact may be the same. + + DESCRIPTION: + The function fread reads nmemb elements of data, each size bytes long, + from the stream pointed to by stream, storing them at the location + given by ptr. + + RETURN VALUE: + return the number of items successfully read or written (i.e., + not the number of characters). If an error occurs, or the + end-of-file is reached, the return value is a short item count + (or zero). + + We do not distinguish between end-of-file and error, and callers + must use feof(3) and ferror(3) to determine which occurred. + */ + ssize_t cdio_stream_read(CdioDataSource_t* p_obj, void *ptr, long i_size, + long nmemb); + + /** + Like fseek(3) and in fact may be the same. + + This function sets the file position indicator for the stream + pointed to by stream. The new position, measured in bytes, is obtained + by adding offset bytes to the position specified by whence. If whence + is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is relative to + the start of the file, the current position indicator, or end-of-file, + respectively. A successful call to the fseek function clears the end- + of-file indicator for the stream and undoes any effects of the + ungetc(3) function on the same stream. + + @return upon successful completion, DRIVER_OP_SUCCESS, else, + DRIVER_OP_ERROR is returned and the global variable errno is set to + indicate the error. + */ + ssize_t cdio_stream_seek(CdioDataSource_t *p_obj, ssize_t i_offset, + int whence); + + /** + Return whatever size of stream reports, I guess unit size is bytes. + On error return -1; + */ + ssize_t cdio_stream_stat(CdioDataSource_t *p_obj); + + /** + Deallocate resources associated with p_obj. After this p_obj is unusable. + */ + void cdio_stream_destroy(CdioDataSource_t *p_obj); + + void cdio_stream_close(CdioDataSource_t *p_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/driver/aix.c b/lib/driver/aix.c new file mode 100644 index 00000000..387f9354 --- /dev/null +++ b/lib/driver/aix.c @@ -0,0 +1,1026 @@ +/* + $Id: aix.c,v 1.3 2008/04/22 15:29:11 karl Exp $ + + Copyright (C) 2004, 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#include +#include +#include +#include +#include "cdio_assert.h" +#include "cdio_private.h" + +#define DEFAULT_CDIO_DEVICE "/dev/rcd0" + +#ifdef HAVE_AIX_CDROM + +static const char _rcsid[] = "$Id: aix.c,v 1.3 2008/04/22 15:29:11 karl Exp $"; + +#ifdef HAVE_GLOB_H +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "cdtext_private.h" + +typedef struct _TRACK_DATA { + uchar Format; + uchar Control : 4; + uchar Adr : 4; + uchar TrackNumber; + uchar Reserved1; + uchar Address[4]; +} TRACK_DATA, *PTRACK_DATA; + +typedef struct _CDROM_TOC { + uchar Length[2]; + uchar FirstTrack; + uchar LastTrack; + TRACK_DATA TrackData[CDIO_CD_MAX_TRACKS+1]; +} CDROM_TOC, *PCDROM_TOC; + + +typedef struct _TRACK_DATA_FULL { + uchar SessionNumber; + uchar Control : 4; + uchar Adr : 4; + uchar TNO; + uchar POINT; /* Tracknumber (of session?) or lead-out/in (0xA0, 0xA1, 0xA2) */ + uchar Min; /* Only valid if disctype is CDDA ? */ + uchar Sec; /* Only valid if disctype is CDDA ? */ + uchar Frame; /* Only valid if disctype is CDDA ? */ + uchar Zero; /* Always zero */ + uchar PMIN; /* start min, if POINT is a track; if lead-out/in 0xA0: First Track */ + uchar PSEC; + uchar PFRAME; +} TRACK_DATA_FULL, *PTRACK_DATA_FULL; + +typedef struct _CDROM_TOC_FULL { + uchar Length[2]; + uchar FirstSession; + uchar LastSession; + TRACK_DATA_FULL TrackData[CDIO_CD_MAX_TRACKS+3]; +} CDROM_TOC_FULL, *PCDROM_TOC_FULL; + + +/* reader */ + +typedef enum { + _AM_NONE, + _AM_CTRL_SCSI +} access_mode_t; + + +typedef struct { + lsn_t start_lsn; + uchar Control : 4; + uchar Format; + cdtext_t cdtext; /* CD-TEXT */ +} track_info_t; + +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + + access_mode_t access_mode; + + /* Some of the more OS specific things. */ + /* Entry info for each track, add 1 for leadout. */ + track_info_t tocent[CDIO_CD_MAX_TRACKS+1]; + +} _img_private_t; + +static track_format_t get_track_format_aix(void *p_user_data, + track_t i_track); + +static access_mode_t +str_to_access_mode_aix(const char *psz_access_mode) +{ + const access_mode_t default_access_mode = _AM_CTRL_SCSI; + + if (NULL==psz_access_mode) return default_access_mode; + + if (!strcmp(psz_access_mode, "SCSI")) + return _AM_CTRL_SCSI; + else { + cdio_warn ("unknown access type: %s. Default SCSI used.", + psz_access_mode); + return default_access_mode; + } +} + + +/*! + Initialize CD device. + */ +static bool +init_aix (_img_private_t *p_env) +{ + + if (p_env->gen.init) { + cdio_warn ("init called more than once"); + return false; + } + + p_env->gen.fd = openx (p_env->gen.source_name, O_RDONLY, NULL, + SC_DIAGNOSTIC); + + /*p_env->gen.fd = openx (p_env->gen.source_name, O_RDONLY, NULL, + IDE_SINGLE);*/ + + if (p_env->gen.fd < 0) + { + cdio_warn ("open (%s): %s", p_env->gen.source_name, strerror (errno)); + return false; + } + + p_env->gen.init = true; + p_env->gen.toc_init = false; + p_env->gen.b_cdtext_init = false; + p_env->gen.b_cdtext_error = false; + p_env->gen.i_joliet_level = 0; /* Assume no Joliet extensions initally */ + p_env->access_mode = _AM_CTRL_SCSI; + + return true; +} + +/*! + Run a SCSI MMC command. + + p_user_data internal CD structure. + i_timeout_ms time in milliseconds we will wait for the command + to complete. + i_cdb Size of p_cdb + p_cdb CDB bytes. + e_direction direction the transfer is to go. + i_buf Size of buffer + p_buf Buffer for data, both sending and receiving + */ +static driver_return_code_t +run_mmc_cmd_aix( void *p_user_data, unsigned int i_timeout_ms, + unsigned int i_cdb, const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + struct sc_passthru cgc; + int i_rc; + + memset (&cgc, 0, sizeof (cgc)); + memcpy(cgc.scsi_cdb, p_cdb, sizeof(mmc_cdb_t)); + +#ifdef AIX_DISABLE_ASYNC + /* This enables synchronous negotiation mode. Some CD-ROM drives + * don't handle this well. + */ + cgc.flags = 0; +#else + cgc.flags = SC_ASYNC; +#endif + + if (0 != i_buf) + cgc.flags |= MMC_DATA_READ == e_direction ? B_READ : B_WRITE; + + cgc.timeout_value = msecs2secs(i_timeout_ms); + cgc.buffer = p_buf; + cgc.data_length = i_buf; + cgc.command_length= i_cdb; + + i_rc = ioctl(p_env->gen.fd, DK_PASSTHRU, &cgc); + if (-1 == i_rc) { + cdio_warn("DKIOCMD error: %s", strerror(errno)); + } + return i_rc; +} + +/*! + Reads audio sectors from CD device into data starting from lsn. + Returns 0 if no error. + + May have to check size of nblocks. There may be a limit that + can be read in one go, e.g. 25 blocks. +*/ + +static driver_return_code_t +_read_audio_sectors_aix (void *p_user_data, void *data, lsn_t lsn, + unsigned int nblocks) +{ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + +#ifdef FINISHED + struct cdrom_msf *msf = (struct cdrom_msf *) &buf; + msf_t _msf; + struct cdrom_cdda cdda; + + _img_private_t *env = p_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 (env->gen.ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (env->gen.ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (env->gen.ioctls_debugged < 75 + || (env->gen.ioctls_debugged < (30 * 75) + && env->gen.ioctls_debugged % 75 == 0) + || env->gen.ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %d", lsn); + + env->gen.ioctls_debugged++; + + cdda.cdda_addr = lsn; + cdda.cdda_length = nblocks; + cdda.cdda_data = (caddr_t) data; + if (ioctl (env->gen.fd, CDROMCDDA, &cdda) == -1) { + perror ("ioctl(..,CDROMCDDA,..)"); + return 1; + /* exit (EXIT_FAILURE); */ + } +#endif + memcpy (data, buf, CDIO_CD_FRAMESIZE_RAW); + + return 0; +} + +/*! + Reads a single mode1 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static driver_return_code_t +_read_mode1_sector_aix (void *env, void *data, lsn_t lsn, + bool b_form2) +{ + +#if FIXED + do something here. +#else + return cdio_generic_read_form1_sector(env, data, lsn); +#endif +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static driver_return_code_t +_read_mode1_sectors_aix (void *p_user_data, void *p_data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *p_env = p_user_data; + unsigned int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode1_sector_aix (p_env, + ((char *)p_data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting from lsn. + Returns 0 if no error. + */ +static driver_return_code_t +_read_mode2_sector_aix (void *p_user_data, void *p_data, lsn_t lsn, + bool b_form2) +{ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + int offset = 0; +#ifdef FINISHED + struct cdrom_msf *msf = (struct cdrom_msf *) &buf; + msf_t _msf; + struct cdrom_cdxa cd_read; + + _img_private_t *p_env = p_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 (p_env->gen.ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged < 75 + || (p_env->gen.ioctls_debugged < (30 * 75) + && p_env->gen.ioctls_debugged % 75 == 0) + || p_env->gen.ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %2.2d:%2.2d:%2.2d", + msf->cdmsf_min0, msf->cdmsf_sec0, msf->cdmsf_frame0); + + p_env->gen.ioctls_debugged++; + + /* Using CDROMXA ioctl will actually use the same uscsi command + * as ATAPI, except we don't need to be root + */ + offset = CDIO_CD_XA_SYNC_HEADER; + cd_read.cdxa_addr = lsn; + cd_read.cdxa_data = buf; + cd_read.cdxa_length = 1; + cd_read.cdxa_format = CDROM_XA_SECTOR_DATA; + + if (ioctl (p_env->gen.fd, CDROMCDXA, &cd_read) == -1) { + perror ("ioctl(..,CDROMCDXA,..)"); + return 1; + /* exit (EXIT_FAILURE); */ + } +#endif + + if (b_form2) + memcpy (p_data, buf + (offset-CDIO_CD_SUBHEADER_SIZE), M2RAW_SECTOR_SIZE); + else + memcpy (((char *)p_data), buf + offset, CDIO_CD_FRAMESIZE); + + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static driver_return_code_t +_read_mode2_sectors_aix (void *p_user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *env = p_user_data; + unsigned int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode2_sector_aix (env, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static lsn_t +get_disc_last_lsn_aix (void *p_user_data) +{ + uint32_t i_size=0; +#ifdef FINISHED + _img_private_t *env = p_user_data; + + struct cdrom_tocentry tocent; + + tocent.cdte_track = CDIO_CDROM_LEADOUT_TRACK; + tocent.cdte_format = CDIO_CDROM_LBA; + if (ioctl (env->gen.fd, CDROMREADTOCENTRY, &tocent) == -1) + { + perror ("ioctl(CDROMREADTOCENTRY)"); + exit (EXIT_FAILURE); + } + + i_size = tocent.cdte_addr.lba; +#endif + + return i_size; +} + +/*! + Set the arg "key" with "value" in the source device. + Currently "source" and "access-mode" are valid keys. + "source" sets the source device in I/O operations + "access-mode" sets the the method of CD access + + 0 is returned if no error was found, and nonzero if there as an error. +*/ +static driver_return_code_t +_set_arg_aix (void *p_user_data, const char key[], const char value[]) +{ + _img_private_t *p_env = p_user_data; + + if (!strcmp (key, "source")) + { + if (!value) return DRIVER_OP_ERROR; + free (p_env->gen.source_name); + p_env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + p_env->access_mode = str_to_access_mode_aix(key); + } + else return DRIVER_OP_ERROR; + + return DRIVER_OP_SUCCESS; +} + +/* + * aixioc_send + Issue ioctl command. + + Args: + p_env - environment + cmd - ioctl command + arg - ioctl argument + b_print_err - whether an error message is to be displayed if the + ioctl fails + + Return: + true/false - ioctl successful +*/ +static bool +aixioc_send(_img_private_t *p_env, int cmd, void *arg, bool b_print_err) +{ + struct cd_audio_cmd *ac; + + if (p_env->gen.fd < 0) + return false; + + if (cmd == DKAUDIO) { + ac = (struct cd_audio_cmd *) arg; + ac->status = 0; /* Nuke status for audio cmds */ + } + + if (ioctl(p_env->gen.fd, cmd, arg) < 0) { + if (b_print_err) { + cdio_warn("errno=%d (%s)", errno, strerror(errno)); + } + return false; + } + return true; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + via a SCSI MMC READ_TOC (FULTOC). Return true if successful or + false if an error. +*/ +static bool +read_toc_ioctl_aix (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + struct cd_audio_cmd cmdbuf; + int i; + + cmdbuf.msf_flag = false; + cmdbuf.audio_cmds = CD_TRK_INFO_AUDIO; + if (!aixioc_send(p_env, IDE_CDAUDIO, (void *) &cmdbuf, true)) + return false; + + p_env->gen.i_first_track = cmdbuf.indexing.track_index.first_track; + p_env->gen.i_tracks = ( cmdbuf.indexing.track_index.last_track + - p_env->gen.i_first_track ) + 1; + + /* Do it again to get the last MSF data */ + cmdbuf.msf_flag = true; + if (!aixioc_send(p_env, IDE_CDAUDIO, (void *) &cmdbuf, true)) + return false; + + cmdbuf.audio_cmds = CD_GET_TRK_MSF; + + for (i = 0; i <= p_env->gen.i_tracks; i++) { + int i_track = i + p_env->gen.i_first_track; + + /* Get the track info */ + cmdbuf.indexing.track_msf.track = i_track; + if (!aixioc_send(p_env, IDE_CDAUDIO, (void *) &cmdbuf, TRUE)) + return false; + + p_env->tocent[ i_track ].start_lsn = + cdio_msf3_to_lba( + cmdbuf.indexing.track_msf.mins, + cmdbuf.indexing.track_msf.secs, + cmdbuf.indexing.track_msf.frames ); + } + + return true; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + via a SCSI MMC READ_TOC (FULTOC). Return true if successful or + false if an error. +*/ +static bool +read_toc_aix (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + mmc_cdb_t cdb = {{0, }}; + CDROM_TOC_FULL cdrom_toc_full; + int i_status, i, i_seen_flag; + int i_track_format = 0; + + /* Operation code */ + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC); + + cdb.field[1] = 0x00; + + /* Format */ + cdb.field[2] = CDIO_MMC_READTOC_FMT_FULTOC; + + memset(&cdrom_toc_full, 0, sizeof(cdrom_toc_full)); + + /* Setup to read header, to get length of data */ + CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(cdrom_toc_full)); + + i_status = run_scsi_cmd_aix (p_env, 1000*60*3, + mmc_get_cmd_len(cdb.field[0]), + &cdb, MMC_DATA_READ, + sizeof(cdrom_toc_full), &cdrom_toc_full); + + if ( 0 != i_status ) { + cdio_debug ("SCSI MMC READ_TOC failed\n"); + return read_toc_ioctl_aix(p_user_data); + } + + i_seen_flag=0; + for( i = 0 ; i <= CDIO_CD_MAX_TRACKS+3; i++ ) { + + if ( 0xA0 == cdrom_toc_full.TrackData[i].POINT ) { + /* First track number */ + p_env->gen.i_first_track = cdrom_toc_full.TrackData[i].PMIN; + i_track_format = cdrom_toc_full.TrackData[i].PSEC; + i_seen_flag|=0x01; + } + + if ( 0xA1 == cdrom_toc_full.TrackData[i].POINT ) { + /* Last track number */ + p_env->gen.i_tracks = + cdrom_toc_full.TrackData[i].PMIN - p_env->gen.i_first_track + 1; + i_seen_flag|=0x02; + } + + if ( 0xA2 == cdrom_toc_full.TrackData[i].POINT ) { + /* Start position of the lead out */ + p_env->tocent[ p_env->gen.i_tracks ].start_lsn = + cdio_msf3_to_lba( + cdrom_toc_full.TrackData[i].PMIN, + cdrom_toc_full.TrackData[i].PSEC, + cdrom_toc_full.TrackData[i].PFRAME ); + p_env->tocent[ p_env->gen.i_tracks ].Control + = cdrom_toc_full.TrackData[i].Control; + p_env->tocent[ p_env->gen.i_tracks ].Format = i_track_format; + i_seen_flag|=0x04; + } + + if (cdrom_toc_full.TrackData[i].POINT > 0 + && cdrom_toc_full.TrackData[i].POINT <= p_env->gen.i_tracks) { + p_env->tocent[ cdrom_toc_full.TrackData[i].POINT - 1 ].start_lsn = + cdio_msf3_to_lba( + cdrom_toc_full.TrackData[i].PMIN, + cdrom_toc_full.TrackData[i].PSEC, + cdrom_toc_full.TrackData[i].PFRAME ); + p_env->tocent[ cdrom_toc_full.TrackData[i].POINT - 1 ].Control = + cdrom_toc_full.TrackData[i].Control; + p_env->tocent[ cdrom_toc_full.TrackData[i].POINT - 1 ].Format = + i_track_format; + + cdio_debug("p_sectors: %i, %lu", i, + (unsigned long int) (p_env->tocent[i].start_lsn)); + + if (cdrom_toc_full.TrackData[i].POINT == p_env->gen.i_tracks) + i_seen_flag|=0x08; + } + + if ( 0x0F == i_seen_flag ) break; + } + if ( 0x0F == i_seen_flag ) { + p_env->gen.toc_init = true; + return true; + } + return false; +} + +/*! + Eject media in CD drive. If successful, as a side effect we + also free obj. + */ +static driver_return_code_t +eject_media_aix (void *p_user_data) { + + _img_private_t *p_env = p_user_data; + driver_return_code_t ret=DRIVER_OP_SUCCESS; + int i_status; + + if (p_env->gen.fd <= -1) return DRIVER_OP_UNINIT; + i_status = ioctl(p_env->gen.fd, DKEJECT); + if ( i_status != 0) { + cdio_generic_free((void *) p_env); + cdio_warn ("DKEJECT failed: %s", strerror(errno)); + ret = DRIVER_OP_ERROR; + } + close(p_env->gen.fd); + p_env->gen.fd = -1; + return ret; +} + +#if 0 +static void * +_cdio_malloc_and_zero(size_t size) { + void *ptr; + + if( !size ) size++; + + if((ptr = malloc(size)) == NULL) { + cdio_warn("malloc() failed: %s", strerror(errno)); + return NULL; + } + + memset(ptr, 0, size); + return ptr; +} +#endif + +/*! + Return the value associated with the key "arg". +*/ +static const char * +get_arg_aix (void *p_user_data, const char key[]) +{ + _img_private_t *p_env = p_user_data; + + if (!strcmp (key, "source")) { + return p_env->gen.source_name; + } else if (!strcmp (key, "access-mode")) { + switch (p_env->access_mode) { + case _AM_CTRL_SCSI: + return "SCSI"; + case _AM_NONE: + return "no access method"; + } + } + return NULL; +} + +/*! + Return a string containing the default CD device if none is specified. + */ +char * +cdio_get_default_device_aix(void) +{ + return strdup(DEFAULT_CDIO_DEVICE); +} + +/*! + Get disc type associated with cd object. +*/ + +static discmode_t +get_discmode_aix (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + struct mode_form_op media; + int ret; + + /* Get the media info */ + media.action= CD_GET_MODE; + + if((ret = ioctl(p_env->gen.fd, DK_CD_MODE, &media)) != 0) { + cdio_warn ("DK_CD_MODE failed: %s", strerror(errno)); + return CDIO_DISC_MODE_NO_INFO; + } + switch(media.cd_mode_form) { + case CD_DA: + return CDIO_DISC_MODE_CD_DA; + case DVD_ROM: + return CDIO_DISC_MODE_DVD_ROM; + case DVD_RAM: + return CDIO_DISC_MODE_DVD_RAM; + case DVD_RW: + return CDIO_DISC_MODE_DVD_RW; + default: /* no valid match */ + return CDIO_DISC_MODE_NO_INFO; + } +} + +/*! + Get format of track. +*/ +static track_format_t +get_track_format_aix(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if ( !p_env ) return TRACK_FORMAT_ERROR; + if (!p_env->gen.init) init_aix(p_env); + if (!p_env->gen.toc_init) read_toc_aix (p_user_data) ; + + if ( (i_track > p_env->gen.i_tracks+p_env->gen.i_first_track) + || i_track < p_env->gen.i_first_track) + return TRACK_FORMAT_ERROR; + + i_track -= p_env->gen.i_first_track; + +#ifdef FINISHED + /* This is pretty much copied from the "badly broken" cdrom_count_tracks + in linux/cdrom.c. + */ + if (p_env->tocent[i_track].cdte_ctrl & CDROM_DATA_TRACK) { + if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_CDI_TRACK) + return TRACK_FORMAT_CDI; + else if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_XA_TRACK) + return TRACK_FORMAT_XA; + else + return TRACK_FORMAT_DATA; + } else + return TRACK_FORMAT_AUDIO; +#else + return TRACK_FORMAT_ERROR; +#endif + +} + +/*! + 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 *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if ( !p_env ) return false; + if (!p_env->gen.init) init_aix(p_env); + if (!p_env->gen.toc_init) read_toc_aix (p_env) ; + + if (i_track >= p_env->gen.i_tracks+p_env->gen.i_first_track + || i_track < p_env->gen.i_first_track) + return false; + + i_track -= p_env->gen.i_first_track; + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cd-info for a while. + */ + +#ifdef FINISHED + return ((p_env->tocent[i_track].cdte_ctrl & 2) != 0); +#else + return false; +#endif +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + track_num in obj. Track numbers usually start at something + greater than 0, usually 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 entry. +*/ +static bool +_cdio_get_track_msf(void *p_user_data, track_t i_track, msf_t *msf) +{ + _img_private_t *p_env = p_user_data; + + if (NULL == msf) return false; + + if (!p_env->gen.init) init_aix(p_env); + if (!p_env->gen.toc_init) read_toc_aix (p_env) ; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) + i_track = p_env->gen.i_tracks + p_env->gen.i_first_track; + +#ifdef FINISHED + if (i_track > (p_env->gen.i_tracks+p_env->gen.i_first_track) + || i_track < p_env->gen.i_first_track) { + return false; + } else { + struct cdrom_tocentry *msf0 = &p_env->tocent[i_track-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; + } +#else + return FALSE; +#endif +} + +#else +/*! + Return a string containing the default VCD device if none is specified. + */ +char * +cdio_get_default_device_aix(void) +{ + return strdup(DEFAULT_CDIO_DEVICE); +} + +#endif /* HAVE_AIX_CDROM */ + +/*! + Return an array of strings giving possible CD devices. + */ +char ** +cdio_get_devices_aix (void) +{ +#ifndef HAVE_AIX_CDROM + return NULL; +#else + struct stat st; + char **drives = NULL; + unsigned int i_files=0; +#ifdef HAVE_GLOB_H + unsigned int i; + glob_t globbuf; + + globbuf.gl_offs = 0; + glob("/dev/rcd?", 0, NULL, &globbuf); + for (i=0; iaccess_mode = _AM_CTRL_SCSI; + _data->gen.init = false; + _data->gen.fd = -1; + _data->gen.toc_init = false; + _data->gen.b_cdtext_init = false; + _data->gen.b_cdtext_error = false; + + if (NULL == psz_orig_source) { + psz_source = cdio_get_default_device_aix(); + if (NULL == psz_source) return NULL; + _set_arg_aix(_data, "source", psz_source); + free(psz_source); + } else { + if (cdio_is_device_generic(psz_orig_source)) + _set_arg_aix(_data, "source", psz_orig_source); + else { + /* The below would be okay if all device drivers worked this way. */ +#if 0 + cdio_info ("source %s is not a device", psz_orig_source); +#endif + return NULL; + } + } + + ret = cdio_new ( (void *) _data, &_funcs ); + ret->driver_id = DRIVER_AIX; + + if (ret == NULL) return NULL; + + if (init_aix(_data)) + return ret; + else { + cdio_generic_free (_data); + return NULL; + } + +#else + return NULL; +#endif /* HAVE_AIX_CDROM */ + +} + +bool +cdio_have_aix (void) +{ +#ifdef HAVE_AIX_CDROM + return true; +#else + return false; +#endif /* HAVE_AIX_CDROM */ +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/driver/audio.c b/lib/driver/audio.c new file mode 100644 index 00000000..d8b19a12 --- /dev/null +++ b/lib/driver/audio.c @@ -0,0 +1,188 @@ +/* + $Id: audio.c,v 1.9 2008/04/22 15:29:11 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/*! Audio (via line output) related routines. */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include "cdio_private.h" + +/* Return the number of seconds (discarding frame portion) of an MSF */ +uint32_t +cdio_audio_get_msf_seconds(msf_t *p_msf) +{ + return + cdio_from_bcd8(p_msf->m)*CDIO_CD_SECS_PER_MIN + cdio_from_bcd8(p_msf->s); +} + +/*! + Get volume of an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +driver_return_code_t +cdio_audio_get_volume (CdIo_t *p_cdio, /*out*/ cdio_audio_volume_t *p_volume) +{ + cdio_audio_volume_t temp_audio_volume; + + if (!p_cdio) return DRIVER_OP_UNINIT; + + if (!p_volume) p_volume = &temp_audio_volume; + if (p_cdio->op.audio_get_volume) { + return p_cdio->op.audio_get_volume (p_cdio->env, p_volume); + } else { + return DRIVER_OP_UNSUPPORTED; + } +} +/*! + Playing CD through analog output + + @param p_cdio the CD object to be acted upon. +*/ +driver_return_code_t +cdio_audio_pause (CdIo_t *p_cdio) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + + if (p_cdio->op.audio_pause) { + return p_cdio->op.audio_pause (p_cdio->env); + } else { + return DRIVER_OP_UNSUPPORTED; + } +} + +/*! + Playing CD through analog output at the given MSF. + + @param p_cdio the CD object to be acted upon. +*/ +driver_return_code_t +cdio_audio_play_msf (CdIo_t *p_cdio, msf_t *p_start_msf, msf_t *p_end_msf) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + + if (p_cdio->op.audio_play_msf) { + return p_cdio->op.audio_play_msf (p_cdio->env, p_start_msf, p_end_msf); + } else { + return DRIVER_OP_UNSUPPORTED; + } +} + +/*! + Playing CD through analog output + + @param p_cdio the CD object to be acted upon. +*/ +driver_return_code_t +cdio_audio_play_track_index (CdIo_t *p_cdio, cdio_track_index_t *p_track_index) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + + if (p_cdio->op.audio_play_track_index) { + return p_cdio->op.audio_play_track_index (p_cdio->env, p_track_index); + } else { + return DRIVER_OP_UNSUPPORTED; + } +} + +/*! + Get subchannel information. + + @param p_cdio the CD object to be acted upon. +*/ +driver_return_code_t +cdio_audio_read_subchannel (CdIo_t *p_cdio, cdio_subchannel_t *p_subchannel) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + + if (p_cdio->op.audio_read_subchannel) { + return p_cdio->op.audio_read_subchannel(p_cdio->env, p_subchannel); + } else { + return DRIVER_OP_UNSUPPORTED; + } +} + +/*! + Resume playing an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +driver_return_code_t +cdio_audio_resume (CdIo_t *p_cdio) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + + if (p_cdio->op.audio_resume) { + return p_cdio->op.audio_resume(p_cdio->env); + } else { + return DRIVER_OP_UNSUPPORTED; + } +} + +/*! + Set volume of an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +driver_return_code_t +cdio_audio_set_volume (CdIo_t *p_cdio, cdio_audio_volume_t *p_volume) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + + if (p_cdio->op.audio_set_volume) { + return p_cdio->op.audio_set_volume(p_cdio->env, p_volume); + } else { + return DRIVER_OP_UNSUPPORTED; + } +} + +/*! + Resume playing an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +driver_return_code_t +cdio_audio_stop (CdIo_t *p_cdio) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + + if (p_cdio->op.audio_stop) { + return p_cdio->op.audio_stop(p_cdio->env); + } else { + return DRIVER_OP_UNSUPPORTED; + } +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/driver/bsdi.c b/lib/driver/bsdi.c new file mode 100644 index 00000000..cf06ceda --- /dev/null +++ b/lib/driver/bsdi.c @@ -0,0 +1,1079 @@ +/* + $Id: bsdi.c,v 1.15 2008/04/22 15:29:11 karl Exp $ + + Copyright (C) 2002, 2003, 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* This file contains BSDI-specific code and implements low-level + control of the CD drive. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: bsdi.c,v 1.15 2008/04/22 15:29:11 karl Exp $"; + +#include +#include +#include +#include "cdio_assert.h" +#include "cdio_private.h" + +#define DEFAULT_CDIO_DEVICE "/dev/rsr0c" +#include + +#ifdef HAVE_BSDI_CDROM + +#include +#include +#include +#include +#include + +/*#define USE_ETC_FSTAB*/ +#ifdef USE_ETC_FSTAB +#include +#endif + +#include +#include +#include +#include +#include +#include +#include "cdtext_private.h" + +#include +/* This function is in the man page but seems to be missing from the + above include (on Steve Schultz BSDI box). */ +extern int cdpause(struct cdinfo *cdinfo, int pause_resume); + +typedef enum { + _AM_NONE, + _AM_IOCTL, +} access_mode_t; + +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + + access_mode_t access_mode; + + /* Some of the more OS specific things. */ + /* Track information */ + struct cdrom_tochdr tochdr; + struct cdrom_tocentry tocent[CDIO_CD_MAX_TRACKS+1]; + struct cdinfo * p_cdinfo; + +} _img_private_t; + +/* Define the Cdrom Generic Command structure */ +typedef struct cgc +{ + mmc_cdb_t cdb; + u_char *buf; + int buflen; + int rw; + unsigned int timeout; + scsi_user_sense_t *sus; +} cgc_t; + + +static bool get_track_msf_bsdi(void *p_user_data, track_t i_track, msf_t *msf); +static lsn_t get_disc_last_lsn_bsdi (void *p_user_data); + +/* + This code adapted from Steven M. Schultz's libdvd +*/ +static driver_return_code_t +run_mmc_cmd_bsdi(void *p_user_data, unsigned int i_timeout_ms, + unsigned int i_cdb, const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + int i_status, i_asc; + struct scsi_user_cdb suc; + struct scsi_sense *sp; + + again: + suc.suc_flags = SCSI_MMC_DATA_READ == e_direction ? + SUC_READ : SUC_WRITE; + suc.suc_cdblen = i_cdb; + memcpy(suc.suc_cdb, p_cdb, i_cdb); + suc.suc_data = p_buf; + suc.suc_datalen = i_buf; + suc.suc_timeout = msecs2secs(i_timeout_ms); + if (ioctl(p_env->gen.fd, SCSIRAWCDB, &suc) == -1) + return(errno); + i_status = suc.suc_sus.sus_status; + +#if 0 + /* + * If the device returns a scsi sense error and debugging is enabled print + * some hopefully useful information on stderr. + */ + if (i_status && debug) + { + unsigned char *cp; + int i; + cp = suc.suc_sus.sus_sense; + fprintf(stderr,"i_status = %x cdb =", + i_status); + for (i = 0; i < cdblen; i++) + fprintf(stderr, " %x", cgc->cdb[i]); + fprintf(stderr, "\nsense ="); + for (i = 0; i < 16; i++) + fprintf(stderr, " %x", cp[i]); + fprintf(stderr, "\n"); + } +#endif + + /* + * HACK! Some drives return a silly "medium changed" on the first + * command AND a non-zero i_status which gets turned into a fatal + * (EIO) error even though the operation was a success. Retrying + * the operation clears the media changed status and gets the + * answer. */ + + sp = (struct scsi_sense *)&suc.suc_sus.sus_sense; + i_asc = XSENSE_ASC(sp); + if (i_status == STS_CHECKCOND && i_asc == 0x28) + goto again; +#if 0 + if (cgc->sus) + memcpy(cgc->sus, &suc.suc_sus, sizeof (struct scsi_user_sense)); +#endif + + return(i_status); +} + + + +/* Check a drive to see if it is a CD-ROM + Return 1 if a CD-ROM. 0 if it exists but isn't a CD-ROM drive + and -1 if no device exists . +*/ +static bool +cdio_is_cdrom(char *drive, char *mnttype) +{ + bool is_cd=false; + int cdfd; + struct cdrom_tochdr tochdr; + + /* If it doesn't exist, return -1 */ + if ( !cdio_is_device_quiet_generic(drive) ) { + return(false); + } + + /* If it does exist, verify that it's an available CD-ROM */ + cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0); + + /* Should we want to test the condition in more detail: + ENOENT is the error for /dev/xxxxx does not exist; + ENODEV means there's no drive present. */ + + if ( cdfd >= 0 ) { + if ( ioctl(cdfd, CDROMREADTOCHDR, &tochdr) != -1 ) { + is_cd = true; + } + close(cdfd); + } + /* Even if we can't read it, it might be mounted */ + else if ( mnttype && (strcmp(mnttype, "cd9660") == 0) ) { + is_cd = true; + } + + return(is_cd); +} + +/*! + Initialize CD device. + */ +static bool +_cdio_init (_img_private_t *p_env) +{ + if (p_env->gen.init) { + cdio_warn ("init called more than once"); + return false; + } + + p_env->gen.fd = open (p_env->gen.source_name, O_RDONLY|O_NONBLOCK, 0); + + if (p_env->gen.fd < 0) + { + cdio_warn ("open (%s): %s", p_env->gen.source_name, strerror (errno)); + return false; + } + + p_env->gen.init = true; + p_env->gen.toc_init = false; + return true; +} + +/*! + Get the volume of an audio CD. + + @param p_cdio the CD object to be acted upon. +*/ +#if 0 +static driver_return_code_t +audio_get_volume_bsdi (void *p_user_data, + /*out*/ cdio_audio_volume_t *p_volume) +{ + + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDROMVOLREAD, p_volume); +} +#endif + +/*! + Pause playing CD through analog output + + @param p_cdio the CD object to be acted upon. +*/ +static driver_return_code_t +audio_pause_bsdi (void *p_user_data) +{ + + _img_private_t *p_env = p_user_data; + if (!p_env->p_cdinfo) { + p_env->p_cdinfo = cdopen(p_env->gen.source_name); + if (!p_env->p_cdinfo) return DRIVER_OP_ERROR; + } + + return cdpause(p_env->p_cdinfo, 0); +} + +/*! + Playing starting at given MSF through analog output + + @param p_cdio the CD object to be acted upon. +*/ +static driver_return_code_t +audio_play_msf_bsdi (void *p_user_data, msf_t *p_start_msf, msf_t *p_end_msf) +{ + + _img_private_t *p_env = p_user_data; + lsn_t i_start_lsn = cdio_msf_to_lba(p_start_msf); + lsn_t i_end_lsn = cdio_msf_to_lba(p_end_msf); + + if (!p_env->p_cdinfo) { + p_env->p_cdinfo = cdopen(p_env->gen.source_name); + if (!p_env->p_cdinfo) return DRIVER_OP_ERROR; + } + return cdplay(p_env->p_cdinfo, i_start_lsn, i_end_lsn); +} + +/*! + Playing CD through analog output at the desired track and index + + @param p_cdio the CD object to be acted upon. + @param p_track_index location to start/end. +*/ +static driver_return_code_t +audio_play_track_index_bsdi (void *p_user_data, + cdio_track_index_t *p_track_index) +{ + _img_private_t *p_env = p_user_data; + msf_t start_msf; + msf_t end_msf; + lsn_t i_start_lsn = cdio_msf_to_lsn(&start_msf); + lsn_t i_end_lsn = cdio_msf_to_lsn(&end_msf); + + if (!p_env->p_cdinfo) { + p_env->p_cdinfo = cdopen(p_env->gen.source_name); + if (!p_env->p_cdinfo) return DRIVER_OP_ERROR; + } + + get_track_msf_bsdi(p_user_data, p_track_index->i_start_track, &start_msf); + get_track_msf_bsdi(p_user_data, p_track_index->i_end_track, &end_msf); + + return cdplay(p_env->p_cdinfo, i_start_lsn, i_end_lsn); +} + +/*! + Read Audio Subchannel information + + @param p_cdio the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_read_subchannel_bsdi (void *p_user_data, cdio_subchannel_t *p_subchannel) +{ + int i_rc; + _img_private_t *p_env = p_user_data; + struct cdstatus cdstat; + + if (!p_env->p_cdinfo) { + p_env->p_cdinfo = cdopen(p_env->gen.source_name); + if (!p_env->p_cdinfo) return DRIVER_OP_ERROR; + } + + i_rc = cdstatus(p_env->p_cdinfo, &cdstat); + if (0 == i_rc) { + msf_t msf; + p_subchannel->control = cdstat.control; + p_subchannel->track = cdstat.track_num; + p_subchannel->index = cdstat.index_num; + + cdio_lba_to_msf(cdstat.abs_frame, &msf); + p_subchannel->abs_addr.m = cdio_to_bcd8(msf.m); + p_subchannel->abs_addr.s = cdio_to_bcd8(msf.s); + p_subchannel->abs_addr.f = cdio_to_bcd8(msf.f); + + cdio_lsn_to_msf(cdstat.rel_frame, &msf); + p_subchannel->rel_addr.m = cdio_to_bcd8(msf.m); + p_subchannel->rel_addr.s = cdio_to_bcd8(msf.s); + p_subchannel->rel_addr.f = cdio_to_bcd8(msf.f); + + switch(cdstat.state) { + case cdstate_unknown: + p_subchannel->audio_status = CDIO_MMC_READ_SUB_ST_NO_STATUS; + break; + case cdstate_stopped: + p_subchannel->audio_status = CDIO_MMC_READ_SUB_ST_COMPLETED; + break; + case cdstate_playing: + p_subchannel->audio_status = CDIO_MMC_READ_SUB_ST_PLAY; + break; + case cdstate_paused: + p_subchannel->audio_status = CDIO_MMC_READ_SUB_ST_PAUSED; + break; + default: + p_subchannel->audio_status = CDIO_MMC_READ_SUB_ST_INVALID; + } + } + + return i_rc; +} + +/*! + Resume playing an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_resume_bsdi (void *p_user_data) +{ + + _img_private_t *p_env = p_user_data; + + if (!p_env->p_cdinfo) { + p_env->p_cdinfo = cdopen(p_env->gen.source_name); + if (!p_env->p_cdinfo) return DRIVER_OP_ERROR; + } + + return cdpause(p_env->p_cdinfo, 1); +} + +/*! + + Set the volume of an audio CD. We use only the first (port 0) volume + level. + + @param p_cdio the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_set_volume_bsdi (void *p_user_data, + cdio_audio_volume_t *p_volume) +{ + _img_private_t *p_env = p_user_data; + + if (!p_env->p_cdinfo) { + p_env->p_cdinfo = cdopen(p_env->gen.source_name); + if (!p_env->p_cdinfo) return DRIVER_OP_ERROR; + } + + /* Convert volume from 0..255 into 0..100. */ + return cdvolume(p_env->p_cdinfo, (p_volume->level[0]*100+128) / 256); +} + +/*! + Stop playing an audio CD. + + @param p_user_data the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_stop_bsdi (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + if (!p_env->p_cdinfo) { + p_env->p_cdinfo = cdopen(p_env->gen.source_name); + if (!p_env->p_cdinfo) return DRIVER_OP_ERROR; + } + return cdstop(p_env->p_cdinfo); +} + +/* Read audio sectors +*/ +static driver_return_code_t +_read_audio_sectors_bsdi (void *user_data, void *data, lsn_t lsn, + uint32_t i_blocks) +{ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + struct cdrom_msf *msf = (struct cdrom_msf *) &buf; + msf_t _msf; + + _img_private_t *p_env = user_data; + + cdio_lba_to_msf (cdio_lsn_to_lba(lsn), &_msf); + msf->cdmsf_min0 = cdio_from_bcd8(_msf.m); + msf->cdmsf_sec0 = cdio_from_bcd8(_msf.s); + msf->cdmsf_frame0 = cdio_from_bcd8(_msf.f); + + if (p_env->gen.ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged < 75 + || (p_env->gen.ioctls_debugged < (30 * 75) + && p_env->gen.ioctls_debugged % 75 == 0) + || p_env->gen.ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %2.2d:%2.2d:%2.2d", + msf->cdmsf_min0, msf->cdmsf_sec0, msf->cdmsf_frame0); + + p_env->gen.ioctls_debugged++; + + switch (p_env->access_mode) { + case _AM_NONE: + cdio_warn ("no way to read audio"); + return 1; + break; + + case _AM_IOCTL: { + unsigned int i; + for (i=0; i < i_blocks; i++) { + if (ioctl (p_env->gen.fd, CDROMREADRAW, &buf) == -1) { + perror ("ioctl()"); + return 1; + /* exit (EXIT_FAILURE); */ + } + memcpy (((char *)data) + (CDIO_CD_FRAMESIZE_RAW * i), buf, + CDIO_CD_FRAMESIZE_RAW); + } + break; + } + } + + return DRIVER_OP_SUCCESS; +} + +/*! + Reads a single mode1 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static driver_return_code_t +_read_mode1_sector_bsdi (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + +#if FIXED + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + do something here. +#else + return cdio_generic_read_form1_sector(user_data, data, lsn); +#endif +} + +/*! + Reads i_blocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static driver_return_code_t +_read_mode1_sectors_bsdi (void *p_user_data, void *p_data, lsn_t lsn, + bool b_form2, uint32_t i_blocks) +{ + _img_private_t *p_env = p_user_data; + unsigned int i; + int retval; + uint16_t blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < i_blocks; i++) { + if ( (retval = _read_mode1_sector_bsdi (p_env, + ((char *)p_data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static driver_return_code_t +_read_mode2_sector_bsdi (void *p_user_data, void *p_data, lsn_t lsn, + bool b_form2) +{ + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + struct cdrom_msf *msf = (struct cdrom_msf *) &buf; + msf_t _msf; + + _img_private_t *p_env = p_user_data; + + cdio_lba_to_msf (cdio_lsn_to_lba(lsn), &_msf); + msf->cdmsf_min0 = cdio_from_bcd8(_msf.m); + msf->cdmsf_sec0 = cdio_from_bcd8(_msf.s); + msf->cdmsf_frame0 = cdio_from_bcd8(_msf.f); + + if (p_env->gen.ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged < 75 + || (p_env->gen.ioctls_debugged < (30 * 75) + && p_env->gen.ioctls_debugged % 75 == 0) + || p_env->gen.ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %2.2d:%2.2d:%2.2d", + msf->cdmsf_min0, msf->cdmsf_sec0, msf->cdmsf_frame0); + + p_env->gen.ioctls_debugged++; + + switch (p_env->access_mode) + { + case _AM_NONE: + cdio_warn ("no way to read mode2"); + return 1; + break; + + case _AM_IOCTL: + if (ioctl (p_env->gen.fd, CDROMREADMODE2, &buf) == -1) + { + perror ("ioctl()"); + return 1; + /* exit (EXIT_FAILURE); */ + } + break; + } + + if (b_form2) + memcpy (p_data, buf, M2RAW_SECTOR_SIZE); + else + memcpy (((char *)p_data), buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE); + + return DRIVER_OP_SUCCESS; +} + +/*! + Reads i_blocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static driver_return_code_t +_read_mode2_sectors_bsdi (void *user_data, void *data, lsn_t lsn, + bool b_form2, uint32_t i_blocks) +{ + _img_private_t *p_env = user_data; + unsigned int i; + unsigned int i_blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + /* For each frame, pick out the data part we need */ + for (i = 0; i < i_blocks; i++) { + int retval = _read_mode2_sector_bsdi(p_env, + ((char *)data) + + (i_blocksize * i), + lsn + i, b_form2); + if (retval) return retval; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static lsn_t +get_disc_last_lsn_bsdi (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + struct cdrom_tocentry tocent; + uint32_t size; + + tocent.cdte_track = CDIO_CDROM_LEADOUT_TRACK; + tocent.cdte_format = CDROM_LBA; + if (ioctl (p_env->gen.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 driver_return_code_t +_set_arg_bsdi (void *user_data, const char key[], const char value[]) +{ + _img_private_t *p_env = user_data; + + if (!strcmp (key, "source")) + { + if (!value) return DRIVER_OP_ERROR; + free (p_env->gen.source_name); + p_env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + if (!strcmp(value, "IOCTL")) + p_env->access_mode = _AM_IOCTL; + else + cdio_warn ("unknown access type: %s. ignored.", value); + } + else return DRIVER_OP_ERROR; + return DRIVER_OP_SUCCESS; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return false if successful or true if an error. +*/ +static bool +read_toc_bsdi (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + int i; + + /* read TOC header */ + if ( ioctl(p_env->gen.fd, CDROMREADTOCHDR, &p_env->tochdr) == -1 ) { + cdio_warn("%s: %s\n", + "error in ioctl CDROMREADTOCHDR", strerror(errno)); + return false; + } + + p_env->gen.i_first_track = p_env->tochdr.cdth_trk0; + p_env->gen.i_tracks = p_env->tochdr.cdth_trk1; + + /* read individual tracks */ + for (i= p_env->gen.i_first_track; i<=p_env->gen.i_tracks; i++) { + struct cdrom_tocentry *p_toc = + &(p_env->tocent[i-p_env->gen.i_first_track]); + + p_toc->cdte_track = i; + p_toc->cdte_format = CDROM_MSF; + if (ioctl(p_env->gen.fd, CDROMREADTOCENTRY, p_toc) == -1) { + cdio_warn("%s %d: %s\n", + "error in ioctl CDROMREADTOCENTRY for track", + i, strerror(errno)); + return false; + } + + set_track_flags(&(p_env->gen.track_flags[i]), p_toc->cdte_ctrl); + + /**** + struct cdrom_msf0 *msf= &p_env->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 */ + p_env->tocent[p_env->gen.i_tracks].cdte_track = CDIO_CDROM_LEADOUT_TRACK; + p_env->tocent[p_env->gen.i_tracks].cdte_format = CDROM_MSF; + + if (ioctl(p_env->gen.fd, CDROMREADTOCENTRY, + &p_env->tocent[p_env->gen.i_tracks]) == -1 ) { + cdio_warn("%s: %s\n", + "error in ioctl CDROMREADTOCENTRY for lead-out", + strerror(errno)); + return false; + } + + /* + struct cdrom_msf0 *msf= &p_env->tocent[p_env->gen.i_tracks].cdte_addr.msf; + + fprintf (stdout, "--- track# %d (msf %2.2x:%2.2x:%2.2x)\n", + i, msf->minute, msf->second, msf->frame); + */ + + p_env->gen.toc_init = true; + return true; +} + +/*! + Eject media in CD drive. If successful, as a side effect we + also free obj. + */ +static driver_return_code_t +eject_media_bsdi (void *p_user_data) { + + _img_private_t *p_env = p_user_data; + int ret=DRIVER_OP_ERROR; + int fd; + + close(p_env->gen.fd); + p_env->gen.fd = -1; + if ((fd = open (p_env->gen.source_name, O_RDONLY|O_NONBLOCK)) > -1) { + if((ret = ioctl(fd, CDROMEJECT, 0)) != 0) { + cdio_warn("ioctl CDROMEJECT failed: %s\n", strerror(errno)); + } + close(fd); + } + return ret; +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +_get_arg_bsdi (void *user_data, const char key[]) +{ + _img_private_t *p_env = user_data; + + if (!strcmp (key, "source")) { + return p_env->gen.source_name; + } else if (!strcmp (key, "access-mode")) { + switch (p_env->access_mode) { + case _AM_IOCTL: + return "ioctl"; + case _AM_NONE: + return "no access method"; + } + } + return NULL; +} + +/*! + Return the media catalog number MCN. + Note: string is malloc'd so caller should free() then returned + string when done with it. + */ +static char * +_get_mcn_bsdi (const void *p_user_data) { + + struct cdrom_mcn mcn; + const _img_private_t *p_env = p_user_data; + if (ioctl(p_env->gen.fd, CDROM_GET_MCN, &mcn) != 0) + return NULL; + return strdup(mcn.medium_catalog_number); +} + +/*! + Get format of track. +*/ +static track_format_t +get_track_format_bsdi(void *user_data, track_t i_track) +{ + _img_private_t *p_env = user_data; + + if (!p_env->gen.toc_init) read_toc_bsdi (p_env) ; + + if (i_track > p_env->gen.i_tracks || i_track == 0) + return TRACK_FORMAT_ERROR; + + i_track -= p_env->gen.i_first_track; + + /* This is pretty much copied from the "badly broken" cdrom_count_tracks + in linux/cdrom.c. + */ + if (p_env->tocent[i_track].cdte_ctrl & CDROM_DATA_TRACK) { + if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_CDI_TRACK) + return TRACK_FORMAT_CDI; + else if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_XA_TRACK) + 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 +_get_track_green_bsdi(void *user_data, track_t i_track) +{ + _img_private_t *p_env = user_data; + + if (!p_env->gen.toc_init) read_toc_bsdi (p_env) ; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = p_env->gen.i_tracks+1; + + if (i_track > p_env->gen.i_tracks+1 || i_track == 0) + return false; + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cdinfo for a while. + */ + return ((p_env->tocent[i_track-1].cdte_ctrl & 2) != 0); +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + i_track in obj. Track numbers start at 1. + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +static bool +get_track_msf_bsdi(void *user_data, track_t i_track, msf_t *msf) +{ + _img_private_t *p_env = user_data; + + if (NULL == msf) return false; + + if (!p_env->gen.toc_init) read_toc_bsdi (p_env) ; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = p_env->gen.i_tracks+1; + + if (i_track > p_env->gen.i_tracks+1 || i_track == 0) { + return false; + } + + i_track -= p_env->gen.i_first_track; + + { + struct cdrom_msf0 *msf0= &p_env->tocent[i_track].cdte_addr.msf; + msf->m = cdio_to_bcd8(msf0->minute); + msf->s = cdio_to_bcd8(msf0->second); + msf->f = cdio_to_bcd8(msf0->frame); + return true; + } +} + +#endif /* HAVE_BSDI_CDROM */ + +/*! + Close tray on CD-ROM. + + @param p_user_data the CD object to be acted upon. + +*/ +driver_return_code_t +close_tray_bsdi (const char *psz_device) +{ +#ifdef HAVE_BSDI_CDROM + int fd = open(psz_device, O_RDONLY | O_NONBLOCK, 0); + + if (fd < 0) return DRIVER_OP_ERROR; + else { + int i_rc = cdrom_tray_move(fd, 0); + close(fd); + return (i_rc > -1) ? DRIVER_OP_SUCCESS : DRIVER_OP_ERROR; + } +#else + return DRIVER_OP_NO_DRIVER; +#endif /*HAVE_BSDI_CDROM*/ +} + +/*! + Return an array of strings giving possible CD devices. + */ +char ** +cdio_get_devices_bsdi (void) +{ +#ifndef HAVE_BSDI_CDROM + return NULL; +#else + char drive[40]; + char **drives = NULL; + unsigned int num_drives=0; + bool exists=true; + char c; + + /* Scan the system for CD-ROM drives. + */ + +#ifdef USE_ETC_FSTAB + + struct fstab *fs; + setfsent(); + + /* Check what's in /etc/fstab... */ + while ( (fs = getfsent()) ) + { + if (strncmp(fs->fs_spec, "/dev/sr", 7)) + cdio_add_device_list(&drives, fs->fs_spec, &num_drives); + } + +#endif + + /* Scan the system for CD-ROM drives. + Not always 100% reliable, so use the USE_MNTENT code above first. + */ + for ( c='0'; exists && c <='9'; c++ ) { + sprintf(drive, "/dev/rsr%cc", c); + exists = cdio_is_cdrom(drive, NULL); + if ( exists ) { + cdio_add_device_list(&drives, drive, &num_drives); + } + } + cdio_add_device_list(&drives, NULL, &num_drives); + return drives; +#endif /*HAVE_BSDI_CDROM*/ +} + +/*! + Return a string containing the default CD device if none is specified. + */ +char * +cdio_get_default_device_bsdi(void) +{ + return strdup(DEFAULT_CDIO_DEVICE); +} + +/*! + 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_t * +cdio_open_am_bsdi (const char *psz_source_name, const char *psz_access_mode) +{ + if (psz_access_mode != NULL) + cdio_warn ("there is only one access mode for bsdi. Arg %s ignored", + psz_access_mode); + return cdio_open_bsdi(psz_source_name); +} + + +/*! + 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_t * +cdio_open_bsdi (const char *psz_orig_source) +{ + +#ifdef HAVE_BSDI_CDROM + CdIo_t *ret; + _img_private_t *_data; + char *psz_source; + + cdio_funcs_t _funcs = { + .audio_pause = audio_pause_bsdi, + .audio_play_msf = audio_play_msf_bsdi, + .audio_play_track_index= audio_play_track_index_bsdi, +#if USE_MMC_SUBCHANNEL + .audio_read_subchannel = audio_read_subchannel_mmc, +#else + .audio_read_subchannel = audio_read_subchannel_bsdi, +#endif + .audio_resume = audio_resume_bsdi, + .audio_set_volume = audio_set_volume_bsdi, + .audio_stop = audio_stop_bsdi, + .eject_media = eject_media_bsdi, + .free = cdio_generic_free, + .get_arg = _get_arg_bsdi, + .get_cdtext = get_cdtext_generic, + .get_default_device = cdio_get_default_device_bsdi, + .get_devices = cdio_get_devices_bsdi, + .get_drive_cap = get_drive_cap_mmc, + .get_disc_last_lsn = get_disc_last_lsn_bsdi, + .get_discmode = get_discmode_generic, + .get_first_track_num = get_first_track_num_generic, + .get_hwinfo = NULL, + .get_media_changed = get_media_changed_mmc, + .get_mcn = _get_mcn_bsdi, + .get_num_tracks = get_num_tracks_generic, + .get_track_channels = get_track_channels_generic, + .get_track_copy_permit = get_track_copy_permit_generic, + .get_track_format = get_track_format_bsdi, + .get_track_green = _get_track_green_bsdi, + .get_track_lba = NULL, /* This could be implemented if need be. */ + .get_track_preemphasis = get_track_preemphasis_generic, + .get_track_msf = get_track_msf_bsdi, + .lseek = cdio_generic_lseek, + .read = cdio_generic_read, + .read_audio_sectors = _read_audio_sectors_bsdi, + .read_data_sectors = read_data_sectors_mmc, + .read_mode1_sector = _read_mode1_sector_bsdi, + .read_mode1_sectors = _read_mode1_sectors_bsdi, + .read_mode2_sector = _read_mode2_sector_bsdi, + .read_mode2_sectors = _read_mode2_sectors_bsdi, + .read_toc = read_toc_bsdi, + .run_mmc_cmd = run_mmc_cmd_bsdi, + .set_arg = _set_arg_bsdi, + }; + + _data = calloc (1, sizeof (_img_private_t)); + _data->access_mode = _AM_IOCTL; + _data->gen.init = false; + _data->gen.fd = -1; + _data->gen.toc_init = false; + _data->gen.b_cdtext_init = false; + _data->gen.b_cdtext_error = false; + + if (NULL == psz_orig_source) { + psz_source=cdio_get_default_device_bsdi(); + if (NULL == psz_source) return NULL; + _set_arg_bsdi(_data, "source", psz_source); + free(psz_source); + } else { + if (cdio_is_device_generic(psz_orig_source)) + _set_arg_bsdi(_data, "source", psz_orig_source); + else { + /* The below would be okay if all device drivers worked this way. */ +#if 0 + cdio_info ("source %s is not a device", psz_orig_source); +#endif + free(_data); + return NULL; + } + } + + ret = cdio_new ( (void *) _data, &_funcs); + if (ret == NULL) return NULL; + + ret->driver_id = DRIVER_BSDI; + + if (_cdio_init(_data)) + return ret; + else { + cdio_generic_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/driver/cd_types.c b/lib/driver/cd_types.c new file mode 100644 index 00000000..082dd8d1 --- /dev/null +++ b/lib/driver/cd_types.c @@ -0,0 +1,360 @@ +/* + $Id: cd_types.c,v 1.9 2008/06/16 19:39:30 flameeyes Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* + This tries to determine what kind of CD-image or filesystems on a + track we've got. +*/ +#include "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#include +#include +#include +#include +#include + +/* +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. + +*/ + +/** The below variables are trickery to force enum symbol values to be + recorded in debug symbol tables. They are used to allow one to refer + to the enumeration value names in the typedefs above in a debugger + and debugger expressions. +*/ +cdio_fs_cap_t debug_cdio_fs_cap; +cdio_fs_t debug_cdio_fs; + +static char buffer[6][CDIO_CD_FRAMESIZE_RAW]; /* for CD-Data */ + +/* Some interesting sector numbers stored in the above buffer. */ +#define ISO_SUPERBLOCK_SECTOR 16 /* buffer[0] */ +#define UFS_SUPERBLOCK_SECTOR 4 /* buffer[2] */ +#define BOOT_SECTOR 17 /* buffer[3] */ +#define VCD_INFO_SECTOR 150 /* buffer[4] */ +#define XISO_SECTOR 32 /* buffer[4] */ +#define UDFX_SECTOR 32 /* buffer[4] */ +#define UDF_ANCHOR_SECTOR 256 /* buffer[5] */ + + +typedef struct signature +{ + unsigned int buf_num; + unsigned int offset; + char sig_str[60]; + char description[60]; +} signature_t; + +static const signature_t sigs[] = + { +/*buffer[x] off look for description */ + {0, 0, "MICROSOFT*XBOX*MEDIA", "XBOX CD"}, + {0, 1, "BEA01", "UDF"}, + {0, 1, ISO_STANDARD_ID, "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, ISO_XA_MARKER_OFFSET, ISO_XA_MARKER_STRING, "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"}, + {4, 0, "SUPERVCD", "SVCD or Chaoji VCD"} + }; + + +/* The below index into the above sigs array. Make sure things match. */ +#define INDEX_XISO 0 /* Microsoft X-BOX filesystem */ +#define INDEX_UDF 1 +#define INDEX_ISOFS 2 +#define INDEX_CD_I 3 +#define INDEX_CDTV 4 +#define INDEX_CD_RTOS 5 +#define INDEX_HS 6 +#define INDEX_BRIDGE 7 +#define INDEX_XA 8 +#define INDEX_PHOTO_CD 9 +#define INDEX_EXT2 10 +#define INDEX_UFS 11 +#define INDEX_BOOTABLE 12 +#define INDEX_VIDEO_CD 13 /* Video CD */ +#define INDEX_SVCD 14 /* CVD *or* SVCD */ + + +/* + Read a particular block into the global array to be used for further + analysis later. +*/ +static driver_return_code_t +_cdio_read_block(const CdIo_t *p_cdio, int superblock, uint32_t offset, + uint8_t bufnum, track_t i_track) +{ + unsigned int track_sec_count = cdio_get_track_sec_count(p_cdio, i_track); + memset(buffer[bufnum], 0, CDIO_CD_FRAMESIZE); + + if ( track_sec_count < superblock) { + cdio_debug("reading block %u skipped track %d has only %u sectors\n", + superblock, i_track, track_sec_count); + return DRIVER_OP_ERROR; + } + + cdio_debug("about to read sector %lu\n", + (long unsigned int) offset+superblock); + + return cdio_read_data_sectors (p_cdio, buffer[bufnum], offset+superblock, + ISO_BLOCKSIZE, 1); +} + +/* + Return true if the previously read-in buffer contains a "signature" that + matches index "num". + */ +static bool +_cdio_is_it(int num) +{ + const signature_t *sigp=&sigs[num]; + int len=strlen(sigp->sig_str); + + /* TODO: check that num < largest sig. */ + return 0 == memcmp(&buffer[sigp->buf_num][sigp->offset], sigp->sig_str, len); +} + +static int +_cdio_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 +_cdio_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 +_cdio_is_joliet(void) +{ + return 2 == buffer[3][0] && buffer[3][88] == 0x25 && buffer[3][89] == 0x2f; +} + +static int +_cdio_is_UDF(void) +{ + return 2 == ((uint16_t)buffer[5][0] | ((uint16_t)buffer[5][1] << 8)); +} + +/* ISO 9660 volume space in M2F1_SECTOR_SIZE byte units */ +static int +_cdio_get_iso9660_fs_sec_count(void) +{ + return ((buffer[0][80] & 0xff) | + ((buffer[0][81] & 0xff) << 8) | + ((buffer[0][82] & 0xff) << 16) | + ((buffer[0][83] & 0xff) << 24)); +} + +static int +_cdio_get_joliet_level( void ) +{ + switch (buffer[3][90]) { + case 0x40: return 1; + case 0x43: return 2; + case 0x45: return 3; + } + return 0; +} + +/* + Try to determine what kind of CD-image and/or filesystem we + have at track i_track. Return information about the CD image + is returned in cdio_analysis and the return value. +*/ +cdio_fs_anal_t +cdio_guess_cd_type(const CdIo_t *p_cdio, int start_session, track_t i_track, + /*out*/ cdio_iso_analysis_t *iso_analysis) +{ + int ret = CDIO_FS_UNKNOWN; + bool sector0_read_ok; + + if (TRACK_FORMAT_AUDIO == cdio_get_track_format(p_cdio, i_track)) + return CDIO_FS_AUDIO; + + if ( DRIVER_OP_SUCCESS != + _cdio_read_block(p_cdio, ISO_PVD_SECTOR, start_session, 0, i_track) ) + return CDIO_FS_UNKNOWN; + + if ( _cdio_is_it(INDEX_XISO) ) + return CDIO_FS_ANAL_XISO; + + if ( DRIVER_OP_SUCCESS != _cdio_read_block(p_cdio, ISO_SUPERBLOCK_SECTOR, + start_session, 0, i_track) ) + return ret; + + if ( _cdio_is_it(INDEX_UDF) ) { + /* Detect UDF version + Test if we have a valid version of UDF the xbox can read natively */ + if (_cdio_read_block(p_cdio, 35, start_session, 5, i_track) < 0) + return CDIO_FS_UNKNOWN; + + iso_analysis->UDFVerMinor=(unsigned int)buffer[5][240]; + iso_analysis->UDFVerMajor=(unsigned int)buffer[5][241]; + /* Read disc label */ + if (_cdio_read_block(p_cdio, 32, start_session, 5, i_track) < 0) + return CDIO_FS_UDF; + + strncpy(iso_analysis->iso_label, buffer[5]+25, 33); + iso_analysis->iso_label[32] = '\0'; + return CDIO_FS_UDF; + } + + /* We have something that smells of a filesystem. */ + if (_cdio_is_it(INDEX_CD_I) && _cdio_is_it(INDEX_CD_RTOS) + && !_cdio_is_it(INDEX_BRIDGE) && !_cdio_is_it(INDEX_XA)) { + return (CDIO_FS_INTERACTIVE | CDIO_FS_ANAL_ISO9660_ANY); + } else { + /* read sector 0 ONLY, when NO greenbook CD-I !!!! */ + + sector0_read_ok = + _cdio_read_block(p_cdio, 0, start_session, 1, i_track) == 0; + + if (_cdio_is_it(INDEX_HS)) + ret |= CDIO_FS_HIGH_SIERRA; + else if (_cdio_is_it(INDEX_ISOFS)) { + if (_cdio_is_it(INDEX_CD_RTOS) && _cdio_is_it(INDEX_BRIDGE)) + ret = (CDIO_FS_ISO_9660_INTERACTIVE | CDIO_FS_ANAL_ISO9660_ANY); + else if (_cdio_is_hfs()) + ret = CDIO_FS_ISO_HFS; + else + ret = (CDIO_FS_ISO_9660 | CDIO_FS_ANAL_ISO9660_ANY); + iso_analysis->isofs_size = _cdio_get_iso9660_fs_sec_count(); + strncpy(iso_analysis->iso_label, buffer[0]+40,33); + iso_analysis->iso_label[32] = '\0'; + + if ( _cdio_read_block(p_cdio, UDF_ANCHOR_SECTOR, start_session, 5, + i_track) < 0) + return ret; + + /* Maybe there is an UDF anchor in IOS session + so its ISO/UDF session and we prefere UDF */ + if ( _cdio_is_UDF() ) { + /* Detect UDF version. + Test if we have a valid version of UDF the xbox can read natively */ + if ( _cdio_read_block(p_cdio, 35, start_session, 5, i_track) < 0) + return ret; + + iso_analysis->UDFVerMinor=(unsigned int)buffer[5][240]; + iso_analysis->UDFVerMajor=(unsigned int)buffer[5][241]; +#if 0 + /* We are using ISO/UDF cd's as iso, + no need to get UDF disc label */ + if (_cdio_read_block(p_cdio, 32, start_session, 5, i_track) < 0) + return ret; + stnrcpy(iso_analysis->iso_label, buffer[5]+25, 33); + iso_analysis->iso_label[32] = '\0'; +#endif + ret=CDIO_FS_ISO_UDF; + } + +#if 0 + if (_cdio_is_rockridge()) + ret |= CDIO_FS_ANAL_ROCKRIDGE; +#endif + + if (_cdio_read_block(p_cdio, BOOT_SECTOR, start_session, 3, i_track) < 0) + return ret; + + if (_cdio_is_joliet()) { + iso_analysis->joliet_level = _cdio_get_joliet_level(); + ret |= (CDIO_FS_ANAL_JOLIET | CDIO_FS_ANAL_ISO9660_ANY); + } + if (_cdio_is_it(INDEX_BOOTABLE)) + ret |= CDIO_FS_ANAL_BOOTABLE; + + if ( _cdio_is_it(INDEX_XA) && _cdio_is_it(INDEX_ISOFS) + && !(sector0_read_ok && _cdio_is_it(INDEX_PHOTO_CD)) ) { + + if ( _cdio_read_block(p_cdio, VCD_INFO_SECTOR, start_session, 4, + i_track) < 0 ) + return ret; + + if (_cdio_is_it(INDEX_BRIDGE) && _cdio_is_it(INDEX_CD_RTOS)) { + ret |= CDIO_FS_ANAL_ISO9660_ANY; + if (_cdio_is_it(INDEX_VIDEO_CD)) ret |= CDIO_FS_ANAL_VIDEOCD; + else if (_cdio_is_it(INDEX_SVCD)) ret |= CDIO_FS_ANAL_SVCD; + } else if (_cdio_is_it(INDEX_SVCD)) ret |= CDIO_FS_ANAL_CVD; + + } + } + else if (_cdio_is_hfs()) ret |= CDIO_FS_HFS; + else if (sector0_read_ok && _cdio_is_it(INDEX_EXT2)) + ret |= (CDIO_FS_EXT2 | CDIO_FS_ANAL_ISO9660_ANY); + else if (_cdio_is_3do()) ret |= CDIO_FS_3DO; + else { + if ( _cdio_read_block(p_cdio, UFS_SUPERBLOCK_SECTOR, start_session, 2, + i_track) < 0 ) + return ret; + + if (sector0_read_ok && _cdio_is_it(INDEX_UFS)) + ret |= CDIO_FS_UFS; + else + ret |= CDIO_FS_UNKNOWN; + } + } + + /* other checks */ + if (_cdio_is_it(INDEX_XA)) + ret |= (CDIO_FS_ANAL_XA | CDIO_FS_ANAL_ISO9660_ANY); + if (_cdio_is_it(INDEX_PHOTO_CD)) + ret |= (CDIO_FS_ANAL_PHOTO_CD | CDIO_FS_ANAL_ISO9660_ANY); + if (_cdio_is_it(INDEX_CDTV)) + ret |= CDIO_FS_ANAL_CDTV; + return ret; +} diff --git a/lib/driver/cdio.c b/lib/driver/cdio.c new file mode 100644 index 00000000..f56bef29 --- /dev/null +++ b/lib/driver/cdio.c @@ -0,0 +1,112 @@ +/* + $Id: cdio.c,v 1.14 2008/04/22 15:29:11 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "cdio_assert.h" +#include +#include +#include "cdio_private.h" + +static const char _rcsid[] = "$Id: cdio.c,v 1.14 2008/04/22 15:29:11 karl Exp $"; + + +/*! + Return the value associatied with key. NULL is returned if obj is NULL + or "key" does not exist. + */ +const char * +cdio_get_arg (const CdIo *obj, const char key[]) +{ + if (obj == NULL) return NULL; + + if (obj->op.get_arg) { + return obj->op.get_arg (obj->env, key); + } else { + return NULL; + } +} + +/*! + Get cdtext information for a CdIo object . + + @param obj the CD object that may contain CD-TEXT information. + @return the CD-TEXT object or NULL if obj is NULL + or CD-TEXT information does not exist. +*/ +cdtext_t * +cdio_get_cdtext (CdIo *obj, track_t i_track) +{ + if (obj == NULL) return NULL; + + if (obj->op.get_cdtext) { + return obj->op.get_cdtext (obj->env, i_track); + } else { + return NULL; + } +} + +CdIo_t * +cdio_new (generic_img_private_t *p_env, cdio_funcs_t *p_funcs) +{ + CdIo_t *p_new_cdio = calloc(1, sizeof (CdIo_t)); + + if (NULL == p_new_cdio) return NULL; + + p_new_cdio->env = p_env; /* This is the private "environment" that + driver-dependent routines use. */ + p_new_cdio->op = *p_funcs; + p_env->cdio = p_new_cdio; /* A way for the driver-dependent routines + to access the higher-level general cdio + object. */ + return p_new_cdio; +} + +/*! + Set the arg "key" with "value" in the source device. +*/ +driver_return_code_t +cdio_set_arg (CdIo_t *p_cdio, const char key[], const char value[]) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + if (!p_cdio->op.set_arg) return DRIVER_OP_UNSUPPORTED; + if (!key) return DRIVER_OP_ERROR; + + return p_cdio->op.set_arg (p_cdio->env, key, value); +} + + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/driver/cdio_assert.h b/lib/driver/cdio_assert.h new file mode 100644 index 00000000..d3e8afa5 --- /dev/null +++ b/lib/driver/cdio_assert.h @@ -0,0 +1,59 @@ +/* + $Id: cdio_assert.h,v 1.2 2008/04/22 15:29:11 karl Exp $ + + Copyright (C) 2008 Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __CDIO_ASSERT_H__ +#define __CDIO_ASSERT_H__ + +#if defined(__GNUC__) + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#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/driver/cdio_private.h b/lib/driver/cdio_private.h new file mode 100644 index 00000000..0bad5a0e --- /dev/null +++ b/lib/driver/cdio_private.h @@ -0,0 +1,547 @@ +/* + $Id: cdio_private.h,v 1.37 2008/04/22 15:29:11 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Internal routines for CD I/O drivers. */ + + +#ifndef __CDIO_PRIVATE_H__ +#define __CDIO_PRIVATE_H__ + +#if defined(HAVE_CONFIG_H) && !defined(LIBCDIO_CONFIG_H) +# include "config.h" +#endif + +#include +#include +#include +#include "mmc_private.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* Opaque type */ + typedef struct _CdioDataSource CdioDataSource_t; + +#ifdef __cplusplus +} + +#endif /* __cplusplus */ + +#include "generic.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + typedef struct { + + /*! + Get volume of an audio CD. + + @param p_env the CD object to be acted upon. + + */ + driver_return_code_t (*audio_get_volume) + (void *p_env, /*out*/ cdio_audio_volume_t *p_volume); + + /*! + Pause playing CD through analog output + + @param p_env the CD object to be acted upon. + */ + driver_return_code_t (*audio_pause) (void *p_env); + + /*! + Playing CD through analog output + + @param p_env the CD object to be acted upon. + */ + driver_return_code_t (*audio_play_msf) ( void *p_env, + msf_t *p_start_msf, + msf_t *p_end_msf ); + + /*! + Playing CD through analog output + + @param p_env the CD object to be acted upon. + */ + driver_return_code_t (*audio_play_track_index) + ( void *p_env, cdio_track_index_t *p_track_index ); + + /*! + Get subchannel information. + + @param p_env the CD object to be acted upon. + */ + driver_return_code_t (*audio_read_subchannel) + ( void *p_env, cdio_subchannel_t *subchannel ); + + /*! + Resume playing an audio CD. + + @param p_env the CD object to be acted upon. + + */ + driver_return_code_t (*audio_resume) ( void *p_env ); + + /*! + Set volume of an audio CD. + + @param p_env the CD object to be acted upon. + + */ + driver_return_code_t (*audio_set_volume) + ( void *p_env, cdio_audio_volume_t *p_volume ); + + /*! + Stop playing an audio CD. + + @param p_env the CD object to be acted upon. + + */ + driver_return_code_t (*audio_stop) ( void *p_env ); + + /*! + Eject media in CD drive. If successful, as a side effect we + also free p_env. + + @param p_env the CD object to be acted upon. + If the CD is ejected *p_env is freed and p_env set to NULL. + */ + driver_return_code_t (*eject_media) ( void *p_env ); + + /*! + Release and free resources associated with cd. + */ + void (*free) (void *p_env); + + /*! + Return the value associated with the key "arg". + */ + const char * (*get_arg) (void *p_env, const char key[]); + + /*! + Get the block size for subsequest read requests, via a SCSI MMC + MODE_SENSE 6 command. + */ + int (*get_blocksize) ( void *p_env ); + + /*! + Get cdtext information for a CdIo object. + + @param obj the CD object that may contain CD-TEXT information. + @return the CD-TEXT object or NULL if obj is NULL + or CD-TEXT information does not exist. + + If i_track is 0 or CDIO_CDROM_LEADOUT_TRACK the track returned + is the information assocated with the CD. + */ + cdtext_t * (*get_cdtext) ( void *p_env, track_t i_track ); + + /*! + Return an array of device names. if CdIo is NULL (we haven't + initialized a specific device driver), then find a suitable device + driver. + + NULL is returned if we couldn't return a list of devices. + */ + char ** (*get_devices) ( void ); + + /*! + Get the default CD device. + + @return a string containing the default CD device or NULL is + if we couldn't get a default device. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + char * (*get_default_device) ( void ); + + /*! + Return the size of the CD in logical block address (LBA) units. + @return the lsn. On error 0 or CDIO_INVALD_LSN. + */ + lsn_t (*get_disc_last_lsn) ( void *p_env ); + + /*! + Get disc mode associated with cd_obj. + */ + discmode_t (*get_discmode) ( void *p_env ); + + /*! + Return the what kind of device we've got. + + See cd_types.h for a list of bitmasks for the drive type; + */ + void (*get_drive_cap) (const void *p_env, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap); + /*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. + */ + track_t (*get_first_track_num) ( void *p_env ); + + /*! + Get the CD-ROM hardware info via a SCSI MMC INQUIRY command. + False is returned if we had an error getting the information. + */ + bool (*get_hwinfo) + ( const CdIo_t *p_cdio, /* out*/ cdio_hwinfo_t *p_hw_info ); + + /*! Get the LSN of the first track of the last session of + on the CD. + + @param p_cdio the CD object to be acted upon. + @param i_last_session pointer to the session number to be returned. + */ + driver_return_code_t (*get_last_session) + ( void *p_env, /*out*/ lsn_t *i_last_session ); + + /*! + Find out if media has changed since the last call. + @param p_env the CD object to be acted upon. + @return 1 if media has changed since last call, 0 if not. Error + return codes are the same as driver_return_code_t + */ + int (*get_media_changed) ( const void *p_env ); + + /*! + Return the media catalog number MCN from the CD or NULL if + there is none or we don't have the ability to get it. + */ + char * (*get_mcn) ( const void *p_env ); + + /*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. + */ + track_t (*get_num_tracks) ( void *p_env ); + + /*! Return number of channels in track: 2 or 4; -2 if not + implemented or -1 for error. + Not meaningful if track is not an audio track. + */ + int (*get_track_channels) ( const void *p_env, track_t i_track ); + + /*! Return 0 if track is copy protected, 1 if not, or -1 for error + or -2 if not implimented (yet). Is this meaningful if not an + audio track? + */ + track_flag_t (*get_track_copy_permit) ( void *p_env, track_t i_track ); + + /*! + Return the starting LBA for track number + i_track in p_env. Tracks numbers start at 1. + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + CDIO_INVALID_LBA is returned on error. + */ + lba_t (*get_track_lba) ( void *p_env, track_t i_track ); + + /*! + Return the starting LBA for the pregap for track number + i_track in p_env. Tracks numbers start at 1. + CDIO_INVALID_LBA is returned on error. + */ + lba_t (*get_track_pregap_lba) ( const void *p_env, track_t i_track ); + + /*! + Return the International Standard Recording Code (ISRC) for track number + i_track in p_cdio. Track numbers start at 1. + + Note: string is malloc'd so caller has to free() the returned + string when done with it. + */ + char * (*get_track_isrc) ( const void *p_env, track_t i_track ); + + /*! + Get format of track. + */ + track_format_t (*get_track_format) ( void *p_env, track_t i_track ); + + /*! + 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 *p_env, track_t i_track ); + + /*! + Return the starting MSF (minutes/secs/frames) for track number + i_track in p_env. Tracks numbers start at 1. + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + False is returned on error. + */ + bool (*get_track_msf) ( void *p_env, track_t i_track, msf_t *p_msf ); + + /*! Return 1 if track has pre-emphasis, 0 if not, or -1 for error + or -2 if not implimented (yet). Is this meaningful if not an + audio track? + */ + track_flag_t (*get_track_preemphasis) + ( const void *p_env, track_t i_track ); + + /*! + lseek - reposition read/write file offset + Returns (off_t) -1 on error. + Similar to libc's lseek() + */ + off_t (*lseek) ( void *p_env, off_t offset, int whence ); + + /*! + Reads into buf the next size bytes. + Returns -1 on error. + Similar to libc's read() + */ + ssize_t (*read) ( void *p_env, void *p_buf, size_t i_size ); + + /*! + Reads a single mode2 sector from cd device into buf starting + from lsn. Returns 0 if no error. + */ + int (*read_audio_sectors) ( void *p_env, void *p_buf, lsn_t i_lsn, + unsigned int i_blocks ); + + /*! + Read a data sector + + @param p_env environment to read from + + @param p_buf place to read data into. The caller should make sure + this location can store at least CDIO_CD_FRAMESIZE, + M2RAW_SECTOR_SIZE, or M2F2_SECTOR_SIZE depending + on the kind of sector getting read. If you don't + know whether you have a Mode 1/2, Form 1/ Form 2/Formless + sector best to reserve space for the maximum, + M2RAW_SECTOR_SIZE. + + @param i_lsn sector to read + @param i_blocksize size of block. Should be either CDIO_CD_FRAMESIZE, + M2RAW_SECTOR_SIZE, or M2F2_SECTOR_SIZE. See comment above under p_buf. + */ + driver_return_code_t (*read_data_sectors) + ( void *p_env, void *p_buf, lsn_t i_lsn, uint16_t i_blocksize, + uint32_t i_blocks ); + + /*! + Reads a single mode2 sector from cd device into buf starting + from lsn. Returns 0 if no error. + */ + int (*read_mode2_sector) + ( void *p_env, void *p_buf, lsn_t i_lsn, bool b_mode2_form2 ); + + /*! + Reads i_blocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ + int (*read_mode2_sectors) + ( void *p_env, void *p_buf, lsn_t i_lsn, bool b_mode2_form2, + unsigned int i_blocks ); + + /*! + Reads a single mode1 sector from cd device into buf starting + from lsn. Returns 0 if no error. + */ + int (*read_mode1_sector) + ( void *p_env, void *p_buf, lsn_t i_lsn, bool mode1_form2 ); + + /*! + Reads i_blocks of mode1 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ + int (*read_mode1_sectors) + ( void *p_env, void *p_buf, lsn_t i_lsn, bool mode1_form2, + unsigned int i_blocks ); + + bool (*read_toc) ( void *p_env ) ; + + /*! + Run a SCSI MMC command. + + cdio CD structure set by cdio_open(). + i_timeout_ms time in milliseconds we will wait for the command + to complete. + cdb_len number of bytes in cdb (6, 10, or 12). + cdb CDB bytes. All values that are needed should be set on + input. + b_return_data TRUE if the command expects data to be returned in + the buffer + len Size of buffer + buf Buffer for data, both sending and receiving + + Returns 0 if command completed successfully. + */ + mmc_run_cmd_fn_t run_mmc_cmd; + + /*! + Set the arg "key" with "value" in the source device. + */ + int (*set_arg) ( void *p_env, const char key[], const char value[] ); + + /*! + Set the blocksize for subsequent reads. + */ + driver_return_code_t (*set_blocksize) ( void *p_env, + uint16_t i_blocksize ); + + /*! + Set the drive speed. + + @return 0 if everything went okay, -1 if we had an error. is -2 + returned if this is not implemented for the current driver. + */ + int (*set_speed) ( void *p_env, int i_speed ); + + } cdio_funcs_t; + + + /*! Implementation of CdIo type */ + struct _CdIo { + driver_id_t driver_id; /**< Particular driver opened. */ + cdio_funcs_t op; /**< driver-specific routines handling + implementation*/ + void *env; /**< environment. Passed to routine above. */ + }; + + /* This is used in drivers that must keep their own internal + position pointer for doing seeks. Stream-based drivers (like bincue, + nrg, toc, network) would use this. + */ + typedef struct + { + off_t buff_offset; /* buffer offset in disk-image seeks. */ + track_t index; /* Current track index in tocent. */ + lba_t lba; /* Current LBA */ + } internal_position_t; + + CdIo_t * cdio_new (generic_img_private_t *p_env, cdio_funcs_t *p_funcs); + + /* The below structure describes a specific CD Input driver */ + typedef struct + { + driver_id_t id; + unsigned int flags; + const char *name; + const char *describe; + bool (*have_driver) (void); + CdIo_t *(*driver_open) (const char *psz_source_name); + CdIo_t *(*driver_open_am) (const char *psz_source_name, + const char *psz_access_mode); + char *(*get_default_device) (void); + bool (*is_device) (const char *psz_source_name); + char **(*get_devices) (void); + driver_return_code_t (*close_tray) (const char *psz_device); + } 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[CDIO_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[CDIO_MAX_DRIVER+1]; + + /*! + Add/allocate a drive to the end of drives. + Use cdio_free_device_list() to free this device_list. + */ + void cdio_add_device_list(char **device_list[], const char *psz_drive, + unsigned int *i_drives); + + driver_return_code_t close_tray_bsdi (const char *psz_drive); + driver_return_code_t close_tray_freebsd (const char *psz_drive); + driver_return_code_t close_tray_linux (const char *psz_drive); + driver_return_code_t close_tray_netbsd (const char *psz_drive); + driver_return_code_t close_tray_osx (const char *psz_drive); + driver_return_code_t close_tray_solaris (const char *psz_drive); + driver_return_code_t close_tray_win32 (const char *psz_drive); + + bool cdio_have_netbsd(void); + CdIo_t * cdio_open_netbsd (const char *psz_source); + char * cdio_get_default_device_netbsd(void); + char **cdio_get_devices_netbsd(void); + /*! Set up CD-ROM for reading using the NetBSD driver. The device_name is + the some sort of device name. + + NULL is returned on error or there is no FreeBSD driver. + + @see cdio_open_cd, cdio_open + */ + CdIo_t * cdio_open_am_netbsd (const char *psz_source, + const char *psz_access_mode); + + /*! DEPRICATED: use cdio_have_driver(). + True if AIX driver is available. */ + bool cdio_have_aix (void); + + /*! DEPRICATED: use cdio_have_driver(). + True if BSDI driver is available. */ + bool cdio_have_bsdi (void); + + /*! DEPRICATED: use cdio_have_driver(). + True if FreeBSD driver is available. */ + bool cdio_have_freebsd (void); + + /*! DEPRICATED: use cdio_have_driver(). + True if GNU/Linux driver is available. */ + bool cdio_have_linux (void); + + /*! DEPRICATED: use cdio_have_driver(). + True if Sun Solaris driver is available. */ + bool cdio_have_solaris (void); + + /*! DEPRICATED: use cdio_have_driver(). + True if Apple OSX driver is available. */ + bool cdio_have_osx (void); + + /*! DEPRICATED: use cdio_have_driver(). + True if Microsoft Windows driver is available. */ + bool cdio_have_win32 (void); + + /*! True if Nero driver is available. */ + bool cdio_have_nrg (void); + + /*! True if BIN/CUE driver is available. */ + bool cdio_have_bincue (void); + + /*! True if cdrdao CDRDAO driver is available. */ + bool cdio_have_cdrdao (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_PRIVATE_H__ */ diff --git a/lib/driver/cdtext.c b/lib/driver/cdtext.c new file mode 100644 index 00000000..0b35a5c2 --- /dev/null +++ b/lib/driver/cdtext.c @@ -0,0 +1,253 @@ +/* + $Id: cdtext.c,v 1.7 2008/06/16 22:41:44 flameeyes Exp $ + + Copyright (C) 2004, 2005, 2008 Rocky Bernstein + toc reading routine adapted from cuetools + Copyright (C) 2003 Svend Sanjay Sorensen + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include "cdtext_private.h" + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +/*! Note: the order and number items (except CDTEXT_INVALID) should + match the cdtext_field_t enumeration. */ +static const char cdtext_keywords[][16] = + { + "ARRANGER", + "COMPOSER", + "DISC_ID", + "GENRE", + "ISRC", + "MESSAGE", + "PERFORMER", + "SIZE_INFO", + "SONGWRITER", + "TITLE", + "TOC_INFO", + "TOC_INFO2", + "UPC_EAN", + }; + + +/*! Return string representation of the enum values above */ +const char * +cdtext_field2str (cdtext_field_t i) +{ + if (i >= MAX_CDTEXT_FIELDS) + return "Invalid CDTEXT field index"; + else + return cdtext_keywords[i]; +} + +/*! Free memory assocated with cdtext*/ +void +cdtext_destroy (cdtext_t *p_cdtext) +{ + cdtext_field_t i; + + if (!p_cdtext) return; + for (i=0; i < MAX_CDTEXT_FIELDS; i++) { + if (p_cdtext->field[i]) { + free(p_cdtext->field[i]); + p_cdtext->field[i] = NULL; + } + + } +} + +/*! + returns the CDTEXT value associated with key. NULL is returned + if key is CDTEXT_INVALID or the field is not set. + */ +char * +cdtext_get (cdtext_field_t key, const cdtext_t *p_cdtext) +{ + if ((key == CDTEXT_INVALID) || (!p_cdtext->field[key])) return NULL; + return strdup(p_cdtext->field[key]); +} + +const char * +cdtext_get_const (cdtext_field_t key, const cdtext_t *p_cdtext) +{ + if (key == CDTEXT_INVALID) return NULL; + return p_cdtext->field[key]; +} + + +/*! Initialize a new cdtext structure. + When the structure is no longer needed, release the + resources using cdtext_delete. +*/ +void +cdtext_init (cdtext_t *p_cdtext) +{ + cdtext_field_t i; + + for (i=0; i < MAX_CDTEXT_FIELDS; i++) { + p_cdtext->field[i] = NULL; + } +} + +/*! + returns 0 if field is a CD-TEXT keyword, returns non-zero otherwise +*/ +cdtext_field_t +cdtext_is_keyword (const char *key) +{ +#if 0 + char *item; + + item = bsearch(key, + cdtext_keywords, 12, + sizeof (char *), + (int (*)(const void *, const void *)) + strcmp); + return (NULL != item) ? 0 : 1; +#else + unsigned int i; + + for (i = 0; i < 13 ; i++) + if (0 == strcmp (cdtext_keywords[i], key)) { + return i; + } + return CDTEXT_INVALID; +#endif +} + +/*! sets cdtext's keyword entry to field. + */ +void +cdtext_set (cdtext_field_t key, const char *p_value, cdtext_t *p_cdtext) +{ + if (NULL == p_value || key == CDTEXT_INVALID) return; + + if (p_cdtext->field[key]) free (p_cdtext->field[key]); + p_cdtext->field[key] = strdup (p_value); + +} + +#define SET_CDTEXT_FIELD(FIELD) \ + (*set_cdtext_field_fn)(p_user_data, i_track, i_first_track, FIELD, buffer); + +/* + parse all CD-TEXT data retrieved. +*/ +bool +cdtext_data_init(void *p_user_data, track_t i_first_track, + unsigned char *wdata, int i_data, + set_cdtext_field_fn_t set_cdtext_field_fn) +{ + CDText_data_t *p_data; + int i = -1; + int j; + char buffer[256]; + int idx; + int i_track; + bool b_ret = false; + + memset( buffer, 0x00, sizeof(buffer) ); + idx = 0; + + p_data = (CDText_data_t *) (&wdata[4]); + + /* For reasons I don't understand - incorrect CDROM TOC reading? + we are off sometimes by 4. + */ + if( (p_data->type < 0x80) || (p_data->type > 0x85) + || (p_data->block == 0) ) { + CDText_data_t *p_data_test = (CDText_data_t *) (&wdata[8]); + if( (p_data_test->type >= 0x80) + && (p_data_test->type <= 0x85) && (p_data_test->block == 0) ) { + p_data = p_data_test; + i_data -= 4; + } + } + + for( ; i_data > 0; + i_data -= sizeof(CDText_data_t), p_data++ ) { + +#if TESTED + if ( p_data->bDBC ) { + cdio_warn("Double-byte characters not supported"); + return false; + } +#endif + + if( (p_data->type >= 0x80) + && (p_data->type <= 0x85) && (p_data->block == 0) ) { + i_track = p_data->i_track; + + i++; + if( p_data->seq != i ) break; + + for( j=0; j < CDIO_CDTEXT_MAX_TEXT_DATA; j++ ) { + if( p_data->text[j] == 0x00 ) { + bool b_field_set=true; + switch( p_data->type) { + case CDIO_CDTEXT_TITLE: + SET_CDTEXT_FIELD(CDTEXT_TITLE); + break; + case CDIO_CDTEXT_PERFORMER: + SET_CDTEXT_FIELD(CDTEXT_PERFORMER); + break; + case CDIO_CDTEXT_SONGWRITER: + SET_CDTEXT_FIELD(CDTEXT_SONGWRITER); + break; + case CDIO_CDTEXT_COMPOSER: + SET_CDTEXT_FIELD(CDTEXT_COMPOSER); + break; + case CDIO_CDTEXT_ARRANGER: + SET_CDTEXT_FIELD(CDTEXT_ARRANGER); + break; + case CDIO_CDTEXT_MESSAGE: + SET_CDTEXT_FIELD(CDTEXT_MESSAGE); + break; + case CDIO_CDTEXT_DISCID: + SET_CDTEXT_FIELD(CDTEXT_DISCID); + break; + case CDIO_CDTEXT_GENRE: + SET_CDTEXT_FIELD(CDTEXT_GENRE); + break; + default : b_field_set = false; + } + if (b_field_set) { + b_ret = true; + i_track++; + idx = 0; + } + } else { + buffer[idx++] = p_data->text[j]; + } + buffer[idx] = 0x00; + } + } + } + return b_ret; +} + diff --git a/lib/driver/cdtext_private.h b/lib/driver/cdtext_private.h new file mode 100644 index 00000000..56b0e626 --- /dev/null +++ b/lib/driver/cdtext_private.h @@ -0,0 +1,139 @@ +/* + $Id: cdtext_private.h,v 1.3 2008/04/22 15:29:11 karl Exp $ + + Copyright (C) 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __CDIO_CDTEXT_PRIVATE_H__ +#define __CDIO_CDTEXT_PRIVATE_H__ + +#include +#include + +/*! An enumeration for some of the CDIO_CDTEXT_* #defines below. This isn't + really an enumeration one would really use in a program it is here + to be helpful in debuggers where wants just to refer to the + ISO_*_ names and get something. + */ +extern enum cdtext_enum1_s { + CDIO_CDTEXT_MAX_PACK_DATA = 255, + CDIO_CDTEXT_MAX_TEXT_DATA = 12, + CDIO_CDTEXT_TITLE = 0x80, + CDIO_CDTEXT_PERFORMER = 0x81, + CDIO_CDTEXT_SONGWRITER = 0x82, + CDIO_CDTEXT_COMPOSER = 0x83, + CDIO_CDTEXT_ARRANGER = 0x84, + CDIO_CDTEXT_MESSAGE = 0x85, + CDIO_CDTEXT_DISCID = 0x86, + CDIO_CDTEXT_GENRE = 0x87, + CDIO_CDTEXT_TOC = 0x88, + CDIO_CDTEXT_TOC2 = 0x89, + CDIO_CDTEXT_UPC = 0x8E, + CDIO_CDTEXT_BLOCKSIZE = 0x8F +} cdtext_enums1; + +#define CDIO_CDTEXT_MAX_PACK_DATA 255 +#define CDIO_CDTEXT_MAX_TEXT_DATA 12 + +/* From table J.2 - Pack Type Indicator Definitions from + Working Draft NCITS XXX T10/1364-D Revision 10G. November 12, 2001. +*/ +/* Title of Alubm name (ID=0) or Track Titles (ID != 0) */ +#define CDIO_CDTEXT_TITLE 0x80 + +/* Name(s) of the performer(s) in ASCII */ +#define CDIO_CDTEXT_PERFORMER 0x81 + +/* Name(s) of the songwriter(s) in ASCII */ +#define CDIO_CDTEXT_SONGWRITER 0x82 + +/* Name(s) of the Composers in ASCII */ +#define CDIO_CDTEXT_COMPOSER 0x83 + +/* Name(s) of the Arrangers in ASCII */ +#define CDIO_CDTEXT_ARRANGER 0x84 + +/* Message(s) from content provider and/or artist in ASCII */ +#define CDIO_CDTEXT_MESSAGE 0x85 + +/* Disc Identificatin information */ +#define CDIO_CDTEXT_DISCID 0x86 + +/* Genre Identification and Genre Information */ +#define CDIO_CDTEXT_GENRE 0x87 + +/* Table of Content Information */ +#define CDIO_CDTEXT_TOC 0x88 + +/* Second Table of Content Information */ +#define CDIO_CDTEXT_TOC2 0x89 + +/* 0x8A, 0x8B, 0x8C are reserved + 0x8D Reserved for content provider only. + */ + +/* UPC/EAN code of the album and ISRC code of each track */ +#define CDIO_CDTEXT_UPC 0x8E + +/* Size information of the Block */ +#define CDIO_CDTEXT_BLOCKSIZE 0x8F + +PRAGMA_BEGIN_PACKED + +struct CDText_data +{ + uint8_t type; + track_t i_track; + uint8_t seq; +#ifdef WORDS_BIGENDIAN + uint8_t bDBC: 1; /* double byte character */ + uint8_t block: 3; /* block number 0..7 */ + uint8_t characterPosition:4; /* character position */ +#else + uint8_t characterPosition:4; /* character position */ + uint8_t block :3; /* block number 0..7 */ + uint8_t bDBC :1; /* double byte character */ +#endif + char text[CDIO_CDTEXT_MAX_TEXT_DATA]; + uint8_t crc[2]; +} GNUC_PACKED; + +PRAGMA_END_PACKED + +typedef struct CDText_data CDText_data_t; + +typedef void (*set_cdtext_field_fn_t) (void *user_data, track_t i_track, + track_t i_first_track, + cdtext_field_t field, + const char *buffer); + +/* + Internal routine to parse all CD-TEXT data retrieved. +*/ +bool cdtext_data_init(void *user_data, track_t i_first_track, + unsigned char *wdata, int i_data, + set_cdtext_field_fn_t set_cdtext_field_fn); + + +#endif /* __CDIO_CDTEXT_PRIVATE_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/driver/device.c b/lib/driver/device.c new file mode 100644 index 00000000..c0910348 --- /dev/null +++ b/lib/driver/device.c @@ -0,0 +1,1038 @@ +/* + $Id: device.c,v 1.41 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/*! device- and driver-related routines. */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include "cdio_private.h" + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +/* This probably will get moved to driver code, i.e _cdio_linux.c */ +#ifdef HAVE_LINUX_MAJOR_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +/* The below array gives of the drivers that are currently available for + on a particular host. */ + +CdIo_driver_t CdIo_driver[CDIO_MAX_DRIVER] = { {0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL} }; + +/* The last valid entry of Cdio_driver. + -1 or (CDIO_DRIVER_UNINIT) means uninitialzed. + -2 means some sort of error. +*/ + +#define CDIO_DRIVER_UNINIT -1 +int CdIo_last_driver = CDIO_DRIVER_UNINIT; + +#ifdef HAVE_AIX_CDROM +const driver_id_t cdio_os_driver = DRIVER_AIX; +#elif HAVE_BSDI_CDROM +const driver_id_t cdio_os_driver = DRIVER_BSDI; +#elif HAVE_FREEBSD_CDROM +const driver_id_t cdio_os_driver = DRIVER_FREEBSD; +#elif HAVE_LINUX_CDROM +const driver_id_t cdio_os_driver = DRIVER_LINUX; +#elif HAVE_DARWIN_CDROM +const driver_id_t cdio_os_driver = DRIVER_OSX; +#elif HAVE_DARWIN_SOLARIS +const driver_id_t cdio_os_driver = DRIVER_SOLARIS; +#elif HAVE_DARWIN_WIN32 +const driver_id_t cdio_os_driver = DRIVER_WIN32; +#else +const driver_id_t cdio_os_driver = DRIVER_UNKNOWN; +#endif + +/** The below variables are trickery to force enum symbol values to be + recorded in debug symbol tables. They are used to allow one to refer + to the enumeration value names in the typedefs above in a debugger + and debugger expressions. +*/ +cdio_drive_cap_misc_t debug_cdio_drive_cap_misc; +cdio_drive_cap_read_t debug_cdio_drive_cap_read_t; +cdio_drive_cap_write_t debug_drive_cap_write_t; +cdio_mmc_hw_len_t debug_cdio_mmc_hw_len; +cdio_src_category_mask_t debug_cdio_src_category_mask; + +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[CDIO_MAX_DRIVER+1] = { + {DRIVER_UNKNOWN, + 0, + "Unknown", + "No driver", + &cdio_have_false, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }, + + {DRIVER_BSDI, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK|CDIO_SRC_IS_SCSI_MASK, + "AIX", + "AIX SCSI driver", + &cdio_have_aix, + &cdio_open_aix, + &cdio_open_am_aix, + &cdio_get_default_device_aix, + &cdio_is_device_generic, + &cdio_get_devices_aix, + NULL + }, + + {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, + &cdio_open_am_bsdi, + &cdio_get_default_device_bsdi, + &cdio_is_device_generic, + &cdio_get_devices_bsdi, + &close_tray_bsdi + }, + + {DRIVER_FREEBSD, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK|CDIO_SRC_IS_SCSI_MASK, + "FreeBSD", + "FreeBSD driver", + &cdio_have_freebsd, + &cdio_open_freebsd, + &cdio_open_am_freebsd, + &cdio_get_default_device_freebsd, + &cdio_is_device_generic, + &cdio_get_devices_freebsd, + &close_tray_freebsd + }, + + {DRIVER_NETBSD, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK|CDIO_SRC_IS_SCSI_MASK, + "NetBSD", + "NetBSD driver", + &cdio_have_netbsd, + &cdio_open_netbsd, + &cdio_open_am_netbsd, + &cdio_get_default_device_netbsd, + &cdio_is_device_generic, + &cdio_get_devices_netbsd, + &close_tray_netbsd + }, + + {DRIVER_LINUX, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK, + "GNU/Linux", + "GNU/Linux ioctl and MMC driver", + &cdio_have_linux, + &cdio_open_linux, + &cdio_open_am_linux, + &cdio_get_default_device_linux, + &cdio_is_device_generic, + &cdio_get_devices_linux, + &close_tray_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, + &cdio_open_am_solaris, + &cdio_get_default_device_solaris, + &cdio_is_device_generic, + &cdio_get_devices_solaris, + &close_tray_solaris + }, + + {DRIVER_OSX, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK|CDIO_SRC_IS_SCSI_MASK, + "OS X", + "Apple Darwin OS X driver", + &cdio_have_osx, + &cdio_open_osx, + &cdio_open_am_osx, + &cdio_get_default_device_osx, + &cdio_is_device_generic, + &cdio_get_devices_osx, + &close_tray_osx + }, + + {DRIVER_WIN32, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK|CDIO_SRC_IS_SCSI_MASK, + "WIN32", + "MS Windows ASPI and ioctl driver", + &cdio_have_win32, + &cdio_open_win32, + &cdio_open_am_win32, + &cdio_get_default_device_win32, + &cdio_is_device_win32, + &cdio_get_devices_win32, + &close_tray_win32 + }, + + {DRIVER_CDRDAO, + CDIO_SRC_IS_DISK_IMAGE_MASK, + "CDRDAO", + "cdrdao (TOC) disk image driver", + &cdio_have_cdrdao, + &cdio_open_cdrdao, + &cdio_open_am_cdrdao, + &cdio_get_default_device_cdrdao, + NULL, + &cdio_get_devices_cdrdao, + NULL + }, + + {DRIVER_BINCUE, + CDIO_SRC_IS_DISK_IMAGE_MASK, + "BIN/CUE", + "bin/cuesheet disk image driver", + &cdio_have_bincue, + &cdio_open_bincue, + &cdio_open_am_bincue, + &cdio_get_default_device_bincue, + NULL, + &cdio_get_devices_bincue, + NULL + }, + + {DRIVER_NRG, + CDIO_SRC_IS_DISK_IMAGE_MASK, + "NRG", + "Nero NRG disk image driver", + &cdio_have_nrg, + &cdio_open_nrg, + &cdio_open_am_nrg, + &cdio_get_default_device_nrg, + NULL, + &cdio_get_devices_nrg, + NULL + } + +}; + +const char * +cdio_driver_errmsg(driver_return_code_t drc) +{ + switch(drc) { + case DRIVER_OP_SUCCESS: + return "driver operation was successful"; + case DRIVER_OP_ERROR: + return "driver I/O error"; + case DRIVER_OP_UNSUPPORTED: + return "driver operatation not supported"; + case DRIVER_OP_UNINIT: + return "driver not initialized"; + case DRIVER_OP_NOT_PERMITTED: + return "driver operatation not permitted"; + case DRIVER_OP_BAD_PARAMETER: + return "bad parameter passed"; + case DRIVER_OP_BAD_POINTER: + return "bad pointer to memory area"; + case DRIVER_OP_NO_DRIVER: + return "driver not available"; + default: + return "unknown or bad driver return status"; + } +} + +static CdIo * +scan_for_driver(driver_id_t start, driver_id_t end, + const char *psz_source, const char *access_mode) +{ + driver_id_t driver_id; + + for (driver_id=start; driver_id<=end; driver_id++) { + if ((*CdIo_all_drivers[driver_id].have_driver)()) { + CdIo *ret= + (*CdIo_all_drivers[driver_id].driver_open_am)(psz_source, access_mode); + if (ret != NULL) { + ret->driver_id = driver_id; + return ret; + } + } + } + return NULL; +} + +const char * +cdio_driver_describe(driver_id_t driver_id) +{ + return CdIo_all_drivers[driver_id].describe; +} + +/*! + 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 != CDIO_DRIVER_UNINIT) { + cdio_warn ("Init routine called more than once."); + return false; + } + + for (driver_id=DRIVER_UNKNOWN+1; driver_id<=CDIO_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; +} + +/*! + Free any resources associated with cdio. +*/ +void +cdio_destroy (CdIo_t *p_cdio) +{ + CdIo_last_driver = CDIO_DRIVER_UNINIT; + if (p_cdio == NULL) return; + + if (p_cdio->op.free != NULL && p_cdio->env) + p_cdio->op.free (p_cdio->env); + p_cdio->env = NULL; + free (p_cdio); +} + +/*! + Close media tray in CD drive if there is a routine to do so. + + @param psz_drive the name of CD-ROM to be closed. If NULL, we will + use the default device. + @param p_driver_id is the driver to be used or that got used if + it was DRIVER_UNKNOWN or DRIVER_DEVICE; If this is NULL, we won't + report back the driver used. +*/ +driver_return_code_t +cdio_close_tray (const char *psz_orig_drive, /*in/out*/ driver_id_t + *p_driver_id) +{ + driver_id_t temp_driver_id = DRIVER_DEVICE; + char *psz_drive; + driver_return_code_t drc; + + if (!p_driver_id) p_driver_id = &temp_driver_id; + + if (!psz_orig_drive || !*psz_orig_drive) + psz_drive = cdio_get_default_device_driver(p_driver_id); + else + psz_drive = strdup(psz_orig_drive); + + if (DRIVER_UNKNOWN == *p_driver_id || DRIVER_DEVICE == *p_driver_id) { + *p_driver_id = CDIO_MIN_DEVICE_DRIVER; + + /* Scan for driver */ + for ( ; *p_driver_id<=CDIO_MAX_DRIVER; (*p_driver_id)++) { + if ( (*CdIo_all_drivers[*p_driver_id].have_driver)() && + *CdIo_all_drivers[*p_driver_id].close_tray ) { + drc = (*CdIo_all_drivers[*p_driver_id].close_tray)(psz_drive); + free(psz_drive); + return drc; + } + } + return DRIVER_OP_UNSUPPORTED; + } + + /* The driver id was specified. Use that. */ + if ( (*CdIo_all_drivers[*p_driver_id].have_driver)() && + *CdIo_all_drivers[*p_driver_id].close_tray ) { + drc = (*CdIo_all_drivers[*p_driver_id].close_tray)(psz_drive); + free(psz_drive); + return drc; + } + return DRIVER_OP_UNSUPPORTED; +} + +/*! + Eject media in CD drive if there is a routine to do so. + + @param p_cdio the CD object to be acted upon. + If the CD is ejected *p_cdio is freed and p_cdio set to NULL. +*/ +driver_return_code_t +cdio_eject_media (CdIo_t **pp_cdio) +{ + if ((pp_cdio == NULL) || (*pp_cdio == NULL)) return DRIVER_OP_UNINIT; + + if ((*pp_cdio)->op.eject_media) { + int ret = (*pp_cdio)->op.eject_media ((*pp_cdio)->env); + if (0 == ret) { + cdio_destroy(*pp_cdio); + *pp_cdio = NULL; + } + return ret; + } else { + cdio_destroy(*pp_cdio); + *pp_cdio = NULL; + return DRIVER_OP_UNSUPPORTED; + } +} + +/*! + Eject media in CD drive if there is a routine to do so. If you want + to scan for any CD-ROM and eject that, pass NULL for psz_drive. + + @param psz_drive the CD object to be acted upon. + If NULL is given as the drive, we'll use the default driver device. +*/ +driver_return_code_t +cdio_eject_media_drive (const char *psz_drive) +{ + CdIo_t *p_cdio = cdio_open (psz_drive, DRIVER_DEVICE); + if (p_cdio) { + return cdio_eject_media(&p_cdio); + } else { + return DRIVER_OP_UNINIT; + } +} + +/*! + Free device list returned by cdio_get_devices or + cdio_get_devices_with_cap. +*/ +void +cdio_free_device_list (char * ppsz_device_list[]) +{ + char **ppsz_device_list_save=ppsz_device_list; + if (!ppsz_device_list) return; + for ( ; NULL != *ppsz_device_list ; ppsz_device_list++ ) { + free(*ppsz_device_list); + *ppsz_device_list = NULL; + } + free(ppsz_device_list_save); +} + + +/*! + Return a string containing the default CD device if none is specified. + if p_cdio is NULL (we haven't initialized a specific device driver), + then find a suitable one and return the default device for that. + + NULL is returned if we couldn't get a default device. + */ +char * +cdio_get_default_device (const CdIo_t *p_cdio) +{ + if (p_cdio == NULL) { + driver_id_t driver_id; + /* Scan for driver */ + for (driver_id=DRIVER_UNKNOWN+1; driver_id<=CDIO_MAX_DRIVER; driver_id++) { + if ( (*CdIo_all_drivers[driver_id].have_driver)() && + *CdIo_all_drivers[driver_id].get_default_device ) { + return (*CdIo_all_drivers[driver_id].get_default_device)(); + } + } + return NULL; + } + + if (p_cdio->op.get_default_device) { + return p_cdio->op.get_default_device (); + } else { + return NULL; + } +} + +/*! + Return a string containing the default CD device if none is specified. + if p_driver_id is DRIVER_UNKNOWN or DRIVER_DEVICE + then find a suitable one set the default device for that. + + NULL is returned if we couldn't get a default device. + */ +char * +cdio_get_default_device_driver (/*in/out*/ driver_id_t *p_driver_id) +{ + if (DRIVER_UNKNOWN == *p_driver_id || DRIVER_DEVICE == *p_driver_id) { + if (DRIVER_UNKNOWN == *p_driver_id) + (*p_driver_id)++; + else + *p_driver_id = CDIO_MIN_DEVICE_DRIVER; + + /* Scan for driver */ + for ( ; *p_driver_id<=CDIO_MAX_DRIVER; (*p_driver_id)++) { + if ( (*CdIo_all_drivers[*p_driver_id].have_driver)() && + *CdIo_all_drivers[*p_driver_id].get_default_device ) { + return (*CdIo_all_drivers[*p_driver_id].get_default_device)(); + } + } + return NULL; + } + + /* The driver id was specified. Use that. */ + if ( (*CdIo_all_drivers[*p_driver_id].have_driver)() && + *CdIo_all_drivers[*p_driver_id].get_default_device ) { + return (*CdIo_all_drivers[*p_driver_id].get_default_device)(); + } + return NULL; +} + +/*!Return an array of device names. If you want a specific + devices, dor a driver give that device, if you want hardware + devices, give DRIVER_DEVICE and if you want all possible devices, + image drivers and hardware drivers give DRIVER_UNKNOWN. + + NULL is returned if we couldn't return a list of devices. +*/ +char ** +cdio_get_devices (driver_id_t driver_id) +{ + /* Probably could get away with &driver_id below. */ + driver_id_t driver_id_temp = driver_id; + return cdio_get_devices_ret (&driver_id_temp); +} + +char ** +cdio_get_devices_ret (/*in/out*/ driver_id_t *p_driver_id) +{ + CdIo_t *p_cdio; + + switch (*p_driver_id) { + /* FIXME: spit out unknown to give image drivers as well. */ + case DRIVER_DEVICE: + p_cdio = scan_for_driver(DRIVER_UNKNOWN+1, CDIO_MAX_DEVICE_DRIVER, + NULL, NULL); + *p_driver_id = cdio_get_driver_id(p_cdio); + break; + case DRIVER_UNKNOWN: + p_cdio = scan_for_driver(DRIVER_UNKNOWN+1, CDIO_MAX_DRIVER, NULL, NULL); + *p_driver_id = cdio_get_driver_id(p_cdio); + break; + default: + return (*CdIo_all_drivers[*p_driver_id].get_devices)(); + } + + if (p_cdio == NULL) return NULL; + if (p_cdio->op.get_devices) { + char **devices = p_cdio->op.get_devices (); + cdio_destroy(p_cdio); + return devices; + } else { + return NULL; + } +} + +/*! + Return an array of device names in search_devices that have at + least the capabilities listed by cap. If search_devices is NULL, + then we'll search all possible CD drives. + + If "any" is set false then every capability listed in the extended + portion of capabilities (i.e. not the basic filesystem) must be + satisified. If "any" is set true, then if any of the capabilities + matches, we call that a success. + + To find a CD-drive of any type, use the mask CDIO_FS_MATCH_ALL. + + NULL is returned if we couldn't get a default device. + It is also possible to return a non NULL but after dereferencing the + the value is NULL. This also means nothing was found. +*/ +char ** +cdio_get_devices_with_cap (/*in*/ char* search_devices[], + cdio_fs_anal_t capabilities, bool any) +{ + driver_id_t p_driver_id; + return cdio_get_devices_with_cap_ret (search_devices, capabilities, any, + &p_driver_id); +} + +char ** +cdio_get_devices_with_cap_ret (/*in*/ char* search_devices[], + cdio_fs_anal_t need_cap, bool b_any, + /*out*/ driver_id_t *p_driver_id) +{ + char **ppsz_drives=search_devices; + char **ppsz_drives_ret=NULL; + unsigned int i_drives=0; + bool b_free_ppsz_drives = false; + + *p_driver_id = DRIVER_DEVICE; + + if (!ppsz_drives) { + ppsz_drives=cdio_get_devices_ret(p_driver_id); + b_free_ppsz_drives = true; + } + + if (!ppsz_drives) return NULL; + + if (need_cap == CDIO_FS_MATCH_ALL) { + /* Duplicate drives into drives_ret. */ + char **d = ppsz_drives; + + for( ; *d != NULL; d++ ) { + cdio_add_device_list(&ppsz_drives_ret, *d, &i_drives); + } + } else { + const cdio_fs_anal_t need_fs = CDIO_FSTYPE(need_cap); + char **d = ppsz_drives; + + for( ; *d != NULL; d++ ) { + CdIo_t *p_cdio = cdio_open(*d, *p_driver_id); + + if (NULL != p_cdio) { + track_t i_first_track = cdio_get_first_track_num(p_cdio); + cdio_iso_analysis_t cdio_iso_analysis; + + if (CDIO_INVALID_TRACK != i_first_track) { + const cdio_fs_anal_t got_cap = + cdio_guess_cd_type(p_cdio, 0, i_first_track, &cdio_iso_analysis); + + /* Match on filesystem. Here either we don't know what the + filesystem is - automatic match, or we no that the file + system is in the set of those specified. + We refine the logic further after this initial test. */ + if ( CDIO_FS_UNKNOWN == need_fs || 0 == need_fs + || (CDIO_FSTYPE(got_cap) == need_fs) ) { + /* Match on analysis type. If we haven't set any + analysis type, then an automatic match. Otherwise + a match is determined by whether we need all + analysis types or any of them. */ + const cdio_fs_anal_t need_anal = need_cap & ~CDIO_FS_MASK; + const cdio_fs_anal_t got_anal = got_cap & ~CDIO_FS_MASK; + const bool b_match = !need_anal + || (b_any + ? (got_anal & need_anal) != 0 + : (got_anal & need_anal) == need_anal); + if (b_match) + cdio_add_device_list(&ppsz_drives_ret, *d, &i_drives); + } + } + + cdio_destroy(p_cdio); + } + } + } + cdio_add_device_list(&ppsz_drives_ret, NULL, &i_drives); + if (b_free_ppsz_drives) { + cdio_free_device_list(ppsz_drives); + } + return ppsz_drives_ret; +} + +/*! + Return the the kind of drive capabilities of device. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +void +cdio_get_drive_cap (const CdIo_t *p_cdio, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap) +{ + /* This seems like a safe bet. */ + *p_read_cap = CDIO_DRIVE_CAP_UNKNOWN; + *p_write_cap = CDIO_DRIVE_CAP_UNKNOWN; + *p_misc_cap = CDIO_DRIVE_CAP_UNKNOWN; + + if (p_cdio && p_cdio->op.get_drive_cap) { + p_cdio->op.get_drive_cap(p_cdio->env, p_read_cap, p_write_cap, p_misc_cap); + } +} + +/*! + Return the the kind of drive capabilities of device. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +void +cdio_get_drive_cap_dev (const char *device, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap) +{ + /* This seems like a safe bet. */ + CdIo_t *cdio=scan_for_driver(CDIO_MIN_DRIVER, CDIO_MAX_DRIVER, + device, NULL); + if (cdio) { + cdio_get_drive_cap(cdio, p_read_cap, p_write_cap, p_misc_cap); + cdio_destroy(cdio); + } else { + *p_read_cap = CDIO_DRIVE_CAP_UNKNOWN; + *p_write_cap = CDIO_DRIVE_CAP_UNKNOWN; + *p_misc_cap = CDIO_DRIVE_CAP_UNKNOWN; + } +} + + +/*! + Return a string containing the name of the driver in use. + if CdIo is NULL (we haven't initialized a specific device driver), + then return NULL. +*/ +const char * +cdio_get_driver_name (const CdIo_t *p_cdio) +{ + if (NULL==p_cdio) return NULL; + return CdIo_all_drivers[p_cdio->driver_id].name; +} + +/*! + Return the driver id. + if CdIo is NULL (we haven't initialized a specific device driver), + then return DRIVER_UNKNOWN. +*/ +driver_id_t +cdio_get_driver_id (const CdIo_t *p_cdio) +{ + if (!p_cdio) return DRIVER_UNKNOWN; + return p_cdio->driver_id; +} + +/*! + Return a string containing the name of the driver in use. + if CdIo is NULL (we haven't initialized a specific device driver), + then return NULL. +*/ +bool +cdio_get_hwinfo (const CdIo_t *p_cdio, cdio_hwinfo_t *hw_info) +{ + if (!p_cdio) return false; + if (p_cdio->op.get_hwinfo) { + return p_cdio->op.get_hwinfo (p_cdio, hw_info); + } else { + /* Perhaps driver forgot to initialize. We are no worse off Using + mmc than returning false here. */ + return mmc_get_hwinfo(p_cdio, hw_info); + } +} + +/*! + Return the session number of the last on the CD. + + @param p_cdio the CD object to be acted upon. + @param i_last_session pointer to the session number to be returned. +*/ +driver_return_code_t cdio_get_last_session (CdIo_t *p_cdio, + /*out*/ lsn_t *i_last_session) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + if (p_cdio->op.get_last_session) + return p_cdio->op.get_last_session(p_cdio->env, i_last_session); + return DRIVER_OP_UNSUPPORTED; +} + +/*! + Find out if media has changed since the last call. + @param p_cdio the CD object to be acted upon. + @return 1 if media has changed since last call, 0 if not. Error + return codes are the same as driver_return_code_t +*/ +int +cdio_get_media_changed(CdIo_t *p_cdio) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + if (p_cdio->op.get_media_changed) + return p_cdio->op.get_media_changed(p_cdio->env); + return DRIVER_OP_UNSUPPORTED; +} + +bool_3way_t +cdio_have_atapi(CdIo_t *p_cdio) +{ + bool_3way_t i_status; + + if (!p_cdio) return nope; + i_status = mmc_have_interface(p_cdio, CDIO_MMC_FEATURE_INTERFACE_ATAPI); + if (dunno != i_status) return i_status; + + { + /* cdparanoia seems to think that if we have a mode sense command + we have an atapi drive or is atapi compatible. + */ + uint8_t buf[22]; + if (DRIVER_OP_SUCCESS == mmc_mode_sense(p_cdio, buf, sizeof(buf), + CDIO_MMC_CAPABILITIES_PAGE) ) { + uint8_t *b = buf; + b+=b[3]+4; + if( CDIO_MMC_CAPABILITIES_PAGE == (b[0]&0x3F) ) { + /* MMC style drive! */ + return yep; + } + } + } + + /* Put these in the various drivers? If we get more, yes! + */ +#ifdef HAVE_LINUX_MAJOR_H + { + /* This too is from cdparanoia. */ + struct stat st; + generic_img_private_t *p_env = p_cdio->env; + if ( 0 == lstat(p_env->source_name, &st) ) { + if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { + int drive_type=(int)(st.st_rdev>>8); + switch (drive_type) { + case IDE0_MAJOR: + case IDE1_MAJOR: + case IDE2_MAJOR: + case IDE3_MAJOR: + /* Yay, ATAPI... */ + return yep; + break; + case CDU31A_CDROM_MAJOR: + case CDU535_CDROM_MAJOR: + case MATSUSHITA_CDROM_MAJOR: + case MATSUSHITA_CDROM2_MAJOR: + case MATSUSHITA_CDROM3_MAJOR: + case MATSUSHITA_CDROM4_MAJOR: + case SANYO_CDROM_MAJOR: + case MITSUMI_CDROM_MAJOR: + case MITSUMI_X_CDROM_MAJOR: + case OPTICS_CDROM_MAJOR: + case AZTECH_CDROM_MAJOR: + case GOLDSTAR_CDROM_MAJOR: + case CM206_CDROM_MAJOR: + case SCSI_CDROM_MAJOR: + case SCSI_GENERIC_MAJOR: + return nope; + break; + default: + return dunno; + } + } + } + } +#endif /*HAVE_LINUX_MAJOR_H*/ + return dunno; +} + +bool +cdio_have_driver(driver_id_t driver_id) +{ + return (*CdIo_all_drivers[driver_id].have_driver)(); +} + +bool +cdio_is_device(const char *psz_source, driver_id_t driver_id) +{ + if (DRIVER_UNKNOWN == driver_id || DRIVER_DEVICE == driver_id) { + if (DRIVER_UNKNOWN == driver_id) + driver_id++; + else + driver_id = CDIO_MIN_DEVICE_DRIVER; + + /* Scan for driver */ + for ( ; driver_id<=CDIO_MAX_DRIVER; driver_id++) { + if ( (*CdIo_all_drivers[driver_id].have_driver)() && + CdIo_all_drivers[driver_id].is_device ) { + return (*CdIo_all_drivers[driver_id].is_device)(psz_source); + } + } + } + if (CdIo_all_drivers[driver_id].is_device == NULL) return false; + return (*CdIo_all_drivers[driver_id].is_device)(psz_source); +} + + +/*! 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. + + NULL is returned on error. +*/ +CdIo_t * +cdio_open (const char *orig_source_name, driver_id_t driver_id) +{ + return cdio_open_am(orig_source_name, driver_id, NULL); +} + +/*! 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. + + NULL is returned on error. +*/ +CdIo_t * +cdio_open_am (const char *psz_orig_source, driver_id_t driver_id, + const char *psz_access_mode) +{ + char *psz_source; + + if (CdIo_last_driver == -1) cdio_init(); + + if (!psz_orig_source || !*psz_orig_source) + psz_source = cdio_get_default_device(NULL); + else + psz_source = strdup(psz_orig_source); + + switch (driver_id) { + case DRIVER_UNKNOWN: + { + CdIo_t *p_cdio=scan_for_driver(CDIO_MIN_DRIVER, CDIO_MAX_DRIVER, + psz_source, psz_access_mode); + free(psz_source); + return p_cdio; + } + case DRIVER_DEVICE: + { + /* Scan for a driver. */ + CdIo_t *ret = cdio_open_am_cd(psz_source, psz_access_mode); + free(psz_source); + return ret; + } + break; + case DRIVER_AIX: + case DRIVER_BSDI: + case DRIVER_FREEBSD: + case DRIVER_LINUX: + case DRIVER_NETBSD: + case DRIVER_SOLARIS: + case DRIVER_WIN32: + case DRIVER_OSX: + case DRIVER_NRG: + case DRIVER_BINCUE: + case DRIVER_CDRDAO: + if ((*CdIo_all_drivers[driver_id].have_driver)()) { + CdIo_t *ret = + (*CdIo_all_drivers[driver_id].driver_open_am)(psz_source, + psz_access_mode); + if (ret) ret->driver_id = driver_id; + free(psz_source); + return ret; + } + } + + free(psz_source); + return NULL; +} + + +/*! + Set up CD-ROM for reading. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no driver for a some sort of hardware CD-ROM. +*/ +CdIo_t * +cdio_open_cd (const char *psz_source) +{ + return cdio_open_am_cd(psz_source, NULL); +} + +/*! + Set up CD-ROM for reading. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no driver for a some sort of hardware CD-ROM. +*/ +/* 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_t * +cdio_open_am_cd (const char *psz_source, const char *psz_access_mode) +{ + if (CdIo_last_driver == -1) cdio_init(); + + /* Scan for a driver. */ + return scan_for_driver(CDIO_MIN_DEVICE_DRIVER, CDIO_MAX_DEVICE_DRIVER, + psz_source, psz_access_mode); +} + +/*! + Set the blocksize for subsequent reads. +*/ +driver_return_code_t +cdio_set_blocksize ( const CdIo_t *p_cdio, int i_blocksize ) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + if (p_cdio->op.set_blocksize) return DRIVER_OP_UNSUPPORTED; + return p_cdio->op.set_blocksize(p_cdio->env, i_blocksize); +} + +/*! + Set the drive speed. + + @param p_cdio CD structure set by cdio_open(). + @param i_drive_speed speed in CD-ROM speed units. Note this + not Kbs as would be used in the MMC spec or + in mmc_set_speed(). To convert CD-ROM speed units + to Kbs, multiply the number by 176 (for raw data) + and by 150 (for filesystem data). On many CD-ROM + drives, specifying a value too large will result + in using the fastest speed. + + @see mmc_set_speed and mmc_set_drive_speed +*/ +driver_return_code_t +cdio_set_speed (const CdIo_t *p_cdio, int i_speed) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + if (!p_cdio->op.set_speed) return DRIVER_OP_UNSUPPORTED; + return p_cdio->op.set_speed(p_cdio->env, i_speed); +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/driver/disc.c b/lib/driver/disc.c new file mode 100644 index 00000000..11d7288a --- /dev/null +++ b/lib/driver/disc.c @@ -0,0 +1,119 @@ +/* + $Id: disc.c,v 1.6 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "cdio_private.h" + +/* Must match discmode enumeration */ +const char *discmode2str[] = { + "CD-DA", + "CD-DATA (Mode 1)", + "CD DATA (Mode 2)", + "CD-ROM Mixed", + "DVD-ROM", + "DVD-RAM", + "DVD-R", + "DVD-RW", + "DVD+R", + "DVD+RW", + "Unknown/unclassified DVD", + "No information", + "Error in getting information", + "CD-i" +}; + +/*! + Get the size of the CD in logical block address (LBA) units. + + @param p_cdio the CD object queried + @return the lsn. On error 0 or CDIO_INVALD_LSN. +*/ +lsn_t +cdio_get_disc_last_lsn(const CdIo_t *p_cdio) +{ + if (!p_cdio) return CDIO_INVALID_LSN; + return p_cdio->op.get_disc_last_lsn (p_cdio->env); +} + +/*! + Get medium associated with cd_obj. +*/ +discmode_t +cdio_get_discmode (CdIo_t *cd_obj) +{ + if (!cd_obj) return CDIO_DISC_MODE_ERROR; + + if (cd_obj->op.get_discmode) { + return cd_obj->op.get_discmode (cd_obj->env); + } else { + return CDIO_DISC_MODE_NO_INFO; + } +} + +/*! + Return a string containing the name of the driver in use. + if CdIo is NULL (we haven't initialized a specific device driver), + then return NULL. +*/ +char * +cdio_get_mcn (const CdIo_t *p_cdio) +{ + if (p_cdio->op.get_mcn) { + return p_cdio->op.get_mcn (p_cdio->env); + } else { + return NULL; + } +} + +bool +cdio_is_discmode_cdrom(discmode_t discmode) +{ + switch (discmode) { + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_NO_INFO: + return true; + default: + return false; + } +} + +bool +cdio_is_discmode_dvd(discmode_t discmode) +{ + switch (discmode) { + case CDIO_DISC_MODE_DVD_ROM: + case CDIO_DISC_MODE_DVD_RAM: + case CDIO_DISC_MODE_DVD_R: + case CDIO_DISC_MODE_DVD_RW: + case CDIO_DISC_MODE_DVD_PR: + case CDIO_DISC_MODE_DVD_PRW: + return true; + default: + return false; + } +} diff --git a/lib/driver/ds.c b/lib/driver/ds.c new file mode 100644 index 00000000..77d390f7 --- /dev/null +++ b/lib/driver/ds.c @@ -0,0 +1,251 @@ +/* + $Id: ds.c,v 1.4 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2005, 2008 Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include "cdio_assert.h" + +static const char _rcsid[] = "$Id: ds.c,v 1.4 2008/04/22 15:29:12 karl Exp $"; + +struct _CdioList +{ + unsigned length; + + CdioListNode_t *begin; + CdioListNode_t *end; +}; + +struct _CdioListNode +{ + CdioList_t *list; + + CdioListNode_t *next; + + void *data; +}; + +/* impl */ + +CdioList_t * +_cdio_list_new (void) +{ + CdioList_t *p_new_obj = calloc (1, sizeof (CdioList_t)); + + return p_new_obj; +} + +void +_cdio_list_free (CdioList_t *p_list, int free_data) +{ + while (_cdio_list_length (p_list)) + _cdio_list_node_free (_cdio_list_begin (p_list), free_data); + + free (p_list); +} + +unsigned +_cdio_list_length (const CdioList_t *p_list) +{ + cdio_assert (p_list != NULL); + + return p_list->length; +} + +void +_cdio_list_prepend (CdioList_t *p_list, void *p_data) +{ + CdioListNode_t *p_new_node; + + cdio_assert (p_list != NULL); + + p_new_node = calloc (1, sizeof (CdioListNode_t)); + + p_new_node->list = p_list; + p_new_node->next = p_list->begin; + p_new_node->data = p_data; + + p_list->begin = p_new_node; + if (p_list->length == 0) + p_list->end = p_new_node; + + p_list->length++; +} + +void +_cdio_list_append (CdioList_t *p_list, void *p_data) +{ + cdio_assert (p_list != NULL); + + if (p_list->length == 0) + { + _cdio_list_prepend (p_list, p_data); + } + else + { + CdioListNode_t *p_new_node = calloc (1, sizeof (CdioListNode_t)); + + p_new_node->list = p_list; + p_new_node->next = NULL; + p_new_node->data = p_data; + + p_list->end->next = p_new_node; + p_list->end = p_new_node; + + p_list->length++; + } +} + +void +_cdio_list_foreach (CdioList_t *p_list, _cdio_list_iterfunc_t func, + void *p_user_data) +{ + CdioListNode_t *node; + + cdio_assert (p_list != NULL); + cdio_assert (func != 0); + + for (node = _cdio_list_begin (p_list); + node != NULL; + node = _cdio_list_node_next (node)) + func (_cdio_list_node_data (node), p_user_data); +} + +CdioListNode_t * +_cdio_list_find (CdioList_t *p_list, _cdio_list_iterfunc_t cmp_func, + void *p_user_data) +{ + CdioListNode_t *p_node; + + cdio_assert (p_list != NULL); + cdio_assert (cmp_func != 0); + + for (p_node = _cdio_list_begin (p_list); + p_node != NULL; + p_node = _cdio_list_node_next (p_node)) + if (cmp_func (_cdio_list_node_data (p_node), p_user_data)) + break; + + return p_node; +} + +CdioListNode_t * +_cdio_list_begin (const CdioList_t *p_list) +{ + cdio_assert (p_list != NULL); + + return p_list->begin; +} + +CdioListNode_t * +_cdio_list_end (CdioList_t *p_list) +{ + cdio_assert (p_list != NULL); + + return p_list->end; +} + +CdioListNode_t * +_cdio_list_node_next (CdioListNode_t *p_node) +{ + if (p_node) + return p_node->next; + + return NULL; +} + +void +_cdio_list_node_free (CdioListNode_t *p_node, int free_data) +{ + CdioList_t *p_list; + CdioListNode_t *prev_node; + + cdio_assert (p_node != NULL); + + p_list = p_node->list; + + cdio_assert (_cdio_list_length (p_list) > 0); + + if (free_data) + free (_cdio_list_node_data (p_node)); + + if (_cdio_list_length (p_list) == 1) + { + cdio_assert (p_list->begin == p_list->end); + + p_list->end = p_list->begin = NULL; + p_list->length = 0; + free (p_node); + return; + } + + cdio_assert (p_list->begin != p_list->end); + + if (p_list->begin == p_node) + { + p_list->begin = p_node->next; + free (p_node); + p_list->length--; + return; + } + + for (prev_node = p_list->begin; prev_node->next; prev_node = prev_node->next) + if (prev_node->next == p_node) + break; + + cdio_assert (prev_node->next != NULL); + + if (p_list->end == p_node) + p_list->end = prev_node; + + prev_node->next = p_node->next; + + p_list->length--; + + free (p_node); +} + +void * +_cdio_list_node_data (CdioListNode_t *p_node) +{ + if (p_node) + return p_node->data; + + return NULL; +} + +/* eof */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ + diff --git a/lib/driver/generic.h b/lib/driver/generic.h new file mode 100644 index 00000000..595d3c1b --- /dev/null +++ b/lib/driver/generic.h @@ -0,0 +1,222 @@ +/* + $Id: generic.h,v 1.16 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2004, 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Internal routines for CD I/O drivers. */ + + +#ifndef __CDIO_GENERIC_H__ +#define __CDIO_GENERIC_H__ + +#if defined(HAVE_CONFIG_H) && !defined(LIBCDIO_CONFIG_H) +# include "config.h" +#endif + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*! + Things common to private device structures. Even though not all + devices may have some of these fields, by listing common ones + we facilitate writing generic routines and even cut-and-paste + code. + */ + typedef struct { + char *source_name; /**< Name used in open. */ + bool init; /**< True if structure has been initialized */ + bool toc_init; /**< True if TOC read in */ + bool b_cdtext_init; /**< True if CD-Text read in */ + bool b_cdtext_error; /**< True if trouble reading CD-Text */ + + int ioctls_debugged; /**< for debugging */ + + /* Only one of data_source or fd is used; fd is for CD-ROM + devices and the data_source for stream reading (bincue, nrg, toc, + network). + */ + CdioDataSource_t *data_source; + int fd; /**< File descriptor of device */ + track_t i_first_track; /**< The starting track number. */ + track_t i_tracks; /**< The number of tracks. */ + + uint8_t i_joliet_level; /**< 0 = no Joliet extensions. + 1-3: Joliet level. */ + iso9660_pvd_t pvd; + iso9660_svd_t svd; + CdIo_t *cdio; /**< a way to call general cdio routines. */ + cdtext_t cdtext; /**< CD-Text for disc. */ + cdtext_t cdtext_track[CDIO_CD_MAX_TRACKS+1]; /**< CD-TEXT for each track*/ + track_flags_t track_flags[CDIO_CD_MAX_TRACKS+1]; + } generic_img_private_t; + + /*! + Bogus eject media when there is no ejectable media, e.g. a disk image + We always return 2. Should we also free resources? + */ + driver_return_code_t cdio_generic_unimplemented_eject_media (void *p_env); + + /*! + Set the blocksize for subsequent reads. + + @return -2 since it's not implemented. + */ + driver_return_code_t + cdio_generic_unimplemented_set_blocksize (void *p_user_data, + uint16_t i_blocksize); + + /*! + Set the drive speed. + + @return -2 since it's not implemented. + */ + driver_return_code_t cdio_generic_unimplemented_set_speed (void *p_user_data, + int i_speed); + + /*! + Release and free resources associated with cd. + */ + void cdio_generic_free (void *p_env); + + /*! + Initialize CD device. + */ + bool cdio_generic_init (void *p_env, int open_mode); + + /*! + Reads into buf the next size bytes. + Returns -1 on error. + Is in fact libc's read(). + */ + off_t cdio_generic_lseek (void *p_env, off_t offset, int whence); + + /*! + Reads into buf the next size bytes. + Returns -1 on error. + Is in fact libc's read(). + */ + ssize_t cdio_generic_read (void *p_env, void *p_buf, size_t size); + + /*! + Reads a single form1 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ + int cdio_generic_read_form1_sector (void * user_data, void *data, + lsn_t lsn); + + /*! + Release and free resources associated with stream or disk image. + */ + void cdio_generic_stdio_free (void *env); + + /*! + Return true if source_name could be a device containing a CD-ROM on + Win32 + */ + bool cdio_is_device_win32(const char *source_name); + + + /*! + Return true if source_name could be a device containing a CD-ROM on + most Unix servers with block and character devices. + */ + bool cdio_is_device_generic(const char *source_name); + + + /*! + Like above, but don't give a warning device doesn't exist. + */ + bool cdio_is_device_quiet_generic(const char *source_name); + + /*! + Get cdtext information for a CdIo object . + + @param obj the CD object that may contain CD-TEXT information. + @return the CD-TEXT object or NULL if obj is NULL + or CD-TEXT information does not exist. + */ + cdtext_t *get_cdtext_generic (void *p_user_data, track_t i_track); + + /*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. + */ + track_t get_first_track_num_generic(void *p_user_data); + + /*! + Return the number of tracks in the current medium. + */ + track_t get_num_tracks_generic(void *p_user_data); + + /*! + Get disc type associated with cd object. + */ + discmode_t get_discmode_generic (void *p_user_data ); + + /*! + Same as above but only handles CD cases + */ + discmode_t get_discmode_cd_generic (void *p_user_data ); + + /*! Return number of channels in track: 2 or 4; -2 if not + implemented or -1 for error. + Not meaningful if track is not an audio track. + */ + int get_track_channels_generic(const void *p_user_data, track_t i_track); + + /*! Return 1 if copy is permitted on the track, 0 if not, or -1 for error. + Is this meaningful if not an audio track? + */ + track_flag_t get_track_copy_permit_generic(void *p_user_data, + track_t i_track); + + /*! Return 1 if track has pre-emphasis, 0 if not, or -1 for error. + Is this meaningful if not an audio track? + + pre-emphasis is a non linear frequency response. + */ + track_flag_t get_track_preemphasis_generic(const void *p_user_data, + track_t i_track); + + void set_cdtext_field_generic(void *user_data, track_t i_track, + track_t i_first_track, + cdtext_field_t e_field, const char *psz_value); + /*! + Read cdtext information for a CdIo object . + + return true on success, false on error or CD-Text information does + not exist. + */ + bool init_cdtext_generic (generic_img_private_t *p_env); + + void set_track_flags(track_flags_t *p_track_flag, uint8_t flag); + + /*! Read mode 1 or mode2 sectors (using cooked mode). */ + driver_return_code_t read_data_sectors_generic (void *p_user_data, + void *p_buf, lsn_t i_lsn, + uint16_t i_blocksize, + uint32_t i_blocks); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_GENERIC_H__ */ diff --git a/lib/driver/gnu_linux.c b/lib/driver/gnu_linux.c new file mode 100644 index 00000000..7e2f3c44 --- /dev/null +++ b/lib/driver/gnu_linux.c @@ -0,0 +1,1671 @@ +/* + $Id: gnu_linux.c,v 1.33 2008/06/25 07:46:21 rocky Exp $ + + Copyright (C) 2001 Herbert Valerio Riedel + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2008 + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* 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: gnu_linux.c,v 1.33 2008/06/25 07:46:21 rocky Exp $"; + +#ifdef HAVE_STRING_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include "cdtext_private.h" +#include "cdio_assert.h" +#include "cdio_private.h" + +#ifdef HAVE_LINUX_CDROM + +#include +#include +#include + +#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 +#include + +#include +#include +#include + +typedef enum { + _AM_NONE, + _AM_IOCTL, + _AM_READ_CD, + _AM_READ_10 +} access_mode_t; + +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + + access_mode_t access_mode; + + /* Some of the more OS specific things. */ + /* Entry info for each track, add 1 for leadout. */ + struct cdrom_tocentry tocent[CDIO_CD_MAX_TRACKS+1]; + + struct cdrom_tochdr tochdr; + +} _img_private_t; + +/* Some ioctl() errno values which occur when the tray is empty */ +#define ERRNO_TRAYEMPTY(errno) \ + ((errno == EIO) || (errno == ENOENT) || (errno == EINVAL)) + +/**** prototypes for static functions ****/ +static bool is_cdrom_linux(const char *drive, char *mnttype); +static bool read_toc_linux (void *p_user_data); +static driver_return_code_t +run_mmc_cmd_linux( void *p_user_data, + unsigned int i_timeout, + unsigned int i_cdb, + const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, + /*in/out*/ void *p_buf ); +static access_mode_t + +str_to_access_mode_linux(const char *psz_access_mode) +{ + const access_mode_t default_access_mode = _AM_IOCTL; + + if (NULL==psz_access_mode) return default_access_mode; + + if (!strcmp(psz_access_mode, "IOCTL")) + return _AM_IOCTL; + else if (!strcmp(psz_access_mode, "READ_CD")) + return _AM_READ_CD; + else if (!strcmp(psz_access_mode, "READ_10")) + return _AM_READ_10; + else { + cdio_warn ("unknown access type: %s. Default IOCTL used.", + psz_access_mode); + return default_access_mode; + } +} + +static char * +check_mounts_linux(const char *mtab) +{ + FILE *mntfp; + struct mntent *mntent; + + mntfp = setmntent(mtab, "r"); + if ( mntfp != NULL ) { + char *tmp; + char *mnt_type; + char *mnt_dev; + unsigned int i_mnt_type; + unsigned int i_mnt_dev; + + while ( (mntent=getmntent(mntfp)) != NULL ) { + i_mnt_type = strlen(mntent->mnt_type) + 1; + mnt_type = calloc(1, i_mnt_type); + if (mnt_type == NULL) + continue; /* maybe you'll get lucky next time. */ + + i_mnt_dev = strlen(mntent->mnt_fsname) + 1; + mnt_dev = calloc(1, i_mnt_dev); + if (mnt_dev == NULL) { + free(mnt_type); + continue; + } + + strncpy(mnt_type, mntent->mnt_type, i_mnt_type); + strncpy(mnt_dev, mntent->mnt_fsname, i_mnt_dev); + + /* Handle "supermount" filesystem mounts */ + if ( strcmp(mnt_type, "supermount") == 0 ) { + tmp = strstr(mntent->mnt_opts, "fs="); + if ( tmp ) { + free(mnt_type); + mnt_type = strdup(tmp + strlen("fs=")); + if ( mnt_type ) { + tmp = strchr(mnt_type, ','); + if ( tmp ) { + *tmp = '\0'; + } + } + } + tmp = strstr(mntent->mnt_opts, "dev="); + if ( tmp ) { + free(mnt_dev); + mnt_dev = strdup(tmp + strlen("dev=")); + if ( mnt_dev ) { + tmp = strchr(mnt_dev, ','); + if ( tmp ) { + *tmp = '\0'; + } + } + } + } + if ( strcmp(mnt_type, "iso9660") == 0 ) { + if (is_cdrom_linux(mnt_dev, mnt_type) > 0) { + free(mnt_type); + endmntent(mntfp); + return mnt_dev; + } + } + free(mnt_dev); + free(mnt_type); + } + endmntent(mntfp); + } + return NULL; +} + +/*! + Get the volume of an audio CD. + + @param p_cdio the CD object to be acted upon. +*/ +static driver_return_code_t +audio_get_volume_linux (void *p_user_data, + /*out*/ cdio_audio_volume_t *p_volume) +{ + + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDROMVOLREAD, p_volume); +} + +/*! + Pause playing CD through analog output + + @param p_cdio the CD object to be acted upon. +*/ +static driver_return_code_t +audio_pause_linux (void *p_user_data) +{ + + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDROMPAUSE); +} + +/*! + Playing starting at given MSF through analog output + + @param p_cdio the CD object to be acted upon. +*/ +static driver_return_code_t +audio_play_msf_linux (void *p_user_data, msf_t *p_start_msf, msf_t *p_end_msf) +{ + + const _img_private_t *p_env = p_user_data; + struct cdrom_msf cdrom_msf; + + cdrom_msf.cdmsf_min0 = cdio_from_bcd8(p_start_msf->m); + cdrom_msf.cdmsf_sec0 = cdio_from_bcd8(p_start_msf->s); + cdrom_msf.cdmsf_frame0 = cdio_from_bcd8(p_start_msf->f); + + cdrom_msf.cdmsf_min1 = cdio_from_bcd8(p_end_msf->m); + cdrom_msf.cdmsf_sec1 = cdio_from_bcd8(p_end_msf->s); + cdrom_msf.cdmsf_frame1 = cdio_from_bcd8(p_end_msf->f); + + return ioctl(p_env->gen.fd, CDROMPLAYMSF, &cdrom_msf); +} + +/*! + Playing CD through analog output at the desired track and index + + @param p_cdio the CD object to be acted upon. + @param p_track_index location to start/end. +*/ +static driver_return_code_t +audio_play_track_index_linux (void *p_user_data, + cdio_track_index_t *p_track_index) +{ + + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDROMPLAYTRKIND, p_track_index); +} + +/*! + Read Audio Subchannel information + + @param p_user_data the CD object to be acted upon. + @param p_subchannel returned information +*/ +static driver_return_code_t +audio_read_subchannel_linux (void *p_user_data, + /*out*/ cdio_subchannel_t *p_subchannel) +{ + + const _img_private_t *p_env = p_user_data; + struct cdrom_subchnl subchannel; + int i_rc; + + subchannel.cdsc_format = CDIO_CDROM_MSF; + i_rc = ioctl(p_env->gen.fd, CDROMSUBCHNL, &subchannel); + if (0 == i_rc) { + p_subchannel->control = subchannel.cdsc_ctrl; + p_subchannel->track = subchannel.cdsc_trk; + p_subchannel->index = subchannel.cdsc_ind; + + p_subchannel->abs_addr.m = + cdio_to_bcd8(subchannel.cdsc_absaddr.msf.minute); + p_subchannel->abs_addr.s = + cdio_to_bcd8(subchannel.cdsc_absaddr.msf.second); + p_subchannel->abs_addr.f = + cdio_to_bcd8(subchannel.cdsc_absaddr.msf.frame); + p_subchannel->rel_addr.m = + cdio_to_bcd8(subchannel.cdsc_reladdr.msf.minute); + p_subchannel->rel_addr.s = + cdio_to_bcd8(subchannel.cdsc_reladdr.msf.second); + p_subchannel->rel_addr.f = + cdio_to_bcd8(subchannel.cdsc_reladdr.msf.frame); + p_subchannel->audio_status = subchannel.cdsc_audiostatus; + + return DRIVER_OP_SUCCESS; + } else { + cdio_info ("ioctl CDROMSUBCHNL failed: %s\n", strerror(errno)); + return DRIVER_OP_ERROR; + } +} + +/*! + Resume playing an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_resume_linux (void *p_user_data) +{ + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDROMRESUME, 0); +} + +/*! + Set the volume of an audio CD. + + @param p_user_data the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_set_volume_linux (void *p_user_data, cdio_audio_volume_t *p_volume) +{ + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDROMVOLCTRL, p_volume); +} + +/*! + Stop playing an audio CD. + + @param p_user_data the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_stop_linux (void *p_user_data) +{ + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDROMSTOP); +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +get_arg_linux (void *env, const char key[]) +{ + _img_private_t *_obj = env; + + if (!strcmp (key, "source")) { + return _obj->gen.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; +} + +#undef USE_LINUX_CAP +#ifdef USE_LINUX_CAP +/*! + Return the the kind of drive capabilities of device. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +static void +get_drive_cap_linux (const void *p_user_data, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap) +{ + const _img_private_t *p_env = p_user_data; + int32_t i_drivetype; + + i_drivetype = ioctl (p_env->gen.fd, CDROM_GET_CAPABILITY, CDSL_CURRENT); + + if (i_drivetype < 0) { + *p_read_cap = CDIO_DRIVE_CAP_ERROR; + *p_write_cap = CDIO_DRIVE_CAP_ERROR; + *p_misc_cap = CDIO_DRIVE_CAP_ERROR; + return; + } + + *p_read_cap = 0; + *p_write_cap = 0; + *p_misc_cap = 0; + + /* Reader */ + if (i_drivetype & CDC_PLAY_AUDIO) + *p_read_cap |= CDIO_DRIVE_CAP_READ_AUDIO; + if (i_drivetype & CDC_CD_R) + *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_R; + if (i_drivetype & CDC_CD_RW) + *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_RW; + if (i_drivetype & CDC_DVD) + *p_read_cap |= CDIO_DRIVE_CAP_READ_DVD_ROM; + + /* Writer */ + if (i_drivetype & CDC_CD_RW) + *p_read_cap |= CDIO_DRIVE_CAP_WRITE_CD_RW; + if (i_drivetype & CDC_DVD_R) + *p_read_cap |= CDIO_DRIVE_CAP_WRITE_DVD_R; + if (i_drivetype & CDC_DVD_RAM) + *p_read_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RAM; + + /* Misc */ + if (i_drivetype & CDC_CLOSE_TRAY) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_CLOSE_TRAY; + if (i_drivetype & CDC_OPEN_TRAY) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_EJECT; + if (i_drivetype & CDC_LOCK) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_LOCK; + if (i_drivetype & CDC_SELECT_SPEED) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_SELECT_SPEED; + if (i_drivetype & CDC_SELECT_DISC) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_SELECT_DISC; + if (i_drivetype & CDC_MULTI_SESSION) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_MULTI_SESSION; + if (i_drivetype & CDC_MEDIA_CHANGED) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED; + if (i_drivetype & CDC_RESET) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_RESET; +} +#endif + +/*! Get the LSN of the first track of the last session of + on the CD. + + @param p_cdio the CD object to be acted upon. + @param i_last_session pointer to the session number to be returned. +*/ +static driver_return_code_t +get_last_session_linux (void *p_user_data, + /*out*/ lsn_t *i_last_session) +{ + const _img_private_t *p_env = p_user_data; + struct cdrom_multisession ms; + int i_rc; + + ms.addr_format = CDROM_LBA; + i_rc = ioctl(p_env->gen.fd, CDROMMULTISESSION, &ms); + if (0 == i_rc) { + *i_last_session = ms.addr.lba; + return DRIVER_OP_SUCCESS; + } else { + cdio_warn ("ioctl CDROMMULTISESSION failed: %s\n", strerror(errno)); + return DRIVER_OP_ERROR; + } +} + + + +/*! + Find out if media has changed since the last call. + @param p_user_data the environment object to be acted upon. + @return 1 if media has changed since last call, 0 if not. Error + return codes are the same as driver_return_code_t +*/ +static int +get_media_changed_linux (const void *p_user_data) { + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDROM_MEDIA_CHANGED, 0); +} + +/*! + Return the media catalog number MCN. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +static char * +get_mcn_linux (const void *p_user_data) { + + struct cdrom_mcn mcn; + const _img_private_t *p_env = p_user_data; + memset(&mcn, 0, sizeof(mcn)); + if (ioctl(p_env->gen.fd, CDROM_GET_MCN, &mcn) != 0) + return NULL; + return strdup((char *)mcn.medium_catalog_number); +} + +/*! + Get format of track. +*/ +static track_format_t +get_track_format_linux(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if ( !p_env ) return TRACK_FORMAT_ERROR; + + if (!p_env->gen.toc_init) read_toc_linux (p_user_data) ; + + if (i_track > (p_env->gen.i_tracks+p_env->gen.i_first_track) + || i_track < p_env->gen.i_first_track) + return TRACK_FORMAT_ERROR; + + i_track -= p_env->gen.i_first_track; + + /* This is pretty much copied from the "badly broken" cdrom_count_tracks + in linux/cdrom.c. + */ + if (p_env->tocent[i_track].cdte_ctrl & CDIO_CDROM_DATA_TRACK) { + if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_CDI_TRACK) + return TRACK_FORMAT_CDI; + else if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_XA_TRACK) + 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 +get_track_green_linux(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if (!p_env->gen.toc_init) read_toc_linux (p_user_data) ; + + if (i_track >= (p_env->gen.i_tracks+p_env->gen.i_first_track) + || i_track < p_env->gen.i_first_track) + return false; + + i_track -= p_env->gen.i_first_track; + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cd-info for a while. + */ + return ((p_env->tocent[i_track].cdte_ctrl & 2) != 0); +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + track_num in obj. Track numbers usually start at something + greater than 0, usually 1. + + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +static bool +get_track_msf_linux(void *p_user_data, track_t i_track, msf_t *msf) +{ + _img_private_t *p_env = p_user_data; + + if (NULL == msf) return false; + + if (!p_env->gen.toc_init) read_toc_linux (p_user_data) ; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) + i_track = p_env->gen.i_tracks + p_env->gen.i_first_track; + + if (i_track > (p_env->gen.i_tracks+p_env->gen.i_first_track) + || i_track < p_env->gen.i_first_track) { + return false; + } else { + struct cdrom_msf0 *msf0= + &p_env->tocent[i_track-p_env->gen.i_first_track].cdte_addr.msf; + msf->m = cdio_to_bcd8(msf0->minute); + msf->s = cdio_to_bcd8(msf0->second); + msf->f = cdio_to_bcd8(msf0->frame); + return true; + } +} + + +/*! + Check, if a device is mounted and return the target (=mountpoint) + needed for umounting (idea taken from libunieject). + */ + +static int is_mounted (const char * device, char * target) { + FILE * fp; + char real_device_1[PATH_MAX]; + char real_device_2[PATH_MAX]; + + char file_device[PATH_MAX]; + char file_target[PATH_MAX]; + + fp = fopen ( "/proc/mounts", "r"); + /* Older systems just have /etc/mtab */ + if(!fp) + fp = fopen ( "/etc/mtab", "r"); + + /* Neither /proc/mounts nor /etc/mtab could be opened, give up here */ + if(!fp) return 0; + + /* Get real device */ + cdio_follow_symlink(device, real_device_1); + + /* Read entries */ + + while ( fscanf(fp, "%s %s %*s %*s %*d %*d\n", file_device, file_target) != EOF ) { + cdio_follow_symlink(file_device, real_device_2); + if(!strcmp(real_device_1, real_device_2)) { + strcpy(target, file_target); + fclose(fp); + return 1; + } + + } + fclose(fp); + return 0; +} + +/*! + Umount a filesystem specified by it's mountpoint. We must do this + by forking and calling the umount command, because the raw umount + (or umount2) system calls will *always* trigger an EPERM even if + we are allowed to umount the filesystem. The umount command is + suid root. + + Code here is inspired by the standard linux eject command by + Jeff Tranter and Frank Lichtenheld. + */ + +static int do_umount(char * target) { + int status; + + switch (fork()) { + case 0: /* child */ + execlp("pumount", "pumount", target, NULL); + execlp("umount", "umount", target, NULL); + return -1; + break; + case -1: + return -1; + break; + default: /* parent */ + wait(&status); + if (WIFEXITED(status) == 0) { + return -1; + } + if (WEXITSTATUS(status) != 0) { + return -1; + } + break; + } + return 0; +} + + +/*! + Eject media in CD-ROM drive. Return DRIVER_OP_SUCCESS if successful, + DRIVER_OP_ERROR on error. + */ + +static driver_return_code_t +eject_media_linux (void *p_user_data) { + + _img_private_t *p_env = p_user_data; + driver_return_code_t ret=DRIVER_OP_SUCCESS; + int status; + bool was_open = false; + char mount_target[PATH_MAX]; + + if ( p_env->gen.fd <= -1 ) { + p_env->gen.fd = open (p_env->gen.source_name, O_RDONLY|O_NONBLOCK); + } + else { + was_open = true; + } + + if ( p_env->gen.fd <= -1 ) return DRIVER_OP_ERROR; + + if ((status = ioctl(p_env->gen.fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) > 0) { + switch(status) { + case CDS_TRAY_OPEN: + cdio_info ("Drive status reports that tray is open\n"); + break; + default: + cdio_info ("Unknown state of CD-ROM (%d)\n", status); + /* Fall through */ + case CDS_DISC_OK: + /* Some systems automount the drive, so we must umount it. + We check if the drive is actually mounted */ + if(is_mounted (p_env->gen.source_name, mount_target)) { + /* Try to umount the drive */ + if(do_umount(mount_target)) { + cdio_log(CDIO_LOG_WARN, "Could not umount %s\n", + p_env->gen.source_name); + ret=DRIVER_OP_ERROR; + break; + } + /* For some reason, we must close and reopen the device after + it got umounted (at least the commandline eject program + opens the device just after umounting it) */ + close(p_env->gen.fd); + p_env->gen.fd = open (p_env->gen.source_name, O_RDONLY|O_NONBLOCK); + } + + if((ret = ioctl(p_env->gen.fd, CDROMEJECT)) != 0) { + int eject_error = errno; + /* Try ejecting the MMC way... */ + ret = mmc_eject_media(p_env->gen.cdio); + if (0 != ret) { + cdio_info("ioctl CDROMEJECT and MMC eject failed: %s", + strerror(eject_error)); + ret = DRIVER_OP_ERROR; + } + } + /* force kernel to reread partition table when new disc inserted */ + if (0 != ioctl(p_env->gen.fd, BLKRRPART)) { + cdio_info ("BLKRRPART request failed: %s\n", strerror(errno)); + } + break; + } + } else { + cdio_warn ("CDROM_DRIVE_STATUS failed: %s\n", strerror(errno)); + ret=DRIVER_OP_ERROR; + } + if(!was_open) { + close(p_env->gen.fd); + p_env->gen.fd = -1; + } + return ret; +} + +/*! + Get disc type associated with cd object. +*/ +static discmode_t +dvd_discmode_linux (_img_private_t *p_env) +{ + discmode_t discmode=CDIO_DISC_MODE_NO_INFO; + + /* See if this is a DVD. */ + cdio_dvd_struct_t dvd; /* DVD READ STRUCT for layer 0. */ + + dvd.physical.type = CDIO_DVD_STRUCT_PHYSICAL; + dvd.physical.layer_num = 0; + if (0 == ioctl (p_env->gen.fd, DVD_READ_STRUCT, &dvd)) { + switch(dvd.physical.layer[0].book_type) { + case CDIO_DVD_BOOK_DVD_ROM: return CDIO_DISC_MODE_DVD_ROM; + case CDIO_DVD_BOOK_DVD_RAM: return CDIO_DISC_MODE_DVD_RAM; + case CDIO_DVD_BOOK_DVD_R: return CDIO_DISC_MODE_DVD_R; + case CDIO_DVD_BOOK_DVD_RW: return CDIO_DISC_MODE_DVD_RW; + case CDIO_DVD_BOOK_DVD_PR: return CDIO_DISC_MODE_DVD_PR; + case CDIO_DVD_BOOK_DVD_PRW: return CDIO_DISC_MODE_DVD_PRW; + default: return CDIO_DISC_MODE_DVD_OTHER; + } + } + return discmode; +} + +/*! + Get disc type associated with the cd object. +*/ +static discmode_t +get_discmode_linux (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + discmode_t discmode; + + if (!p_env) return CDIO_DISC_MODE_ERROR; + + /* Try DVD types first. See note below. */ + discmode = dvd_discmode_linux(p_env); + + if (CDIO_DISC_MODE_NO_INFO != discmode) return discmode; + /* + Justin B Ruggles reports that the + GNU/Linux ioctl(.., CDROM_DISC_STATUS) does not return "CD DATA + Form 2" for SVCD's even though they are are form 2. In + mmc_get_discmode we issue a SCSI MMC-2 TOC command first to + try get more accurate information. But we took care above *not* + to issue a FULL TOC on DVD media. + */ + discmode = mmc_get_discmode(p_env->gen.cdio); + if (CDIO_DISC_MODE_NO_INFO != discmode) + return discmode; + else { + int32_t i_discmode = ioctl (p_env->gen.fd, CDROM_DISC_STATUS); + + if (i_discmode < 0) return CDIO_DISC_MODE_ERROR; + + switch(i_discmode) { + case CDS_AUDIO: + return CDIO_DISC_MODE_CD_DA; + case CDS_DATA_1: + case CDS_DATA_2: /* Actually, recent GNU/Linux kernels don't return + CDS_DATA_2, but just in case. */ + return CDIO_DISC_MODE_CD_DATA; + case CDS_MIXED: + return CDIO_DISC_MODE_CD_MIXED; + case CDS_XA_2_1: + case CDS_XA_2_2: + return CDIO_DISC_MODE_CD_XA; + case CDS_NO_INFO: + return CDIO_DISC_MODE_NO_INFO; + default: + return CDIO_DISC_MODE_ERROR; + } + } +} + +/* Check a drive to see if it is a CD-ROM + Return 1 if a CD-ROM. 0 if it exists but isn't a CD-ROM drive + and -1 if no device exists . +*/ +static bool +is_cdrom_linux(const char *drive, char *mnttype) +{ + bool is_cd=false; + int cdfd; + + /* If it doesn't exist, return -1 */ + if ( !cdio_is_device_quiet_generic(drive) ) { + return(false); + } + + /* If it does exist, verify that it's an available CD-ROM */ + cdfd = open(drive, (O_RDONLY|O_NONBLOCK), 0); + if ( cdfd >= 0 ) { + if ( ioctl(cdfd, CDROM_GET_CAPABILITY, 0) != -1 ) { + is_cd = true; + } + close(cdfd); + } + /* Even if we can't read it, it might be mounted */ + else if ( mnttype && (strcmp(mnttype, "iso9660") == 0) ) { + is_cd = true; + } + return(is_cd); +} + +/* MMC driver to read audio sectors. + Can read only up to 25 blocks. +*/ +static driver_return_code_t +read_audio_sectors_linux (void *p_user_data, void *p_buf, lsn_t i_lsn, + uint32_t i_blocks) +{ + _img_private_t *p_env = p_user_data; + return mmc_read_sectors( p_env->gen.cdio, p_buf, i_lsn, + CDIO_MMC_READ_TYPE_CDDA, i_blocks); +} + +/* Packet driver to read mode2 sectors. + Can read only up to 25 blocks. +*/ +static driver_return_code_t +_read_mode2_sectors_mmc (_img_private_t *p_env, void *p_buf, lba_t lba, + uint32_t i_blocks, bool b_read_10) +{ + mmc_cdb_t cdb = {{0, }}; + + CDIO_MMC_SET_READ_LBA(cdb.field, lba); + + if (b_read_10) { + int retval; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_10); + CDIO_MMC_SET_READ_LENGTH16(cdb.field, i_blocks); + + if ((retval = mmc_set_blocksize (p_env->gen.cdio, M2RAW_SECTOR_SIZE))) + return retval; + + if ((retval = run_mmc_cmd_linux (p_env, 0, + mmc_get_cmd_len(cdb.field[0]), + &cdb, + SCSI_MMC_DATA_READ, + M2RAW_SECTOR_SIZE * i_blocks, + p_buf))) + { + mmc_set_blocksize (p_env->gen.cdio, CDIO_CD_FRAMESIZE); + return retval; + } + + /* Restore blocksize. */ + retval = mmc_set_blocksize (p_env->gen.cdio, CDIO_CD_FRAMESIZE); + return retval; + } else { + + cdb.field[1] = 0; /* sector size mode2 */ + cdb.field[9] = 0x58; /* 2336 mode2 */ + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_CD); + CDIO_MMC_SET_READ_LENGTH24(cdb.field, i_blocks); + + return run_mmc_cmd_linux (p_env, 0, + mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_READ, + M2RAW_SECTOR_SIZE * i_blocks, p_buf); + } +} + +static driver_return_code_t +_read_mode2_sectors (_img_private_t *p_env, void *p_buf, lba_t lba, + uint32_t i_blocks, bool b_read_10) +{ + unsigned int l = 0; + int retval = 0; + + while (i_blocks > 0) + { + const unsigned i_blocks2 = (i_blocks > 25) ? 25 : i_blocks; + void *p_buf2 = ((char *)p_buf ) + (l * M2RAW_SECTOR_SIZE); + + retval |= _read_mode2_sectors_mmc (p_env, p_buf2, lba + l, + i_blocks2, b_read_10); + + if (retval) + break; + + i_blocks -= i_blocks2; + l += i_blocks2; + } + + return retval; +} + +/*! + Reads a single mode1 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static driver_return_code_t +_read_mode1_sector_linux (void *p_user_data, void *p_data, lsn_t lsn, + bool b_form2) +{ + +#if 0 + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + struct cdrom_msf *p_msf = (struct cdrom_msf *) &buf; + msf_t _msf; + + _img_private_t *p_env = p_user_data; + + cdio_lsn_to_msf (lsn, &_msf); + p_msf->cdmsf_min0 = cdio_from_bcd8(_msf.m); + p_msf->cdmsf_sec0 = cdio_from_bcd8(_msf.s); + p_msf->cdmsf_frame0 = cdio_from_bcd8(_msf.f); + + retry: + switch (p_env->access_mode) + { + case _AM_NONE: + cdio_warn ("no way to read mode1"); + return 1; + break; + + case _AM_IOCTL: + if (ioctl (p_env->gen.fd, CDROMREADMODE1, &buf) == -1) + { + perror ("ioctl()"); + return 1; + /* exit (EXIT_FAILURE); */ + } + break; + + case _AM_READ_CD: + case _AM_READ_10: + if (_read_mode2_sectors (p_env, buf, lsn, 1, + (p_env->access_mode == _AM_READ_10))) + { + perror ("ioctl()"); + if (p_env->access_mode == _AM_READ_CD) + { + cdio_info ("READ_CD failed; switching to READ_10 mode..."); + p_env->access_mode = _AM_READ_10; + goto retry; + } + else + { + cdio_info ("READ_10 failed; switching to ioctl(CDROMREADMODE2) mode..."); + p_env->access_mode = _AM_IOCTL; + goto retry; + } + return 1; + } + break; + } + + memcpy (p_data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE, + b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE); + +#else + return cdio_generic_read_form1_sector(p_user_data, p_data, lsn); +#endif + return 0; +} + +/*! + Reads i_blocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static driver_return_code_t +_read_mode1_sectors_linux (void *p_user_data, void *p_data, lsn_t lsn, + bool b_form2, uint32_t i_blocks) +{ + _img_private_t *p_env = p_user_data; + unsigned int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < i_blocks; i++) { + if ( (retval = _read_mode1_sector_linux (p_env, + ((char *)p_data) + (blocksize*i), + lsn + i, b_form2)) ) + return retval; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static driver_return_code_t +_read_mode2_sector_linux (void *p_user_data, void *p_data, lsn_t lsn, + bool b_form2) +{ + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + struct cdrom_msf *msf = (struct cdrom_msf *) &buf; + msf_t _msf; + + _img_private_t *p_env = p_user_data; + + cdio_lba_to_msf (cdio_lsn_to_lba(lsn), &_msf); + msf->cdmsf_min0 = cdio_from_bcd8(_msf.m); + msf->cdmsf_sec0 = cdio_from_bcd8(_msf.s); + msf->cdmsf_frame0 = cdio_from_bcd8(_msf.f); + + retry: + switch (p_env->access_mode) + { + case _AM_NONE: + cdio_warn ("no way to read mode2"); + return 1; + break; + + case _AM_IOCTL: + if (ioctl (p_env->gen.fd, CDROMREADMODE2, &buf) == -1) + { + perror ("ioctl()"); + return 1; + /* exit (EXIT_FAILURE); */ + } + break; + + case _AM_READ_CD: + case _AM_READ_10: + if (_read_mode2_sectors (p_env, buf, lsn, 1, + (p_env->access_mode == _AM_READ_10))) + { + perror ("ioctl()"); + if (p_env->access_mode == _AM_READ_CD) + { + cdio_info ("READ_CD failed; switching to READ_10 mode..."); + p_env->access_mode = _AM_READ_10; + goto retry; + } + else + { + cdio_info ("READ_10 failed; switching to ioctl(CDROMREADMODE2) mode..."); + p_env->access_mode = _AM_IOCTL; + goto retry; + } + return 1; + } + break; + } + + if (b_form2) + memcpy (p_data, buf, M2RAW_SECTOR_SIZE); + else + memcpy (((char *)p_data), buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE); + + return DRIVER_OP_SUCCESS; +} + +/*! + Reads i_blocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static driver_return_code_t +_read_mode2_sectors_linux (void *p_user_data, void *data, lsn_t lsn, + bool b_form2, uint32_t i_blocks) +{ + _img_private_t *p_env = p_user_data; + unsigned int i; + uint16_t i_blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + /* For each frame, pick out the data part we need */ + for (i = 0; i < i_blocks; i++) { + int retval; + if ( (retval = _read_mode2_sector_linux (p_env, + ((char *)data) + (i_blocksize*i), + lsn + i, b_form2)) ) + return retval; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return false if successful or true if an error. +*/ +static bool +read_toc_linux (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + int i; + + /* read TOC header */ + if ( ioctl(p_env->gen.fd, CDROMREADTOCHDR, &p_env->tochdr) == -1 ) { + cdio_warn("%s: %s\n", + "error in ioctl CDROMREADTOCHDR", strerror(errno)); + return false; + } + + p_env->gen.i_first_track = p_env->tochdr.cdth_trk0; + p_env->gen.i_tracks = p_env->tochdr.cdth_trk1; + + /* read individual tracks */ + for (i= p_env->gen.i_first_track; i<=p_env->gen.i_tracks; i++) { + struct cdrom_tocentry *p_toc = + &(p_env->tocent[i-p_env->gen.i_first_track]); + + p_toc->cdte_track = i; + p_toc->cdte_format = CDROM_MSF; + if ( ioctl(p_env->gen.fd, CDROMREADTOCENTRY, p_toc) == -1 ) { + cdio_warn("%s %d: %s\n", + "error in ioctl CDROMREADTOCENTRY for track", + i, strerror(errno)); + return false; + } + + set_track_flags(&(p_env->gen.track_flags[i]), p_toc->cdte_ctrl); + + /**** + struct cdrom_msf0 *msf= &env->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 */ + p_env->tocent[p_env->gen.i_tracks].cdte_track = CDIO_CDROM_LEADOUT_TRACK; + p_env->tocent[p_env->gen.i_tracks].cdte_format = CDROM_MSF; + + if (ioctl(p_env->gen.fd, CDROMREADTOCENTRY, + &p_env->tocent[p_env->gen.i_tracks]) == -1 ) { + cdio_warn("%s: %s\n", + "error in ioctl CDROMREADTOCENTRY for lead-out", + strerror(errno)); + return false; + } + + /* + struct cdrom_msf0 *msf= &env->tocent[p_env->gen.i_tracks].cdte_addr.msf; + + fprintf (stdout, "--- track# %d (msf %2.2x:%2.2x:%2.2x)\n", + i, msf->minute, msf->second, msf->frame); + */ + + p_env->gen.toc_init = true; + return true; +} + +/*! + Run a SCSI MMC command. + + cdio CD structure set by cdio_open(). + i_timeout time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + p_buf Buffer for data, both sending and receiving + i_buf Size of buffer + e_direction direction the transfer is to go. + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + We return true if command completed successfully and false if not. + */ +static driver_return_code_t +run_mmc_cmd_linux( void *p_user_data, + unsigned int i_timeout_ms, + unsigned int i_cdb, const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + struct cdrom_generic_command cgc; + memset (&cgc, 0, sizeof (struct cdrom_generic_command)); + memcpy(&cgc.cmd, p_cdb, i_cdb); + cgc.buflen = i_buf; + cgc.buffer = p_buf; + cgc.data_direction = (SCSI_MMC_DATA_READ == cgc.data_direction) + ? CGC_DATA_READ : CGC_DATA_WRITE; + +#ifdef HAVE_LINUX_CDROM_TIMEOUT + cgc.timeout = i_timeout_ms; +#endif + + { + int i_rc = ioctl (p_env->gen.fd, CDROM_SEND_PACKET, &cgc); + if (0 == i_rc) return DRIVER_OP_SUCCESS; + if (-1 == i_rc) { + cdio_info ("ioctl CDROM_SEND_PACKET failed: %s", strerror(errno)); + switch (errno) { + case EPERM: + return DRIVER_OP_NOT_PERMITTED; + break; + case EINVAL: + return DRIVER_OP_BAD_PARAMETER; + break; + case EFAULT: + return DRIVER_OP_BAD_POINTER; + break; + case EIO: + default: + return DRIVER_OP_ERROR; + break; + } + } + return DRIVER_OP_ERROR; + } +} + +/*! + Return the size of the CD in logical block address (LBA) units. + @return the lsn. On error return CDIO_INVALID_LSN. + + As of GNU/Linux 2.6, CDROMTOCENTRY gives + ioctl CDROMREADTOCENTRY failed: Invalid argument + + + In some cases CDROMREADTOCHDR seems to fix this, but I haven't been + able to find anything that documents this requirement or behavior. It's + not the way CDROMREADTOCHDR works on other 'nixs. + + Also note that in one at least one test the corresponding MMC gives + a different answer, so there may be some disagreement about what is in + fact the last lsn. + */ +static lsn_t +get_disc_last_lsn_linux (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + struct cdrom_tocentry tocent; + uint32_t i_size; + + if (!p_env->gen.toc_init) read_toc_linux (p_user_data) ; + + tocent.cdte_track = CDIO_CDROM_LEADOUT_TRACK; + tocent.cdte_format = CDROM_LBA; + if (ioctl (p_env->gen.fd, CDROMREADTOCENTRY, &tocent) == -1) + { + cdio_warn ("ioctl CDROMREADTOCENTRY failed: %s\n", strerror(errno)); + return CDIO_INVALID_LSN; + } + + i_size = tocent.cdte_addr.lba; + + return i_size; +} + +/*! + Set the arg "key" with "value" in the source device. + Currently "source" and "access-mode" are valid keys. + "source" sets the source device in I/O operations + "access-mode" sets the the method of CD access + + DRIVER_OP_SUCCESS is returned if no error was found, + and nonzero if there as an error. +*/ +static driver_return_code_t +set_arg_linux (void *p_user_data, const char key[], const char value[]) +{ + _img_private_t *p_env = p_user_data; + + if (!strcmp (key, "source")) + { + if (!value) return DRIVER_OP_ERROR; + free (p_env->gen.source_name); + p_env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + return str_to_access_mode_linux(value); + } + else return DRIVER_OP_ERROR; + + return DRIVER_OP_SUCCESS; +} + +/* checklist: /dev/cdrom, /dev/dvd /dev/hd?, /dev/scd? /dev/sr? */ +static const char checklist1[][40] = { + {"cdrom"}, {"dvd"} +}; +static const int checklist1_size = sizeof(checklist1) / sizeof(checklist1[0]); + +static const struct + { + char format[22]; + int num_min; + int num_max; + } +checklist2[] = + { + { "/dev/hd%c", 'a', 'z' }, + { "/dev/scd%d", 0, 27 }, + { "/dev/sr%d", 0, 27 }, + }; +static const int checklist2_size = sizeof(checklist2) / sizeof(checklist2[0]); + + +/* Set CD-ROM drive speed */ +static driver_return_code_t +set_speed_linux (void *p_user_data, int i_drive_speed) +{ + const _img_private_t *p_env = p_user_data; + + if (!p_env) return DRIVER_OP_UNINIT; + return ioctl(p_env->gen.fd, CDROM_SELECT_SPEED, i_drive_speed); +} + +#endif /* HAVE_LINUX_CDROM */ + +/*! + Return an array of strings giving possible CD devices. + */ +char ** +cdio_get_devices_linux (void) +{ +#ifndef HAVE_LINUX_CDROM + return NULL; +#else + unsigned int i; + char drive[40]; + char *ret_drive; + char **drives = NULL; + unsigned int num_drives=0; + + /* Scan the system for CD-ROM drives. + */ + for ( i=0; i < checklist1_size; ++i ) { + if (snprintf(drive, sizeof(drive), "/dev/%s", checklist1[i]) < 0) + continue; + if ( is_cdrom_linux(drive, NULL) > 0 ) { + cdio_add_device_list(&drives, drive, &num_drives); + } + } + + /* Now check the currently mounted CD drives */ + if (NULL != (ret_drive = check_mounts_linux("/etc/mtab"))) { + cdio_add_device_list(&drives, ret_drive, &num_drives); + free(ret_drive); + } + + /* Finally check possible mountable drives in /etc/fstab */ + if (NULL != (ret_drive = check_mounts_linux("/etc/fstab"))) { + cdio_add_device_list(&drives, ret_drive, &num_drives); + free(ret_drive); + } + + /* Scan the system for CD-ROM drives. + Not always 100% reliable, so use the USE_MNTENT code above first. + */ + for ( i=0; i < checklist2_size; ++i ) { + unsigned int j; + for ( j=checklist2[i].num_min; j<=checklist2[i].num_max; ++j ) { + if (snprintf(drive, sizeof(drive), checklist2[i].format, j) < 0) + continue; + if ( (is_cdrom_linux(drive, NULL)) > 0 ) { + cdio_add_device_list(&drives, drive, &num_drives); + } + } + } + cdio_add_device_list(&drives, NULL, &num_drives); + return drives; +#endif /*HAVE_LINUX_CDROM*/ +} + +/*! + Return a string containing the default CD device. + */ +char * +cdio_get_default_device_linux(void) +{ +#ifndef HAVE_LINUX_CDROM + return NULL; + +#else + unsigned int i; + char drive[40]; + char *ret_drive; + + /* Scan the system for CD-ROM drives. + */ + for ( i=0; strlen(checklist1[i]) > 0; ++i ) { + if (snprintf(drive, sizeof(drive), "/dev/%s", checklist1[i]) < 0) + continue; + if ( is_cdrom_linux(drive, NULL) > 0 ) { + return strdup(drive); + } + } + + /* Now check the currently mounted CD drives */ + if (NULL != (ret_drive = check_mounts_linux("/etc/mtab"))) + return ret_drive; + + /* Finally check possible mountable drives in /etc/fstab */ + if (NULL != (ret_drive = check_mounts_linux("/etc/fstab"))) + return ret_drive; + + /* Scan the system for CD-ROM drives. + Not always 100% reliable, so use the USE_MNTENT code above first. + */ + for ( i=0; checklist2[i].format; ++i ) { + unsigned int j; + for ( j=checklist2[i].num_min; j<=checklist2[i].num_max; ++j ) { + if (snprintf(drive, sizeof(drive), checklist2[i].format, j) < 0) + continue; + if ( is_cdrom_linux(drive, NULL) > 0 ) { + return(strdup(drive)); + } + } + } + return NULL; +#endif /*HAVE_LINUX_CDROM*/ +} + +/*! + Close tray on CD-ROM. + + @param psz_device the CD-ROM drive to be closed. + +*/ +driver_return_code_t +close_tray_linux (const char *psz_device) +{ +#ifdef HAVE_LINUX_CDROM + int i_rc; + int fd = open (psz_device, O_RDONLY|O_NONBLOCK); + int status; + + if ( fd > -1 ) { + if((status = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) > 0) { + switch(status) { + case CDS_TRAY_OPEN: + goto try_anyway; + break; + case CDS_DISC_OK: + cdio_info ("Tray already closed."); + i_rc = DRIVER_OP_SUCCESS; + break; + default: + cdio_info ("Unknown CD-ROM status (%d), trying anyway", status); + goto try_anyway; + } + } else { + cdio_info ("CDROM_DRIVE_STATUS failed: %s, trying anyway", + strerror(errno)); + try_anyway: + i_rc = DRIVER_OP_SUCCESS; + if((i_rc = ioctl(fd, CDROMCLOSETRAY)) != 0) { + cdio_warn ("ioctl CDROMCLOSETRAY failed: %s\n", strerror(errno)); + i_rc = DRIVER_OP_ERROR; + } + } + close(fd); + } else + i_rc = DRIVER_OP_ERROR; + return i_rc; +#else + return DRIVER_OP_NO_DRIVER; +#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_t * +cdio_open_linux (const char *psz_source_name) +{ +#ifdef HAVE_LINUX_CDROM + return cdio_open_am_linux(psz_source_name, NULL); +#else + return NULL; +#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_t * +cdio_open_am_linux (const char *psz_orig_source, const char *access_mode) +{ + +#ifdef HAVE_LINUX_CDROM + CdIo_t *ret; + _img_private_t *_data; + char *psz_source; + + cdio_funcs_t _funcs = { + .audio_get_volume = audio_get_volume_linux, + .audio_pause = audio_pause_linux, + .audio_play_msf = audio_play_msf_linux, + .audio_play_track_index= audio_play_track_index_linux, +#if USE_MMC_SUBCHANNEL + .audio_read_subchannel = audio_read_subchannel_mmc, +#else + .audio_read_subchannel = audio_read_subchannel_linux, +#endif + .audio_resume = audio_resume_linux, + .audio_set_volume = audio_set_volume_linux, + .audio_stop = audio_stop_linux, + .eject_media = eject_media_linux, + .free = cdio_generic_free, + .get_arg = get_arg_linux, + .get_blocksize = get_blocksize_mmc, + .get_cdtext = get_cdtext_generic, + .get_default_device = cdio_get_default_device_linux, + .get_devices = cdio_get_devices_linux, + .get_disc_last_lsn = get_disc_last_lsn_linux, + .get_discmode = get_discmode_linux, +#if USE_LINUX_CAP + .get_drive_cap = get_drive_cap_linux, +#else + .get_drive_cap = get_drive_cap_mmc, +#endif + .get_first_track_num = get_first_track_num_generic, + .get_hwinfo = NULL, + .get_last_session = get_last_session_linux, + .get_media_changed = get_media_changed_linux, + .get_mcn = get_mcn_linux, + .get_num_tracks = get_num_tracks_generic, + .get_track_channels = get_track_channels_generic, + .get_track_copy_permit = get_track_copy_permit_generic, + .get_track_format = get_track_format_linux, + .get_track_green = get_track_green_linux, + .get_track_lba = NULL, /* This could be implemented if need be. */ + .get_track_preemphasis = get_track_preemphasis_generic, + .get_track_msf = get_track_msf_linux, + .lseek = cdio_generic_lseek, + .read = cdio_generic_read, + .read_audio_sectors = read_audio_sectors_linux, +#if 1 + .read_data_sectors = read_data_sectors_generic, +#else + .read_data_sectors = read_data_sectors_mmc, +#endif + .read_mode1_sector = _read_mode1_sector_linux, + .read_mode1_sectors = _read_mode1_sectors_linux, + .read_mode2_sector = _read_mode2_sector_linux, + .read_mode2_sectors = _read_mode2_sectors_linux, + .read_toc = read_toc_linux, + .run_mmc_cmd = run_mmc_cmd_linux, + .set_arg = set_arg_linux, + .set_blocksize = set_blocksize_mmc, +#if 1 + .set_speed = set_speed_linux, +#else + .set_speed = set_speed_mmc, +#endif + }; + + _data = calloc (1, sizeof (_img_private_t)); + + _data->access_mode = str_to_access_mode_linux(access_mode); + _data->gen.init = false; + _data->gen.toc_init = false; + _data->gen.fd = -1; + _data->gen.b_cdtext_init = false; + _data->gen.b_cdtext_error = false; + + if (NULL == psz_orig_source) { + psz_source=cdio_get_default_device_linux(); + if (NULL == psz_source) { + free(_data); + return NULL; + } + + set_arg_linux(_data, "source", psz_source); + free(psz_source); + } else { + if (cdio_is_device_generic(psz_orig_source)) + set_arg_linux(_data, "source", psz_orig_source); + else { + /* The below would be okay if all device drivers worked this way. */ +#if 0 + cdio_info ("source %s is not a device", psz_orig_source); +#endif + free(_data); + return NULL; + } + } + + ret = cdio_new ((void *)_data, &_funcs); + if (ret == NULL) return NULL; + + ret->driver_id = DRIVER_LINUX; + + if (cdio_generic_init(_data, O_RDONLY|O_NONBLOCK)) { + return ret; + } else { + cdio_generic_free (_data); + free(ret); + 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 */ +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/driver/image.h b/lib/driver/image.h new file mode 100644 index 00000000..acdb96ba --- /dev/null +++ b/lib/driver/image.h @@ -0,0 +1,81 @@ +/* + $Id: image.h,v 1.9 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/*! + Header for image drivers. In contrast to image_common.h which contains + routines, this header like most C headers does not depend on anything + defined before it is included. +*/ + +#ifndef __CDIO_IMAGE_H__ +#define __CDIO_IMAGE_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include "cdio_private.h" +#include + +/*! + The universal format for information about a track for CD image readers + It may be that some fields can be derived from other fields. + Over time this structure may get cleaned up. Possibly this can be + expanded/reused for real CD formats. +*/ + +typedef struct { + track_t track_num; /**< Probably is index+1 */ + msf_t start_msf; + lba_t start_lba; + int start_index; + lba_t pregap; /**< pre-gap */ + lba_t silence; /**< pre-gap with zero audio data */ + int sec_count; /**< Number of sectors in this track. Does not + include pregap */ + int num_indices; + flag_t flags; /**< "[NO] COPY", "4CH", "[NO] PREMPAHSIS" */ + char *isrc; /**< IRSC Code (5.22.4) exactly 12 bytes */ + char *filename; + CdioDataSource_t *data_source; + off_t offset; /**< byte offset into data_start of track + beginning. In cdrdao for example, one + filename may cover many tracks and + each track would then have a different + offset. + */ + track_format_t track_format; + bool track_green; + cdtext_t cdtext; /**< CD-TEXT */ + + trackmode_t mode; + uint16_t datasize; /**< How much is in the portion we return + back? */ + uint16_t datastart; /**< Offset from begining of frame + that data starts */ + uint16_t endsize; /**< How much stuff at the end to skip over. + This stuff may have error correction + (EDC, or ECC).*/ + uint16_t blocksize; /**< total block size = start + size + end */ +} track_info_t; + + +#endif /* __CDIO_IMAGE_H__ */ diff --git a/lib/driver/image/Makefile b/lib/driver/image/Makefile new file mode 100644 index 00000000..5524a1f7 --- /dev/null +++ b/lib/driver/image/Makefile @@ -0,0 +1,21 @@ +# $Id: Makefile,v 1.2 2008/04/21 18:30:22 karl Exp $ +# +# Copyright (C) 2004, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# The make is done above. This boilerplate Makefile just transfers the call + + +all install check clean: + cd .. && $(MAKE) $@ diff --git a/lib/driver/image/bincue.c b/lib/driver/image/bincue.c new file mode 100644 index 00000000..61106332 --- /dev/null +++ b/lib/driver/image/bincue.c @@ -0,0 +1,1217 @@ +/* + $Id: bincue.c,v 1.23 2008/04/21 18:30:22 karl Exp $ + + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2008 + Rocky Bernstein + Copyright (C) 2001 Herbert Valerio Riedel + cue parsing routine adapted from cuetools + Copyright (C) 2003 Svend Sanjay Sorensen + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* This code implements low-level access functions for a CD images + residing inside a disk file (*.bin) and its associated cue sheet. + (*.cue). +*/ + +static const char _rcsid[] = "$Id: bincue.c,v 1.23 2008/04/21 18:30:22 karl Exp $"; + +#include "image.h" +#include "cdio_assert.h" +#include "cdio_private.h" +#include "_cdio_stdio.h" + +#include +#include +#include + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STRINGS_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_GLOB_H +#include +#endif +#include + +#include "portable.h" +/* reader */ + +#define DEFAULT_CDIO_DEVICE "videocd.bin" +#define DEFAULT_CDIO_CUE "videocd.cue" + +static lsn_t get_disc_last_lsn_bincue (void *p_user_data); +#include "image_common.h" +static bool parse_cuefile (_img_private_t *cd, const char *toc_name); + +/*! + Initialize image structures. + */ +static bool +_init_bincue (_img_private_t *p_env) +{ + lsn_t lead_lsn; + + if (p_env->gen.init) + return false; + + if (!(p_env->gen.data_source = cdio_stdio_new (p_env->gen.source_name))) { + cdio_warn ("init failed"); + return false; + } + + /* Have to set init before calling get_disc_last_lsn_bincue() or we will + get into infinite recursion calling passing right here. + */ + p_env->gen.init = true; + p_env->gen.i_first_track = 1; + p_env->psz_mcn = NULL; + p_env->disc_mode = CDIO_DISC_MODE_NO_INFO; + + cdtext_init (&(p_env->gen.cdtext)); + + lead_lsn = get_disc_last_lsn_bincue( (_img_private_t *) p_env); + + if (-1 == lead_lsn) return false; + + if ((p_env->psz_cue_name == NULL)) return false; + + /* Read in CUE sheet. */ + if ( !parse_cuefile(p_env, p_env->psz_cue_name) ) return false; + + /* Fake out leadout track and sector count for last track*/ + cdio_lsn_to_msf (lead_lsn, &p_env->tocent[p_env->gen.i_tracks].start_msf); + p_env->tocent[p_env->gen.i_tracks].start_lba = cdio_lsn_to_lba(lead_lsn); + p_env->tocent[p_env->gen.i_tracks - p_env->gen.i_first_track].sec_count = + cdio_lsn_to_lba(lead_lsn - + p_env->tocent[p_env->gen.i_tracks - p_env->gen.i_first_track].start_lba); + + return true; +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + Would be libc's seek() but we have to adjust for the extra track header + information in each sector. +*/ +static off_t +_lseek_bincue (void *p_user_data, off_t offset, int whence) +{ + _img_private_t *p_env = p_user_data; + + /* real_offset is the real byte offset inside the disk image + The number below was determined empirically. I'm guessing + the 1st 24 bytes of a bin file are used for something. + */ + off_t real_offset=0; + + unsigned int i; + + p_env->pos.lba = 0; + for (i=0; igen.i_tracks; i++) { + track_info_t *this_track=&(p_env->tocent[i]); + p_env->pos.index = i; + if ( (this_track->sec_count*this_track->datasize) >= offset) { + int blocks = offset / this_track->datasize; + int rem = offset % this_track->datasize; + int block_offset = blocks * this_track->blocksize; + real_offset += block_offset + rem; + p_env->pos.buff_offset = rem; + p_env->pos.lba += blocks; + break; + } + real_offset += this_track->sec_count*this_track->blocksize; + offset -= this_track->sec_count*this_track->datasize; + p_env->pos.lba += this_track->sec_count; + } + + if (i==p_env->gen.i_tracks) { + cdio_warn ("seeking outside range of disk image"); + return DRIVER_OP_ERROR; + } else { + real_offset += p_env->tocent[i].datastart; + return cdio_stream_seek(p_env->gen.data_source, real_offset, whence); + } +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + FIXME: + At present we assume a read doesn't cross sector or track + boundaries. +*/ +static ssize_t +_read_bincue (void *p_user_data, void *data, size_t size) +{ + _img_private_t *p_env = p_user_data; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + char *p = data; + ssize_t final_size=0; + ssize_t this_size; + track_info_t *this_track=&(p_env->tocent[p_env->pos.index]); + ssize_t skip_size = this_track->datastart + this_track->endsize; + + while (size > 0) { + long int rem = this_track->datasize - p_env->pos.buff_offset; + if ((long int) size <= rem) { + this_size = cdio_stream_read(p_env->gen.data_source, buf, size, 1); + final_size += this_size; + memcpy (p, buf, this_size); + break; + } + + /* Finish off reading this sector. */ + cdio_warn ("Reading across block boundaries not finished"); + + size -= rem; + this_size = cdio_stream_read(p_env->gen.data_source, buf, rem, 1); + final_size += this_size; + memcpy (p, buf, this_size); + p += this_size; + this_size = cdio_stream_read(p_env->gen.data_source, buf, rem, 1); + + /* Skip over stuff at end of this sector and the beginning of the next. + */ + cdio_stream_read(p_env->gen.data_source, buf, skip_size, 1); + + /* Get ready to read another sector. */ + p_env->pos.buff_offset=0; + p_env->pos.lba++; + + /* Have gone into next track. */ + if (p_env->pos.lba >= p_env->tocent[p_env->pos.index+1].start_lba) { + p_env->pos.index++; + this_track=&(p_env->tocent[p_env->pos.index]); + skip_size = this_track->datastart + this_track->endsize; + } + } + return final_size; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static lsn_t +get_disc_last_lsn_bincue (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + long size; + + size = cdio_stream_stat (p_env->gen.data_source); + + if (size % CDIO_CD_FRAMESIZE_RAW) + { + cdio_warn ("image %s size (%ld) not multiple of blocksize (%d)", + p_env->gen.source_name, size, CDIO_CD_FRAMESIZE_RAW); + if (size % M2RAW_SECTOR_SIZE == 0) + cdio_warn ("this may be a 2336-type disc image"); + else if (size % CDIO_CD_FRAMESIZE_RAW == 0) + cdio_warn ("this may be a 2352-type disc image"); + /* exit (EXIT_FAILURE); */ + } + + size /= CDIO_CD_FRAMESIZE_RAW; + + return size; +} + +#define MAXLINE 4096 /* maximum line length + 1 */ + +static bool +parse_cuefile (_img_private_t *cd, const char *psz_cue_name) +{ + /* The below declarations may be common in other image-parse routines. */ + FILE *fp; + char psz_line[MAXLINE]; /* text of current line read in file fp. */ + unsigned int i_line=0; /* line number in file of psz_line. */ + int i = -1; /* Position in tocent. Same as + cd->gen.i_tracks - 1 */ + char *psz_keyword, *psz_field; + cdio_log_level_t log_level = (NULL == cd) ? CDIO_LOG_INFO : CDIO_LOG_WARN; + cdtext_field_t cdtext_key; + + /* The below declarations may be unique to this image-parse routine. */ + int start_index; + bool b_first_index_for_track=false; + + if (NULL == psz_cue_name) + return false; + + fp = fopen (psz_cue_name, "r"); + if (fp == NULL) { + cdio_log(log_level, "error opening %s for reading: %s", + psz_cue_name, strerror(errno)); + return false; + } + + if (cd) { + cd->gen.i_tracks=0; + cd->gen.i_first_track=1; + cd->gen.b_cdtext_init = true; + cd->gen.b_cdtext_error = false; + cd->psz_mcn=NULL; + } + + while ((fgets(psz_line, MAXLINE, fp)) != NULL) { + + i_line++; + + if (NULL != (psz_keyword = strtok (psz_line, " \t\n\r"))) { + /* REM remarks ... */ + if (0 == strcmp ("REM", psz_keyword)) { + ; + + /* global section */ + /* CATALOG ddddddddddddd */ + } else if (0 == strcmp ("CATALOG", psz_keyword)) { + if (-1 == i) { + if (NULL == (psz_field = strtok (NULL, " \t\n\r"))) { + cdio_log(log_level, + "%s line %d after word CATALOG: ", + psz_cue_name, i_line); + cdio_log(log_level, + "expecting 13-digit media catalog number, got nothing."); + goto err_exit; + } + if (strlen(psz_field) != 13) { + cdio_log(log_level, + "%s line %d after word CATALOG: ", + psz_cue_name, i_line); + cdio_log(log_level, + "Token %s has length %ld. Should be 13 digits.", + psz_field, (long int) strlen(psz_field)); + goto err_exit; + } else { + /* Check that we have all digits*/ + unsigned int i; + for (i=0; i<13; i++) { + if (!isdigit(psz_field[i])) { + cdio_log(log_level, + "%s line %d after word CATALOG:", + psz_cue_name, i_line); + cdio_log(log_level, + "Character \"%c\" at postition %i of token \"%s\" " + "is not all digits.", + psz_field[i], i+1, psz_field); + goto err_exit; + } + } + } + + if (cd) cd->psz_mcn = strdup (psz_field); + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + } else { + goto not_in_global_section; + } + + /* FILE "" */ + } else if (0 == strcmp ("FILE", psz_keyword)) { + if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) { + if (cd) cd->tocent[i + 1].filename = strdup (psz_field); + } else { + goto format_error; + } + + /* TRACK N */ + } else if (0 == strcmp ("TRACK", psz_keyword)) { + int i_track; + + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + if (1!=sscanf(psz_field, "%d", &i_track)) { + cdio_log(log_level, + "%s line %d after word TRACK:", + psz_cue_name, i_line); + cdio_log(log_level, + "Expecting a track number, got %s", psz_field); + goto err_exit; + } + } + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + track_info_t *this_track=NULL; + + if (cd) { + this_track = &(cd->tocent[cd->gen.i_tracks]); + this_track->track_num = cd->gen.i_tracks; + this_track->num_indices = 0; + b_first_index_for_track = false; + cdtext_init (&(cd->gen.cdtext_track[cd->gen.i_tracks])); + cd->gen.i_tracks++; + } + i++; + + if (0 == strcmp ("AUDIO", psz_field)) { + if (cd) { + this_track->mode = AUDIO; + this_track->blocksize = CDIO_CD_FRAMESIZE_RAW; + this_track->datasize = CDIO_CD_FRAMESIZE_RAW; + this_track->datastart = 0; + this_track->endsize = 0; + this_track->track_format = TRACK_FORMAT_AUDIO; + this_track->track_green = false; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_DA; + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_XA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else if (0 == strcmp ("MODE1/2048", psz_field)) { + if (cd) { + this_track->mode = MODE1; + this_track->blocksize = 2048; + this_track->track_format= TRACK_FORMAT_DATA; + this_track->track_green = false; + /* Is the below correct? */ + this_track->datastart = 0; + this_track->datasize = CDIO_CD_FRAMESIZE; + this_track->endsize = 0; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_DATA; + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_XA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else if (0 == strcmp ("MODE1/2352", psz_field)) { + if (cd) { + this_track->blocksize = 2352; + this_track->track_format= TRACK_FORMAT_DATA; + this_track->track_green = false; + this_track->datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE; + this_track->datasize = CDIO_CD_FRAMESIZE; + this_track->endsize = CDIO_CD_EDC_SIZE + + CDIO_CD_M1F1_ZERO_SIZE + CDIO_CD_ECC_SIZE; + this_track->mode = MODE1_RAW; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_DATA; + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_XA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else if (0 == strcmp ("MODE2/2336", psz_field)) { + if (cd) { + this_track->blocksize = 2336; + this_track->track_format= TRACK_FORMAT_XA; + this_track->track_green = true; + this_track->mode = MODE2; + this_track->datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE; + this_track->datasize = M2RAW_SECTOR_SIZE; + this_track->endsize = 0; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_DATA; + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_XA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else if (0 == strcmp ("MODE2/2048", psz_field)) { + if (cd) { + this_track->blocksize = 2048; + this_track->track_format= TRACK_FORMAT_XA; + this_track->track_green = true; + this_track->mode = MODE2_FORM1; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_DATA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else if (0 == strcmp ("MODE2/2324", psz_field)) { + if (cd) { + this_track->blocksize = 2324; + this_track->track_format= TRACK_FORMAT_XA; + this_track->track_green = true; + this_track->mode = MODE2_FORM2; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_DATA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else if (0 == strcmp ("MODE2/2336", psz_field)) { + if (cd) { + this_track->blocksize = 2336; + this_track->track_format= TRACK_FORMAT_XA; + this_track->track_green = true; + this_track->mode = MODE2_FORM_MIX; + this_track->datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE; + this_track->datasize = M2RAW_SECTOR_SIZE; + this_track->endsize = 0; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_DATA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else if (0 == strcmp ("MODE2/2352", psz_field)) { + if (cd) { + this_track->blocksize = 2352; + this_track->track_format= TRACK_FORMAT_XA; + this_track->track_green = true; + this_track->mode = MODE2_RAW; + this_track->datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE; + this_track->datasize = CDIO_CD_FRAMESIZE; + this_track->endsize = CDIO_CD_SYNC_SIZE + CDIO_CD_ECC_SIZE; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_DATA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else { + cdio_log(log_level, + "%s line %d after word TRACK:", + psz_cue_name, i_line); + cdio_log(log_level, + "Unknown track mode %s", psz_field); + goto err_exit; + } + } else { + goto format_error; + } + + /* FLAGS flag1 flag2 ... */ + } else if (0 == strcmp ("FLAGS", psz_keyword)) { + if (0 <= i) { + while (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + if (0 == strcmp ("PRE", psz_field)) { + if (cd) cd->tocent[i].flags |= PRE_EMPHASIS; + } else if (0 == strcmp ("DCP", psz_field)) { + if (cd) cd->tocent[i].flags |= COPY_PERMITTED; + } else if (0 == strcmp ("4CH", psz_field)) { + if (cd) cd->tocent[i].flags |= FOUR_CHANNEL_AUDIO; + } else if (0 == strcmp ("SCMS", psz_field)) { + if (cd) cd->tocent[i].flags |= SCMS; + } else { + goto format_error; + } + } + } else { + goto format_error; + } + + /* ISRC CCOOOYYSSSSS */ + } else if (0 == strcmp ("ISRC", psz_keyword)) { + if (0 <= i) { + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + if (cd) cd->tocent[i].isrc = strdup (psz_field); + } else { + goto format_error; + } + } else { + goto in_global_section; + } + + /* PREGAP MM:SS:FF */ + } else if (0 == strcmp ("PREGAP", psz_keyword)) { + if (0 <= i) { + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + lba_t lba = cdio_lsn_to_lba(cdio_mmssff_to_lba (psz_field)); + if (CDIO_INVALID_LBA == lba) { + cdio_log(log_level, "%s line %d: after word PREGAP:", + psz_cue_name, i_line); + cdio_log(log_level, "Invalid MSF string %s", + psz_field); + goto err_exit; + } + if (cd) { + cd->tocent[i].silence = lba; + } + } else { + goto format_error; + } if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + } else { + goto in_global_section; + } + + /* INDEX [##] MM:SS:FF */ + } else if (0 == strcmp ("INDEX", psz_keyword)) { + if (0 <= i) { + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) + if (1!=sscanf(psz_field, "%d", &start_index)) { + cdio_log(log_level, + "%s line %d after word INDEX:", + psz_cue_name, i_line); + cdio_log(log_level, + "expecting an index number, got %s", + psz_field); + goto err_exit; + } + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + lba_t lba = cdio_mmssff_to_lba (psz_field); + if (CDIO_INVALID_LBA == lba) { + cdio_log(log_level, "%s line %d: after word INDEX:", + psz_cue_name, i_line); + cdio_log(log_level, "Invalid MSF string %s", + psz_field); + goto err_exit; + } + if (cd) { +#if FIXED_ME + cd->tocent[i].indexes[cd->tocent[i].nindex++] = lba; +#else + track_info_t *this_track= + &(cd->tocent[cd->gen.i_tracks - cd->gen.i_first_track]); + + switch (start_index) { + + case 0: + lba += CDIO_PREGAP_SECTORS; + this_track->pregap = lba; + break; + + case 1: + if (!b_first_index_for_track) { + lba += CDIO_PREGAP_SECTORS; + cdio_lba_to_msf(lba, &(this_track->start_msf)); + b_first_index_for_track = true; + this_track->start_lba = lba; + } + + if (cd->gen.i_tracks > 1) { + /* Figure out number of sectors for previous track */ + track_info_t *prev_track=&(cd->tocent[cd->gen.i_tracks-2]); + if ( this_track->start_lba < prev_track->start_lba ) { + cdio_log (log_level, + "track %d at LBA %lu starts before track %d at LBA %lu", + cd->gen.i_tracks, + (unsigned long int) this_track->start_lba, + cd->gen.i_tracks, + (unsigned long int) prev_track->start_lba); + prev_track->sec_count = 0; + } else if ( this_track->start_lba >= prev_track->start_lba + + CDIO_PREGAP_SECTORS ) { + prev_track->sec_count = this_track->start_lba - + prev_track->start_lba - CDIO_PREGAP_SECTORS ; + } else { + cdio_log (log_level, + "%lu fewer than pregap (%d) sectors in track %d", + (long unsigned int) + this_track->start_lba - prev_track->start_lba, + CDIO_PREGAP_SECTORS, + cd->gen.i_tracks); + /* Include pregap portion in sec_count. Maybe the pregap + was omitted. */ + prev_track->sec_count = this_track->start_lba - + prev_track->start_lba; + } + } + this_track->num_indices++; + break; + + default: + break; + } + } +#endif + } else { + goto format_error; + } + } else { + goto in_global_section; + } + + /* CD-Text */ + } else if ( CDTEXT_INVALID != + (cdtext_key = cdtext_is_keyword (psz_keyword)) ) { + if (-1 == i) { + if (cd) { + cdtext_set (cdtext_key, + strtok (NULL, "\"\t\n\r"), + &(cd->gen.cdtext)); + } + } else { + if (cd) { + cdtext_set (cdtext_key, strtok (NULL, "\"\t\n\r"), + &(cd->gen.cdtext_track[i])); + } + } + + /* unrecognized line */ + } else { + cdio_log(log_level, "%s line %d: warning: unrecognized keyword: %s", + psz_cue_name, i_line, psz_keyword); + goto err_exit; + } + } + } + + if (NULL != cd) { + cd->gen.toc_init = true; + } + + fclose (fp); + return true; + + format_error: + cdio_log(log_level, "%s line %d after word %s", + psz_cue_name, i_line, psz_keyword); + goto err_exit; + + in_global_section: + cdio_log(log_level, "%s line %d: word %s not allowed in global section", + psz_cue_name, i_line, psz_keyword); + goto err_exit; + + not_in_global_section: + cdio_log(log_level, "%s line %d: word %s only allowed in global section", + psz_cue_name, i_line, psz_keyword); + + err_exit: + fclose (fp); + return false; + +} + +/*! + Reads a single audio sector from CD device into data starting + from lsn. Returns 0 if no error. + */ +static driver_return_code_t +_read_audio_sectors_bincue (void *p_user_data, void *data, lsn_t lsn, + unsigned int nblocks) +{ + _img_private_t *p_env = p_user_data; + int ret; + + ret = cdio_stream_seek (p_env->gen.data_source, + lsn * CDIO_CD_FRAMESIZE_RAW, SEEK_SET); + if (ret!=0) return ret; + + ret = cdio_stream_read (p_env->gen.data_source, data, + CDIO_CD_FRAMESIZE_RAW, nblocks); + + /* ret is number of bytes if okay, but we need to return 0 okay. */ + return ret == 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static driver_return_code_t +_read_mode1_sector_bincue (void *p_user_data, void *data, lsn_t lsn, + bool b_form2) +{ + _img_private_t *p_env = p_user_data; + int ret; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + int blocksize = CDIO_CD_FRAMESIZE_RAW; + + ret = cdio_stream_seek (p_env->gen.data_source, lsn * blocksize, SEEK_SET); + if (ret!=0) return ret; + + /* FIXME: Not completely sure the below is correct. */ + ret = cdio_stream_read (p_env->gen.data_source, buf, CDIO_CD_FRAMESIZE_RAW, 1); + if (ret==0) return ret; + + memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE, + b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE); + + return DRIVER_OP_SUCCESS; +} + +/*! + Reads nblocks of mode1 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static driver_return_code_t +_read_mode1_sectors_bincue (void *p_user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *p_env = p_user_data; + int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode1_sector_bincue (p_env, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Reads a single mode1 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static driver_return_code_t +_read_mode2_sector_bincue (void *p_user_data, void *data, lsn_t lsn, + bool b_form2) +{ + _img_private_t *p_env = p_user_data; + int ret; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + + /* NOTE: The logic below seems a bit wrong and convoluted + to me, but passes the regression tests. (Perhaps it is why we get + valgrind errors in vcdxrip). Leave it the way it was for now. + Review this sector 2336 stuff later. + */ + + int blocksize = CDIO_CD_FRAMESIZE_RAW; + + ret = cdio_stream_seek (p_env->gen.data_source, lsn * blocksize, SEEK_SET); + if (ret!=0) return ret; + + ret = cdio_stream_read (p_env->gen.data_source, buf, CDIO_CD_FRAMESIZE_RAW, 1); + if (ret==0) return ret; + + + /* See NOTE above. */ + if (b_form2) + memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE, + M2RAW_SECTOR_SIZE); + else + memcpy (data, buf + CDIO_CD_XA_SYNC_HEADER, CDIO_CD_FRAMESIZE); + + return DRIVER_OP_SUCCESS; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static driver_return_code_t +_read_mode2_sectors_bincue (void *p_user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *p_env = p_user_data; + int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode2_sector_bincue (p_env, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/*! + Return an array of strings giving possible BIN/CUE disk images. + */ +char ** +cdio_get_devices_bincue (void) +{ + char **drives = NULL; + unsigned int num_files=0; +#ifdef HAVE_GLOB_H + unsigned int i; + glob_t globbuf; + globbuf.gl_offs = 0; + glob("*.cue", GLOB_DOOFFS, NULL, &globbuf); + for (i=0; ipsz_vendor, "libcdio", + sizeof(hw_info->psz_vendor)-1); + hw_info->psz_vendor[sizeof(hw_info->psz_vendor)-1] = '\0'; + strncpy(hw_info->psz_model, "CDRWIN", + sizeof(hw_info->psz_model)-1); + hw_info->psz_model[sizeof(hw_info->psz_model)-1] = '\0'; + strncpy(hw_info->psz_revision, CDIO_VERSION, + sizeof(hw_info->psz_revision)-1); + hw_info->psz_revision[sizeof(hw_info->psz_revision)-1] = '\0'; + return true; + +} + +/*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_format_t +_get_track_format_bincue(void *p_user_data, track_t i_track) +{ + const _img_private_t *p_env = p_user_data; + + if (!p_env->gen.init) return TRACK_FORMAT_ERROR; + + if (i_track > p_env->gen.i_tracks || i_track == 0) + return TRACK_FORMAT_ERROR; + + return p_env->tocent[i_track-p_env->gen.i_first_track].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 +_get_track_green_bincue(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if ( NULL == p_env || + ( i_track < p_env->gen.i_first_track + || i_track >= p_env->gen.i_tracks + p_env->gen.i_first_track ) ) + return false; + + return p_env->tocent[i_track-p_env->gen.i_first_track].track_green; +} + +/*! + Return the starting LSN track number + i_track in obj. Track numbers start at 1. + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +static lba_t +_get_lba_track_bincue(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = p_env->gen.i_tracks+1; + + if (i_track <= p_env->gen.i_tracks + p_env->gen.i_first_track + && i_track != 0) { + return p_env->tocent[i_track-p_env->gen.i_first_track].start_lba; + } else + return CDIO_INVALID_LBA; +} + +/*! + Return corresponding BIN file if psz_cue_name is a cue file or NULL + if not a CUE file. +*/ +char * +cdio_is_cuefile(const char *psz_cue_name) +{ + int i; + char *psz_bin_name; + + if (psz_cue_name == NULL) return NULL; + + /* FIXME? Now that we have cue parsing, should we really force + the filename extension requirement or is it enough just to + parse the cuefile? + */ + + psz_bin_name=strdup(psz_cue_name); + i=strlen(psz_bin_name)-strlen("cue"); + + if (i>0) { + if (psz_cue_name[i]=='c' && psz_cue_name[i+1]=='u' && psz_cue_name[i+2]=='e') { + psz_bin_name[i++]='b'; psz_bin_name[i++]='i'; psz_bin_name[i++]='n'; + if (parse_cuefile(NULL, psz_cue_name)) + return psz_bin_name; + else + goto error; + } + else if (psz_cue_name[i]=='C' && psz_cue_name[i+1]=='U' && psz_cue_name[i+2]=='E') { + psz_bin_name[i++]='B'; psz_bin_name[i++]='I'; psz_bin_name[i++]='N'; + if (parse_cuefile(NULL, psz_cue_name)) + return psz_bin_name; + else + goto error; + } + } + error: + free(psz_bin_name); + return NULL; +} + +/*! + Return corresponding CUE file if psz_bin_name is a bin file or NULL + if not a BIN file. +*/ +char * +cdio_is_binfile(const char *psz_bin_name) +{ + int i; + char *psz_cue_name; + + if (psz_bin_name == NULL) return NULL; + + psz_cue_name=strdup(psz_bin_name); + i=strlen(psz_bin_name)-strlen("bin"); + + if (i>0) { + if (psz_bin_name[i]=='b' && psz_bin_name[i+1]=='i' && psz_bin_name[i+2]=='n') { + psz_cue_name[i++]='c'; psz_cue_name[i++]='u'; psz_cue_name[i++]='e'; + return psz_cue_name; + } + else if (psz_bin_name[i]=='B' && psz_bin_name[i+1]=='I' && psz_bin_name[i+2]=='N') { + psz_cue_name[i++]='C'; psz_cue_name[i++]='U'; psz_cue_name[i++]='E'; + return psz_cue_name; + } + } + free(psz_cue_name); + return NULL; +} + +/*! + 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_t * +cdio_open_am_bincue (const char *psz_source_name, const char *psz_access_mode) +{ + if (psz_access_mode != NULL) + cdio_warn ("there is only one access mode for bincue. Arg %s ignored", + psz_access_mode); + return cdio_open_bincue(psz_source_name); +} + +/*! + 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_t * +cdio_open_bincue (const char *psz_source) +{ + char *psz_bin_name = cdio_is_cuefile(psz_source); + + if (NULL != psz_bin_name) { + free(psz_bin_name); + return cdio_open_cue(psz_source); + } else { + char *psz_cue_name = cdio_is_binfile(psz_source); + CdIo_t *cdio = cdio_open_cue(psz_cue_name); + free(psz_cue_name); + return cdio; + } +} + +CdIo_t * +cdio_open_cue (const char *psz_cue_name) +{ + CdIo_t *ret; + _img_private_t *p_data; + char *psz_bin_name; + + cdio_funcs_t _funcs; + + memset( &_funcs, 0, sizeof(_funcs) ); + + _funcs.eject_media = _eject_media_image; + _funcs.free = _free_image; + _funcs.get_arg = _get_arg_image; + _funcs.get_cdtext = get_cdtext_generic; + _funcs.get_devices = cdio_get_devices_bincue; + _funcs.get_default_device = cdio_get_default_device_bincue; + _funcs.get_disc_last_lsn = get_disc_last_lsn_bincue; + _funcs.get_discmode = _get_discmode_image; + _funcs.get_drive_cap = _get_drive_cap_image; + _funcs.get_first_track_num = _get_first_track_num_image; + _funcs.get_hwinfo = get_hwinfo_bincue; + _funcs.get_media_changed = get_media_changed_image; + _funcs.get_mcn = _get_mcn_image; + _funcs.get_num_tracks = _get_num_tracks_image; + _funcs.get_track_channels = get_track_channels_image; + _funcs.get_track_copy_permit = get_track_copy_permit_image; + _funcs.get_track_format = _get_track_format_bincue; + _funcs.get_track_green = _get_track_green_bincue; + _funcs.get_track_lba = _get_lba_track_bincue; + _funcs.get_track_msf = _get_track_msf_image; + _funcs.get_track_preemphasis = get_track_preemphasis_image; + _funcs.get_track_pregap_lba = get_track_pregap_lba_image; + _funcs.get_track_isrc = get_track_isrc_image; + _funcs.lseek = _lseek_bincue; + _funcs.read = _read_bincue; + _funcs.read_audio_sectors = _read_audio_sectors_bincue; + _funcs.read_data_sectors = read_data_sectors_image; + _funcs.read_mode1_sector = _read_mode1_sector_bincue; + _funcs.read_mode1_sectors = _read_mode1_sectors_bincue; + _funcs.read_mode2_sector = _read_mode2_sector_bincue; + _funcs.read_mode2_sectors = _read_mode2_sectors_bincue; + _funcs.run_mmc_cmd = NULL; + _funcs.set_arg = _set_arg_image; + _funcs.set_speed = cdio_generic_unimplemented_set_speed; + _funcs.set_blocksize = cdio_generic_unimplemented_set_blocksize; + + if (NULL == psz_cue_name) return NULL; + + p_data = calloc(1, sizeof (_img_private_t)); + p_data->gen.init = false; + p_data->psz_cue_name = NULL; + + ret = cdio_new ((void *)p_data, &_funcs); + + if (ret == NULL) { + free(p_data); + return NULL; + } + + ret->driver_id = DRIVER_BINCUE; + psz_bin_name = cdio_is_cuefile(psz_cue_name); + + if (NULL == psz_bin_name) { + cdio_error ("source name %s is not recognized as a CUE file", + psz_cue_name); + } + + _set_arg_image (p_data, "cue", psz_cue_name); + _set_arg_image (p_data, "source", psz_bin_name); + _set_arg_image (p_data, "access-mode", "bincue"); + free(psz_bin_name); + + if (_init_bincue(p_data)) { + return ret; + } else { + _free_image(p_data); + free(ret); + return NULL; + } +} + +bool +cdio_have_bincue (void) +{ + return true; +} diff --git a/lib/driver/image/cdrdao.c b/lib/driver/image/cdrdao.c new file mode 100644 index 00000000..c7083b29 --- /dev/null +++ b/lib/driver/image/cdrdao.c @@ -0,0 +1,1341 @@ +/* + $Id: cdrdao.c,v 1.27 2008/04/21 18:30:22 karl Exp $ + + Copyright (C) 2004, 2005, 2006, 2007, 2008 Rocky Bernstein + toc reading routine adapted from cuetools + Copyright (C) 2003 Svend Sanjay Sorensen + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* This code implements low-level access functions for a CD images + residing inside a disk file (*.bin) and its associated cue sheet. + (*.cue). +*/ + +static const char _rcsid[] = "$Id: cdrdao.c,v 1.27 2008/04/21 18:30:22 karl Exp $"; + +#include "image.h" +#include "cdio_assert.h" +#include "_cdio_stdio.h" + +#include +#include +#include +#include + +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STRINGS_H +#include +#endif +#ifdef HAVE_GLOB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include + +#include "portable.h" + +/* reader */ + +#define DEFAULT_CDIO_DEVICE "videocd.bin" +#define DEFAULT_CDIO_CDRDAO "videocd.toc" + +#include "image_common.h" + +static lsn_t get_disc_last_lsn_cdrdao (void *p_user_data); +static bool parse_tocfile (_img_private_t *cd, const char *p_toc_name); + + +static bool +check_track_is_blocksize_multiple(const char *psz_fname, + track_t i_track, long i_size, + uint16_t i_blocksize) +{ + if (i_size % i_blocksize) { + cdio_info ("image %s track %d size (%ld) not a multiple" + " of the blocksize (%ld)", psz_fname, i_track, i_size, + (long int) i_blocksize); + if (i_size % M2RAW_SECTOR_SIZE == 0) + cdio_info ("this may be a 2336-type disc image"); + else if (i_size % CDIO_CD_FRAMESIZE_RAW == 0) + cdio_info ("this may be a 2352-type disc image"); + return false; + } + return true; +} + + +/*! + Initialize image structures. + */ +static bool +_init_cdrdao (_img_private_t *env) +{ + lsn_t lead_lsn; + + if (env->gen.init) + return false; + + /* Have to set init before calling get_disc_last_lsn_cdrdao() or we will + get into infinite recursion calling passing right here. + */ + env->gen.init = true; + env->gen.i_first_track = 1; + env->psz_mcn = NULL; + env->disc_mode = CDIO_DISC_MODE_NO_INFO; + + cdtext_init (&(env->gen.cdtext)); + + /* Read in TOC sheet. */ + if ( !parse_tocfile(env, env->psz_cue_name) ) return false; + + lead_lsn = get_disc_last_lsn_cdrdao( (_img_private_t *) env); + + if (-1 == lead_lsn) + return false; + + /* Fake out leadout track and sector count for last track*/ + cdio_lsn_to_msf (lead_lsn, &env->tocent[env->gen.i_tracks].start_msf); + env->tocent[env->gen.i_tracks].start_lba = cdio_lsn_to_lba(lead_lsn); + env->tocent[env->gen.i_tracks-env->gen.i_first_track].sec_count = + cdio_lsn_to_lba(lead_lsn - env->tocent[env->gen.i_tracks-1].start_lba); + + return true; +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + Would be libc's seek() but we have to adjust for the extra track header + information in each sector. +*/ +static off_t +_lseek_cdrdao (void *user_data, off_t offset, int whence) +{ + _img_private_t *env = user_data; + + /* real_offset is the real byte offset inside the disk image + The number below was determined empirically. I'm guessing + the 1st 24 bytes of a bin file are used for something. + */ + off_t real_offset=0; + + unsigned int i; + + env->pos.lba = 0; + for (i=0; igen.i_tracks; i++) { + track_info_t *this_track=&(env->tocent[i]); + env->pos.index = i; + if ( (this_track->sec_count*this_track->datasize) >= offset) { + int blocks = offset / this_track->datasize; + int rem = offset % this_track->datasize; + int block_offset = blocks * this_track->blocksize; + real_offset += block_offset + rem; + env->pos.buff_offset = rem; + env->pos.lba += blocks; + break; + } + real_offset += this_track->sec_count*this_track->blocksize; + offset -= this_track->sec_count*this_track->datasize; + env->pos.lba += this_track->sec_count; + } + + if (i==env->gen.i_tracks) { + cdio_warn ("seeking outside range of disk image"); + return -1; + } else { + real_offset += env->tocent[i].datastart; + return cdio_stream_seek(env->tocent[i].data_source, real_offset, whence); + } +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + FIXME: + At present we assume a read doesn't cross sector or track + boundaries. +*/ +static ssize_t +_read_cdrdao (void *user_data, void *data, size_t size) +{ + _img_private_t *env = user_data; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + char *p = data; + ssize_t final_size=0; + ssize_t this_size; + track_info_t *this_track=&(env->tocent[env->pos.index]); + ssize_t skip_size = this_track->datastart + this_track->endsize; + + while (size > 0) { + int rem = this_track->datasize - env->pos.buff_offset; + if (size <= rem) { + this_size = cdio_stream_read(this_track->data_source, buf, size, 1); + final_size += this_size; + memcpy (p, buf, this_size); + break; + } + + /* Finish off reading this sector. */ + cdio_warn ("Reading across block boundaries not finished"); + + size -= rem; + this_size = cdio_stream_read(this_track->data_source, buf, rem, 1); + final_size += this_size; + memcpy (p, buf, this_size); + p += this_size; + this_size = cdio_stream_read(this_track->data_source, buf, rem, 1); + + /* Skip over stuff at end of this sector and the beginning of the next. + */ + cdio_stream_read(this_track->data_source, buf, skip_size, 1); + + /* Get ready to read another sector. */ + env->pos.buff_offset=0; + env->pos.lba++; + + /* Have gone into next track. */ + if (env->pos.lba >= env->tocent[env->pos.index+1].start_lba) { + env->pos.index++; + this_track=&(env->tocent[env->pos.index]); + skip_size = this_track->datastart + this_track->endsize; + } + } + return final_size; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static lsn_t +get_disc_last_lsn_cdrdao (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + track_t i_leadout = p_env->gen.i_tracks; + uint16_t i_blocksize = p_env->tocent[i_leadout-1].blocksize; + long i_size; + + if (p_env->tocent[i_leadout-1].sec_count) { + i_size = p_env->tocent[i_leadout-1].sec_count; + } else { + i_size = cdio_stream_stat(p_env->tocent[i_leadout-1].data_source) + - p_env->tocent[i_leadout-1].offset; + if (check_track_is_blocksize_multiple(p_env->tocent[i_leadout-1].filename, + i_leadout-1, i_size, i_blocksize)) { + i_size /= i_blocksize; + } else { + /* Round up */ + i_size = (i_size / i_blocksize) + 1; + } + } + + i_size += p_env->tocent[i_leadout-1].start_lba; + i_size -= CDIO_PREGAP_SECTORS; + + return i_size; +} + +#define MAXLINE 512 +#define UNIMPLIMENTED_MSG \ + cdio_log(log_level, "%s line %d: unimplimented keyword: %s", \ + psz_cue_name, i_line, psz_keyword) + + +static bool +parse_tocfile (_img_private_t *cd, const char *psz_cue_name) +{ + /* The below declarations may be common in other image-parse routines. */ + FILE *fp; + char psz_line[MAXLINE]; /* text of current line read in file fp. */ + unsigned int i_line=0; /* line number in file of psz_line. */ + int i = -1; /* Position in tocent. Same as + cd->gen.i_tracks - 1 */ + char *psz_keyword, *psz_field; + cdio_log_level_t log_level = (cd) ? CDIO_LOG_WARN : CDIO_LOG_INFO ; + cdtext_field_t cdtext_key; + + /* The below declaration(s) may be unique to this image-parse routine. */ + unsigned int i_cdtext_nest = 0; + + if (NULL == psz_cue_name) + return false; + + fp = fopen (psz_cue_name, "r"); + if (fp == NULL) { + cdio_log(log_level, "error opening %s for reading: %s", + psz_cue_name, strerror(errno)); + return false; + } + + if (cd) { + cd->gen.b_cdtext_init = true; + cd->gen.b_cdtext_error = false; + } + + while (fgets(psz_line, MAXLINE, fp)) { + + i_line++; + + /* strip comment from line */ + /* todo: // in quoted strings? */ + /* //comment */ + if ((psz_field = strstr (psz_line, "//"))) + *psz_field = '\0'; + + if ((psz_keyword = strtok (psz_line, " \t\n\r"))) { + /* CATALOG "ddddddddddddd" */ + if (0 == strcmp ("CATALOG", psz_keyword)) { + if (-1 == i) { + if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) { + if (13 != strlen(psz_field)) { + cdio_log(log_level, + "%s line %d after word CATALOG:", + psz_cue_name, i_line); + cdio_log(log_level, + "Token %s has length %ld. Should be 13 digits.", + psz_field, (long int) strlen(psz_field)); + + goto err_exit; + } else { + /* Check that we have all digits*/ + unsigned int i; + for (i=0; i<13; i++) { + if (!isdigit(psz_field[i])) { + cdio_log(log_level, + "%s line %d after word CATALOG:", + psz_cue_name, i_line); + cdio_log(log_level, + "Character \"%c\" at postition %i of token \"%s\"" + " is not all digits.", + psz_field[i], i+1, psz_field); + goto err_exit; + } + } + if (NULL != cd) cd->psz_mcn = strdup (psz_field); + } + } else { + cdio_log(log_level, + "%s line %d after word CATALOG:", + psz_cue_name, i_line); + cdio_log(log_level, "Expecting 13 digits; nothing seen."); + goto err_exit; + } + } else { + goto err_exit; + } + + /* CD_DA | CD_ROM | CD_ROM_XA */ + } else if (0 == strcmp ("CD_DA", psz_keyword)) { + if (-1 == i) { + if (NULL != cd) + cd->disc_mode = CDIO_DISC_MODE_CD_DA; + } else { + goto not_in_global_section; + } + } else if (0 == strcmp ("CD_ROM", psz_keyword)) { + if (-1 == i) { + if (NULL != cd) + cd->disc_mode = CDIO_DISC_MODE_CD_DATA; + } else { + goto not_in_global_section; + } + + } else if (0 == strcmp ("CD_ROM_XA", psz_keyword)) { + if (-1 == i) { + if (NULL != cd) + cd->disc_mode = CDIO_DISC_MODE_CD_XA; + } else { + goto not_in_global_section; + } + + /* TRACK [] */ + } else if (0 == strcmp ("TRACK", psz_keyword)) { + i++; + if (NULL != cd) cdtext_init (&(cd->gen.cdtext_track[i])); + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + if (0 == strcmp ("AUDIO", psz_field)) { + if (NULL != cd) { + cd->tocent[i].track_format = TRACK_FORMAT_AUDIO; + cd->tocent[i].blocksize = CDIO_CD_FRAMESIZE_RAW; + cd->tocent[i].datasize = CDIO_CD_FRAMESIZE_RAW; + cd->tocent[i].datastart = 0; + cd->tocent[i].endsize = 0; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_DA; + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_XA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + + } + } else if (0 == strcmp ("MODE1", psz_field)) { + if (NULL != cd) { + cd->tocent[i].track_format = TRACK_FORMAT_DATA; + cd->tocent[i].blocksize = CDIO_CD_FRAMESIZE_RAW; + cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE; + cd->tocent[i].datasize = CDIO_CD_FRAMESIZE; + cd->tocent[i].endsize = CDIO_CD_EDC_SIZE + + CDIO_CD_M1F1_ZERO_SIZE + CDIO_CD_ECC_SIZE; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_DATA; + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_XA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else if (0 == strcmp ("MODE1_RAW", psz_field)) { + if (NULL != cd) { + cd->tocent[i].track_format = TRACK_FORMAT_DATA; + cd->tocent[i].blocksize = CDIO_CD_FRAMESIZE_RAW; + cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE; + cd->tocent[i].datasize = CDIO_CD_FRAMESIZE; + cd->tocent[i].endsize = CDIO_CD_EDC_SIZE + + CDIO_CD_M1F1_ZERO_SIZE + CDIO_CD_ECC_SIZE; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_DATA; + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_XA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else if (0 == strcmp ("MODE2", psz_field)) { + if (NULL != cd) { + cd->tocent[i].track_format = TRACK_FORMAT_XA; + cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE; + cd->tocent[i].datasize = M2RAW_SECTOR_SIZE; + cd->tocent[i].endsize = 0; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_DATA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else if (0 == strcmp ("MODE2_FORM1", psz_field)) { + if (NULL != cd) { + cd->tocent[i].track_format = TRACK_FORMAT_XA; + cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE; + cd->tocent[i].datasize = CDIO_CD_FRAMESIZE_RAW; + cd->tocent[i].endsize = 0; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_DATA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else if (0 == strcmp ("MODE2_FORM2", psz_field)) { + if (NULL != cd) { + cd->tocent[i].track_format = TRACK_FORMAT_XA; + cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE; + cd->tocent[i].datasize = CDIO_CD_FRAMESIZE; + cd->tocent[i].endsize = CDIO_CD_SYNC_SIZE + + CDIO_CD_ECC_SIZE; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_DATA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else if (0 == strcmp ("MODE2_FORM_MIX", psz_field)) { + if (NULL != cd) { + cd->tocent[i].track_format = TRACK_FORMAT_XA; + cd->tocent[i].datasize = M2RAW_SECTOR_SIZE; + cd->tocent[i].blocksize = CDIO_CD_FRAMESIZE_RAW; + cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE; + cd->tocent[i].track_green = true; + cd->tocent[i].endsize = 0; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_DATA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else if (0 == strcmp ("MODE2_RAW", psz_field)) { + if (NULL != cd) { + cd->tocent[i].track_format = TRACK_FORMAT_XA; + cd->tocent[i].blocksize = CDIO_CD_FRAMESIZE_RAW; + cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE; + cd->tocent[i].datasize = CDIO_CD_FRAMESIZE; + cd->tocent[i].track_green = true; + cd->tocent[i].endsize = 0; + switch(cd->disc_mode) { + case CDIO_DISC_MODE_NO_INFO: + cd->disc_mode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* Disc type stays the same. */ + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_DATA: + cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + default: + cd->disc_mode = CDIO_DISC_MODE_ERROR; + } + } + } else { + cdio_log(log_level, "%s line %d after TRACK:", + psz_cue_name, i_line); + cdio_log(log_level, "'%s' not a valid mode.", psz_field); + goto err_exit; + } + } + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + /* \todo: set sub-channel-mode */ +#ifdef TODO + if (0 == strcmp ("RW", psz_field)) + ; + else if (0 == strcmp ("RW_RAW", psz_field)) + ; +#endif + } + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + + /* track flags */ + /* [NO] COPY | [NO] PRE_EMPHASIS */ + } else if (0 == strcmp ("NO", psz_keyword)) { + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + if (0 == strcmp ("COPY", psz_field)) { + if (NULL != cd) + cd->tocent[i].flags &= ~CDIO_TRACK_FLAG_COPY_PERMITTED; + + } else if (0 == strcmp ("PRE_EMPHASIS", psz_field)) + if (NULL != cd) { + cd->tocent[i].flags &= ~CDIO_TRACK_FLAG_PRE_EMPHASIS; + goto err_exit; + } + } else { + goto format_error; + } + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + } else if (0 == strcmp ("COPY", psz_keyword)) { + if (NULL != cd) + cd->tocent[i].flags |= CDIO_TRACK_FLAG_COPY_PERMITTED; + } else if (0 == strcmp ("PRE_EMPHASIS", psz_keyword)) { + if (NULL != cd) + cd->tocent[i].flags |= CDIO_TRACK_FLAG_PRE_EMPHASIS; + /* TWO_CHANNEL_AUDIO */ + } else if (0 == strcmp ("TWO_CHANNEL_AUDIO", psz_keyword)) { + if (NULL != cd) + cd->tocent[i].flags &= ~CDIO_TRACK_FLAG_FOUR_CHANNEL_AUDIO; + /* FOUR_CHANNEL_AUDIO */ + } else if (0 == strcmp ("FOUR_CHANNEL_AUDIO", psz_keyword)) { + if (NULL != cd) + cd->tocent[i].flags |= CDIO_TRACK_FLAG_FOUR_CHANNEL_AUDIO; + + /* ISRC "CCOOOYYSSSSS" */ + } else if (0 == strcmp ("ISRC", psz_keyword)) { + if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) { + if (NULL != cd) + cd->tocent[i].isrc = strdup(psz_field); + } else { + goto format_error; + } + + /* SILENCE */ + } else if (0 == strcmp ("SILENCE", psz_keyword)) { + UNIMPLIMENTED_MSG; + + /* ZERO */ + } else if (0 == strcmp ("ZERO", psz_keyword)) { + UNIMPLIMENTED_MSG; + + /* [FILE|AUDIOFILE] "" [] */ + } else if (0 == strcmp ("FILE", psz_keyword) + || 0 == strcmp ("AUDIOFILE", psz_keyword)) { + if (0 <= i) { + if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) { + long i_size; + + /* Handle "" */ + if (cd) { + cd->tocent[i].filename = strdup (psz_field); + /* To do: do something about reusing existing files. */ + if (!(cd->tocent[i].data_source = cdio_stdio_new (psz_field))) { + cdio_log (log_level, + "%s line %d: can't open file `%s' for reading", + psz_cue_name, i_line, psz_field); + goto err_exit; + } + i_size = cdio_stream_stat(cd->tocent[i].data_source); + } else { + CdioDataSource_t *s = cdio_stdio_new (psz_field); + if (!s) { + cdio_log (log_level, + "%s line %d: can't open file `%s' for reading", + psz_cue_name, i_line, psz_field); + goto err_exit; + } + i_size = cdio_stream_stat(s); + cdio_stdio_destroy (s); + } + } + + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + /* Handle */ + lba_t i_start_lba = + cdio_lsn_to_lba(cdio_mmssff_to_lba (psz_field)); + if (CDIO_INVALID_LBA == i_start_lba) { + cdio_log(log_level, "%s line %d: invalid MSF string %s", + psz_cue_name, i_line, psz_field); + goto err_exit; + } + + if (NULL != cd) { + cd->tocent[i].start_lba = i_start_lba; + cdio_lba_to_msf(i_start_lba, &(cd->tocent[i].start_msf)); + } + } + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + /* Handle */ + lba_t lba = cdio_mmssff_to_lba (psz_field); + if (CDIO_INVALID_LBA == lba) { + cdio_log(log_level, "%s line %d: invalid MSF string %s", + psz_cue_name, i_line, psz_field); + goto err_exit; + } + if (cd) { + long i_size = cdio_stream_stat(cd->tocent[i].data_source); + if (lba) { + if ( (lba * cd->tocent[i].datasize) > i_size) { + cdio_log(log_level, + "%s line %d: MSF length %s exceeds end of file", + psz_cue_name, i_line, psz_field); + goto err_exit; + } + } else { + lba = i_size / cd->tocent[i].blocksize; + } + cd->tocent[i].sec_count = lba; + } + } + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + } else { + goto not_in_global_section; + } + + /* DATAFILE "" #byte-offset */ + } else if (0 == strcmp ("DATAFILE", psz_keyword)) { + if (0 <= i) { + if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) { + /* Handle */ + if (cd) { + cd->tocent[i].filename = strdup (psz_field); + /* To do: do something about reusing existing files. */ + if (!(cd->tocent[i].data_source = cdio_stdio_new (psz_field))) { + cdio_log (log_level, + "%s line %d: can't open file `%s' for reading", + psz_cue_name, i_line, psz_field); + goto err_exit; + } + } else { + CdioDataSource_t *s = cdio_stdio_new (psz_field); + if (!s) { + cdio_log (log_level, + "%s line %d: can't open file `%s' for reading", + psz_cue_name, i_line, psz_field); + goto err_exit; + } + cdio_stdio_destroy (s); + } + } + + psz_field = strtok (NULL, " \t\n\r"); + if (psz_field) { + /* Handle optional #byte-offset */ + if ( psz_field[0] == '#') { + long int offset; + psz_field++; + errno = 0; + offset = strtol(psz_field, (char **)NULL, 10); + if ( (LONG_MIN == offset || LONG_MAX == offset) + && 0 != errno ) { + cdio_log (log_level, + "%s line %d: can't convert `%s' to byte offset", + psz_cue_name, i_line, psz_field); + goto err_exit; + } else { + if (NULL != cd) { + cd->tocent[i].offset = offset; + } + } + psz_field = strtok (NULL, " \t\n\r"); + } + } + if (psz_field) { + /* Handle start-msf */ + lba_t lba = cdio_mmssff_to_lba (psz_field); + if (CDIO_INVALID_LBA == lba) { + cdio_log(log_level, "%s line %d: invalid MSF string %s", + psz_cue_name, i_line, psz_field); + goto err_exit; + } + if (cd) { + cd->tocent[i].start_lba = lba; + cdio_lba_to_msf(cd->tocent[i].start_lba, + &(cd->tocent[i].start_msf)); + } + } else { + /* No start-msf. */ + if (cd) { + if (i) { + uint16_t i_blocksize = cd->tocent[i-1].blocksize; + long i_size = + cdio_stream_stat(cd->tocent[i-1].data_source); + + check_track_is_blocksize_multiple(cd->tocent[i-1].filename, + i-1, i_size, i_blocksize); + /* Append size of previous datafile. */ + cd->tocent[i].start_lba = cd->tocent[i-1].start_lba + + (i_size / i_blocksize); + } + cd->tocent[i].offset = 0; + cd->tocent[i].start_lba += CDIO_PREGAP_SECTORS; + cdio_lba_to_msf(cd->tocent[i].start_lba, + &(cd->tocent[i].start_msf)); + } + } + + } else { + goto not_in_global_section; + } + + /* FIFO "" [] */ + } else if (0 == strcmp ("FIFO", psz_keyword)) { + goto unimplimented_error; + + /* START MM:SS:FF */ + } else if (0 == strcmp ("START", psz_keyword)) { + if (0 <= i) { + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + /* todo: line is too long! */ + if (NULL != cd) { + cd->tocent[i].pregap = cd->tocent[i].start_lba; + cd->tocent[i].start_lba += cdio_mmssff_to_lba (psz_field); + cdio_lba_to_msf(cd->tocent[i].start_lba, + &(cd->tocent[i].start_msf)); + } + } + + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + } else { + goto not_in_global_section; + } + + /* PREGAP MM:SS:FF */ + } else if (0 == strcmp ("PREGAP", psz_keyword)) { + if (0 <= i) { + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + if (NULL != cd) + cd->tocent[i].silence = cdio_mmssff_to_lba (psz_field); + } else { + goto format_error; + } + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + } else { + goto not_in_global_section; + } + + /* INDEX MM:SS:FF */ + } else if (0 == strcmp ("INDEX", psz_keyword)) { + if (0 <= i) { + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + if (NULL != cd) { +#if 0 + if (1 == cd->tocent[i].nindex) { + cd->tocent[i].indexes[1] = cd->tocent[i].indexes[0]; + cd->tocent[i].nindex++; + } + cd->tocent[i].indexes[cd->tocent[i].nindex++] = + cdio_mmssff_to_lba (psz_field) + cd->tocent[i].indexes[0]; +#else + ; + +#endif + } + } else { + goto format_error; + } + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + } else { + goto not_in_global_section; + } + + /* CD_TEXT { ... } */ + /* todo: opening { must be on same line as CD_TEXT */ + } else if (0 == strcmp ("CD_TEXT", psz_keyword)) { + if (NULL == (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + if ( 0 == strcmp( "{", psz_field ) ) { + i_cdtext_nest++; + } else { + cdio_log (log_level, + "%s line %d: expecting '{'", psz_cue_name, i_line); + goto err_exit; + } + + } else if (0 == strcmp ("LANGUAGE_MAP", psz_keyword)) { + /* LANGUAGE d { ... } */ + } else if (0 == strcmp ("LANGUAGE", psz_keyword)) { + if (NULL == (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + /* Language number */ + if (NULL == (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + if ( 0 == strcmp( "{", psz_field ) ) { + i_cdtext_nest++; + } + } else if (0 == strcmp ("{", psz_keyword)) { + i_cdtext_nest++; + } else if (0 == strcmp ("}", psz_keyword)) { + if (i_cdtext_nest > 0) i_cdtext_nest--; + } else if ( CDTEXT_INVALID != + (cdtext_key = cdtext_is_keyword (psz_keyword)) ) { + if (-1 == i) { + if (NULL != cd) { + cdtext_set (cdtext_key, + strtok (NULL, "\"\t\n\r"), + &(cd->gen.cdtext)); + } + } else { + if (NULL != cd) { + cdtext_set (cdtext_key, + strtok (NULL, "\"\t\n\r"), + &(cd->gen.cdtext_track[i])); + } + } + + /* unrecognized line */ + } else { + cdio_log(log_level, "%s line %d: warning: unrecognized word: %s", + psz_cue_name, i_line, psz_keyword); + goto err_exit; + } + } + } + + if (NULL != cd) { + cd->gen.i_tracks = i+1; + cd->gen.toc_init = true; + } + + fclose (fp); + return true; + + unimplimented_error: + UNIMPLIMENTED_MSG; + goto err_exit; + + format_error: + cdio_log(log_level, "%s line %d after word %s", + psz_cue_name, i_line, psz_keyword); + goto err_exit; + + not_in_global_section: + cdio_log(log_level, "%s line %d: word %s only allowed in global section", + psz_cue_name, i_line, psz_keyword); + + err_exit: + fclose (fp); + return false; +} + +/*! + Reads a single audio sector from CD device into data starting + from lsn. Returns 0 if no error. + */ +static driver_return_code_t +_read_audio_sectors_cdrdao (void *user_data, void *data, lsn_t lsn, + unsigned int nblocks) +{ + _img_private_t *env = user_data; + int ret; + + ret = cdio_stream_seek (env->tocent[0].data_source, + lsn * CDIO_CD_FRAMESIZE_RAW, SEEK_SET); + if (ret!=0) return ret; + + ret = cdio_stream_read (env->tocent[0].data_source, data, + CDIO_CD_FRAMESIZE_RAW, nblocks); + + /* ret is number of bytes if okay, but we need to return 0 okay. */ + return ret == 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static driver_return_code_t +_read_mode1_sector_cdrdao (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + _img_private_t *env = user_data; + int ret; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + + ret = cdio_stream_seek (env->tocent[0].data_source, + lsn * CDIO_CD_FRAMESIZE_RAW, SEEK_SET); + if (ret!=0) return ret; + + /* FIXME: Not completely sure the below is correct. */ + ret = cdio_stream_read (env->tocent[0].data_source, buf, + CDIO_CD_FRAMESIZE_RAW, 1); + if (ret==0) return ret; + + memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE, + b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE); + + return DRIVER_OP_SUCCESS; +} + +/*! + Reads nblocks of mode1 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode1_sectors_cdrdao (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *env = user_data; + int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode1_sector_cdrdao (env, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Reads a single mode1 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static driver_return_code_t +_read_mode2_sector_cdrdao (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + _img_private_t *env = user_data; + int ret; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + long unsigned int i_off = lsn * CDIO_CD_FRAMESIZE_RAW; + + /* For sms's VCD's (mwc1.toc) it is more like this: + if (i_off > 272) i_off -= 272; + There is that magic 272 that we find in read_audio_sectors_cdrdao again. + */ + + /* NOTE: The logic below seems a bit wrong and convoluted + to me, but passes the regression tests. (Perhaps it is why we get + valgrind errors in vcdxrip). Leave it the way it was for now. + Review this sector 2336 stuff later. + */ + + ret = cdio_stream_seek (env->tocent[0].data_source, i_off, SEEK_SET); + if (ret!=0) return ret; + + ret = cdio_stream_read (env->tocent[0].data_source, buf, + CDIO_CD_FRAMESIZE_RAW, 1); + if (ret==0) return ret; + + + /* See NOTE above. */ + if (b_form2) + memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE, + M2RAW_SECTOR_SIZE); + else + memcpy (data, buf + CDIO_CD_XA_SYNC_HEADER, CDIO_CD_FRAMESIZE); + + return DRIVER_OP_SUCCESS; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static driver_return_code_t +_read_mode2_sectors_cdrdao (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *env = user_data; + int i; + int retval; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode2_sector_cdrdao (env, + ((char *)data) + (CDIO_CD_FRAMESIZE * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/*! + Return an array of strings giving possible TOC disk images. + */ +char ** +cdio_get_devices_cdrdao (void) +{ + char **drives = NULL; + unsigned int num_files=0; +#ifdef HAVE_GLOB_H + unsigned int i; + glob_t globbuf; + globbuf.gl_offs = 0; + glob("*.toc", GLOB_DOOFFS, NULL, &globbuf); + for (i=0; ipsz_vendor, "libcdio", + sizeof(hw_info->psz_vendor)-1); + hw_info->psz_vendor[sizeof(hw_info->psz_vendor)-1] = '\0'; + strncpy(hw_info->psz_model, "cdrdao", + sizeof(hw_info->psz_model)-1); + hw_info->psz_model[sizeof(hw_info->psz_model)-1] = '\0'; + strncpy(hw_info->psz_revision, CDIO_VERSION, + sizeof(hw_info->psz_revision)-1); + hw_info->psz_revision[sizeof(hw_info->psz_revision)-1] = '\0'; + return true; +} + +/*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_format_t +_get_track_format_cdrdao(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if (!p_env->gen.init) return TRACK_FORMAT_ERROR; + + if (i_track > p_env->gen.i_tracks || i_track == 0) + return TRACK_FORMAT_ERROR; + + return p_env->tocent[i_track-p_env->gen.i_first_track].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 +_get_track_green_cdrdao(void *user_data, track_t i_track) +{ + _img_private_t *env = user_data; + + if (!env->gen.init) _init_cdrdao(env); + + if (i_track > env->gen.i_tracks || i_track == 0) + return false; + + return env->tocent[i_track-env->gen.i_first_track].track_green; +} + +/*! + Return the starting LSN track number + i_track in obj. Track numbers start at 1. + The "leadout" track is specified either by + using i_track CDIO_CDROM_LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +static lba_t +_get_lba_track_cdrdao(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + _init_cdrdao (p_env); + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) + i_track = p_env->gen.i_tracks+1; + + if (i_track <= p_env->gen.i_tracks+1 && i_track != 0) { + return p_env->tocent[i_track-1].start_lba; + } else + return CDIO_INVALID_LBA; +} + +/*! + Check that a TOC file is valid. We parse the entire file. + +*/ +bool +cdio_is_tocfile(const char *psz_cue_name) +{ + int i; + + if (psz_cue_name == NULL) return false; + + i=strlen(psz_cue_name)-strlen("toc"); + + if (i>0) { + if ( (psz_cue_name[i]=='t' && psz_cue_name[i+1]=='o' && psz_cue_name[i+2]=='c') + || (psz_cue_name[i]=='T' && psz_cue_name[i+1]=='O' && psz_cue_name[i+2]=='C') ) { + return parse_tocfile(NULL, psz_cue_name); + } + } + return false; +} + +/*! + 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_t * +cdio_open_am_cdrdao (const char *psz_source_name, const char *psz_access_mode) +{ + if (psz_access_mode != NULL && strcmp(psz_access_mode, "image")) + cdio_warn ("there is only one access mode, 'image' for cdrdao. Arg %s ignored", + psz_access_mode); + return cdio_open_cdrdao(psz_source_name); +} + +/*! + 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_t * +cdio_open_cdrdao (const char *psz_cue_name) +{ + CdIo_t *ret; + _img_private_t *p_data; + + cdio_funcs_t _funcs; + + memset( &_funcs, 0, sizeof(_funcs) ); + + _funcs.eject_media = _eject_media_image; + _funcs.free = _free_image; + _funcs.get_arg = _get_arg_image; + _funcs.get_cdtext = get_cdtext_generic; + _funcs.get_devices = cdio_get_devices_cdrdao; + _funcs.get_default_device = cdio_get_default_device_cdrdao; + _funcs.get_disc_last_lsn = get_disc_last_lsn_cdrdao; + _funcs.get_discmode = _get_discmode_image; + _funcs.get_drive_cap = _get_drive_cap_image; + _funcs.get_first_track_num = _get_first_track_num_image; + _funcs.get_hwinfo = get_hwinfo_cdrdao; + _funcs.get_media_changed = get_media_changed_image; + _funcs.get_mcn = _get_mcn_image; + _funcs.get_num_tracks = _get_num_tracks_image; + _funcs.get_track_channels = get_track_channels_image; + _funcs.get_track_copy_permit = get_track_copy_permit_image; + _funcs.get_track_format = _get_track_format_cdrdao; + _funcs.get_track_green = _get_track_green_cdrdao; + _funcs.get_track_lba = _get_lba_track_cdrdao; + _funcs.get_track_msf = _get_track_msf_image; + _funcs.get_track_preemphasis = get_track_preemphasis_image; + _funcs.get_track_pregap_lba = get_track_pregap_lba_image; + _funcs.get_track_isrc = get_track_isrc_image; + _funcs.lseek = _lseek_cdrdao; + _funcs.read = _read_cdrdao; + _funcs.read_audio_sectors = _read_audio_sectors_cdrdao; + _funcs.read_data_sectors = read_data_sectors_image; + _funcs.read_mode1_sector = _read_mode1_sector_cdrdao; + _funcs.read_mode1_sectors = _read_mode1_sectors_cdrdao; + _funcs.read_mode2_sector = _read_mode2_sector_cdrdao; + _funcs.read_mode2_sectors = _read_mode2_sectors_cdrdao; + _funcs.run_mmc_cmd = NULL; + _funcs.set_arg = _set_arg_image; + _funcs.set_speed = cdio_generic_unimplemented_set_speed; + _funcs.set_blocksize = cdio_generic_unimplemented_set_blocksize; + + if (NULL == psz_cue_name) return NULL; + + p_data = calloc(1, sizeof (_img_private_t)); + p_data->gen.init = false; + p_data->psz_cue_name = NULL; + p_data->gen.data_source = NULL; + p_data->gen.source_name = NULL; + + ret = cdio_new ((void *)p_data, &_funcs); + + if (ret == NULL) { + free(p_data); + return NULL; + } + + ret->driver_id = DRIVER_CDRDAO; + if (!cdio_is_tocfile(psz_cue_name)) { + cdio_debug ("source name %s is not recognized as a TOC file", + psz_cue_name); + free(p_data); + free(ret); + return NULL; + } + + _set_arg_image (p_data, "cue", psz_cue_name); + _set_arg_image (p_data, "source", psz_cue_name); + _set_arg_image (p_data, "access-mode", "cdrdao"); + + if (_init_cdrdao(p_data)) { + return ret; + } else { + _free_image(p_data); + free(ret); + return NULL; + } +} + +bool +cdio_have_cdrdao (void) +{ + return true; +} diff --git a/lib/driver/image/nrg.c b/lib/driver/image/nrg.c new file mode 100644 index 00000000..7d9c6ab8 --- /dev/null +++ b/lib/driver/image/nrg.c @@ -0,0 +1,1352 @@ +/* + $Id: nrg.c,v 1.31 2008/04/21 18:30:22 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2008 Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/*! This code implements low-level access functions for the Nero native + CD-image format residing inside a disk file (*.nrg). +*/ + +#include "image.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_GLOB_H +#include +#endif + +#include +#include +#include +#include +#include +#include "cdio_assert.h" +#include "_cdio_stdio.h" +#include "nrg.h" + +static const char _rcsid[] = "$Id: nrg.c,v 1.31 2008/04/21 18:30:22 karl Exp $"; + +nero_id_t nero_id; +nero_dtype_t nero_dtype; + +/* reader */ +#define DEFAULT_CDIO_DEVICE "image.nrg" + +/* + Link element of track structure as a linked list. + Possibly redundant with above track_info_t */ +typedef struct { + uint32_t start_lsn; + uint32_t sec_count; /* Number of sectors in track. Does not + include pregap before next entry. */ + uint64_t img_offset; /* Bytes offset from beginning of disk image file.*/ + uint32_t blocksize; /* Number of bytes in a block */ +} _mapping_t; + + +#define NEED_NERO_STRUCT +#include "image_common.h" + +static bool parse_nrg (_img_private_t *env, const char *psz_cue_name, + const cdio_log_level_t log_level); +static lsn_t get_disc_last_lsn_nrg (void *p_user_data); + +/* Updates internal track TOC, so we can later + simulate ioctl(CDROMREADTOCENTRY). + */ +static void +_register_mapping (_img_private_t *env, lsn_t start_lsn, uint32_t sec_count, + uint64_t img_offset, uint32_t blocksize, + track_format_t track_format, bool track_green) +{ + const int track_num=env->gen.i_tracks; + track_info_t *this_track=&(env->tocent[env->gen.i_tracks]); + _mapping_t *_map = calloc(1, sizeof (_mapping_t)); + + _map->start_lsn = start_lsn; + _map->sec_count = sec_count; + _map->img_offset = img_offset; + _map->blocksize = blocksize; + + if (!env->mapping) env->mapping = _cdio_list_new (); + _cdio_list_append (env->mapping, _map); + + env->size = MAX (env->size, (start_lsn + sec_count)); + + /* Update *this_track 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), &(this_track->start_msf)); + this_track->start_lba = cdio_msf_to_lba(&this_track->start_msf); + this_track->track_num = track_num+1; + this_track->blocksize = blocksize; + if (env->is_cues) + this_track->datastart = img_offset; + else + this_track->datastart = 0; + + if (track_green) + this_track->datastart += CDIO_CD_SUBHEADER_SIZE; + + this_track->sec_count = sec_count; + + this_track->track_format= track_format; + this_track->track_green = track_green; + + switch (this_track->track_format) { + case TRACK_FORMAT_AUDIO: + this_track->blocksize = CDIO_CD_FRAMESIZE_RAW; + this_track->datasize = CDIO_CD_FRAMESIZE_RAW; + /*this_track->datastart = 0;*/ + this_track->endsize = 0; + break; + case TRACK_FORMAT_CDI: + this_track->datasize=CDIO_CD_FRAMESIZE; + break; + case TRACK_FORMAT_XA: + if (track_green) { + this_track->blocksize = CDIO_CD_FRAMESIZE; + /*this_track->datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE;*/ + this_track->datasize = M2RAW_SECTOR_SIZE; + this_track->endsize = 0; + } else { + /*this_track->datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE + + CDIO_CD_SUBHEADER_SIZE;*/ + this_track->datasize = CDIO_CD_FRAMESIZE; + this_track->endsize = CDIO_CD_SYNC_SIZE + CDIO_CD_ECC_SIZE; + } + break; + case TRACK_FORMAT_DATA: + if (track_green) { + /*this_track->datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE;*/ + this_track->datasize = CDIO_CD_FRAMESIZE; + this_track->endsize = CDIO_CD_EDC_SIZE + CDIO_CD_M1F1_ZERO_SIZE + + CDIO_CD_ECC_SIZE; + } else { + /* Is the below correct? */ + /*this_track->datastart = 0;*/ + this_track->datasize = CDIO_CD_FRAMESIZE; + this_track->endsize = 0; + } + break; + default: + /*this_track->datasize=CDIO_CD_FRAMESIZE_RAW;*/ + cdio_warn ("track %d has unknown format %d", + env->gen.i_tracks, this_track->track_format); + } + + env->gen.i_tracks++; + + cdio_debug ("start lsn: %lu sector count: %0lu -> %8ld (%08lx)", + (long unsigned int) start_lsn, + (long unsigned int) sec_count, + (long unsigned int) img_offset, + (long unsigned int) img_offset); +} + + +/* + Disk and track information for a Nero file are located at the end + of the file. This routine extracts that information. + + FIXME: right now psz_nrg_name is not used. It will be in the future. + */ +static bool +parse_nrg (_img_private_t *p_env, const char *psz_nrg_name, + const cdio_log_level_t log_level) +{ + long unsigned int footer_start; + long unsigned int size; + char *footer_buf = NULL; + size = cdio_stream_stat (p_env->gen.data_source); + if (-1 == size) return false; + + { + _footer_t buf; + cdio_assert (sizeof (buf) == 12); + + cdio_stream_seek (p_env->gen.data_source, size - sizeof (buf), SEEK_SET); + cdio_stream_read (p_env->gen.data_source, (void *) &buf, sizeof (buf), 1); + + if (buf.v50.ID == UINT32_TO_BE (NERO_ID)) { + cdio_debug ("detected Nero version 5.0 (32-bit offsets) NRG magic"); + footer_start = uint32_to_be (buf.v50.footer_ofs); + } else if (buf.v55.ID == UINT32_TO_BE (NER5_ID)) { + cdio_debug ("detected Nero version 5.5.x (64-bit offsets) NRG magic"); + footer_start = uint64_from_be (buf.v55.footer_ofs); + } else { + cdio_log (log_level, "Image not recognized as either version 5.0 or " + "version 5.5.x-6.x type NRG"); + return false; + } + + cdio_debug (".NRG footer start = %ld, length = %ld", + (long) footer_start, (long) (size - footer_start)); + + cdio_assert ((size - footer_start) <= 4096); + + footer_buf = calloc(1, size - footer_start); + + cdio_stream_seek (p_env->gen.data_source, footer_start, SEEK_SET); + cdio_stream_read (p_env->gen.data_source, footer_buf, + size - footer_start, 1); + } + { + int pos = 0; + + while (pos < size - footer_start) { + _chunk_t *chunk = (void *) (footer_buf + pos); + uint32_t opcode = UINT32_FROM_BE (chunk->id); + + bool break_out = false; + + switch (opcode) { + + case CUES_ID: /* "CUES" Seems to have sector size 2336 and 150 sector + pregap seems to be included at beginning of image. + */ + case CUEX_ID: /* "CUEX" */ + { + unsigned entries = UINT32_FROM_BE (chunk->len); + _cuex_array_t *_entries = (void *) chunk->data; + + cdio_assert (p_env->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); + + if (CUES_ID == opcode) { + lsn_t lsn = UINT32_FROM_BE (_entries[0].lsn); + unsigned int idx; + unsigned int i = 0; + + cdio_debug ("CUES type image detected" ); + + /* CUES LSN has 150 pregap include at beginning? -/ + cdio_assert (lsn == 0?); + */ + + p_env->is_cues = true; /* HACK alert. */ + p_env->gen.i_tracks = 0; + p_env->gen.i_first_track = 1; + for (idx = 1; idx < entries-1; idx += 2, i++) { + lsn_t sec_count; + int cdte_format = _entries[idx].addr_ctrl / 16; + int cdte_ctrl = _entries[idx].type >> 4; + + if ( COPY_PERMITTED & cdte_ctrl ) { + if (p_env) p_env->tocent[i].flags |= COPY_PERMITTED; + } else { + if (p_env) p_env->tocent[i].flags &= ~COPY_PERMITTED; + } + + if ( PRE_EMPHASIS & cdte_ctrl ) { + if (p_env) p_env->tocent[i].flags |= PRE_EMPHASIS; + } else { + if (p_env) p_env->tocent[i].flags &= ~PRE_EMPHASIS; + } + + if ( FOUR_CHANNEL_AUDIO & cdte_ctrl ) { + if (p_env) p_env->tocent[i].flags |= FOUR_CHANNEL_AUDIO; + } else { + if (p_env) p_env->tocent[i].flags &= ~FOUR_CHANNEL_AUDIO; + } + + cdio_assert (_entries[idx].track == _entries[idx + 1].track); + + /* lsn and sec_count*2 aren't correct, but it comes closer on the + single example I have: svcdgs.nrg + We are picking up the wrong fields and/or not interpreting + them correctly. + */ + + switch (cdte_format) { + case 0: + lsn = UINT32_FROM_BE (_entries[idx].lsn); + break; + case 1: + { +#if 0 + msf_t msf = (msf_t) _entries[idx].lsn; + lsn = cdio_msf_to_lsn(&msf); +#else + lsn = CDIO_INVALID_LSN; +#endif + cdio_log (log_level, + "untested (i.e. probably wrong) CUE MSF code"); + break; + } + default: + lsn = CDIO_INVALID_LSN; + cdio_log(log_level, + "unknown cdte_format %d", cdte_format); + } + + sec_count = UINT32_FROM_BE (_entries[idx + 1].lsn); + + _register_mapping (p_env, lsn, sec_count*2, + (lsn+CDIO_PREGAP_SECTORS) * M2RAW_SECTOR_SIZE, + M2RAW_SECTOR_SIZE, TRACK_FORMAT_XA, true); + } + } else { + lsn_t lsn = UINT32_FROM_BE (_entries[0].lsn); + unsigned int idx; + unsigned int i = 0; + + cdio_debug ("CUEX type image detected"); + + /* LSN must start at -150 (LBA 0)? */ + cdio_assert (lsn == -150); + + for (idx = 2; idx < entries; idx += 2, i++) { + lsn_t sec_count; + int cdte_format = _entries[idx].addr_ctrl >> 4; + int cdte_ctrl = _entries[idx].type >> 4; + + if ( COPY_PERMITTED & cdte_ctrl ) { + if (p_env) p_env->tocent[i].flags |= COPY_PERMITTED; + } else { + if (p_env) p_env->tocent[i].flags &= ~COPY_PERMITTED; + } + + if ( PRE_EMPHASIS & cdte_ctrl ) { + if (p_env) p_env->tocent[i].flags |= PRE_EMPHASIS; + } else { + if (p_env) p_env->tocent[i].flags &= ~PRE_EMPHASIS; + } + + if ( FOUR_CHANNEL_AUDIO & cdte_ctrl ) { + if (p_env) p_env->tocent[i].flags |= FOUR_CHANNEL_AUDIO; + } else { + if (p_env) p_env->tocent[i].flags &= ~FOUR_CHANNEL_AUDIO; + } + + /* extractnrg.pl has cdte_format for LBA's 0, and + for MSF 1. ??? + + FIXME: Should decode as appropriate for cdte_format. + */ + cdio_assert ( cdte_format == 0 || cdte_format == 1 ); + + cdio_assert (_entries[idx].track != _entries[idx + 1].track); + + lsn = UINT32_FROM_BE (_entries[idx].lsn); + sec_count = UINT32_FROM_BE (_entries[idx + 1].lsn); + + _register_mapping (p_env, lsn, sec_count - lsn, + (lsn + CDIO_PREGAP_SECTORS)*M2RAW_SECTOR_SIZE, + M2RAW_SECTOR_SIZE, TRACK_FORMAT_XA, true); + } + } + break; + } + + case DAOX_ID: /* "DAOX" */ + case DAOI_ID: /* "DAOI" */ + { + _daox_t *_xentries = NULL; + _daoi_t *_ientries = NULL; + _dao_array_common_t *_dao_array_common = NULL; + _dao_common_t *_dao_common = (void *) chunk->data; + int disc_mode = _dao_common->unknown[1]; + track_format_t track_format; + int i; + + /* We include an extra 0 byte so these can be used as C strings.*/ + p_env->psz_mcn = calloc(1, CDIO_MCN_SIZE+1); + memcpy(p_env->psz_mcn, &(_dao_common->psz_mcn), CDIO_MCN_SIZE); + p_env->psz_mcn[CDIO_MCN_SIZE] = '\0'; + + if (DAOX_ID == opcode) { + _xentries = (void *) chunk->data; + p_env->dtyp = _xentries->track_info[0].common.unknown[2]; + } else { + _ientries = (void *) chunk->data; + p_env->dtyp = _ientries->track_info[0].common.unknown[2]; + } + + p_env->is_dao = true; + cdio_debug ("DAO%c tag detected, track format %d, mode %x\n", + opcode==DAOX_ID ? 'X': 'I', p_env->dtyp, disc_mode); + switch (p_env->dtyp) { + case 0: + /* Mode 1 */ + track_format = TRACK_FORMAT_DATA; + p_env->disc_mode = CDIO_DISC_MODE_CD_DATA; + break; + case 2: + /* Mode 2 form 1 */ + disc_mode = 0; + track_format = TRACK_FORMAT_XA; + p_env->disc_mode = CDIO_DISC_MODE_CD_XA; + break; + case 3: + /* Mode 2 */ + track_format = TRACK_FORMAT_XA; + p_env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ?? */ + break; + case 0x6: + /* Mode2 form mix */ + track_format = TRACK_FORMAT_XA; + p_env->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + case 0x20: /* ??? Mode2 form 2, Mode2 raw?? */ + track_format = TRACK_FORMAT_XA; + p_env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ??. */ + break; + case 0x7: + track_format = TRACK_FORMAT_AUDIO; + p_env->disc_mode = CDIO_DISC_MODE_CD_DA; + break; + default: + cdio_log (log_level, "Unknown track format %x\n", + p_env->dtyp); + track_format = TRACK_FORMAT_AUDIO; + } + if (0 == disc_mode) { + for (i=0; igen.i_tracks; i++) { + cdtext_init (&(p_env->gen.cdtext_track[i])); + p_env->tocent[i].track_format= track_format; + p_env->tocent[i].datastart = 0; + p_env->tocent[i].track_green = false; + if (TRACK_FORMAT_AUDIO == track_format) { + p_env->tocent[i].blocksize = CDIO_CD_FRAMESIZE_RAW; + p_env->tocent[i].datasize = CDIO_CD_FRAMESIZE_RAW; + p_env->tocent[i].endsize = 0; + } else { + p_env->tocent[i].datasize = CDIO_CD_FRAMESIZE; + p_env->tocent[i].datastart = 0; + } + } + } else if (2 == disc_mode) { + for (i=0; igen.i_tracks; i++) { + cdtext_init (&(p_env->gen.cdtext_track[i])); + p_env->tocent[i].track_green = true; + p_env->tocent[i].track_format= track_format; + p_env->tocent[i].datasize = CDIO_CD_FRAMESIZE; + if (TRACK_FORMAT_XA == track_format) { + p_env->tocent[i].datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE; + p_env->tocent[i].endsize = CDIO_CD_SYNC_SIZE + + CDIO_CD_ECC_SIZE; + } else { + p_env->tocent[i].datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE; + p_env->tocent[i].endsize = CDIO_CD_EDC_SIZE + + CDIO_CD_M1F1_ZERO_SIZE + CDIO_CD_ECC_SIZE; + + } + } + } else if (0x20 == disc_mode) { + cdio_debug ("Mixed mode CD?\n"); + } else { + /* Mixed mode CD */ + cdio_log (log_level, + "Don't know if mode 1, mode 2 or mixed: %x\n", + disc_mode); + } + + for (i=0; igen.i_tracks; i++) { + + if (DAOX_ID == opcode) { + _dao_array_common = &_xentries->track_info[i].common; + } else { + _dao_array_common = &_ientries->track_info[i].common; + } + p_env->tocent[i].isrc = calloc(1, CDIO_ISRC_SIZE+1); + memcpy(p_env->tocent[i].isrc, _dao_array_common->psz_isrc, CDIO_ISRC_SIZE); + p_env->tocent[i].isrc[CDIO_ISRC_SIZE] = '\0'; + if (p_env->tocent[i].isrc[0]) { + cdio_info("nrg isrc has value \"%s\"", p_env->tocent[i].isrc); + } + + if (!p_env->tocent[i].datasize) { + continue; + } + if (DAOX_ID == opcode) { + p_env->tocent[i].pregap = (uint64_from_be + (_xentries->track_info[i].index0)) / (p_env->tocent[i].datasize); + } else { + p_env->tocent[i].pregap = (uint32_from_be + (_ientries->track_info[i].index0)) / (p_env->tocent[i].datasize); + } + } + + break; + } + case NERO_ID: + case NER5_ID: + cdio_error ("unexpected nrg magic ID NER%c detected", + opcode==NERO_ID ? 'O': '5'); + free(footer_buf); + return false; + 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 (p_env->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_debug ("SAO type image (ETNF) detected"); + + { + 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); + uint32_t track_mode= uint32_from_be (_entries[idx].type); + bool track_green = true; + track_format_t track_format = TRACK_FORMAT_XA; + uint16_t blocksize; + + switch (track_mode) { + case 0: + /* Mode 1 */ + track_format = TRACK_FORMAT_DATA; + track_green = false; /* ?? */ + blocksize = CDIO_CD_FRAMESIZE; + p_env->disc_mode = CDIO_DISC_MODE_CD_DATA; + cdio_debug ("Format DATA, blocksize %u", CDIO_CD_FRAMESIZE); + break; + case 2: + /* Mode 2 form 1 */ + track_format = TRACK_FORMAT_XA; + track_green = false; /* ?? */ + blocksize = CDIO_CD_FRAMESIZE; + p_env->disc_mode = CDIO_DISC_MODE_CD_XA; + cdio_debug ("Format XA, blocksize %u", CDIO_CD_FRAMESIZE); + break; + case 3: + /* Mode 2 */ + track_format = TRACK_FORMAT_XA; + track_green = true; + blocksize = M2RAW_SECTOR_SIZE; + p_env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ?? */ + cdio_debug ("Format XA, blocksize %u", M2RAW_SECTOR_SIZE); + break; + case 06: + /* Mode2 form mix */ + track_format = TRACK_FORMAT_XA; + track_green = true; + blocksize = M2RAW_SECTOR_SIZE; + p_env->disc_mode = CDIO_DISC_MODE_CD_MIXED; + cdio_debug ("Format MIXED CD, blocksize %u", M2RAW_SECTOR_SIZE); + break; + case 0x20: /* ??? Mode2 form 2, Mode2 raw?? */ + track_format = TRACK_FORMAT_XA; + track_green = true; + blocksize = M2RAW_SECTOR_SIZE; + p_env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ??. */ + cdio_debug ("Format MIXED CD, blocksize %u", M2RAW_SECTOR_SIZE); + break; + case 7: + track_format = TRACK_FORMAT_AUDIO; + track_green = false; + blocksize = CDIO_CD_FRAMESIZE_RAW; + p_env->disc_mode = CDIO_DISC_MODE_CD_DA; + cdio_debug ("Format CD_DA, blocksize %u", CDIO_CD_FRAMESIZE_RAW); + break; + default: + cdio_log (log_level, + "Don't know how to handle track mode (%lu)?", + (long unsigned int) track_mode); + free(footer_buf); + return false; + } + + cdio_assert (_len % blocksize == 0); + + _len /= blocksize; + + cdio_assert (_start * blocksize == _start2); + + _start += idx * CDIO_PREGAP_SECTORS; + _register_mapping (p_env, _start, _len, _start2, blocksize, + track_format, track_green); + + } + } + 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 (p_env->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_debug ("SAO type image (ETN2) detected"); + + { + 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); + uint32_t track_mode= uint32_from_be (_entries[idx].type); + bool track_green = true; + track_format_t track_format = TRACK_FORMAT_XA; + uint16_t blocksize; + + + switch (track_mode) { + case 0: + track_format = TRACK_FORMAT_DATA; + track_green = false; /* ?? */ + blocksize = CDIO_CD_FRAMESIZE; + break; + case 2: + track_format = TRACK_FORMAT_XA; + track_green = false; /* ?? */ + blocksize = CDIO_CD_FRAMESIZE; + break; + case 3: + track_format = TRACK_FORMAT_XA; + track_green = true; + blocksize = M2RAW_SECTOR_SIZE; + break; + case 7: + track_format = TRACK_FORMAT_AUDIO; + track_green = false; + blocksize = CDIO_CD_FRAMESIZE_RAW; + break; + default: + cdio_log (log_level, + "Don't know how to handle track mode (%lu)?", + (long unsigned int) track_mode); + free(footer_buf); + return false; + } + + if (_len % blocksize != 0) { + cdio_log (log_level, + "length is not a multiple of blocksize " + "len %lu, size %d, rem %lu", + (long unsigned int) _len, blocksize, + (long unsigned int) _len % blocksize); + if (0 == _len % CDIO_CD_FRAMESIZE) { + cdio_log(log_level, "Adjusting blocksize to %d", + CDIO_CD_FRAMESIZE); + blocksize = CDIO_CD_FRAMESIZE; + } else if (0 == _len % M2RAW_SECTOR_SIZE) { + cdio_log(log_level, + "Adjusting blocksize to %d", M2RAW_SECTOR_SIZE); + blocksize = M2RAW_SECTOR_SIZE; + } else if (0 == _len % CDIO_CD_FRAMESIZE_RAW) { + cdio_log(log_level, + "Adjusting blocksize to %d", CDIO_CD_FRAMESIZE_RAW); + blocksize = CDIO_CD_FRAMESIZE_RAW; + } + } + + _len /= blocksize; + + if (_start * blocksize != _start2) { + cdio_log (log_level, + "%lu * %d != %lu", + (long unsigned int) _start, blocksize, + (long unsigned int) _start2); + if (_start * CDIO_CD_FRAMESIZE == _start2) { + cdio_log(log_level, + "Adjusting blocksize to %d", CDIO_CD_FRAMESIZE); + blocksize = CDIO_CD_FRAMESIZE; + } else if (_start * M2RAW_SECTOR_SIZE == _start2) { + cdio_log(log_level, + "Adjusting blocksize to %d", M2RAW_SECTOR_SIZE); + blocksize = M2RAW_SECTOR_SIZE; + } else if (_start * CDIO_CD_FRAMESIZE_RAW == _start2) { + cdio_log(log_level, + "Adjusting blocksize to %d", CDIO_CD_FRAMESIZE_RAW); + blocksize = CDIO_CD_FRAMESIZE_RAW; + } + } + + _start += idx * CDIO_PREGAP_SECTORS; + _register_mapping (p_env, _start, _len, _start2, blocksize, + track_format, track_green); + } + } + break; + } + + case SINF_ID: { /* "SINF" */ + + uint32_t *_sessions = (void *) chunk->data; + + cdio_assert (UINT32_FROM_BE (chunk->len) == 4); + + cdio_debug ("SINF: %lu sessions", + (long unsigned int) UINT32_FROM_BE (*_sessions)); + } + break; + + case MTYP_ID: { /* "MTYP" */ + uint32_t *mtyp_p = (void *) chunk->data; + uint32_t mtyp = UINT32_FROM_BE (*mtyp_p); + + cdio_assert (UINT32_FROM_BE (chunk->len) == 4); + + cdio_debug ("MTYP: %lu", + (long unsigned int) UINT32_FROM_BE (*mtyp_p)); + + if (mtyp != MTYP_AUDIO_CD) { + cdio_log (log_level, + "Unknown MTYP value: %u", (unsigned int) mtyp); + } + p_env->mtyp = mtyp; + } + break; + + case CDTX_ID: { /* "CD TEXT" */ + uint8_t *wdata = (uint8_t *) chunk->data; + int len = UINT32_FROM_BE (chunk->len); + cdio_assert (len % sizeof (CDText_data_t) == 0); + cdtext_data_init (&p_env->gen, p_env->gen.i_first_track, &wdata[-4], len, set_cdtext_field_generic); + break; + } + + default: + cdio_log (log_level, + "unknown tag %8.8x seen", + (unsigned int) 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 get_disc_last_lsn_nrg since that will lead to recursion since + we haven't fully initialized things yet. + */ + cdio_lsn_to_msf (p_env->size, &p_env->tocent[p_env->gen.i_tracks].start_msf); + p_env->tocent[p_env->gen.i_tracks].start_lba = cdio_lsn_to_lba(p_env->size); + p_env->tocent[p_env->gen.i_tracks-1].sec_count = + cdio_lsn_to_lba(p_env->size - p_env->tocent[p_env->gen.i_tracks-1].start_lba); + + p_env->gen.b_cdtext_init = true; + p_env->gen.b_cdtext_error = false; + p_env->gen.toc_init = true; + free(footer_buf); + return true; +} + +/*! + Initialize image structures. + */ +static bool +_init_nrg (_img_private_t *p_env) +{ + if (p_env->gen.init) { + cdio_error ("init called more than once"); + return false; + } + + if (!(p_env->gen.data_source = cdio_stdio_new (p_env->gen.source_name))) { + cdio_warn ("can't open nrg image file %s for reading", + p_env->gen.source_name); + return false; + } + + p_env->psz_mcn = NULL; + p_env->disc_mode = CDIO_DISC_MODE_NO_INFO; + + cdtext_init (&(p_env->gen.cdtext)); + + if ( !parse_nrg (p_env, p_env->gen.source_name, CDIO_LOG_WARN) ) { + cdio_warn ("image file %s is not a Nero image", + p_env->gen.source_name); + return false; + } + + p_env->gen.init = true; + return true; + +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + Would be libc's seek() but we have to adjust for the extra track header + information in each sector. +*/ +static off_t +_lseek_nrg (void *p_user_data, off_t offset, int whence) +{ + _img_private_t *p_env = p_user_data; + + /* real_offset is the real byte offset inside the disk image + The number below was determined empirically. + */ + off_t real_offset= p_env->is_dao ? 0x4b000 : 0; + + unsigned int i; + + p_env->pos.lba = 0; + for (i=0; igen.i_tracks; i++) { + track_info_t *this_track=&(p_env->tocent[i]); + p_env->pos.index = i; + if ( (this_track->sec_count*this_track->datasize) >= offset) { + int blocks = offset / this_track->datasize; + int rem = offset % this_track->datasize; + int block_offset = blocks * this_track->blocksize; + real_offset += block_offset + rem; + p_env->pos.buff_offset = rem; + p_env->pos.lba += blocks; + break; + } + real_offset += this_track->sec_count*this_track->blocksize; + offset -= this_track->sec_count*this_track->datasize; + p_env->pos.lba += this_track->sec_count; + } + + if (i==p_env->gen.i_tracks) { + cdio_warn ("seeking outside range of disk image"); + return -1; + } else + real_offset += p_env->tocent[i].datastart; + return cdio_stream_seek(p_env->gen.data_source, real_offset, whence); +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + FIXME: + At present we assume a read doesn't cross sector or track + boundaries. +*/ +static ssize_t +_read_nrg (void *p_user_data, void *buf, size_t size) +{ + _img_private_t *p_env = p_user_data; + return cdio_stream_read(p_env->gen.data_source, buf, size, 1); +} + +/*! + Get the size of the CD in logical block address (LBA) units. + + @param p_cdio the CD object queried + @return the lsn. On error 0 or CDIO_INVALD_LSN. +*/ +static lsn_t +get_disc_last_lsn_nrg (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + return p_env->size; +} + +/*! + Reads a single audio sector from CD device into data starting + from LSN. + */ +static driver_return_code_t +_read_audio_sectors_nrg (void *p_user_data, void *data, lsn_t lsn, + unsigned int nblocks) +{ + _img_private_t *p_env = p_user_data; + CdioListNode_t *node; + + if (lsn >= p_env->size) + { + cdio_warn ("trying to read beyond image size (%lu >= %lu)", + (long unsigned int) lsn, (long unsigned int) p_env->size); + return -1; + } + + if (p_env->is_dao) { + int ret; + + ret = cdio_stream_seek (p_env->gen.data_source, + (lsn + CDIO_PREGAP_SECTORS) * CDIO_CD_FRAMESIZE_RAW, SEEK_SET); + if (ret!=0) return ret; + + ret = cdio_stream_read (p_env->gen.data_source, data, + CDIO_CD_FRAMESIZE_RAW, nblocks); + + /* ret is number of bytes if okay, but we need to return 0 okay. */ + return ret == 0; + } + + _CDIO_LIST_FOREACH (node, p_env->mapping) { + _mapping_t *_map = _cdio_list_node_data (node); + + if (IN (lsn, _map->start_lsn, (_map->start_lsn + _map->sec_count - 1))) { + int ret; + long int img_offset = _map->img_offset; + + img_offset += (lsn - _map->start_lsn) * CDIO_CD_FRAMESIZE_RAW; + + ret = cdio_stream_seek (p_env->gen.data_source, img_offset, + SEEK_SET); + if (ret!=0) return ret; + ret = cdio_stream_read (p_env->gen.data_source, data, + CDIO_CD_FRAMESIZE_RAW, nblocks); + if (ret==0) return ret; + break; + } + } + + if (!node) cdio_warn ("reading into pre gap (lsn %lu)", + (long unsigned int) lsn); + + return 0; +} + +static driver_return_code_t +_read_mode1_sector_nrg (void *p_user_data, void *data, lsn_t lsn, + bool b_form2) +{ + _img_private_t *p_env = p_user_data; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + + CdioListNode_t *node; + + if (lsn >= p_env->size) + { + cdio_warn ("trying to read beyond image size (%lu >= %lu)", + (long unsigned int) lsn, (long unsigned int) p_env->size); + return -1; + } + + _CDIO_LIST_FOREACH (node, p_env->mapping) { + _mapping_t *_map = _cdio_list_node_data (node); + + if (IN (lsn, _map->start_lsn, (_map->start_lsn + _map->sec_count - 1))) { + int ret; + long int img_offset = _map->img_offset; + + img_offset += (lsn - _map->start_lsn) * _map->blocksize; + + ret = cdio_stream_seek (p_env->gen.data_source, img_offset, + SEEK_SET); + if (ret!=0) return ret; + + /* FIXME: Not completely sure the below is correct. */ + ret = cdio_stream_read (p_env->gen.data_source, + (M2RAW_SECTOR_SIZE == _map->blocksize) + ? (buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE) + : buf, + _map->blocksize, 1); + if (ret==0) return ret; + break; + } + } + + if (!node) + cdio_warn ("reading into pre gap (lsn %lu)", (long unsigned int) lsn); + + memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE, + b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE); + + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + */ +static driver_return_code_t +_read_mode1_sectors_nrg (void *p_user_data, void *data, lsn_t lsn, + bool b_form2, unsigned nblocks) +{ + _img_private_t *p_env = p_user_data; + int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode1_sector_nrg (p_env, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +static driver_return_code_t +_read_mode2_sector_nrg (void *p_user_data, void *data, lsn_t lsn, + bool b_form2) +{ + _img_private_t *p_env = p_user_data; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + + CdioListNode_t *node; + + if (lsn >= p_env->size) + { + cdio_warn ("trying to read beyond image size (%lu >= %lu)", + (long unsigned int) lsn, (long unsigned int) p_env->size); + return -1; + } + + _CDIO_LIST_FOREACH (node, p_env->mapping) { + _mapping_t *_map = _cdio_list_node_data (node); + + if (IN (lsn, _map->start_lsn, (_map->start_lsn + _map->sec_count - 1))) { + int ret; + long int img_offset = _map->img_offset; + + img_offset += (lsn - _map->start_lsn) * _map->blocksize; + + ret = cdio_stream_seek (p_env->gen.data_source, img_offset, + SEEK_SET); + if (ret!=0) return ret; + ret = cdio_stream_read (p_env->gen.data_source, + (M2RAW_SECTOR_SIZE == _map->blocksize) + ? (buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE) + : buf, + _map->blocksize, 1); + if (ret==0) return ret; + break; + } + } + + if (!node) + cdio_warn ("reading into pre gap (lsn %lu)", (long unsigned int) lsn); + + if (b_form2) + memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE, + M2RAW_SECTOR_SIZE); + else + memcpy (data, buf + CDIO_CD_XA_SYNC_HEADER, CDIO_CD_FRAMESIZE); + + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static driver_return_code_t +_read_mode2_sectors_nrg (void *p_user_data, void *data, lsn_t lsn, + bool b_form2, unsigned nblocks) +{ + _img_private_t *p_env = p_user_data; + int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode2_sector_nrg (p_env, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/* + Free memory resources associated with NRG object. +*/ +static void +_free_nrg (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + if (NULL == p_env) return; + if (NULL != p_env->mapping) + _cdio_list_free (p_env->mapping, true); + + /* The remaining part of the image is like the other image drivers, + so free that in the same way. */ + _free_image(p_user_data); +} + +/*! + Eject media -- there's nothing to do here except free resources. + We always return 2. + */ +static driver_return_code_t +_eject_media_nrg(void *obj) +{ + _free_nrg (obj); + return DRIVER_OP_UNSUPPORTED; +} + +/*! + Return an array of strings giving possible NRG disk images. + */ +char ** +cdio_get_devices_nrg (void) +{ + char **drives = NULL; + unsigned int num_files=0; +#ifdef HAVE_GLOB_H + unsigned int i; + glob_t globbuf; + globbuf.gl_offs = 0; + glob("*.nrg", GLOB_DOOFFS, NULL, &globbuf); + for (i=0; ipsz_vendor, "libcdio", + sizeof(hw_info->psz_vendor)-1); + hw_info->psz_vendor[sizeof(hw_info->psz_vendor)-1] = '\0'; + strncpy(hw_info->psz_model, "Nero", + sizeof(hw_info->psz_model)-1); + hw_info->psz_model[sizeof(hw_info->psz_model)-1] = '\0'; + strncpy(hw_info->psz_revision, CDIO_VERSION, + sizeof(hw_info->psz_revision)-1); + hw_info->psz_revision[sizeof(hw_info->psz_revision)-1] = '\0'; + return true; + +} + +/*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_format_t +get_track_format_nrg(void *p_user_data, track_t track_num) +{ + _img_private_t *p_env = p_user_data; + + if (track_num > p_env->gen.i_tracks || track_num == 0) + return TRACK_FORMAT_ERROR; + + if ( p_env->dtyp != DTYP_INVALID) { + switch (p_env->dtyp) { + case DTYP_MODE2_XA: + return TRACK_FORMAT_XA; + case DTYP_MODE1: + return TRACK_FORMAT_DATA; + default: ; + } + } + + /*if ( MTYP_AUDIO_CD == p_env->mtyp) return TRACK_FORMAT_AUDIO; */ + return p_env->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 +_get_track_green_nrg(void *p_user_data, track_t track_num) +{ + _img_private_t *p_env = p_user_data; + + if (track_num > p_env->gen.i_tracks || track_num == 0) + return false; + + if ( MTYP_AUDIO_CD == p_env->mtyp) return false; + return p_env->tocent[track_num-1].track_green; +} + +/*! + Check that a NRG file is valid. +*/ +bool +cdio_is_nrg(const char *psz_nrg) +{ + _img_private_t env; + bool is_nrg = false; + + if (psz_nrg == NULL) return false; + + memset(&env, 0, sizeof(env)); + if (!(env.gen.data_source = cdio_stdio_new (psz_nrg))) { + cdio_warn ("can't open nrg image file %s for reading", psz_nrg); + return false; + } + + if (parse_nrg(&env, psz_nrg, CDIO_LOG_INFO)) { + is_nrg = true; +#ifdef ALSO_TEST_NAME + size_t psz_len; + psz_len = strlen(psz_nrg); + /* At least 4 characters needed for .nrg extension */ + if ( psz_len < 4 ) return false; + + is_nrg = strncasecmp( psz_nrg+(psz_len-3), "nrg", 3 ) == 0; +#endif + } + cdio_stdio_destroy(env.gen.data_source); + return is_nrg; +} + +/*! + 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_am_nrg (const char *psz_source_name, const char *psz_access_mode) +{ + if (psz_access_mode != NULL && strcmp(psz_access_mode, "image")) + cdio_warn ("there is only one access mode for nrg. Arg %s ignored", + psz_access_mode); + return cdio_open_nrg(psz_source_name); +} + + +CdIo * +cdio_open_nrg (const char *psz_source) +{ + CdIo *ret; + _img_private_t *_data; + + cdio_funcs_t _funcs; + + memset( &_funcs, 0, sizeof(_funcs) ); + + _funcs.eject_media = _eject_media_nrg; + _funcs.free = _free_nrg; + _funcs.get_arg = _get_arg_image; + _funcs.get_cdtext = get_cdtext_generic; + _funcs.get_devices = cdio_get_devices_nrg; + _funcs.get_default_device = cdio_get_default_device_nrg; + _funcs.get_disc_last_lsn = get_disc_last_lsn_nrg; + _funcs.get_discmode = _get_discmode_image; + _funcs.get_drive_cap = _get_drive_cap_image; + _funcs.get_first_track_num = _get_first_track_num_image; + _funcs.get_hwinfo = get_hwinfo_nrg; + _funcs.get_media_changed = get_media_changed_image; + _funcs.get_mcn = _get_mcn_image; + _funcs.get_num_tracks = _get_num_tracks_image; + _funcs.get_track_channels = get_track_channels_generic; + _funcs.get_track_copy_permit = get_track_copy_permit_image; + _funcs.get_track_format = get_track_format_nrg; + _funcs.get_track_green = _get_track_green_nrg; + _funcs.get_track_lba = NULL; /* Will use generic routine via msf */ + _funcs.get_track_msf = _get_track_msf_image; + _funcs.get_track_preemphasis = get_track_preemphasis_generic; + _funcs.get_track_pregap_lba = get_track_pregap_lba_image; + _funcs.get_track_isrc = get_track_isrc_image; + _funcs.lseek = _lseek_nrg; + _funcs.read = _read_nrg; + _funcs.read_audio_sectors = _read_audio_sectors_nrg; + _funcs.read_data_sectors = read_data_sectors_image; + _funcs.read_mode1_sector = _read_mode1_sector_nrg; + _funcs.read_mode1_sectors = _read_mode1_sectors_nrg; + _funcs.read_mode2_sector = _read_mode2_sector_nrg; + _funcs.read_mode2_sectors = _read_mode2_sectors_nrg; + _funcs.run_mmc_cmd = NULL; + _funcs.set_arg = _set_arg_image; + + _data = calloc(1, sizeof (_img_private_t)); + _data->gen.init = false; + + _data->gen.i_tracks = 0; + _data->mtyp = 0; + _data->dtyp = DTYP_INVALID; + _data->gen.i_first_track= 1; + _data->is_dao = false; + _data->is_cues = false; /* FIXME: remove is_cues. */ + + ret = cdio_new ((void *)_data, &_funcs); + + if (ret == NULL) { + free(_data); + return NULL; + } + + ret->driver_id = DRIVER_NRG; + _set_arg_image(_data, "source", (NULL == psz_source) + ? DEFAULT_CDIO_DEVICE: psz_source); + _set_arg_image (_data, "access-mode", "Nero"); + + _data->psz_cue_name = strdup(_get_arg_image(_data, "source")); + + if (!cdio_is_nrg(_data->psz_cue_name)) { + cdio_debug ("source name %s is not recognized as a NRG image", + _data->psz_cue_name); + _free_nrg(_data); + free(ret); + return NULL; + } + + if (_init_nrg(_data)) + return ret; + else { + _free_nrg(_data); + free(ret); + return NULL; + } + +} + +bool +cdio_have_nrg (void) +{ + return true; +} diff --git a/lib/driver/image/nrg.h b/lib/driver/image/nrg.h new file mode 100644 index 00000000..fe51c9c2 --- /dev/null +++ b/lib/driver/image/nrg.h @@ -0,0 +1,155 @@ +/* + $Id: nrg.h,v 1.7 2008/06/10 00:45:08 pjcreath Exp $ + + Copyright (C) 2004, 2006, 2008 Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* NERO (NRG) file format structures. */ + +/* 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 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; +} _footer_t; + +typedef struct { + uint32_t start GNUC_PACKED; + uint32_t length GNUC_PACKED; + uint32_t type GNUC_PACKED; /* 0x0 -> MODE1, 0x2 -> MODE2 form1, + 0x3 -> 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 32-bit 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; /* 0x0 -> MODE1, 0x2 -> MODE2 form1, + 0x3 -> MIXED_MODE2 2336 blocksize + */ + uint32_t start_lsn GNUC_PACKED; + uint64_t _unknown GNUC_PACKED; /* wtf is this for? -- always zero... */ +} _etn2_array_t; + +typedef struct { + uint8_t type; /* has track copy bit and whether audiofile + or datafile. Is often 0x41 == 'A' */ + uint8_t track; /* binary or BCD?? */ + uint8_t addr_ctrl; /* addresstype: MSF or LBA in lower 4 bits + control in upper 4 bits. + makes 0->1 transitions */ + uint8_t res; /* ?? */ + uint32_t lsn GNUC_PACKED; +} _cuex_array_t; + +/* New DAO[XI] Information from http://en.wikipedia.org/wiki/NRG_(file_format) +*/ + +typedef struct { + char psz_isrc[CDIO_ISRC_SIZE]; + uint8_t unknown[6]; +} _dao_array_common_t; + +typedef struct { + _dao_array_common_t common; + uint64_t index0 GNUC_PACKED; + uint64_t index1 GNUC_PACKED; + uint64_t end_of_track GNUC_PACKED; +} _daox_array_t; + +typedef struct { + _dao_array_common_t common; + uint32_t index0 GNUC_PACKED; + uint32_t index1 GNUC_PACKED; + uint32_t end_of_track GNUC_PACKED; +} _daoi_array_t; + +typedef struct GNUC_PACKED { + uint32_t chunk_size_le GNUC_PACKED; + char psz_mcn[CDIO_MCN_SIZE]; + uint8_t unknown[3]; + uint8_t first_track; + uint8_t last_track; +} _dao_common_t; + +typedef struct { + _dao_common_t common; + _daox_array_t track_info[EMPTY_ARRAY_SIZE]; +} _daox_t; + +typedef struct { + _dao_common_t common; + _daoi_array_t track_info[EMPTY_ARRAY_SIZE]; +} _daoi_t; + +typedef struct { + uint32_t id GNUC_PACKED; + uint32_t len GNUC_PACKED; + char data[EMPTY_ARRAY_SIZE]; +} _chunk_t; + +PRAGMA_END_PACKED + +/* Nero images are Big Endian. */ +typedef enum { + CDTX_ID = 0x43445458, /* CD TEXT */ + CUEX_ID = 0x43554558, /* Nero version 5.5.x-6.x */ + CUES_ID = 0x43554553, /* Nero pre version 5.5.x-6.x */ + DAOX_ID = 0x44414f58, /* Nero version 5.5.x-6.x */ + DAOI_ID = 0x44414f49, + END1_ID = 0x454e4421, + ETN2_ID = 0x45544e32, + ETNF_ID = 0x45544e46, + NER5_ID = 0x4e455235, /* Nero version 5.5.x */ + NERO_ID = 0x4e45524f, /* Nero pre 5.5.x */ + SINF_ID = 0x53494e46, /* Session information */ + MTYP_ID = 0x4d545950, /* Disc Media type? */ +} nero_id_t; + +#define MTYP_AUDIO_CD 1 /* This isn't correct. But I don't know the + the right thing is and it sometimes works (and + sometimes is wrong). */ + +/* Disk track type Values gleaned from DAOX */ +typedef enum { + DTYP_MODE1 = 0, + DTYP_MODE2_XA = 2, + DTYP_INVALID = 255 +} nero_dtype_t; + +/** The below variables are trickery to force the above enum symbol + values to be recorded in debug symbol tables. They are used to + allow one to refer to the enumeration value names in the typedefs + above in a debugger and debugger expressions. +*/ +extern nero_id_t nero_id; +extern nero_dtype_t nero_dtype; + diff --git a/lib/driver/image_common.c b/lib/driver/image_common.c new file mode 100644 index 00000000..301b08dc --- /dev/null +++ b/lib/driver/image_common.c @@ -0,0 +1,373 @@ +/* + $Id: image_common.c,v 1.15 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/*! Common image routines. + + Because _img_private_t may vary over image formats, the routines are + included into the image drivers after _img_private_t is defined. In + order for the below routines to work, there is a large part of + _img_private_t that is common among image drivers. For example, see + image.h +*/ + +#include "image.h" +#include "image_common.h" +#include "_cdio_stdio.h" + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +/*! + Eject media -- there's nothing to do here except free resources. + We always return DRIVER_OP_UNSUPPORTED. + */ +driver_return_code_t +_eject_media_image(void *p_user_data) +{ + _free_image (p_user_data); + return DRIVER_OP_UNSUPPORTED; +} + +/*! + We don't need the image any more. Free all memory associated with + it. + */ +void +_free_image (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + track_t i_track; + + if (NULL == p_env) return; + + for (i_track=0; i_track < p_env->gen.i_tracks; i_track++) { + track_info_t *p_tocent = &(p_env->tocent[i_track]); + free_if_notnull(p_tocent->filename); + free_if_notnull(p_tocent->isrc); + cdtext_destroy(&(p_tocent->cdtext)); + if (p_tocent->data_source) cdio_stdio_destroy(p_tocent->data_source); + } + + free_if_notnull(p_env->psz_mcn); + free_if_notnull(p_env->psz_cue_name); + free_if_notnull(p_env->psz_access_mode); + cdtext_destroy(&(p_env->gen.cdtext)); + cdio_generic_stdio_free(p_env); + free(p_env); +} + +/*! + Return the value associated with the key "arg". +*/ +const char * +_get_arg_image (void *user_data, const char key[]) +{ + _img_private_t *p_env = user_data; + + if (!strcmp (key, "source")) { + return p_env->gen.source_name; + } else if (!strcmp (key, "cue")) { + return p_env->psz_cue_name; + } else if (!strcmp(key, "access-mode")) { + return "image"; + } + return NULL; +} + +/*! + Get disc type associated with cd_obj. +*/ +discmode_t +_get_discmode_image (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + return p_env->disc_mode; +} + +/*! + Return the the kind of drive capabilities of device. + + */ +void +_get_drive_cap_image (const void *user_data, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap) +{ + + *p_read_cap = CDIO_DRIVE_CAP_READ_CD_DA + | CDIO_DRIVE_CAP_READ_CD_G + | CDIO_DRIVE_CAP_READ_CD_R + | CDIO_DRIVE_CAP_READ_CD_RW + | CDIO_DRIVE_CAP_READ_MODE2_FORM1 + | CDIO_DRIVE_CAP_READ_MODE2_FORM2 + | CDIO_DRIVE_CAP_READ_MCN + ; + + *p_write_cap = 0; + + /* In the future we may want to simulate + LOCK, OPEN_TRAY, CLOSE_TRAY, SELECT_SPEED, etc. + */ + *p_misc_cap = CDIO_DRIVE_CAP_MISC_FILE; +} + +/*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. +*/ +track_t +_get_first_track_num_image(void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + return p_env->gen.i_first_track; +} + +/*! + Find out if media has changed since the last call. + @param p_user_data the CD object to be acted upon. + @return 1 if media has changed since last call, 0 if not. Error + return codes are the same as driver_return_code_t + There is no such thing as changing a media image so we will + always return 0 - no change. + */ +int +get_media_changed_image(const void *p_user_data) +{ + return 0; +} + +/*! + Return the media catalog number (MCN) from the CD or NULL if there + is none or we don't have the ability to get it. + + Note: string is malloc'd so caller has to free() the returned + string when done with it. + */ +char * +_get_mcn_image(const void *p_user_data) +{ + const _img_private_t *p_env = p_user_data; + + if (!p_env || !p_env->psz_mcn) return NULL; + return strdup(p_env->psz_mcn); +} + +/*! + Return the number of tracks. +*/ +track_t +_get_num_tracks_image(void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + return p_env->gen.i_tracks; +} + +/*! + Return the starting MSF (minutes/secs/frames) for the 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. + +*/ +bool +_get_track_msf_image(void *p_user_data, track_t i_track, msf_t *msf) +{ + const _img_private_t *p_env = p_user_data; + + if (NULL == msf) return false; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = p_env->gen.i_tracks+1; + + if (i_track <= p_env->gen.i_tracks+1 && i_track != 0) { + *msf = p_env->tocent[i_track-p_env->gen.i_first_track].start_msf; + return true; + } else + return false; +} + +/*! Return number of channels in track: 2 or 4; -2 if not + implemented or -1 for error. + Not meaningful if track is not an audio track. +*/ +int +get_track_channels_image(const void *p_user_data, track_t i_track) +{ + const _img_private_t *p_env = p_user_data; + return ( p_env->tocent[i_track-p_env->gen.i_first_track].flags + & FOUR_CHANNEL_AUDIO ) ? 4 : 2; +} + +/*! Return 1 if copy is permitted on the track, 0 if not, or -1 for error. + Is this meaningful if not an audio track? +*/ +track_flag_t +get_track_copy_permit_image(void *p_user_data, track_t i_track) +{ + const _img_private_t *p_env = p_user_data; + return ( p_env->tocent[i_track-p_env->gen.i_first_track].flags + & COPY_PERMITTED ) ? CDIO_TRACK_FLAG_TRUE : CDIO_TRACK_FLAG_FALSE; +} + +/*! Return 1 if track has pre-emphasis, 0 if not, or -1 for error. + Is this meaningful if not an audio track? + + pre-emphasis is a non linear frequency response. +*/ +track_flag_t +get_track_preemphasis_image(const void *p_user_data, track_t i_track) +{ + const _img_private_t *p_env = p_user_data; + return ( p_env->tocent[i_track-p_env->gen.i_first_track].flags + & PRE_EMPHASIS ) ? CDIO_TRACK_FLAG_TRUE : CDIO_TRACK_FLAG_FALSE; +} + +/*! Return the starting LBA for the pregap for track number i_track. + Track numbers start at 1. + CDIO_INVALID_LBA is returned on error. +*/ +lba_t +get_track_pregap_lba_image(const void *p_user_data, track_t i_track) +{ + const _img_private_t *p_env = p_user_data; + lba_t pregap, start_lba; + + pregap = p_env->tocent[i_track-p_env->gen.i_first_track].pregap; + start_lba = p_env->tocent[i_track-p_env->gen.i_first_track].start_lba; + + /* avoid initializing pregap to CDIO_INVALID_LBA by letting calloc + do the work. also, nero files have the pregap set equal + to the start of the track when there is no pregap + */ + if (!pregap || pregap == start_lba) { + pregap = CDIO_INVALID_LBA; + } + + return pregap; +} + +/*! + Return the International Standard Recording Code (ISRC) for track number + i_track in p_cdio. Track numbers start at 1. + + Note: string is malloc'd so caller has to free() the returned + string when done with it. +*/ +char * +get_track_isrc_image(const void *p_user_data, track_t i_track) +{ + const _img_private_t *p_env = p_user_data; + char *isrc = p_env->tocent[i_track-p_env->gen.i_first_track].isrc; + + if (isrc && isrc[0]) { + return strdup(isrc); + } else { + return NULL; + } +} + +/*! + Read a data sector + + @param p_cdio object to read from + + @param p_buf place to read data into. The caller should make sure + this location can store at least ISO_BLOCKSIZE, M2RAW_SECTOR_SIZE, + or M2F2_SECTOR_SIZE depending on the kind of sector getting read. If + you don't know whether you have a Mode 1/2, Form 1/ Form 2/Formless + sector best to reserve space for the maximum, M2RAW_SECTOR_SIZE. + + @param i_lsn sector to read + + @param i_blocksize size of block. Should be either ISO_BLOCKSIZE + M2RAW_SECTOR_SIZE, or M2F2_SECTOR_SIZE. See comment above under + p_buf. + */ +driver_return_code_t +read_data_sectors_image ( void *p_user_data, void *p_buf, + lsn_t i_lsn, uint16_t i_blocksize, + uint32_t i_blocks ) +{ + const _img_private_t *p_env = p_user_data; + + if (!p_env || !p_env->gen.cdio) return DRIVER_OP_UNINIT; + + { + CdIo_t *p_cdio = p_env->gen.cdio; + track_t i_track = cdio_get_track(p_cdio, i_lsn); + track_format_t e_track_format = cdio_get_track_format(p_cdio, i_track); + + switch(e_track_format) { + case TRACK_FORMAT_PSX: + case TRACK_FORMAT_AUDIO: + case TRACK_FORMAT_ERROR: + return DRIVER_OP_ERROR; + case TRACK_FORMAT_DATA: + return cdio_read_mode1_sectors (p_cdio, p_buf, i_lsn, false, i_blocks); + case TRACK_FORMAT_CDI: + case TRACK_FORMAT_XA: + return cdio_read_mode2_sectors (p_cdio, p_buf, i_lsn, false, i_blocks); + } + } + return DRIVER_OP_ERROR; +} + + +/*! + Set the arg "key" with "value" in the source device. + Currently "source" to set the source device in I/O operations + is the only valid key. + +*/ +driver_return_code_t +_set_arg_image (void *p_user_data, const char key[], const char value[]) +{ + _img_private_t *p_env = p_user_data; + + if (!strcmp (key, "source")) + { + free_if_notnull (p_env->gen.source_name); + if (!value) return DRIVER_OP_ERROR; + p_env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "cue")) + { + free_if_notnull (p_env->psz_cue_name); + if (!value) return DRIVER_OP_ERROR; + p_env->psz_cue_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + free_if_notnull (p_env->psz_access_mode); + if (!value) return DRIVER_OP_ERROR; + p_env->psz_access_mode = strdup (value); + } + else + return DRIVER_OP_ERROR; + + return DRIVER_OP_SUCCESS; +} + diff --git a/lib/driver/image_common.h b/lib/driver/image_common.h new file mode 100644 index 00000000..f7949817 --- /dev/null +++ b/lib/driver/image_common.h @@ -0,0 +1,202 @@ +/* + $Id: image_common.h,v 1.13 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/*! Common image routines. + + Because _img_private_t may vary over image formats, the routines are + included into the image drivers after _img_private_t is defined. In + order for the below routines to work, there is a large part of + _img_private_t that is common among image drivers. For example, see + image.h +*/ + +#ifndef __CDIO_IMAGE_COMMON_H__ +#define __CDIO_IMAGE_COMMON_H__ + +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + internal_position_t pos; + + char *psz_cue_name; + char *psz_access_mode; /* Just the name of the driver. + We add this for regularity with other + real CD drivers which has an access mode. + */ + char *psz_mcn; /* Media Catalog Number (5.22.3) + exactly 13 bytes */ + track_info_t tocent[CDIO_CD_MAX_TRACKS+1]; /* entry info for each track + add 1 for leadout. */ + discmode_t disc_mode; + +#ifdef NEED_NERO_STRUCT + /* Nero Specific stuff. Note: for the image_free to work, this *must* + be last. */ + bool is_dao; /* True if some of disk at once. False + if some sort of track at once. */ + uint32_t mtyp; /* Value of MTYP (media type?) tag */ + uint8_t dtyp; /* Value of DAOX media type tag */ + + /* This is a hack because I don't really understnad NERO better. */ + bool is_cues; + + CdioList_t *mapping; /* List of track information */ + uint32_t size; +#endif +} _img_private_t; + +#define free_if_notnull(p_obj) \ + if (NULL != p_obj) { free(p_obj); p_obj=NULL; }; + +/*! + We don't need the image any more. Free all memory associated with + it. + */ +void _free_image (void *p_user_data); + +int _eject_media_image(void *p_user_data); + +/*! + Return the value associated with the key "arg". +*/ +const char * _get_arg_image (void *user_data, const char key[]); + +/*! + Get disc type associated with cd_obj. +*/ +discmode_t _get_discmode_image (void *p_user_data); + +/*! + Return the the kind of drive capabilities of device. + + */ +void _get_drive_cap_image (const void *user_data, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap); + +/*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. +*/ +track_t _get_first_track_num_image(void *p_user_data); + +/*! + Find out if media has changed since the last call. + @param p_user_data the CD object to be acted upon. + @return 1 if media has changed since last call, 0 if not. Error + return codes are the same as driver_return_code_t + We always return DRIVER_OP_UNSUPPORTED. + */ +int get_media_changed_image(const void *p_user_data); + +/*! + Return the media catalog number (MCN) from the CD or NULL if there + is none or we don't have the ability to get it. + + Note: string is malloc'd so caller has to free() the returned + string when done with it. + */ +char * _get_mcn_image(const void *p_user_data); + +/*! + Return the number of tracks. +*/ +track_t _get_num_tracks_image(void *p_user_data); + + +/*! + Return the starting MSF (minutes/secs/frames) for the 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. + +*/ +bool _get_track_msf_image(void *p_user_data, track_t i_track, msf_t *msf); + +/*! Return number of channels in track: 2 or 4; -2 if not + implemented or -1 for error. + Not meaningful if track is not an audio track. +*/ +int get_track_channels_image(const void *p_user_data, track_t i_track); + +/*! Return 1 if copy is permitted on the track, 0 if not, or -1 for error. + Is this meaningful if not an audio track? +*/ +track_flag_t get_track_copy_permit_image(void *p_user_data, track_t i_track); + +/*! Return 1 if track has pre-emphasis, 0 if not, or -1 for error. + Is this meaningful if not an audio track? + + pre-emphasis is a non linear frequency response. +*/ +track_flag_t get_track_preemphasis_image(const void *p_user_data, + track_t i_track); + +/*! Return the starting LBA for the pregap for track number i_track. + Track numbers start at 1. + CDIO_INVALID_LBA is returned on error. +*/ +lba_t get_track_pregap_lba_image(const void *p_user_data, track_t i_track); + +/*! + Return the International Standard Recording Code (ISRC) for track number + i_track in p_cdio. Track numbers start at 1. + + Note: string is malloc'd so caller has to free() the returned + string when done with it. +*/ +char *get_track_isrc_image(const void *p_user_data, track_t i_track); + +/*! + Read a data sector + + @param p_cdio object to read from + + @param p_buf place to read data into. The caller should make sure + this location can store at least ISO_BLOCKSIZE, M2RAW_SECTOR_SIZE, + or M2F2_SECTOR_SIZE depending on the kind of sector getting read. If + you don't know whether you have a Mode 1/2, Form 1/ Form 2/Formless + sector best to reserve space for the maximum, M2RAW_SECTOR_SIZE. + + @param i_lsn sector to read + + @param i_blocksize size of block. Should be either ISO_BLOCKSIZE + M2RAW_SECTOR_SIZE, or M2F2_SECTOR_SIZE. See comment above under + p_buf. + + @param i_blocks number of blocks to read. + + */ +driver_return_code_t +read_data_sectors_image ( void *p_user_data, void *p_buf, + lsn_t i_lsn, uint16_t i_blocksize, + uint32_t i_blocks ); + +/*! + Set the arg "key" with "value" in the source device. + Currently "source" to set the source device in I/O operations + is the only valid key. + + 0 is returned if no error was found, and nonzero if there as an error. +*/ +int _set_arg_image (void *user_data, const char key[], const char value[]); + +#endif /* __CDIO_IMAGE_COMMON_H__ */ diff --git a/lib/driver/libcdio.sym b/lib/driver/libcdio.sym new file mode 100644 index 00000000..9382ca51 --- /dev/null +++ b/lib/driver/libcdio.sym @@ -0,0 +1,220 @@ +CDIO_SECTOR_SYNC_HEADER +_cdio_list_append +_cdio_list_begin +_cdio_list_end +_cdio_list_find +_cdio_list_foreach +_cdio_list_free +_cdio_list_length +_cdio_list_new +_cdio_list_node_data +_cdio_list_node_free +_cdio_list_node_next +_cdio_list_prepend +_cdio_malloc +_cdio_strfreev +_cdio_strsplit +cdio_audio_get_msf_seconds +cdio_audio_get_volume +cdio_audio_pause +cdio_audio_play_msf +cdio_audio_play_track_index +cdio_audio_read_subchannel +cdio_audio_resume +cdio_audio_set_volume +cdio_audio_stop +cdio_close_tray +cdio_debug +cdio_destroy +cdio_driver_describe +cdio_driver_errmsg +cdio_eject_media +cdio_eject_media_drive +cdio_error +cdio_free_device_list +cdio_from_bcd8 +cdio_get_arg +cdio_get_cdtext +cdio_get_default_device +cdio_get_default_device_bincue +cdio_get_default_device_bsdi +cdio_get_default_device_cdrdao +cdio_get_default_device_driver +cdio_get_default_device_freebsd +cdio_get_default_device_linux +cdio_get_default_device_nrg +cdio_get_default_device_osx +cdio_get_default_device_solaris +cdio_get_default_device_win32 +cdio_get_devices +cdio_get_devices_bincue +cdio_get_devices_bsdi +cdio_get_devices_cdrdao +cdio_get_devices_freebsd +cdio_get_devices_linux +cdio_get_devices_nrg +cdio_get_devices_osx +cdio_get_devices_ret +cdio_get_devices_solaris +cdio_get_devices_win32 +cdio_get_devices_with_cap +cdio_get_devices_with_cap_ret +cdio_get_disc_last_lsn +cdio_get_discmode +cdio_get_drive_cap +cdio_get_drive_cap_dev +cdio_get_driver_id +cdio_get_driver_name +cdio_get_first_track_num +cdio_get_hwinfo +cdio_get_joliet_level +cdio_get_last_session +cdio_get_last_track_num +cdio_get_mcn +cdio_get_media_changed +cdio_get_num_tracks +cdio_get_track +cdio_get_track_channels +cdio_get_track_copy_permit +cdio_get_track_format +cdio_get_track_green +cdio_get_track_last_lsn +cdio_get_track_lba +cdio_get_track_pregap_lba +cdio_get_track_pregap_lsn +cdio_get_track_isrc +cdio_get_track_lsn +cdio_get_track_msf +cdio_get_track_preemphasis +cdio_get_track_sec_count +cdio_guess_cd_type +cdio_have_atapi +cdio_have_bincue +cdio_have_bsdi +cdio_have_cdrdao +cdio_have_driver +cdio_have_freebsd +cdio_have_linux +cdio_have_netbsd +cdio_have_nrg +cdio_have_osx +cdio_have_solaris +cdio_have_win32 +cdio_info +cdio_init +cdio_is_binfile +cdio_is_cuefile +cdio_is_device +cdio_is_discmode_cdrom +cdio_is_discmode_dvd +cdio_is_nrg +cdio_is_tocfile +cdio_lba_to_lsn +cdio_lba_to_msf +cdio_lba_to_msf_str +cdio_log +cdio_log_set_handler +cdio_loglevel_default +cdio_lseek +cdio_lsn_to_lba +cdio_lsn_to_msf +cdio_msf_to_lba +cdio_msf_to_lsn +cdio_msf_to_str +cdio_open +cdio_open_am +cdio_open_am_bincue +cdio_open_am_bsdi +cdio_open_am_cd +cdio_open_am_cdrdao +cdio_open_am_freebsd +cdio_open_am_linux +cdio_open_am_netbsd +cdio_open_am_nrg +cdio_open_am_osx +cdio_open_am_solaris +cdio_open_am_win32 +cdio_open_bincue +cdio_open_bsdi +cdio_open_cd +cdio_open_cdrdao +cdio_open_cue +cdio_open_freebsd +cdio_open_linux +cdio_open_netbsd +cdio_open_nrg +cdio_open_osx +cdio_open_solaris +cdio_open_win32 +cdio_os_driver +cdio_read +cdio_read_audio_sector +cdio_read_audio_sectors +cdio_read_data_sectors +cdio_read_mode1_sector +cdio_read_mode1_sectors +cdio_read_mode2_sector +cdio_read_mode2_sectors +cdio_read_sector +cdio_read_sectors +cdio_set_arg +cdio_set_blocksize +cdio_set_drive_speed +cdio_set_speed +cdio_stdio_destroy +cdio_stdio_new +cdio_stream_getpos +cdio_stream_read +cdio_stream_seek +cdio_to_bcd8 +cdio_warn +cdtext_destroy +cdtext_field2str +cdtext_get +cdtext_get_const +cdtext_init +cdtext_is_keyword +cdtext_set +debug_cdio_mmc_feature +debug_cdio_mmc_feature_interface +debug_cdio_mmc_feature_profile +debug_cdio_mmc_get_conf +debug_cdio_mmc_gpcmd +debug_cdio_mmc_read_sub_state +discmode2str +mmc_audio_read_subchannel +mmc_audio_state2str +mmc_close_tray +mmc_eject_media +mmc_feature2str +mmc_feature_profile2str +mmc_get_blocksize +mmc_get_cmd_len +mmc_get_discmode +mmc_get_drive_mmc_cap +mmc_get_dvd_struct_physical +mmc_get_hwinfo +mmc_get_last_lsn +mmc_get_mcn +mmc_get_media_changed +mmc_get_tray_status +mmc_have_interface +mmc_mode_sense +mmc_mode_sense_10 +mmc_mode_sense_6 +mmc_read_cd +mmc_read_data_sectors +mmc_read_sectors +mmc_read_timeout_ms +mmc_run_cmd +mmc_run_cmd_len +mmc_set_blocksize +mmc_set_speed +mmc_start_stop_media +mmc_timeout_ms +track_format2str +cdio_charset_converter_create +cdio_charset_converter_destroy +cdio_charset_convert +cdio_charset_from_utf8 +cdio_charset_to_utf8 diff --git a/lib/driver/logging.c b/lib/driver/logging.c new file mode 100644 index 00000000..5a4a7b38 --- /dev/null +++ b/lib/driver/logging.c @@ -0,0 +1,142 @@ +/* + $Id: logging.c,v 1.2 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2003, 2004, 2008 Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include +#include "cdio_assert.h" +#include "portable.h" + +static const char _rcsid[] = "$Id: logging.c,v 1.2 2008/04/22 15:29:12 karl Exp $"; + +cdio_log_level_t cdio_loglevel_default = CDIO_LOG_WARN; + +static void +default_cdio_log_handler (cdio_log_level_t level, const char message[]) +{ + switch (level) + { + case CDIO_LOG_ERROR: + if (level >= cdio_loglevel_default) { + fprintf (stderr, "**ERROR: %s\n", message); + fflush (stderr); + } + exit (EXIT_FAILURE); + break; + case CDIO_LOG_DEBUG: + if (level >= cdio_loglevel_default) { + fprintf (stdout, "--DEBUG: %s\n", message); + } + break; + case CDIO_LOG_WARN: + if (level >= cdio_loglevel_default) { + fprintf (stdout, "++ WARN: %s\n", message); + } + break; + case CDIO_LOG_INFO: + if (level >= cdio_loglevel_default) { + fprintf (stdout, " INFO: %s\n", message); + } + break; + case CDIO_LOG_ASSERT: + if (level >= cdio_loglevel_default) { + 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/driver/mmc.c b/lib/driver/mmc.c new file mode 100644 index 00000000..2267defc --- /dev/null +++ b/lib/driver/mmc.c @@ -0,0 +1,1468 @@ +/* Common Multimedia Command (MMC) routines. + + $Id: mmc.c,v 1.40 2008/05/09 09:54:39 edsdead Exp $ + + Copyright (C) 2004, 2005, 2006, 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include "cdio_private.h" + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_STDIO_H +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +/** The below variables are trickery to force enum symbol values to be + recorded in debug symbol tables. They are used to allow one to refer + to the enumeration value names in the typedefs above in a debugger + and debugger expressions +*/ +cdio_mmc_feature_t debug_cdio_mmc_feature; +cdio_mmc_feature_interface_t debug_cdio_mmc_feature_interface; +cdio_mmc_feature_profile_t debug_cdio_mmc_feature_profile; +cdio_mmc_get_conf_t debug_cdio_mmc_get_conf; +cdio_mmc_gpcmd_t debug_cdio_mmc_gpcmd; +cdio_mmc_read_sub_state_t debug_cdio_mmc_read_sub_state; +cdio_mmc_read_cd_type_t debug_cdio_mmc_read_cd_type; +cdio_mmc_readtoc_t debug_cdio_mmc_readtoc; +cdio_mmc_mode_page_t debug_cdio_mmc_mode_page; + +/************************************************************************* + MMC CdIo Operations which a driver may use. + These are not accessible directly. + + Most of these routines just pick out the cdio pointer and call the + corresponding publically-accessible routine. +*************************************************************************/ + +/*! The maximum value in milliseconds that we will wait on an MMC + command. */ +uint32_t mmc_timeout_ms = MMC_TIMEOUT_DEFAULT; + +/*! The maximum value in milliseconds that we will wait on an MMC read + command. */ +uint32_t mmc_read_timeout_ms = MMC_READ_TIMEOUT_DEFAULT; + +/*! + Read Audio Subchannel information + + @param p_user_data the CD object to be acted upon. + +*/ +driver_return_code_t +audio_read_subchannel_mmc ( void *p_user_data, cdio_subchannel_t *p_subchannel) +{ + generic_img_private_t *p_env = p_user_data; + if (!p_env) return DRIVER_OP_UNINIT; + return mmc_audio_read_subchannel(p_env->cdio, p_subchannel); +} + +/*! + Return a string containing the name of the audio state as returned from + the Q_SUBCHANNEL. + */ +const char *mmc_audio_state2str( uint8_t i_audio_state ) +{ + switch(i_audio_state) { + case CDIO_MMC_READ_SUB_ST_INVALID: + return "invalid"; + case CDIO_MMC_READ_SUB_ST_PLAY: + return "playing"; + case CDIO_MMC_READ_SUB_ST_PAUSED: + return "paused"; + case CDIO_MMC_READ_SUB_ST_COMPLETED: + return "completed"; + case CDIO_MMC_READ_SUB_ST_ERROR: + return "error"; + case CDIO_MMC_READ_SUB_ST_NO_STATUS: + return "no status"; + default: + return "unknown"; + } +} + +/*! + Get the block size for subsequest read requests, via MMC. + @return the blocksize if > 0; error if <= 0 + */ +int +get_blocksize_mmc (void *p_user_data) +{ + generic_img_private_t *p_env = p_user_data; + if (!p_env) return DRIVER_OP_UNINIT; + return mmc_get_blocksize(p_env->cdio); +} + +/*! + Get the lsn of the end of the CD (via MMC). + + @return the lsn. On error return CDIO_INVALID_LSN. +*/ +lsn_t +get_disc_last_lsn_mmc (void *p_user_data) +{ + generic_img_private_t *p_env = p_user_data; + if (!p_env) return CDIO_INVALID_LSN; + return mmc_get_disc_last_lsn(p_env->cdio); +} + +void +get_drive_cap_mmc (const void *p_user_data, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap) +{ + const generic_img_private_t *p_env = p_user_data; + mmc_get_drive_cap( p_env->cdio, + p_read_cap, p_write_cap, p_misc_cap ); +} + +/*! Find out if media has changed since the last call. @param + p_user_data the environment of the CD object to be acted upon. + @return 1 if media has changed since last call, 0 if not. Error + return codes are the same as driver_return_code_t + */ +int +get_media_changed_mmc (const void *p_user_data) +{ + const generic_img_private_t *p_env = p_user_data; + return mmc_get_media_changed( p_env->cdio ); +} + +char * +get_mcn_mmc (const void *p_user_data) +{ + const generic_img_private_t *p_env = p_user_data; + return mmc_get_mcn( p_env->cdio ); +} + +driver_return_code_t +get_tray_status (const void *p_user_data) +{ + const generic_img_private_t *p_env = p_user_data; + return mmc_get_tray_status( p_env->cdio ); +} + +/*! Read sectors using SCSI-MMC GPCMD_READ_CD. + Can read only up to 25 blocks. +*/ +driver_return_code_t +read_data_sectors_mmc ( void *p_user_data, void *p_buf, + lsn_t i_lsn, uint16_t i_blocksize, + uint32_t i_blocks ) +{ + const generic_img_private_t *p_env = p_user_data; + return mmc_read_data_sectors( p_env->cdio, p_buf, i_lsn, i_blocksize, + i_blocks ); +} + +/* Set read blocksize (via MMC) */ +driver_return_code_t +set_blocksize_mmc (void *p_user_data, uint16_t i_blocksize) +{ + generic_img_private_t *p_env = p_user_data; + if (!p_env) return DRIVER_OP_UNINIT; + return mmc_set_blocksize(p_env->cdio, i_blocksize); +} + +/* Set the drive speed Set the drive speed in K bytes per second. (via + MMC). */ +driver_return_code_t +set_speed_mmc (void *p_user_data, int i_speed) +{ + generic_img_private_t *p_env = p_user_data; + if (!p_env) return DRIVER_OP_UNINIT; + return mmc_set_speed( p_env->cdio, i_speed ); +} + +/* Set the drive speed in CD-ROM speed units (via MMC). */ +driver_return_code_t +set_drive_speed_mmc (void *p_user_data, int i_Kbs_speed) +{ + generic_img_private_t *p_env = p_user_data; + if (!p_env) return DRIVER_OP_UNINIT; + return mmc_set_drive_speed( p_env->cdio, i_Kbs_speed ); +} + +/** Get the output port volumes and port selections used on AUDIO PLAY + commands via a MMC MODE SENSE command using the CD Audio Control + Page. + */ +driver_return_code_t +mmc_audio_get_volume( CdIo_t *p_cdio, /*out*/ mmc_audio_volume_t *p_volume ) +{ + uint8_t buf[16]; + int i_rc = mmc_mode_sense(p_cdio, buf, sizeof(buf), CDIO_MMC_AUDIO_CTL_PAGE); + + if ( DRIVER_OP_SUCCESS == i_rc ) { + p_volume->port[0].selection = 0xF & buf[8]; + p_volume->port[0].volume = buf[9]; + p_volume->port[1].selection = 0xF & buf[10]; + p_volume->port[1].volume = buf[11]; + p_volume->port[2].selection = 0xF & buf[12]; + p_volume->port[2].volume = buf[13]; + p_volume->port[3].selection = 0xF & buf[14]; + p_volume->port[3].volume = buf[15]; + return DRIVER_OP_SUCCESS; + } + return i_rc; +} + +/*! + On input a MODE_SENSE command was issued and we have the results + in p. We interpret this and return a bit mask set according to the + capabilities. + */ +void +mmc_get_drive_cap_buf(const uint8_t *p, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap) +{ + /* Reader */ + if (p[2] & 0x01) *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_R; + if (p[2] & 0x02) *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_RW; + if (p[2] & 0x08) *p_read_cap |= CDIO_DRIVE_CAP_READ_DVD_ROM; + if (p[4] & 0x01) *p_read_cap |= CDIO_DRIVE_CAP_READ_AUDIO; + if (p[4] & 0x10) *p_read_cap |= CDIO_DRIVE_CAP_READ_MODE2_FORM1; + if (p[4] & 0x20) *p_read_cap |= CDIO_DRIVE_CAP_READ_MODE2_FORM2; + if (p[5] & 0x01) *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_DA; + if (p[5] & 0x10) *p_read_cap |= CDIO_DRIVE_CAP_READ_C2_ERRS; + if (p[5] & 0x20) *p_read_cap |= CDIO_DRIVE_CAP_READ_ISRC; + if (p[5] & 0x40) *p_read_cap |= CDIO_DRIVE_CAP_READ_MCN; + + /* Writer */ + if (p[3] & 0x01) *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_R; + if (p[3] & 0x02) *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_RW; + if (p[3] & 0x10) *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_R; + if (p[3] & 0x20) *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RAM; + if (p[4] & 0x80) *p_misc_cap |= CDIO_DRIVE_CAP_WRITE_BURN_PROOF; + + /* Misc */ + if (p[4] & 0x40) *p_misc_cap |= CDIO_DRIVE_CAP_MISC_MULTI_SESSION; + if (p[6] & 0x01) *p_misc_cap |= CDIO_DRIVE_CAP_MISC_LOCK; + if (p[6] & 0x08) *p_misc_cap |= CDIO_DRIVE_CAP_MISC_EJECT; + if (p[6] >> 5 != 0) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_CLOSE_TRAY; +} + +/*! + Get the DVD type associated with cd object. +*/ +discmode_t +mmc_get_dvd_struct_physical_private ( void *p_env, + mmc_run_cmd_fn_t run_mmc_cmd, + cdio_dvd_struct_t *s) +{ + mmc_cdb_t cdb = {{0, }}; + unsigned char buf[4 + 4 * 20], *base; + int i_status; + uint8_t layer_num = s->physical.layer_num; + + cdio_dvd_layer_t *layer; + + if (!p_env) return DRIVER_OP_UNINIT; + if (!run_mmc_cmd) return DRIVER_OP_UNSUPPORTED; + + if (layer_num >= CDIO_DVD_MAX_LAYERS) + return -EINVAL; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_DVD_STRUCTURE); + cdb.field[6] = layer_num; + cdb.field[7] = CDIO_DVD_STRUCT_PHYSICAL; + cdb.field[9] = sizeof(buf) & 0xff; + + i_status = run_mmc_cmd(p_env, mmc_timeout_ms, + mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + sizeof(buf), &buf); + if (0 != i_status) + return CDIO_DISC_MODE_ERROR; + + base = &buf[4]; + layer = &s->physical.layer[layer_num]; + + /* + * place the data... really ugly, but at least we won't have to + * worry about endianess in userspace. + */ + memset(layer, 0, sizeof(*layer)); + layer->book_version = base[0] & 0xf; + layer->book_type = base[0] >> 4; + layer->min_rate = base[1] & 0xf; + layer->disc_size = base[1] >> 4; + layer->layer_type = base[2] & 0xf; + layer->track_path = (base[2] >> 4) & 1; + layer->nlayers = (base[2] >> 5) & 3; + layer->track_density = base[3] & 0xf; + layer->linear_density = base[3] >> 4; + layer->start_sector = base[5] << 16 | base[6] << 8 | base[7]; + layer->end_sector = base[9] << 16 | base[10] << 8 | base[11]; + layer->end_sector_l0 = base[13] << 16 | base[14] << 8 | base[15]; + layer->bca = base[16] >> 7; + + return DRIVER_OP_SUCCESS; +} + +/*! + Return the media catalog number MCN. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +char * +mmc_get_mcn_private ( void *p_env, + const mmc_run_cmd_fn_t run_mmc_cmd + ) +{ + mmc_cdb_t cdb = {{0, }}; + char buf[28] = { 0, }; + int i_status; + + if ( ! p_env || ! run_mmc_cmd ) + return NULL; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_SUBCHANNEL); + CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(buf)); + + cdb.field[1] = 0x0; + cdb.field[2] = 0x40; + cdb.field[3] = CDIO_SUBCHANNEL_MEDIA_CATALOG; + + i_status = run_mmc_cmd(p_env, mmc_timeout_ms, + mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + sizeof(buf), buf); + if(i_status == 0) { + return strdup(&buf[9]); + } + return NULL; +} + +/* Run a MODE SENSE command (either the 6- or 10-byte version + @return DRIVER_OP_SUCCESS if we ran the command ok. + */ +int +mmc_mode_sense( CdIo_t *p_cdio, /*out*/ void *p_buf, int i_size, + int page) +{ + /* We used to make a choice as to which routine we'd use based + cdio_have_atapi(). But since that calls this in its determination, + we had an infinite recursion. So we can't use cdio_have_atapi() + (until we put in better capability checks.) + */ + if ( DRIVER_OP_SUCCESS == mmc_mode_sense_6(p_cdio, p_buf, i_size, page) ) + return DRIVER_OP_SUCCESS; + return mmc_mode_sense_10(p_cdio, p_buf, i_size, page); +} + +/*! Run a MODE_SENSE command (6-byte version) + and put the results in p_buf + @return DRIVER_OP_SUCCESS if we ran the command ok. +*/ +int +mmc_mode_sense_6( CdIo_t *p_cdio, void *p_buf, int i_size, int page) +{ + mmc_cdb_t cdb = {{0, }}; + + if ( ! p_cdio ) return DRIVER_OP_UNINIT; + if ( ! p_cdio->op.run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED; + + memset (p_buf, 0, i_size); + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_MODE_SENSE_6); + + cdb.field[2] = 0x3F & page; + cdb.field[4] = i_size; + + return p_cdio->op.run_mmc_cmd (p_cdio->env, + mmc_timeout_ms, + mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_READ, i_size, p_buf); +} + + +/*! Run a MODE_SENSE command (10-byte version) + and put the results in p_buf + @return DRIVER_OP_SUCCESS if we ran the command ok. +*/ +int +mmc_mode_sense_10( CdIo_t *p_cdio, void *p_buf, int i_size, int page) +{ + mmc_cdb_t cdb = {{0, }}; + + if ( ! p_cdio ) return DRIVER_OP_UNINIT; + if ( ! p_cdio->op.run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED; + + memset (p_buf, 0, i_size); + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_MODE_SENSE_10); + CDIO_MMC_SET_READ_LENGTH16(cdb.field, i_size); + + cdb.field[2] = 0x3F & page; + + return p_cdio->op.run_mmc_cmd (p_cdio->env, + mmc_timeout_ms, + mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_READ, i_size, p_buf); +} + + +/* + Read cdtext information for a CdIo_t object . + + return true on success, false on error or CD-Text information does + not exist. +*/ +bool +mmc_init_cdtext_private ( void *p_user_data, + const mmc_run_cmd_fn_t run_mmc_cmd, + set_cdtext_field_fn_t set_cdtext_field_fn + ) +{ + + generic_img_private_t *p_env = p_user_data; + mmc_cdb_t cdb = {{0, }}; + unsigned char wdata[5000] = { 0, }; + int i_status, i_errno; + + if ( ! p_env || ! run_mmc_cmd || p_env->b_cdtext_error ) + return false; + + /* Operation code */ + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC); + + /* Setup to read header, to get length of data */ + CDIO_MMC_SET_READ_LENGTH8(cdb.field, 4); + + cdb.field[1] = CDIO_CDROM_MSF; + /* Format */ + cdb.field[2] = CDIO_MMC_READTOC_FMT_CDTEXT; + + errno = 0; + + /* We may need to give CD-Text a little more time to complete. */ + /* First off, just try and read the size */ + i_status = run_mmc_cmd (p_env, mmc_read_timeout_ms, + mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + 4, &wdata); + + if (i_status != 0) { + cdio_info ("CD-Text read failed for header: %s\n", strerror(errno)); + i_errno = errno; + p_env->b_cdtext_error = true; + return false; + } else { + /* Now read the CD-Text data */ + int i_cdtext = CDIO_MMC_GET_LEN16(wdata); + + if (i_cdtext > sizeof(wdata)) i_cdtext = sizeof(wdata); + + CDIO_MMC_SET_READ_LENGTH16(cdb.field, i_cdtext); + i_status = run_mmc_cmd (p_env, mmc_read_timeout_ms, + mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + i_cdtext, &wdata); + if (i_status != 0) { + cdio_info ("CD-Text read for text failed: %s\n", strerror(errno)); + i_errno = errno; + p_env->b_cdtext_error = true; + return false; + } + p_env->b_cdtext_init = true; + return cdtext_data_init(p_env, p_env->i_first_track, wdata, i_cdtext-2, + set_cdtext_field_fn); + } +} + +driver_return_code_t +mmc_set_blocksize_private ( void *p_env, + const mmc_run_cmd_fn_t run_mmc_cmd, + uint16_t i_blocksize) +{ + mmc_cdb_t cdb = {{0, }}; + + 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; + + if ( ! p_env ) return DRIVER_OP_UNINIT; + if ( ! run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED; + + memset (&mh, 0, sizeof (mh)); + mh.block_desc_length = 0x08; + mh.block_length_hi = (i_blocksize >> 16) & 0xff; + mh.block_length_med = (i_blocksize >> 8) & 0xff; + mh.block_length_lo = (i_blocksize >> 0) & 0xff; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_MODE_SELECT_6); + + cdb.field[1] = 1 << 4; + cdb.field[4] = 12; + + return run_mmc_cmd (p_env, mmc_timeout_ms, + mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_WRITE, sizeof(mh), &mh); +} + +/*********************************************************** + User-accessible Operations. +************************************************************/ +/*! + Return the number of length in bytes of the Command Descriptor + buffer (CDB) for a given MMC command. The length will be + either 6, 10, or 12. +*/ +uint8_t +mmc_get_cmd_len(uint8_t scsi_cmd) +{ + static const uint8_t scsi_cdblen[8] = {6, 10, 10, 12, 12, 12, 10, 10}; + return scsi_cdblen[((scsi_cmd >> 5) & 7)]; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + @return the lsn. On error 0 or CDIO_INVALD_LSN. + */ +lsn_t +mmc_get_disc_last_lsn ( const CdIo_t *p_cdio ) +{ + mmc_cdb_t cdb = {{0, }}; + uint8_t buf[12] = { 0, }; + + lsn_t retval = 0; + int i_status; + + /* Operation code */ + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC); + + cdb.field[1] = 0; /* lba; msf: 0x2 */ + + /* Format */ + cdb.field[2] = CDIO_MMC_READTOC_FMT_TOC; + + CDIO_MMC_SET_START_TRACK(cdb.field, CDIO_CDROM_LEADOUT_TRACK); + + CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(buf)); + + i_status = mmc_run_cmd(p_cdio, mmc_timeout_ms, &cdb, SCSI_MMC_DATA_READ, + sizeof(buf), buf); + + if (i_status) return CDIO_INVALID_LSN; + + { + int i; + for (i = 8; i < 12; i++) { + retval <<= 8; + retval += buf[i]; + } + } + + return retval; +} + +/*! + Read Audio Subchannel information + + @param p_cdio the CD object to be acted upon. + +*/ +driver_return_code_t +mmc_audio_read_subchannel (CdIo_t *p_cdio, cdio_subchannel_t *p_subchannel) +{ + mmc_cdb_t cdb; + driver_return_code_t i_rc; + cdio_mmc_subchannel_t mmc_subchannel; + + if (!p_cdio) return DRIVER_OP_UNINIT; + + memset(&mmc_subchannel, 0, sizeof(mmc_subchannel)); + mmc_subchannel.format = CDIO_CDROM_MSF; + memset(&cdb, 0, sizeof(mmc_cdb_t)); + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_SUBCHANNEL); + CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(cdio_mmc_subchannel_t)); + + cdb.field[1] = CDIO_CDROM_MSF; + cdb.field[2] = 0x40; /* subq */ + cdb.field[3] = CDIO_SUBCHANNEL_CURRENT_POSITION; + cdb.field[6] = 0; /* track number (only in isrc mode, ignored) */ + + i_rc = mmc_run_cmd(p_cdio, mmc_timeout_ms, &cdb, SCSI_MMC_DATA_READ, + sizeof(cdio_mmc_subchannel_t), &mmc_subchannel); + if (DRIVER_OP_SUCCESS == i_rc) { + p_subchannel->format = mmc_subchannel.format; + p_subchannel->audio_status = mmc_subchannel.audio_status; + p_subchannel->address = mmc_subchannel.address; + p_subchannel->control = mmc_subchannel.control; + p_subchannel->track = mmc_subchannel.track; + p_subchannel->index = mmc_subchannel.index; + p_subchannel->abs_addr.m = cdio_to_bcd8(mmc_subchannel.abs_addr[1]); + p_subchannel->abs_addr.s = cdio_to_bcd8(mmc_subchannel.abs_addr[2]); + p_subchannel->abs_addr.f = cdio_to_bcd8(mmc_subchannel.abs_addr[3]); + p_subchannel->rel_addr.m = cdio_to_bcd8(mmc_subchannel.rel_addr[1]); + p_subchannel->rel_addr.s = cdio_to_bcd8(mmc_subchannel.rel_addr[2]); + p_subchannel->rel_addr.f = cdio_to_bcd8(mmc_subchannel.rel_addr[3]); + } + return i_rc; +} + +/*! + Return the discmode as reported by the SCSI-MMC Read (FULL) TOC + command. + + Information was obtained from Section 5.1.13 (Read TOC/PMA/ATIP) + pages 56-62 from the MMC draft specification, revision 10a + at http://www.t10.org/ftp/t10/drafts/mmc/mmc-r10a.pdf See + especially tables 72, 73 and 75. + */ +discmode_t +mmc_get_discmode( const CdIo_t *p_cdio ) + +{ + uint8_t buf[14] = { 0, }; + mmc_cdb_t cdb; + + memset(&cdb, 0, sizeof(mmc_cdb_t)); + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC); + CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(buf)); + + cdb.field[1] = CDIO_CDROM_MSF; /* The MMC-5 spec may require this. */ + cdb.field[2] = CDIO_MMC_READTOC_FMT_FULTOC; + + mmc_run_cmd(p_cdio, 2000, &cdb, SCSI_MMC_DATA_READ, sizeof(buf), buf); + if (buf[7] == 0xA0) { + if (buf[13] == 0x00) { + if (buf[5] & 0x04) + return CDIO_DISC_MODE_CD_DATA; + else + return CDIO_DISC_MODE_CD_DA; + } + else if (buf[13] == 0x10) + return CDIO_DISC_MODE_CD_I; + else if (buf[13] == 0x20) + return CDIO_DISC_MODE_CD_XA; + } + return CDIO_DISC_MODE_NO_INFO; +} + +void +mmc_get_drive_cap (CdIo_t *p_cdio, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap) +{ + /* Largest buffer size we use. */ +#define BUF_MAX 2048 + uint8_t buf[BUF_MAX] = { 0, }; + + int i_status; + uint16_t i_data = BUF_MAX; + int page = CDIO_MMC_ALL_PAGES; + + if ( ! p_cdio ) return; + retry: + + /* In the first run we run MODE SENSE 10 we are trying to get the + length of the data features. */ + i_status = mmc_mode_sense_10(p_cdio, buf, 8, CDIO_MMC_ALL_PAGES); + + if (DRIVER_OP_SUCCESS == i_status) { + uint16_t i_data_try = (uint16_t) CDIO_MMC_GET_LEN16(buf); + if (i_data_try < BUF_MAX) i_data = i_data_try; + } + + /* Now try getting all features with length set above, possibly + truncated or the default length if we couldn't get the proper + length. */ + i_status = mmc_mode_sense_10(p_cdio, buf, i_data, CDIO_MMC_ALL_PAGES); + if (0 != i_status && CDIO_MMC_CAPABILITIES_PAGE != page) { + page = CDIO_MMC_CAPABILITIES_PAGE; + goto retry; + } + + if (DRIVER_OP_SUCCESS == i_status) { + uint8_t *p; + uint8_t *p_max = buf + 256; + + *p_read_cap = 0; + *p_write_cap = 0; + *p_misc_cap = 0; + + /* set to first sense mask, and then walk through the masks */ + p = buf + 8; + while( (p < &(buf[2+i_data])) && (p < p_max) ) { + uint8_t which_page; + + which_page = p[0] & 0x3F; + switch( which_page ) + { + case CDIO_MMC_AUDIO_CTL_PAGE: + case CDIO_MMC_R_W_ERROR_PAGE: + case CDIO_MMC_CDR_PARMS_PAGE: + /* Don't handle these yet. */ + break; + case CDIO_MMC_CAPABILITIES_PAGE: + mmc_get_drive_cap_buf(p, p_read_cap, p_write_cap, p_misc_cap); + break; + default: ; + } + p += (p[1] + 2); + } + } else { + cdio_info("%s: %s\n", "error in MODE_SELECT", strerror(errno)); + *p_read_cap = CDIO_DRIVE_CAP_ERROR; + *p_write_cap = CDIO_DRIVE_CAP_ERROR; + *p_misc_cap = CDIO_DRIVE_CAP_ERROR; + } + return; +} + +/*! + Get the MMC level supported by the device. +*/ +cdio_mmc_level_t +mmc_get_drive_mmc_cap(CdIo_t *p_cdio) +{ + uint8_t buf[256] = { 0, }; + uint8_t len; + int rc = mmc_mode_sense(p_cdio, buf, sizeof(buf), + CDIO_MMC_CAPABILITIES_PAGE); + + if (DRIVER_OP_SUCCESS != rc) { + return CDIO_MMC_LEVEL_NONE; + } + + len = buf[1]; + if (16 > len) { + return CDIO_MMC_LEVEL_WEIRD; + } else if (28 <= len) { + return CDIO_MMC_LEVEL_3; + } else if (24 <= len) { + return CDIO_MMC_LEVEL_2; + printf("MMC 2"); + } else if (20 <= len) { + return CDIO_MMC_LEVEL_1; + } else { + return CDIO_MMC_LEVEL_WEIRD; + } +} + +/*! + Get the DVD type associated with cd object. +*/ +discmode_t +mmc_get_dvd_struct_physical ( const CdIo_t *p_cdio, cdio_dvd_struct_t *s) +{ + if ( ! p_cdio ) return -2; + return + mmc_get_dvd_struct_physical_private (p_cdio->env, + p_cdio->op.run_mmc_cmd, + s); +} + +/*! + Get the CD-ROM hardware info via a MMC INQUIRY command. + False is returned if we had an error getting the information. +*/ +bool +mmc_get_hwinfo ( const CdIo_t *p_cdio, + /*out*/ cdio_hwinfo_t *hw_info ) +{ + int i_status; /* Result of MMC command */ + char buf[36] = { 0, }; /* Place to hold returned data */ + mmc_cdb_t cdb = {{0, }}; /* Command Descriptor Block */ + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_INQUIRY); + cdb.field[4] = sizeof(buf); + + if (! p_cdio || ! hw_info ) return false; + + i_status = mmc_run_cmd(p_cdio, mmc_timeout_ms, + &cdb, SCSI_MMC_DATA_READ, + sizeof(buf), &buf); + if (i_status == 0) { + + memcpy(hw_info->psz_vendor, + buf + 8, + sizeof(hw_info->psz_vendor)-1); + hw_info->psz_vendor[sizeof(hw_info->psz_vendor)-1] = '\0'; + memcpy(hw_info->psz_model, + buf + 8 + CDIO_MMC_HW_VENDOR_LEN, + sizeof(hw_info->psz_model)-1); + hw_info->psz_model[sizeof(hw_info->psz_model)-1] = '\0'; + memcpy(hw_info->psz_revision, + buf + 8 + CDIO_MMC_HW_VENDOR_LEN + CDIO_MMC_HW_MODEL_LEN, + sizeof(hw_info->psz_revision)-1); + hw_info->psz_revision[sizeof(hw_info->psz_revision)-1] = '\0'; + return true; + } + return false; +} + +/*! + Return results of media status + @param p_cdio the CD object to be acted upon. + @return DRIVER_OP_SUCCESS (0) if we got the status. + return codes are the same as driver_return_code_t + */ +int mmc_get_event_status(const CdIo_t *p_cdio, uint8_t out_buf[2]) +{ + mmc_cdb_t cdb = {{0, }}; + uint8_t buf[8] = { 0, }; + int i_status; + + if ( ! p_cdio ) return DRIVER_OP_UNINIT; + if ( ! p_cdio->op.run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_GET_EVENT_STATUS); + + /* Setup to read header, to get length of data */ + CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(buf)); + + cdb.field[1] = 1; /* We poll for info */ + cdb.field[4] = 1 << 4; /* We want Media events */ + + i_status = p_cdio->op.run_mmc_cmd(p_cdio->env, mmc_timeout_ms, + mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + sizeof(buf), buf); + if(i_status == 0) { + out_buf[0] = buf[4]; + out_buf[1] = buf[5]; + return DRIVER_OP_SUCCESS; + } + return DRIVER_OP_ERROR; +} + +/*! + Find out if media has changed since the last call. + @param p_cdio the CD object to be acted upon. + @return 1 if media has changed since last call, 0 if not. Error + return codes are the same as driver_return_code_t + */ +int mmc_get_media_changed(const CdIo_t *p_cdio) +{ + uint8_t status_buf[2]; + int i_status; + + i_status = mmc_get_event_status(p_cdio, status_buf); + if (i_status != DRIVER_OP_SUCCESS) + return i_status; + return (status_buf[0] & 0x02) ? 1 : 0; +} + +char * +mmc_get_mcn ( const CdIo_t *p_cdio ) +{ + if ( ! p_cdio ) return NULL; + return mmc_get_mcn_private (p_cdio->env, p_cdio->op.run_mmc_cmd ); +} + +/*! + Find out if media tray is open or closed. + @param p_cdio the CD object to be acted upon. + @return 1 if media is open, 0 if closed. Error + return codes are the same as driver_return_code_t + */ +int mmc_get_tray_status(const CdIo_t *p_cdio) +{ + uint8_t status_buf[2]; + int i_status; + + i_status = mmc_get_event_status(p_cdio, status_buf); + if (i_status != DRIVER_OP_SUCCESS) + return i_status; + return (status_buf[1] & 0x01) ? 1 : 0; +} + +/*! + Run a MMC command. + + cdio CD structure set by cdio_open(). + i_timeout time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + buf Buffer for data, both sending and receiving + len Size of buffer + e_direction direction the transfer is to go + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + */ +driver_return_code_t +mmc_run_cmd( const CdIo_t *p_cdio, unsigned int i_timeout_ms, + const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, unsigned int i_buf, + /*in/out*/ void *p_buf ) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + if (!p_cdio->op.run_mmc_cmd) return DRIVER_OP_UNSUPPORTED; + return p_cdio->op.run_mmc_cmd(p_cdio->env, i_timeout_ms, + mmc_get_cmd_len(p_cdb->field[0]), + p_cdb, e_direction, i_buf, p_buf); +} + +/* Added by SukkoPera to allow CDB length to be specified manually */ +driver_return_code_t +mmc_run_cmd_len( const CdIo_t *p_cdio, unsigned int i_timeout_ms, + const mmc_cdb_t *p_cdb, unsigned int i_cdb, + cdio_mmc_direction_t e_direction, unsigned int i_buf, + /*in/out*/ void *p_buf ) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + if (!p_cdio->op.run_mmc_cmd) return DRIVER_OP_UNSUPPORTED; + return p_cdio->op.run_mmc_cmd(p_cdio->env, i_timeout_ms, + i_cdb, + p_cdb, e_direction, i_buf, p_buf); +} + + +/*! Return the byte size returned on a MMC READ command (e.g. READ_10, + READ_MSF, ..) +*/ +int +mmc_get_blocksize ( CdIo_t *p_cdio) +{ + int i_status; + + uint8_t buf[255] = { 0, }; + uint8_t *p; + + /* First try using the 6-byte MODE SENSE command. */ + i_status = mmc_mode_sense_6(p_cdio, buf, sizeof(buf), + CDIO_MMC_R_W_ERROR_PAGE); + + if (DRIVER_OP_SUCCESS == i_status && buf[3]>=8) { + p = &buf[4+5]; + return CDIO_MMC_GET_LEN16(p); + } + + /* Next try using the 10-byte MODE SENSE command. */ + i_status = mmc_mode_sense_10(p_cdio, buf, sizeof(buf), + CDIO_MMC_R_W_ERROR_PAGE); + p = &buf[6]; + if (DRIVER_OP_SUCCESS == i_status && CDIO_MMC_GET_LEN16(p)>=8) { + return CDIO_MMC_GET_LEN16(p); + } + +#ifdef IS_THIS_CORRECT + /* Lastly try using the READ CAPACITY command. */ + { + lba_t lba = 0; + uint16_t i_blocksize; + + i_status = mmc_read_capacity(p_cdio, &lba, &i_blocksize); + if ( DRIVER_OP_SUCCESS == i_status ) + return i_blocksize; +#endif + + return DRIVER_OP_UNSUPPORTED; +} + + +/*! + * Load or Unload media using a MMC START STOP command. + */ +driver_return_code_t +mmc_start_stop_media(const CdIo_t *p_cdio, bool b_eject, bool b_immediate, + uint8_t power_condition) +{ + mmc_cdb_t cdb = {{0, }}; + uint8_t buf[1]; + + if ( ! p_cdio ) return DRIVER_OP_UNINIT; + if ( ! p_cdio->op.run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_START_STOP); + + if (b_immediate) cdb.field[1] |= 1; + + if (power_condition) + cdb.field[4] = power_condition << 4; + else { + if (b_eject) + cdb.field[4] = 2; /* eject */ + else + cdb.field[4] = 3; /* close tray for tray-type */ + } + + return p_cdio->op.run_mmc_cmd (p_cdio->env, mmc_timeout_ms, + mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_WRITE, 0, &buf); +} + +/** + * Close tray using a MMC START STOP command. + */ +driver_return_code_t +mmc_close_tray( CdIo_t *p_cdio ) +{ + if (p_cdio) { + return mmc_start_stop_media(p_cdio, false, false, 0); + } else { + return DRIVER_OP_ERROR; + } +} + +/*! + Eject using MMC commands. If CD-ROM is "locked" we'll unlock it. + Command is not "immediate" -- we'll wait for the command to complete. + For a more general (and lower-level) routine, @see mmc_start_stop_media. + */ +driver_return_code_t +mmc_eject_media( const CdIo_t *p_cdio ) +{ + int i_status = 0; + mmc_cdb_t cdb = {{0, }}; + uint8_t buf[1]; + + if ( ! p_cdio ) return DRIVER_OP_UNINIT; + if ( ! p_cdio->op.run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_ALLOW_MEDIUM_REMOVAL); + + i_status = p_cdio->op.run_mmc_cmd (p_cdio->env, mmc_timeout_ms, + mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_WRITE, 0, &buf); + if (0 != i_status) return i_status; + + return mmc_start_stop_media(p_cdio, true, false, 0); + +} + +/*! + Return a string containing the name of the given feature + */ +const char *mmc_feature2str( int i_feature ) +{ + switch(i_feature) { + case CDIO_MMC_FEATURE_PROFILE_LIST: + return "Profile List"; + case CDIO_MMC_FEATURE_CORE: + return "Core"; + case CDIO_MMC_FEATURE_MORPHING: + return "Morphing" ; + case CDIO_MMC_FEATURE_REMOVABLE_MEDIUM: + return "Removable Medium"; + case CDIO_MMC_FEATURE_WRITE_PROTECT: + return "Write Protect"; + case CDIO_MMC_FEATURE_RANDOM_READABLE: + return "Random Readable"; + case CDIO_MMC_FEATURE_MULTI_READ: + return "Multi-Read"; + case CDIO_MMC_FEATURE_CD_READ: + return "CD Read"; + case CDIO_MMC_FEATURE_DVD_READ: + return "DVD Read"; + case CDIO_MMC_FEATURE_RANDOM_WRITABLE: + return "Random Writable"; + case CDIO_MMC_FEATURE_INCR_WRITE: + return "Incremental Streaming Writable"; + case CDIO_MMC_FEATURE_SECTOR_ERASE: + return "Sector Erasable"; + case CDIO_MMC_FEATURE_FORMATABLE: + return "Formattable"; + case CDIO_MMC_FEATURE_DEFECT_MGMT: + return "Management Ability of the Logical Unit/media system " + "to provide an apparently defect-free space."; + case CDIO_MMC_FEATURE_WRITE_ONCE: + return "Write Once"; + case CDIO_MMC_FEATURE_RESTRICT_OVERW: + return "Restricted Overwrite"; + case CDIO_MMC_FEATURE_CD_RW_CAV: + return "CD-RW CAV Write"; + case CDIO_MMC_FEATURE_MRW: + return "MRW"; + case CDIO_MMC_FEATURE_ENHANCED_DEFECT: + return "Enhanced Defect Reporting"; + case CDIO_MMC_FEATURE_DVD_PRW: + return "DVD+RW"; + case CDIO_MMC_FEATURE_DVD_PR: + return "DVD+R"; + case CDIO_MMC_FEATURE_RIGID_RES_OVERW: + return "Rigid Restricted Overwrite"; + case CDIO_MMC_FEATURE_CD_TAO: + return "CD Track at Once"; + case CDIO_MMC_FEATURE_CD_SAO: + return "CD Mastering (Session at Once)"; + case CDIO_MMC_FEATURE_DVD_R_RW_WRITE: + return "DVD-R/RW Write"; + case CDIO_MMC_FEATURE_CD_RW_MEDIA_WRITE: + return "CD-RW Media Write Support"; + case CDIO_MMC_FEATURE_DVD_PR_2_LAYER: + return "DVD+R Double Layer"; + case CDIO_MMC_FEATURE_POWER_MGMT: + return "Initiator- and Device-directed Power Management"; + case CDIO_MMC_FEATURE_CDDA_EXT_PLAY: + return "CD Audio External Play"; + case CDIO_MMC_FEATURE_MCODE_UPGRADE: + return "Ability for the device to accept new microcode via the interface"; + case CDIO_MMC_FEATURE_TIME_OUT: + return "Ability to respond to all commands within a specific time"; + case CDIO_MMC_FEATURE_DVD_CSS: + return "Ability to perform DVD CSS/CPPM authentication via RPC"; + case CDIO_MMC_FEATURE_RT_STREAMING: + return "Ability to read and write using Initiator requested performance" + " parameters"; + case CDIO_MMC_FEATURE_LU_SN: + return "The Logical Unit Unique Identifier"; + default: + { + static char buf[100]; + if ( 0 != (i_feature & 0xFF00) ) { + snprintf( buf, sizeof(buf), + "Vendor-specific code %x", i_feature ); + } else { + snprintf( buf, sizeof(buf), + "Unknown code %x", i_feature ); + } + return buf; + } + } +} + + +/*! + Return a string containing the name of the given feature profile. + */ +const char *mmc_feature_profile2str( int i_feature_profile ) +{ + switch(i_feature_profile) { + case CDIO_MMC_FEATURE_PROF_NON_REMOVABLE: + return "Non-removable"; + case CDIO_MMC_FEATURE_PROF_REMOVABLE: + return "disk Re-writable; with removable media"; + case CDIO_MMC_FEATURE_PROF_MO_ERASABLE: + return "Erasable Magneto-Optical disk with sector erase capability"; + case CDIO_MMC_FEATURE_PROF_MO_WRITE_ONCE: + return "Write Once Magneto-Optical write once"; + case CDIO_MMC_FEATURE_PROF_AS_MO: + return "Advance Storage Magneto-Optical"; + case CDIO_MMC_FEATURE_PROF_CD_ROM: + return "Read only Compact Disc capable"; + case CDIO_MMC_FEATURE_PROF_CD_R: + return "Write once Compact Disc capable"; + case CDIO_MMC_FEATURE_PROF_CD_RW: + return "CD-RW Re-writable Compact Disc capable"; + case CDIO_MMC_FEATURE_PROF_DVD_ROM: + return "Read only DVD"; + case CDIO_MMC_FEATURE_PROF_DVD_R_SEQ: + return "Re-recordable DVD using Sequential recording"; + case CDIO_MMC_FEATURE_PROF_DVD_RAM: + return "Re-writable DVD"; + case CDIO_MMC_FEATURE_PROF_DVD_RW_RO: + return "Re-recordable DVD using Restricted Overwrite"; + case CDIO_MMC_FEATURE_PROF_DVD_RW_SEQ: + return "Re-recordable DVD using Sequential recording"; + case CDIO_MMC_FEATURE_PROF_DVD_PRW: + return "DVD+RW - DVD ReWritable"; + case CDIO_MMC_FEATURE_RIGID_RES_OVERW: + return "Rigid Restricted Overwrite"; + case CDIO_MMC_FEATURE_PROF_DVD_PR: + return "DVD+R - DVD Recordable"; + case CDIO_MMC_FEATURE_PROF_DDCD_ROM: + return "Read only DDCD"; + case CDIO_MMC_FEATURE_PROF_DVD_PR2: + return "DVD+R Double Layer - DVD Recordable Double Layer"; + case CDIO_MMC_FEATURE_PROF_DDCD_R: + return "DDCD-R Write only DDCD"; + case CDIO_MMC_FEATURE_PROF_DDCD_RW: + return "Re-Write only DDCD"; + case CDIO_MMC_FEATURE_PROF_NON_CONFORM: + return "The Logical Unit does not conform to any Profile"; + default: + { + static char buf[100]; + snprintf(buf, sizeof(buf), "Unknown Profile %x", i_feature_profile); + return buf; + } + } +} + + +/*! + * See if CD-ROM has feature with value value + * @return true if we have the feature and false if not. + */ +bool_3way_t +mmc_have_interface( CdIo_t *p_cdio, cdio_mmc_feature_interface_t e_interface ) +{ + int i_status; /* Result of MMC command */ + uint8_t buf[500] = { 0, }; /* Place to hold returned data */ + mmc_cdb_t cdb = {{0, }}; /* Command Descriptor Buffer */ + + if (!p_cdio || !p_cdio->op.run_mmc_cmd) return nope; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_GET_CONFIGURATION); + CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(buf)); + + cdb.field[1] = CDIO_MMC_GET_CONF_NAMED_FEATURE; + cdb.field[3] = CDIO_MMC_FEATURE_CORE; + + i_status = mmc_run_cmd(p_cdio, 0, &cdb, SCSI_MMC_DATA_READ, sizeof(buf), + &buf); + if (DRIVER_OP_SUCCESS == i_status) { + uint8_t *p; + uint32_t i_data; + uint8_t *p_max = buf + 65530; + + i_data = (unsigned int) CDIO_MMC_GET_LEN32(buf); + /* set to first sense feature code, and then walk through the masks */ + p = buf + 8; + while( (p < &(buf[i_data])) && (p < p_max) ) { + uint16_t i_feature; + uint8_t i_feature_additional = p[3]; + + i_feature = CDIO_MMC_GET_LEN16(p); + if (CDIO_MMC_FEATURE_CORE == i_feature) { + uint8_t *q = p+4; + uint32_t i_interface_standard = CDIO_MMC_GET_LEN32(q); + if (e_interface == i_interface_standard) return yep; + } + p += i_feature_additional + 4; + } + return nope; + } else + return dunno; +} + +/* Maximum blocks to retrieve. Would be nice to customize this based on + drive capabilities. +*/ +#define MAX_CD_READ_BLOCKS 16 +#define CD_READ_TIMEOUT_MS mmc_timeout_ms * (MAX_CD_READ_BLOCKS/2) + +/*! issue a MMC READ_CD command. +*/ +driver_return_code_t +mmc_read_cd ( const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn, + int read_sector_type, bool b_digital_audio_play, + bool b_sync, uint8_t header_codes, bool b_user_data, + bool b_edc_ecc, uint8_t c2_error_information, + uint8_t subchannel_selection, uint16_t i_blocksize, + uint32_t i_blocks ) +{ + mmc_cdb_t cdb = {{0, }}; + + mmc_run_cmd_fn_t run_mmc_cmd; + uint8_t cdb9 = 0; + + if (!p_cdio) return DRIVER_OP_UNINIT; + if (!p_cdio->op.run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED; + + run_mmc_cmd = p_cdio->op.run_mmc_cmd; + + CDIO_MMC_SET_COMMAND (cdb.field, CDIO_MMC_GPCMD_READ_CD); + CDIO_MMC_SET_READ_TYPE(cdb.field, read_sector_type); + if (b_digital_audio_play) cdb.field[1] |= 0x2; + + if (b_sync) cdb9 |= 128; + if (b_user_data) cdb9 |= 16; + if (b_edc_ecc) cdb9 |= 8; + cdb9 |= (header_codes & 3) << 5; + cdb9 |= (c2_error_information & 3) << 1; + cdb.field[9] = cdb9; + cdb.field[10] = (subchannel_selection & 7); + + { + unsigned int j = 0; + int i_ret = DRIVER_OP_SUCCESS; + const uint8_t i_cdb = mmc_get_cmd_len(cdb.field[0]); + + while (i_blocks > 0) { + const unsigned i_blocks2 = (i_blocks > MAX_CD_READ_BLOCKS) + ? MAX_CD_READ_BLOCKS : i_blocks; + void *p_buf2 = ((char *)p_buf ) + (j * i_blocksize); + + CDIO_MMC_SET_READ_LBA (cdb.field, (i_lsn+j)); + CDIO_MMC_SET_READ_LENGTH24(cdb.field, i_blocks2); + + i_ret = run_mmc_cmd (p_cdio->env, CD_READ_TIMEOUT_MS, + i_cdb, &cdb, + SCSI_MMC_DATA_READ, + i_blocksize * i_blocks2, + p_buf2); + + if (i_ret) return i_ret; + + i_blocks -= i_blocks2; + j += i_blocks2; + } + return i_ret; + } +} + +/*! Read sectors using SCSI-MMC GPCMD_READ_CD. +*/ +driver_return_code_t +mmc_read_data_sectors ( CdIo_t *p_cdio, void *p_buf, + lsn_t i_lsn, uint16_t i_blocksize, + uint32_t i_blocks ) +{ + return mmc_read_cd(p_cdio, + p_buf, /* place to store data */ + i_lsn, /* lsn */ + 0, /* read_sector_type */ + false, /* digital audio play */ + false, /* return sync header */ + 0, /* header codes */ + true, /* return user data */ + false, /* return EDC ECC */ + false, /* return C2 Error information */ + 0, /* subchannel selection bits */ + ISO_BLOCKSIZE, /* blocksize*/ + i_blocks /* Number of blocks. */); + +} + +/*! Read sectors using SCSI-MMC GPCMD_READ_CD. + Can read only up to 25 blocks. +*/ +driver_return_code_t +mmc_read_sectors ( const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn, + int sector_type, uint32_t i_blocks ) +{ + mmc_cdb_t cdb = {{0, }}; + + mmc_run_cmd_fn_t run_mmc_cmd; + + if (!p_cdio) return DRIVER_OP_UNINIT; + if (!p_cdio->op.run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED; + + run_mmc_cmd = p_cdio->op.run_mmc_cmd; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_CD); + CDIO_MMC_SET_READ_TYPE (cdb.field, sector_type); + CDIO_MMC_SET_READ_LBA (cdb.field, i_lsn); + CDIO_MMC_SET_READ_LENGTH24(cdb.field, i_blocks); + CDIO_MMC_SET_MAIN_CHANNEL_SELECTION_BITS(cdb.field, + CDIO_MMC_MCSB_ALL_HEADERS); + + return run_mmc_cmd (p_cdio->env, mmc_timeout_ms, + mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_READ, + CDIO_CD_FRAMESIZE_RAW * i_blocks, + p_buf); +} + +driver_return_code_t +mmc_set_blocksize ( const CdIo_t *p_cdio, uint16_t i_blocksize) +{ + if ( ! p_cdio ) return DRIVER_OP_UNINIT; + return + mmc_set_blocksize_private (p_cdio->env, p_cdio->op.run_mmc_cmd, + i_blocksize); +} + + +/*! + Set the drive speed in CD-ROM speed units. + + @param p_cdio CD structure set by cdio_open(). + @param i_drive_speed speed in CD-ROM speed units. Note this + not Kbytes/sec as would be used in the MMC spec or + in mmc_set_speed(). To convert CD-ROM speed units + to Kbs, multiply the number by 176 (for raw data) + and by 150 (for filesystem data). On many CD-ROM + drives, specifying a value too large will result + in using the fastest speed. + + @return the drive speed if greater than 0. -1 if we had an error. is -2 + returned if this is not implemented for the current driver. + + @see cdio_set_speed and mmc_set_speed +*/ +driver_return_code_t +mmc_set_drive_speed( const CdIo_t *p_cdio, int i_drive_speed ) +{ + return mmc_set_speed(p_cdio, i_drive_speed * 176); +} + + +/*! + Set the drive speed in K bytes per second. + + @return the drive speed if greater than 0. -1 if we had an error. is -2 + returned if this is not implemented for the current driver. +*/ +int +mmc_set_speed( const CdIo_t *p_cdio, int i_Kbs_speed ) + +{ + uint8_t buf[14] = { 0, }; + mmc_cdb_t cdb; + + /* If the requested speed is less than 1x 176 kb/s this command + will return an error - it's part of the ATAPI specs. Therefore, + test and stop early. */ + + if ( i_Kbs_speed < 176 ) return -1; + + memset(&cdb, 0, sizeof(mmc_cdb_t)); + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_SET_SPEED); + CDIO_MMC_SET_LEN16(cdb.field, 2, i_Kbs_speed); + /* Some drives like the Creative 24x CDRW require one to set a + nonzero write speed or else one gets an error back. Some + specifications have setting the value 0xfffff indicate setting to + the maximum allowable speed. + */ + CDIO_MMC_SET_LEN16(cdb.field, 4, 0xffff); + return mmc_run_cmd(p_cdio, 2000, &cdb, SCSI_MMC_DATA_WRITE, + sizeof(buf), buf); +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/driver/mmc_private.h b/lib/driver/mmc_private.h new file mode 100644 index 00000000..d06d2387 --- /dev/null +++ b/lib/driver/mmc_private.h @@ -0,0 +1,155 @@ +/* private MMC helper routines. + + $Id: mmc_private.h,v 1.12 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2004, 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include "cdtext_private.h" + +/*! Convert milliseconds to seconds taking the ceiling value, i.e. + 1002 milliseconds gets rounded to 2 seconds. +*/ +#define SECS2MSECS 1000 +static inline unsigned int +msecs2secs(unsigned int msecs) +{ + return (msecs+(SECS2MSECS-1)) / SECS2MSECS; +} +#undef SECS2MSECS + +/*********************************************************** + MMC CdIo Operations which a driver may use. + These are not directly user-accessible. +************************************************************/ +/*! + Read Audio Subchannel information + + @param p_user_data the CD object to be acted upon. + +*/ +driver_return_code_t +audio_read_subchannel_mmc ( void *p_user_data, + cdio_subchannel_t *p_subchannel); + +/*! + Get the block size for subsequest read requests, via a SCSI MMC + MODE_SENSE 6 command. +*/ +int get_blocksize_mmc (void *p_user_data); + +/*! + Get the lsn of the end of the CD + + @return the lsn. On error return CDIO_INVALID_LSN. +*/ +lsn_t get_disc_last_lsn_mmc( void *p_user_data ); + +void get_drive_cap_mmc (const void *p_user_data, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap); + +int get_media_changed_mmc (const void *p_user_data); + +char *get_mcn_mmc (const void *p_user_data); + +driver_return_code_t get_tray_status (const void *p_user_data); + +/*! Read just the user data part of some sort of data sector (via + mmc_read_cd). + + @param p_user_data object to read from + + @param p_buf place to read data into. The caller should make sure + this location can store at least CDIO_CD_FRAMESIZE, + M2RAW_SECTOR_SIZE, or M2F2_SECTOR_SIZE depending on + the kind of sector getting read. If you don't know + whether you have a Mode 1/2, Form 1/ Form 2/Formless + sector best to reserve space for the maximum, + M2RAW_SECTOR_SIZE. + + @param i_lsn sector to read + @param i_blocksize size of block. Should be either CDIO_CD_FRAMESIZE, + M2RAW_SECTOR_SIZE, or M2F2_SECTOR_SIZE. See comment above under p_buf. + +*/ +driver_return_code_t read_data_sectors_mmc ( void *p_user_data, + void *p_buf, lsn_t i_lsn, + uint16_t i_blocksize, + uint32_t i_blocks ); +char *get_mcn_mmc (const void *p_user_data); + +/* Set read blocksize (via MMC) */ +driver_return_code_t set_blocksize_mmc (void *p_user_data, + uint16_t i_blocksize); + +/* Set the drive speed in CD-ROM speed units (via MMC). */ +driver_return_code_t set_drive_speed_mmc (void *p_user_data, int i_speed); + +/* Set CD-ROM drive speed in K bytes per second. (via MMC) */ +driver_return_code_t set_speed_mmc (void *p_user_data, int i_Kbs_speed); + +/*********************************************************** + Miscellaenous other "private" routines. Probably need + to better classify these. +************************************************************/ + +typedef driver_return_code_t (*mmc_run_cmd_fn_t) + ( void *p_user_data, + unsigned int i_timeout_ms, + unsigned int i_cdb, + const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ); + +int mmc_set_blocksize_mmc_private ( const void *p_env, const + mmc_run_cmd_fn_t run_mmc_cmd, + uint16_t i_blocksize ); + +/*! + Get the DVD type associated with cd object. +*/ +discmode_t +mmc_get_dvd_struct_physical_private ( void *p_env, + mmc_run_cmd_fn_t run_mmc_cmd, + cdio_dvd_struct_t *s ); + + +char *mmc_get_mcn_private ( void *p_env, + mmc_run_cmd_fn_t run_mmc_cmd + ); + +bool mmc_init_cdtext_private ( void *p_user_data, + mmc_run_cmd_fn_t run_mmc_cmd, + set_cdtext_field_fn_t set_cdtext_field_fn + ); + +/*! + On input a MODE_SENSE command was issued and we have the results + in p. We interpret this and return a bit mask set according to the + capabilities. + */ +void mmc_get_drive_cap_buf(const uint8_t *p, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap); + +driver_return_code_t +mmc_set_blocksize_private ( void *p_env, + const mmc_run_cmd_fn_t run_mmc_cmd, + uint16_t i_blocksize); diff --git a/lib/driver/netbsd.c b/lib/driver/netbsd.c new file mode 100644 index 00000000..555038b6 --- /dev/null +++ b/lib/driver/netbsd.c @@ -0,0 +1,656 @@ +/* + $Id: netbsd.c,v 1.4 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Changes up to version 0.76 */ +/* + * Copyright (c) 2003 + * Matthias Drochner. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "cdio_assert.h" +#include "cdio_private.h" + +#ifdef __i386__ +#define DEFAULT_CDIO_DEVICE "/dev/rcd0d" +#else +#define DEFAULT_CDIO_DEVICE "/dev/rcd0c" +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_NETBSD_CDROM +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define TOTAL_TRACKS (_obj->tochdr.ending_track \ + - _obj->tochdr.starting_track + 1) +#define FIRST_TRACK_NUM (_obj->tochdr.starting_track) + +typedef struct { + generic_img_private_t gen; + + bool toc_valid; + struct ioc_toc_header tochdr; + struct cd_toc_entry tocent[100]; + + bool sessionformat_valid; + int sessionformat[100]; /* format of the session the track is in */ +} _img_private_t; + +static driver_return_code_t +run_scsi_cmd_netbsd(void *p_user_data, unsigned int i_timeout_ms, + unsigned int i_cdb, const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, void *p_buf ) +{ + const _img_private_t *_obj = p_user_data; + scsireq_t req; + + memset(&req, 0, sizeof(req)); + memcpy(&req.cmd[0], p_cdb, i_cdb); + req.cmdlen = i_cdb; + req.datalen = i_buf; + req.databuf = p_buf; + req.timeout = i_timeout_ms; + req.flags = e_direction == SCSI_MMC_DATA_READ ? SCCMD_READ : SCCMD_WRITE; + + if (ioctl(_obj->gen.fd, SCIOCCOMMAND, &req) < 0) { + perror("SCIOCCOMMAND"); + return -1; + } + if (req.retsts != SCCMD_OK) { + fprintf(stderr, "SCIOCCOMMAND cmd 0x%02x sts %d\n", req.cmd[0], req.retsts); + return -1; + } + + return 0; +} + +static int +read_audio_sectors_netbsd(void *user_data, void *data, lsn_t lsn, + unsigned int nblocks) +{ + scsireq_t req; + _img_private_t *_obj = user_data; + + memset(&req, 0, sizeof(req)); + req.cmd[0] = 0xbe; + req.cmd[1] = 0; + req.cmd[2] = (lsn >> 24) & 0xff; + req.cmd[3] = (lsn >> 16) & 0xff; + req.cmd[4] = (lsn >> 8) & 0xff; + req.cmd[5] = (lsn >> 0) & 0xff; + req.cmd[6] = (nblocks >> 16) & 0xff; + req.cmd[7] = (nblocks >> 8) & 0xff; + req.cmd[8] = (nblocks >> 0) & 0xff; + req.cmd[9] = 0x78; + req.cmdlen = 10; + + req.datalen = nblocks * CDIO_CD_FRAMESIZE_RAW; + req.databuf = data; + req.timeout = 10000; + req.flags = SCCMD_READ; + + if (ioctl(_obj->gen.fd, SCIOCCOMMAND, &req) < 0) { + perror("SCIOCCOMMAND"); + return 1; + } + if (req.retsts != SCCMD_OK) { + fprintf(stderr, "SCIOCCOMMAND cmd 0xbe sts %d\n", req.retsts); + return 1; + } + + return 0; +} + +static int +read_mode2_sector_netbsd(void *user_data, void *data, lsn_t lsn, + bool mode2_form2) +{ + scsireq_t req; + _img_private_t *_obj = user_data; + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + + memset(&req, 0, sizeof(req)); + req.cmd[0] = 0xbe; + req.cmd[1] = 0; + req.cmd[2] = (lsn >> 24) & 0xff; + req.cmd[3] = (lsn >> 16) & 0xff; + req.cmd[4] = (lsn >> 8) & 0xff; + req.cmd[5] = (lsn >> 0) & 0xff; + req.cmd[6] = 0; + req.cmd[7] = 0; + req.cmd[8] = 1; + req.cmd[9] = 0x58; /* subheader + userdata + ECC */ + req.cmdlen = 10; + + req.datalen = M2RAW_SECTOR_SIZE; + req.databuf = buf; + req.timeout = 10000; + req.flags = SCCMD_READ; + + if (ioctl(_obj->gen.fd, SCIOCCOMMAND, &req) < 0) { + perror("SCIOCCOMMAND"); + return 1; + } + if (req.retsts != SCCMD_OK) { + fprintf(stderr, "SCIOCCOMMAND cmd 0xbe sts %d\n", req.retsts); + return 1; + } + + if (mode2_form2) + memcpy(data, buf, M2RAW_SECTOR_SIZE); + else + memcpy(data, buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE); + + return 0; +} + +static int +read_mode2_sectors_netbsd(void *user_data, void *data, lsn_t lsn, + bool mode2_form2, unsigned int nblocks) +{ + int i, res; + char *buf = data; + + for (i = 0; i < nblocks; i++) { + res = read_mode2_sector_netbsd(user_data, buf, lsn, mode2_form2); + if (res) + return res; + + buf += (mode2_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE); + lsn++; + } + + return 0; +} + +static int +set_arg_netbsd(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->gen.source_name); + _obj->gen.source_name = strdup(value); + } else if (!strcmp(key, "access-mode")) { + if (strcmp(value, "READ_CD")) + cdio_error("unknown access type: %s ignored.", value); + } else + return -1; + + return 0; +} + +static bool +_cdio_read_toc(_img_private_t *_obj) +{ + int res; + struct ioc_read_toc_entry req; + + res = ioctl(_obj->gen.fd, CDIOREADTOCHEADER, &_obj->tochdr); + if (res < 0) { + cdio_error("error in ioctl(CDIOREADTOCHEADER): %s\n", + strerror(errno)); + return false; + } + + req.address_format = CD_MSF_FORMAT; + req.starting_track = FIRST_TRACK_NUM; + req.data_len = (TOTAL_TRACKS + 1) /* leadout! */ + * sizeof(struct cd_toc_entry); + req.data = _obj->tocent; + + res = ioctl(_obj->gen.fd, CDIOREADTOCENTRIES, &req); + if (res < 0) { + cdio_error("error in ioctl(CDROMREADTOCENTRIES): %s\n", + strerror(errno)); + return false; + } + + _obj->toc_valid = 1; + return true; +} + +static bool +read_toc_netbsd (void *p_user_data) +{ + + return _cdio_read_toc(p_user_data); +} + +static int +_cdio_read_discinfo(_img_private_t *_obj) +{ + scsireq_t req; +#define FULLTOCBUF (4 + 1000*11) + unsigned char buf[FULLTOCBUF] = { 0, }; + int i, j; + + memset(&req, 0, sizeof(req)); + req.cmd[0] = 0x43; /* READ TOC/PMA/ATIP */ + req.cmd[1] = 0x02; + req.cmd[2] = 0x02; /* full TOC */ + req.cmd[3] = 0; + req.cmd[4] = 0; + req.cmd[5] = 0; + req.cmd[6] = 0; + req.cmd[7] = FULLTOCBUF / 256; + req.cmd[8] = FULLTOCBUF % 256; + req.cmd[9] = 0; + req.cmdlen = 10; + + req.datalen = FULLTOCBUF; + req.databuf = buf; + req.timeout = 10000; + req.flags = SCCMD_READ; + + if (ioctl(_obj->gen.fd, SCIOCCOMMAND, &req) < 0) { + perror("SCIOCCOMMAND"); + return 1; + } + if (req.retsts != SCCMD_OK) { + fprintf(stderr, "SCIOCCOMMAND cmd 0x43 sts %d\n", req.retsts); + return 1; + } +#if 1 + printf("discinfo:"); + for (i = 0; i < 4; i++) + printf(" %02x", buf[i]); + printf("\n"); + for (i = 0; i < buf[1] - 2; i++) { + printf(" %02x", buf[i + 4]); + if (!((i + 1) % 11)) + printf("\n"); + } +#endif + + for (i = 4; i < req.datalen_used; i += 11) { + if (buf[i + 3] == 0xa0) { /* POINT */ + /* XXX: assume entry 0xa1 follows */ + for (j = buf[i + 8] - 1; j <= buf[i + 11 + 8] - 1; j++) + _obj->sessionformat[j] = buf[i + 9]; + } + } + + _obj->sessionformat_valid = true; + return 0; +} + +static int +eject_media_netbsd(void *user_data) { + + _img_private_t *_obj = user_data; + int fd, res, ret = 0; + + fd = open(_obj->gen.source_name, O_RDONLY|O_NONBLOCK); + if (fd < 0) + return 2; + + res = ioctl(fd, CDIOCALLOW); + if (res < 0) { + cdio_error("ioctl(fd, CDIOCALLOW) failed: %s\n", + strerror(errno)); + /* go on... */ + } + res = ioctl(fd, CDIOCEJECT); + if (res < 0) { + cdio_error("ioctl(CDIOCEJECT) failed: %s\n", + strerror(errno)); + ret = 1; + } + + return ret; +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +get_arg_netbsd(void *user_data, const char key[]) +{ + _img_private_t *_obj = user_data; + + if (!strcmp(key, "source")) { + return _obj->gen.source_name; + } else if (!strcmp(key, "access-mode")) { + return "READ_CD"; + } + + return NULL; +} + +static track_t +get_first_track_num_netbsd(void *user_data) +{ + _img_private_t *_obj = user_data; + int res; + + if (!_obj->toc_valid) { + res = _cdio_read_toc(_obj); + if (!res) + return CDIO_INVALID_TRACK; + } + + return FIRST_TRACK_NUM; +} + +static track_t +get_num_tracks_netbsd(void *user_data) +{ + _img_private_t *_obj = user_data; + int res; + + if (!_obj->toc_valid) { + res = _cdio_read_toc(_obj); + if (!res) + return CDIO_INVALID_TRACK; + } + + return TOTAL_TRACKS; +} + +/*! + Get format of track. +*/ +static track_format_t +get_track_format_netbsd(void *user_data, track_t track_num) +{ + _img_private_t *_obj = user_data; + int res; + + if (!_obj->toc_valid) { + res = _cdio_read_toc(_obj); + if (!res) + return CDIO_INVALID_TRACK; + } + + if (track_num > TOTAL_TRACKS || track_num == 0) + return TRACK_FORMAT_ERROR; + + if (_obj->tocent[track_num - 1].control & 0x04) { + if (!_obj->sessionformat_valid) { + res = _cdio_read_discinfo(_obj); + if (res) + return CDIO_INVALID_TRACK; + } + + if (_obj->sessionformat[track_num - 1] == 0x10) + return TRACK_FORMAT_CDI; + else if (_obj->sessionformat[track_num - 1] == 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 +get_track_green_netbsd(void *user_data, track_t track_num) +{ + + return (get_track_format_netbsd(user_data, track_num) + == TRACK_FORMAT_XA); +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + track_num in obj. Track numbers usually start at something + greater than 0, usually 1. + + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +static bool +get_track_msf_netbsd(void *user_data, track_t track_num, msf_t *msf) +{ + _img_private_t *_obj = user_data; + int res; + + if (!msf) + return false; + + if (!_obj->toc_valid) { + res = _cdio_read_toc(_obj); + if (!res) + return CDIO_INVALID_TRACK; + } + + if (track_num == CDIO_CDROM_LEADOUT_TRACK) + track_num = TOTAL_TRACKS + 1; + + if (track_num > TOTAL_TRACKS + 1 || track_num == 0) + return false; + + msf->m = cdio_to_bcd8(_obj->tocent[track_num - 1].addr.msf.minute); + msf->s = cdio_to_bcd8(_obj->tocent[track_num - 1].addr.msf.second); + msf->f = cdio_to_bcd8(_obj->tocent[track_num - 1].addr.msf.frame); + + return true; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + @return the lsn. On error return CDIO_INVALID_LSN. + + Also note that in one at least one test the corresponding MMC gives + a different answer, so there may be some disagreement about what is in + fact the last lsn. + */ +static lsn_t +get_disc_last_lsn_netbsd(void *user_data) +{ + msf_t msf; + + get_track_msf_netbsd(user_data, CDIO_CDROM_LEADOUT_TRACK, &msf); + + return (((msf.m * 60) + msf.s) * CDIO_CD_FRAMES_PER_SEC + msf.f); +} +#endif /* HAVE_NETBSD_CDROM */ + +char ** +cdio_get_devices_netbsd (void) +{ +#ifndef HAVE_NETBSD_CDROM + return NULL; +#else + return NULL; +#endif /* HAVE_NETBSD_CDROM */ +} + +/*! + Return a string containing the default CD device. + */ +char * +cdio_get_default_device_netbsd() +{ + return strdup(DEFAULT_CDIO_DEVICE); +} + +/*! + Close tray on CD-ROM. + + @param psz_device the CD-ROM drive to be closed. + +*/ +driver_return_code_t +close_tray_netbsd (const char *psz_device) +{ +#ifdef HAVE_NETBSD_CDROM + return DRIVER_OP_UNSUPPORTED; +#else + return DRIVER_OP_NO_DRIVER; +#endif +} + +#ifdef HAVE_NETBSD_CDROM +static cdio_funcs_t _funcs = { + .audio_read_subchannel = audio_read_subchannel_mmc, + .eject_media = eject_media_netbsd, + .free = cdio_generic_free, + .get_arg = get_arg_netbsd, + .get_blocksize = get_blocksize_mmc, + .get_cdtext = get_cdtext_generic, + .get_default_device = cdio_get_default_device_netbsd, + .get_devices = cdio_get_devices_netbsd, + .get_disc_last_lsn = get_disc_last_lsn_netbsd, + .get_discmode = get_discmode_generic, + .get_drive_cap = get_drive_cap_mmc, + .get_first_track_num = get_first_track_num_netbsd, + .get_hwinfo = NULL, + .get_mcn = get_mcn_mmc, + .get_num_tracks = get_num_tracks_netbsd, + .get_track_channels = get_track_channels_generic, + .get_track_copy_permit = get_track_copy_permit_generic, + .get_track_format = get_track_format_netbsd, + .get_track_green = get_track_green_netbsd, + .get_track_lba = NULL, /* This could be implemented if need be. */ + .get_track_preemphasis = get_track_preemphasis_generic, + .get_track_msf = get_track_msf_netbsd, + .lseek = cdio_generic_lseek, + .read = cdio_generic_read, + .read_audio_sectors = read_audio_sectors_netbsd, + .read_data_sectors = read_data_sectors_generic, + .read_mode2_sector = read_mode2_sector_netbsd, + .read_mode2_sectors = read_mode2_sectors_netbsd, + .read_toc = read_toc_netbsd, +#if 1 + .run_mmc_cmd = run_scsi_cmd_netbsd, +#endif + .set_arg = set_arg_netbsd, +}; +#endif /*HAVE_NETBSD_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_t * +cdio_open_netbsd(const char *source_name) +{ +#ifdef HAVE_NETBSD_CDROM + CdIo_t *ret; + _img_private_t *_data; + + _data = calloc(1, sizeof(_img_private_t)); + _data->gen.init = false; + _data->gen.fd = -1; + _data->toc_valid = false; + _data->sessionformat_valid = false; + + set_arg_netbsd(_data, "source", + (source_name ? source_name : DEFAULT_CDIO_DEVICE)); + + if (source_name && !cdio_is_device_generic(source_name)) + return (NULL); + + ret = cdio_new(&_data->gen, &_funcs); + if (!ret) + return NULL; + + if (cdio_generic_init(_data, O_RDONLY)) { + return ret; + } else { + cdio_generic_free(_data); + return NULL; + } +#else + return NULL; +#endif /* HAVE_BSDI_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_t * +cdio_open_am_netbsd(const char *source_name, const char *am) +{ + return (cdio_open_netbsd(source_name)); +} + +bool +cdio_have_netbsd (void) +{ +#ifdef HAVE_NETBSD_CDROM + return true; +#else + return false; +#endif /* HAVE_NETBSD_CDROM */ +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/driver/osx.c b/lib/driver/osx.c new file mode 100644 index 00000000..cbb96275 --- /dev/null +++ b/lib/driver/osx.c @@ -0,0 +1,1979 @@ +/* + $Id: osx.c,v 1.14 2008/10/17 11:58:52 rocky Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2008 Rocky Bernstein + from vcdimager code: + Copyright (C) 2001 Herbert Valerio Riedel + and VideoLAN code Copyright (C) 1998-2001 VideoLAN + Authors: Johan Bilien + Gildas Bazin + Jon Lech Johansen + Derk-Jan Hartman + Justin F. Hallett + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* This file contains OSX-specific code and implements low-level + control of the CD drive. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +static const char _rcsid[] = "$Id: osx.c,v 1.14 2008/10/17 11:58:52 rocky Exp $"; + +#include +#include +#include + +/* For SCSI TR_* enumerations */ +#include + +#include "cdio_assert.h" +#include "cdio_private.h" + +#include + +#ifdef HAVE_DARWIN_CDROM +#undef VERSION + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_DISKARBITRATION +#include +#endif + +/* FIXME */ +#define MAX_BIG_BUFF_SIZE 65535 + +#define kIOCDBlockStorageDeviceClassString "IOCDBlockStorageDevice" + +/* Note leadout is normally defined 0xAA, But on OSX 0xA0 is "lead in" while + 0xA2 is "lead out". I don't understand the distinction, and therefore + something could be wrong. */ +#define OSX_CDROM_LEADOUT_TRACK 0xA2 + +#define TOTAL_TRACKS (p_env->i_last_track - p_env->gen.i_first_track + 1) + +#define CDROM_CDI_TRACK 0x1 +#define CDROM_XA_TRACK 0x2 + +typedef enum { + _AM_NONE, + _AM_OSX, +} access_mode_t; + +#define MAX_SERVICE_NAME 1000 +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + + access_mode_t access_mode; + + /* Track information */ + CDTOC *pTOC; + int i_descriptors; + track_t i_last_track; /* highest track number */ + track_t i_last_session; /* highest session number */ + track_t i_first_session; /* first session number */ + lsn_t *pp_lba; + io_service_t MediaClass_service; + char psz_MediaClass_service[MAX_SERVICE_NAME]; + SCSITaskDeviceInterface **pp_scsiTaskDeviceInterface; + + // io_service_t obj; + // SCSITaskDeviceInterface **scsi; + SCSITaskInterface **scsi_task; + MMCDeviceInterface **mmc; + IOCFPlugInInterface **plugin; + + SCSI_Sense_Data sense; + SCSITaskStatus status; + UInt64 realized_len; + + +} _img_private_t; + +static bool read_toc_osx (void *p_user_data); +static track_format_t get_track_format_osx(void *p_user_data, + track_t i_track); + +/**** + * GetRegistryEntryProperties - Gets the registry entry properties for + * an io_service_t. + *****/ + +static CFMutableDictionaryRef +GetRegistryEntryProperties ( io_service_t service ) +{ + IOReturn err = kIOReturnSuccess; + CFMutableDictionaryRef dict = 0; + + err = IORegistryEntryCreateCFProperties (service, &dict, + kCFAllocatorDefault, 0); + if ( err != kIOReturnSuccess ) + cdio_warn( "IORegistryEntryCreateCFProperties: 0x%08x", err ); + + return dict; +} + +#ifdef GET_SCSI_FIXED +static bool +get_scsi(_img_private_t *p_env) +{ + SInt32 score; + kern_return_t err; + HRESULT herr; + + err = IOCreatePlugInInterfaceForService(p_env->MediaClass_service, + kIOMMCDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, + &p_env->plugin, + &score); + + if (err != noErr) { + fprintf(stderr, "Error %x accessing MMC plugin.\n", err); + return false; + } + + herr = (*p_env->plugin) -> + QueryInterface(p_env->plugin, CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID), + (void *)&p_env->mmc); + + if (herr != S_OK) { + fprintf(stderr, "Error %x accessing MMC interface.\n", (int) herr); + IODestroyPlugInInterface(p_env->plugin); + return false; + } + + p_env->pp_scsiTaskDeviceInterface = + (*p_env->mmc)->GetSCSITaskDeviceInterface(p_env->mmc); + + if (!p_env->pp_scsiTaskDeviceInterface) { + fprintf(stderr, + "Could not get SCSITaskkDevice interface from MMC interface.\n"); + (*p_env->mmc)->Release(p_env->mmc); + IODestroyPlugInInterface(p_env->plugin); + return false; + } + + err = (*p_env->pp_scsiTaskDeviceInterface)-> + ObtainExclusiveAccess(p_env->pp_scsiTaskDeviceInterface); + if (err != kIOReturnSuccess) { + fprintf(stderr, "Could not obtain exclusive access to the device (%x).\n", + err); + + if (err == kIOReturnBusy) + fprintf(stderr, "The volume is already mounted.\n"); + else if (err == kIOReturnExclusiveAccess) + fprintf(stderr, "Another application already has exclusive access " + "to this device.\n"); + else + fprintf(stderr, "I don't know why.\n"); + + (*p_env->pp_scsiTaskDeviceInterface)-> + Release(p_env->pp_scsiTaskDeviceInterface); + (*p_env->mmc)->Release(p_env->mmc); + IODestroyPlugInInterface(p_env->plugin); + return false; + } + + p_env->scsi_task = + (*p_env->pp_scsiTaskDeviceInterface) -> + CreateSCSITask(p_env->pp_scsiTaskDeviceInterface); + + if (!p_env->scsi_task) { + fprintf(stderr, "Could not create a SCSITask interface.\n"); + (*p_env->pp_scsiTaskDeviceInterface)-> + ReleaseExclusiveAccess(p_env->pp_scsiTaskDeviceInterface); + (*p_env->pp_scsiTaskDeviceInterface)-> + Release(p_env->pp_scsiTaskDeviceInterface); + (*p_env->mmc)->Release(p_env->mmc); + IODestroyPlugInInterface(p_env->plugin); + return false; + } + + return true; +} +#endif + +static bool +init_osx(_img_private_t *p_env) { + char *psz_devname; + kern_return_t ret; + io_iterator_t iterator; + + /* Only open if not already opened. Otherwise, too many descriptors + are holding the device busy. */ + if (-1 == p_env->gen.fd) + p_env->gen.fd = open( p_env->gen.source_name, O_RDONLY | O_NONBLOCK ); + + if (-1 == p_env->gen.fd) { + cdio_warn("Failed to open %s: %s", p_env->gen.source_name, + strerror(errno)); + return false; + } + + /* Get the device name. */ + psz_devname = strrchr( p_env->gen.source_name, '/'); + if( NULL != psz_devname ) + ++psz_devname; + else + psz_devname = p_env->gen.source_name; + + /* Unraw the device name. */ + if( *psz_devname == 'r' ) + ++psz_devname; + + ret = IOServiceGetMatchingServices( kIOMasterPortDefault, + IOBSDNameMatching(kIOMasterPortDefault, + 0, psz_devname), + &iterator ); + + /* Get service iterator for the device. */ + if( ret != KERN_SUCCESS ) + { + cdio_warn( "IOServiceGetMatchingServices: 0x%08x", ret ); + return false; + } + + /* first service */ + p_env->MediaClass_service = IOIteratorNext( iterator ); + IOObjectRelease( iterator ); + + /* search for kIOCDMediaClass or kIOCDVDMediaClass */ + while( p_env->MediaClass_service && + (!IOObjectConformsTo(p_env->MediaClass_service, kIOCDMediaClass)) && + (!IOObjectConformsTo(p_env->MediaClass_service, kIODVDMediaClass)) ) + { + + ret = IORegistryEntryGetParentIterator( p_env->MediaClass_service, + kIOServicePlane, + &iterator ); + if( ret != KERN_SUCCESS ) + { + cdio_warn( "IORegistryEntryGetParentIterator: 0x%08x", ret ); + IOObjectRelease( p_env->MediaClass_service ); + return false; + } + + IOObjectRelease( p_env->MediaClass_service ); + p_env->MediaClass_service = IOIteratorNext( iterator ); + IOObjectRelease( iterator ); + } + + if ( 0 == p_env->MediaClass_service ) { + cdio_warn( "search for kIOCDMediaClass/kIODVDMediaClass came up empty" ); + return false; + } + + /* Save the name so we can compare against this in case we have to do + another scan. FIXME: this is hoaky and there's got to be a better + variable to test or way to do. + */ + IORegistryEntryGetPath(p_env->MediaClass_service, kIOServicePlane, + p_env->psz_MediaClass_service); +#ifdef GET_SCSI_FIXED + return get_scsi(p_env); +#else + return true; +#endif +} + +/*! + Run a SCSI MMC command. + + cdio CD structure set by cdio_open(). + i_timeout time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + p_buf Buffer for data, both sending and receiving + i_buf Size of buffer + e_direction direction the transfer is to go. + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + We return true if command completed successfully and false if not. + */ +#if 1 + +/* process a complete scsi command. */ +// handle_scsi_cmd(cdrom_drive *d, +static int +run_mmc_cmd_osx( void *p_user_data, + unsigned int i_timeout_ms, + unsigned int i_cdb, const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + _img_private_t *p_env = p_user_data; + uint8_t cmdbuf[16]; + UInt8 dir; + IOVirtualRange buf; + IOReturn ret; + + if (!p_env->scsi_task) return DRIVER_OP_UNSUPPORTED; + + memcpy(cmdbuf, p_cdb, i_cdb); + + dir = ( SCSI_MMC_DATA_READ == e_direction) + ? kSCSIDataTransfer_FromTargetToInitiator + : kSCSIDataTransfer_FromInitiatorToTarget; + + if (!i_buf) + dir = kSCSIDataTransfer_NoDataTransfer; + + if (i_buf > MAX_BIG_BUFF_SIZE) { + fprintf(stderr, "Excessive request size: %d bytes\n", i_buf); + return TR_ILLEGAL; + } + + buf.address = (IOVirtualAddress)p_buf; + buf.length = i_buf; + + ret = (*p_env->scsi_task)->SetCommandDescriptorBlock(p_env->scsi_task, + cmdbuf, i_cdb); + if (ret != kIOReturnSuccess) { + fprintf(stderr, "SetCommandDescriptorBlock: %x\n", ret); + return TR_UNKNOWN; + } + + ret = (*p_env->scsi_task)->SetScatterGatherEntries(p_env->scsi_task, &buf, 1, + i_buf, dir); + if (ret != kIOReturnSuccess) { + fprintf(stderr, "SetScatterGatherEntries: %x\n", ret); + return TR_UNKNOWN; + } + + ret = (*p_env->scsi_task)->ExecuteTaskSync(p_env->scsi_task, &p_env->sense, + &p_env->status, + &p_env->realized_len); + if (ret != kIOReturnSuccess) { + fprintf(stderr, "ExecuteTaskSync: %x\n", ret); + return TR_UNKNOWN; + } + + if (p_env->status != kSCSITaskStatus_GOOD) { + int i; + + fprintf(stderr, "SCSI status: %x\n", p_env->status); + fprintf(stderr, "Sense: %x %x %x\n", + p_env->sense.SENSE_KEY, + p_env->sense.ADDITIONAL_SENSE_CODE, + p_env->sense.ADDITIONAL_SENSE_CODE_QUALIFIER); + + for (i = 0; i < i_cdb; i++) + fprintf(stderr, "%02x ", cmdbuf[i]); + + fprintf(stderr, "\n"); + return TR_UNKNOWN; + } + + if (p_env->sense.VALID_RESPONSE_CODE) { + char key = p_env->sense.SENSE_KEY & 0xf; + char ASC = p_env->sense.ADDITIONAL_SENSE_CODE; + char ASCQ = p_env->sense.ADDITIONAL_SENSE_CODE_QUALIFIER; + + switch (key) { + case 0: + if (errno == 0) + errno = EIO; + return (TR_UNKNOWN); + case 1: + break; + case 2: + if (errno == 0) + errno = EBUSY; + return (TR_BUSY); + case 3: + if (ASC == 0x0C && ASCQ == 0x09) { + /* loss of streaming */ + if (errno == 0) + errno = EIO; + return (TR_STREAMING); + } else { + if (errno == 0) + errno = EIO; + return (TR_MEDIUM); + } + case 4: + if (errno == 0) + errno = EIO; + return (TR_FAULT); + case 5: + if (errno == 0) + errno = EINVAL; + return (TR_ILLEGAL); + default: + if (errno == 0) + errno = EIO; + return (TR_UNKNOWN); + } + } + + errno = 0; + return (0); +} +#endif + +#if 0 +/*! + Run a SCSI MMC command. + + cdio CD structure set by cdio_open(). + i_timeout time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + p_buf Buffer for data, both sending and receiving + i_buf Size of buffer + e_direction direction the transfer is to go. + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + We return true if command completed successfully and false if not. + */ +static int +run_mmc_cmd_osx( const void *p_user_data, + unsigned int i_timeout_ms, + unsigned int i_cdb, const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + +#ifndef SCSI_MMC_FIXED + return DRIVER_OP_UNSUPPORTED; +#else + const _img_private_t *p_env = p_user_data; + SCSITaskDeviceInterface **sc; + SCSITaskInterface **cmd = NULL; + IOVirtualRange iov; + SCSI_Sense_Data senseData; + SCSITaskStatus status; + UInt64 bytesTransferred; + IOReturn ioReturnValue; + int ret = 0; + + if (NULL == p_user_data) return 2; + + /* Make sure pp_scsiTaskDeviceInterface is initialized. FIXME: The code + should probably be reorganized better for this. */ + if (!p_env->gen.toc_init) read_toc_osx (p_user_data) ; + + sc = p_env->pp_scsiTaskDeviceInterface; + + if (NULL == sc) return 3; + + cmd = (*sc)->CreateSCSITask(sc); + if (cmd == NULL) { + cdio_warn("Failed to create SCSI task"); + return -1; + } + + iov.address = (IOVirtualAddress) p_buf; + iov.length = i_buf; + + ioReturnValue = (*cmd)->SetCommandDescriptorBlock(cmd, (UInt8 *) p_cdb, + i_cdb); + if (ioReturnValue != kIOReturnSuccess) { + cdio_warn("SetCommandDescriptorBlock failed with status %x", + ioReturnValue); + return -1; + } + + ioReturnValue = (*cmd)->SetScatterGatherEntries(cmd, &iov, 1, i_buf, + (SCSI_MMC_DATA_READ == e_direction ) ? + kSCSIDataTransfer_FromTargetToInitiator : + kSCSIDataTransfer_FromInitiatorToTarget); + if (ioReturnValue != kIOReturnSuccess) { + cdio_warn("SetScatterGatherEntries failed with status %x", ioReturnValue); + return -1; + } + + ioReturnValue = (*cmd)->SetTimeoutDuration(cmd, i_timeout_ms ); + if (ioReturnValue != kIOReturnSuccess) { + cdio_warn("SetTimeoutDuration failed with status %x", ioReturnValue); + return -1; + } + + memset(&senseData, 0, sizeof(senseData)); + + ioReturnValue = (*cmd)->ExecuteTaskSync(cmd,&senseData, &status, & + bytesTransferred); + + if (ioReturnValue != kIOReturnSuccess) { + cdio_warn("Command execution failed with status %x", ioReturnValue); + return -1; + } + + if (cmd != NULL) { + (*cmd)->Release(cmd); + } + + return (ret); +#endif +} +#endif /* 0*/ + +/*************************************************************************** + * GetDeviceIterator - Gets an io_iterator_t for our class type + ***************************************************************************/ + +static io_iterator_t +GetDeviceIterator ( const char * deviceClass ) +{ + + IOReturn err = kIOReturnSuccess; + io_iterator_t iterator = MACH_PORT_NULL; + + err = IOServiceGetMatchingServices ( kIOMasterPortDefault, + IOServiceMatching ( deviceClass ), + &iterator ); + check ( err == kIOReturnSuccess ); + + return iterator; + +} + +/*************************************************************************** + * GetFeaturesFlagsForDrive -Gets the bitfield which represents the + * features flags. + ***************************************************************************/ + +static bool +GetFeaturesFlagsForDrive ( CFDictionaryRef dict, + uint32_t *i_cdFlags, + uint32_t *i_dvdFlags ) +{ + CFDictionaryRef propertiesDict = 0; + CFNumberRef flagsNumberRef = 0; + + *i_cdFlags = 0; + *i_dvdFlags= 0; + + propertiesDict = ( CFDictionaryRef ) + CFDictionaryGetValue ( dict, + CFSTR ( kIOPropertyDeviceCharacteristicsKey ) ); + + if ( propertiesDict == 0 ) return false; + + /* Get the CD features */ + flagsNumberRef = ( CFNumberRef ) + CFDictionaryGetValue ( propertiesDict, + CFSTR ( kIOPropertySupportedCDFeatures ) ); + if ( flagsNumberRef != 0 ) { + CFNumberGetValue ( flagsNumberRef, kCFNumberLongType, i_cdFlags ); + } + + /* Get the DVD features */ + flagsNumberRef = ( CFNumberRef ) + CFDictionaryGetValue ( propertiesDict, + CFSTR ( kIOPropertySupportedDVDFeatures ) ); + if ( flagsNumberRef != 0 ) { + CFNumberGetValue ( flagsNumberRef, kCFNumberLongType, i_dvdFlags ); + } + + return true; +} + +/*! + Get disc type associated with the cd object. +*/ +static discmode_t +get_discmode_osx (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + char str[10]; + int32_t i_discmode = CDIO_DISC_MODE_ERROR; + CFDictionaryRef propertiesDict = 0; + CFStringRef data; + + propertiesDict = GetRegistryEntryProperties ( p_env->MediaClass_service ); + + if ( propertiesDict == 0 ) return i_discmode; + + data = ( CFStringRef ) + CFDictionaryGetValue ( propertiesDict, CFSTR ( kIODVDMediaTypeKey ) ); + + if( CFStringGetCString( data, str, sizeof(str), + kCFStringEncodingASCII ) ) { + if (0 == strncmp(str, "DVD+R", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_DVD_PR; + else if (0 == strncmp(str, "DVD+RW", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_DVD_PRW; + else if (0 == strncmp(str, "DVD-R", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_DVD_R; + else if (0 == strncmp(str, "DVD-RW", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_DVD_RW; + else if (0 == strncmp(str, "DVD-ROM", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_DVD_ROM; + else if (0 == strncmp(str, "DVD-RAM", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_DVD_RAM; + else if (0 == strncmp(str, "CD-ROM", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_CD_DATA; + else if (0 == strncmp(str, "CDR", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_CD_DATA; + else if (0 == strncmp(str, "CDRW", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_CD_DATA; + //?? Handled by below? CFRelease( data ); + } + CFRelease( propertiesDict ); + if (CDIO_DISC_MODE_CD_DATA == i_discmode) { + /* Need to do more classification */ + return get_discmode_cd_generic(p_user_data); + } + return i_discmode; + +} + +static io_service_t +get_drive_service_osx(const _img_private_t *p_env) +{ + io_service_t service; + io_iterator_t service_iterator; + + service_iterator = GetDeviceIterator ( kIOCDBlockStorageDeviceClassString ); + + if( service_iterator == MACH_PORT_NULL ) return 0; + + service = IOIteratorNext( service_iterator ); + if( service == 0 ) return 0; + + do + { + char psz_service[MAX_SERVICE_NAME]; + IORegistryEntryGetPath(service, kIOServicePlane, psz_service); + psz_service[MAX_SERVICE_NAME-1] = '\0'; + + /* FIXME: This is all hoaky. Here we need info from a parent class, + psz_service of what we opened above. We are relying on the + fact that the name will be a substring of the name we + openned with. + */ + if (0 == strncmp(psz_service, p_env->psz_MediaClass_service, + strlen(psz_service))) { + /* Found our device */ + IOObjectRelease( service_iterator ); + return service; + } + + IOObjectRelease( service ); + + } while( ( service = IOIteratorNext( service_iterator ) ) != 0 ); + + IOObjectRelease( service_iterator ); + return service; +} + +static void +get_drive_cap_osx(const void *p_user_data, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap) +{ + const _img_private_t *p_env = p_user_data; + uint32_t i_cdFlags; + uint32_t i_dvdFlags; + + io_service_t service = get_drive_service_osx(p_env); + + if( service == 0 ) goto err_exit; + + /* Found our device */ + { + CFDictionaryRef properties = GetRegistryEntryProperties ( service ); + + if (! GetFeaturesFlagsForDrive ( properties, &i_cdFlags, + &i_dvdFlags ) ) { + IOObjectRelease( service ); + goto err_exit; + } + + /* Reader */ + + if ( 0 != (i_cdFlags & kCDFeaturesAnalogAudioMask) ) + *p_read_cap |= CDIO_DRIVE_CAP_READ_AUDIO; + + if ( 0 != (i_cdFlags & kCDFeaturesWriteOnceMask) ) + *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_R; + + if ( 0 != (i_cdFlags & kCDFeaturesCDDAStreamAccurateMask) ) + *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_DA; + + if ( 0 != (i_dvdFlags & kDVDFeaturesReadStructuresMask) ) + *p_read_cap |= CDIO_DRIVE_CAP_READ_DVD_ROM; + + if ( 0 != (i_cdFlags & kCDFeaturesReWriteableMask) ) + *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_RW; + + if ( 0 != (i_dvdFlags & kDVDFeaturesWriteOnceMask) ) + *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_R; + + if ( 0 != (i_dvdFlags & kDVDFeaturesRandomWriteableMask) ) + *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RAM; + + if ( 0 != (i_dvdFlags & kDVDFeaturesReWriteableMask) ) + *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RW; + + /*** + if ( 0 != (i_dvdFlags & kDVDFeaturesPlusRMask) ) + *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_PR; + + if ( 0 != (i_dvdFlags & kDVDFeaturesPlusRWMask ) + *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_PRW; + ***/ + + /* FIXME: fill out. For now assume CD-ROM is relatively modern. */ + *p_misc_cap = ( + CDIO_DRIVE_CAP_MISC_CLOSE_TRAY + | CDIO_DRIVE_CAP_MISC_EJECT + | CDIO_DRIVE_CAP_MISC_LOCK + | CDIO_DRIVE_CAP_MISC_SELECT_SPEED + | CDIO_DRIVE_CAP_MISC_MULTI_SESSION + | CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED + | CDIO_DRIVE_CAP_MISC_RESET + | CDIO_DRIVE_CAP_READ_MCN + | CDIO_DRIVE_CAP_READ_ISRC + ); + + IOObjectRelease( service ); + } + + return; + + err_exit: + *p_misc_cap = *p_write_cap = *p_read_cap = CDIO_DRIVE_CAP_UNKNOWN; + return; +} + +#if 1 +/**************************************************************************** + * GetDriveDescription - Gets drive description. + ****************************************************************************/ + +static bool +get_hwinfo_osx ( const CdIo_t *p_cdio, /*out*/ cdio_hwinfo_t *hw_info) +{ + _img_private_t *p_env = (_img_private_t *) p_cdio->env; + io_service_t service = get_drive_service_osx(p_env); + + if ( service == 0 ) return false; + + /* Found our device */ + { + CFStringRef vendor = NULL; + CFStringRef product = NULL; + CFStringRef revision = NULL; + + CFDictionaryRef properties = GetRegistryEntryProperties ( service ); + CFDictionaryRef deviceDict = ( CFDictionaryRef ) + CFDictionaryGetValue ( properties, + CFSTR ( kIOPropertyDeviceCharacteristicsKey ) ); + + if ( deviceDict == 0 ) return false; + + vendor = ( CFStringRef ) + CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertyVendorNameKey ) ); + + if ( CFStringGetCString( vendor, + (char *) &(hw_info->psz_vendor), + sizeof(hw_info->psz_vendor), + kCFStringEncodingASCII ) ) + CFRelease( vendor ); + + product = ( CFStringRef ) + CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertyProductNameKey ) ); + + if ( CFStringGetCString( product, + (char *) &(hw_info->psz_model), + sizeof(hw_info->psz_model), + kCFStringEncodingASCII ) ) + CFRelease( product ); + + revision = ( CFStringRef ) + CFDictionaryGetValue ( deviceDict, + CFSTR ( kIOPropertyProductRevisionLevelKey ) ); + + if ( CFStringGetCString( revision, + (char *) &(hw_info->psz_revision), + sizeof(hw_info->psz_revision), + kCFStringEncodingASCII ) ) + CFRelease( revision ); + } + return true; + +} +#endif + +/* + Get cdtext information in p_user_data for track i_track. + For disc information i_track is 0. + + Return the CD-TEXT or NULL if obj is NULL, + CD-TEXT information does not exist, or (as is the case here) + we don't know how to get this implemented. +*/ +static cdtext_t * +get_cdtext_osx (void *p_user_data, track_t i_track) +{ + return NULL; +} + +static void +_free_osx (void *p_user_data) { + _img_private_t *p_env = p_user_data; + if (NULL == p_env) return; + if (p_env->gen.fd != -1) + close(p_env->gen.fd); + if (p_env->MediaClass_service) + IOObjectRelease( p_env->MediaClass_service ); + cdio_generic_free(p_env); + if (NULL != p_env->pp_lba) free((void *) p_env->pp_lba); + if (NULL != p_env->pTOC) free((void *) p_env->pTOC); + + if (p_env->scsi_task) + (*p_env->scsi_task)->Release(p_env->scsi_task); + + if (p_env->pp_scsiTaskDeviceInterface) + (*p_env->pp_scsiTaskDeviceInterface) -> + ReleaseExclusiveAccess(p_env->pp_scsiTaskDeviceInterface); + if (p_env->pp_scsiTaskDeviceInterface) + (*p_env->pp_scsiTaskDeviceInterface) -> + Release ( p_env->pp_scsiTaskDeviceInterface ); + + if (p_env->mmc) + (*p_env->mmc)->Release(p_env->mmc); + + if (p_env->plugin) + IODestroyPlugInInterface(p_env->plugin); + +} + +/*! + Reads i_blocks of data sectors from cd device into p_data starting + from i_lsn. + Returns DRIVER_OP_SUCCESS if no error. + */ +static driver_return_code_t +read_data_sectors_osx (void *p_user_data, void *p_data, lsn_t i_lsn, + uint16_t i_blocksize, uint32_t i_blocks) +{ + _img_private_t *p_env = p_user_data; + + if (!p_user_data) return DRIVER_OP_UNINIT; + + { + dk_cd_read_t cd_read; + track_t i_track = cdio_get_track(p_env->gen.cdio, i_lsn); + + memset( &cd_read, 0, sizeof(cd_read) ); + + cd_read.sectorArea = kCDSectorAreaUser; + cd_read.buffer = p_data; + + /* FIXME: Do I have to put use get_track_green_osx? */ + switch(get_track_format_osx(p_user_data, i_track)) { + case TRACK_FORMAT_CDI: + case TRACK_FORMAT_DATA: + cd_read.sectorType = kCDSectorTypeMode1; + cd_read.offset = i_lsn * kCDSectorSizeMode1; + break; + case TRACK_FORMAT_XA: + cd_read.sectorType = kCDSectorTypeMode2; + cd_read.offset = i_lsn * kCDSectorSizeMode2; + break; + default: + return DRIVER_OP_ERROR; + } + + cd_read.bufferLength = i_blocksize * i_blocks; + + if( ioctl( p_env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 ) + { + cdio_info( "could not read block %d, %s", i_lsn, strerror(errno) ); + return DRIVER_OP_ERROR; + } + return DRIVER_OP_SUCCESS; + } +} + + +/*! + Reads i_blocks of mode2 form2 sectors from cd device into data starting + from i_lsn. + Returns 0 if no error. + */ +static driver_return_code_t +read_mode1_sectors_osx (void *p_user_data, void *p_data, lsn_t i_lsn, + bool b_form2, uint32_t i_blocks) +{ + _img_private_t *p_env = p_user_data; + dk_cd_read_t cd_read; + + memset( &cd_read, 0, sizeof(cd_read) ); + + cd_read.sectorArea = kCDSectorAreaUser; + cd_read.buffer = p_data; + cd_read.sectorType = kCDSectorTypeMode1; + + if (b_form2) { + cd_read.offset = i_lsn * kCDSectorSizeMode2; + cd_read.bufferLength = kCDSectorSizeMode2 * i_blocks; + } else { + cd_read.offset = i_lsn * kCDSectorSizeMode1; + cd_read.bufferLength = kCDSectorSizeMode1 * i_blocks; + } + + if( ioctl( p_env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 ) + { + cdio_info( "could not read block %d, %s", i_lsn, strerror(errno) ); + return DRIVER_OP_ERROR; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Reads i_blocks of mode2 form2 sectors from cd device into data starting + from lsn. + Returns DRIVER_OP_SUCCESS if no error. + */ +static driver_return_code_t +read_mode2_sectors_osx (void *p_user_data, void *p_data, lsn_t i_lsn, + bool b_form2, uint32_t i_blocks) +{ + _img_private_t *p_env = p_user_data; + dk_cd_read_t cd_read; + + memset( &cd_read, 0, sizeof(cd_read) ); + + cd_read.sectorArea = kCDSectorAreaUser; + cd_read.buffer = p_data; + + if (b_form2) { + cd_read.offset = i_lsn * kCDSectorSizeMode2Form2; + cd_read.sectorType = kCDSectorTypeMode2Form2; + cd_read.bufferLength = kCDSectorSizeMode2Form2 * i_blocks; + } else { + cd_read.offset = i_lsn * kCDSectorSizeMode2Form1; + cd_read.sectorType = kCDSectorTypeMode2Form1; + cd_read.bufferLength = kCDSectorSizeMode2Form1 * i_blocks; + } + + if( ioctl( p_env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 ) + { + cdio_info( "could not read block %d, %s", i_lsn, strerror(errno) ); + return DRIVER_OP_ERROR; + } + return DRIVER_OP_SUCCESS; +} + + +/*! + Reads a single audio sector from CD device into p_data starting from lsn. + Returns 0 if no error. + */ +static int +read_audio_sectors_osx (void *user_data, void *p_data, lsn_t lsn, + unsigned int i_blocks) +{ + _img_private_t *env = user_data; + dk_cd_read_t cd_read; + + memset( &cd_read, 0, sizeof(cd_read) ); + + cd_read.offset = lsn * kCDSectorSizeCDDA; + cd_read.sectorArea = kCDSectorAreaUser; + cd_read.sectorType = kCDSectorTypeCDDA; + + cd_read.buffer = p_data; + cd_read.bufferLength = kCDSectorSizeCDDA * i_blocks; + + if( ioctl( env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 ) + { + cdio_info( "could not read block %d\n%s", lsn, + strerror(errno)); + return DRIVER_OP_ERROR; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Reads a single mode2 sector from cd device into p_data starting + from lsn. Returns 0 if no error. + */ +static driver_return_code_t +read_mode1_sector_osx (void *p_user_data, void *p_data, lsn_t i_lsn, + bool b_form2) +{ + return read_mode1_sectors_osx(p_user_data, p_data, i_lsn, b_form2, 1); +} + +/*! + Reads a single mode2 sector from cd device into p_data starting + from lsn. Returns 0 if no error. + */ +static driver_return_code_t +read_mode2_sector_osx (void *p_user_data, void *p_data, lsn_t i_lsn, + bool b_form2) +{ + return read_mode2_sectors_osx(p_user_data, p_data, i_lsn, b_form2, 1); +} + +/*! + Set the key "arg" to "value" in source device. +*/ +static driver_return_code_t +_set_arg_osx (void *p_user_data, const char key[], const char value[]) +{ + _img_private_t *p_env = p_user_data; + + if (!strcmp (key, "source")) + { + if (!value) return DRIVER_OP_ERROR; + free (p_env->gen.source_name); + p_env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + if (!strcmp(value, "OSX")) + p_env->access_mode = _AM_OSX; + else + cdio_warn ("unknown access type: %s. ignored.", value); + } + else return DRIVER_OP_ERROR; + + return DRIVER_OP_SUCCESS; +} + +#if 0 +static void +TestDevice(_img_private_t *p_env, io_service_t service) +{ + SInt32 score; + HRESULT herr; + kern_return_t err; + IOCFPlugInInterface **plugInInterface = NULL; + MMCDeviceInterface **mmcInterface = NULL; + + /* Create the IOCFPlugIn interface so we can query it. */ + + err = IOCreatePlugInInterfaceForService ( service, + kIOMMCDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, + &plugInInterface, + &score ); + if ( err != noErr ) { + printf("IOCreatePlugInInterfaceForService returned %d\n", err); + return; + } + + /* Query the interface for the MMCDeviceInterface. */ + + herr = ( *plugInInterface )->QueryInterface ( plugInInterface, + CFUUIDGetUUIDBytes ( kIOMMCDeviceInterfaceID ), + ( LPVOID ) &mmcInterface ); + + if ( herr != S_OK ) { + printf("QueryInterface returned %ld\n", herr); + return; + } + + p_env->pp_scsiTaskDeviceInterface = + ( *mmcInterface )->GetSCSITaskDeviceInterface ( mmcInterface ); + + if ( NULL == p_env->pp_scsiTaskDeviceInterface ) { + printf("GetSCSITaskDeviceInterface returned NULL\n"); + return; + } + + ( *mmcInterface )->Release ( mmcInterface ); + IODestroyPlugInInterface ( plugInInterface ); +} +#endif + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return false if successful or true if an error. +*/ +static bool +read_toc_osx (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + CFDictionaryRef propertiesDict = 0; + CFDataRef data; + + /* create a CF dictionary containing the TOC */ + propertiesDict = GetRegistryEntryProperties( p_env->MediaClass_service ); + + if ( 0 == propertiesDict ) { + return false; + } + + /* get the TOC from the dictionary */ + data = (CFDataRef) CFDictionaryGetValue( propertiesDict, + CFSTR(kIOCDMediaTOCKey) ); + if ( data != NULL ) { + CFRange range; + CFIndex buf_len; + + buf_len = CFDataGetLength( data ) + 1; + range = CFRangeMake( 0, buf_len ); + + if( ( p_env->pTOC = (CDTOC *)malloc( buf_len ) ) != NULL ) { + CFDataGetBytes( data, range, (u_char *) p_env->pTOC ); + } else { + cdio_warn( "Trouble allocating CDROM TOC" ); + CFRelease( propertiesDict ); + return false; + } + } else { + cdio_warn( "Trouble reading TOC" ); + CFRelease( propertiesDict ); + return false; + } + + /* TestDevice(p_env, service); */ + CFRelease( propertiesDict ); + + p_env->i_descriptors = CDTOCGetDescriptorCount ( p_env->pTOC ); + + /* Read in starting sectors. There may be non-tracks mixed in with + the real tracks. So find the first and last track number by + scanning. Also find the lead-out track position. + */ + { + int i, i_leadout = -1; + + CDTOCDescriptor *pTrackDescriptors; + + p_env->pp_lba = malloc( p_env->i_descriptors * sizeof(int) ); + if( p_env->pp_lba == NULL ) + { + cdio_warn("Out of memory in allocating track starting LSNs" ); + free( p_env->pTOC ); + return false; + } + + pTrackDescriptors = p_env->pTOC->descriptors; + + p_env->gen.i_first_track = CDIO_CD_MAX_TRACKS+1; + p_env->i_last_track = CDIO_CD_MIN_TRACK_NO; + p_env->i_first_session = CDIO_CD_MAX_TRACKS+1; + p_env->i_last_session = CDIO_CD_MIN_TRACK_NO; + + for( i = 0; i < p_env->i_descriptors; i++ ) + { + track_t i_track = pTrackDescriptors[i].point; + session_t i_session = pTrackDescriptors[i].session; + + cdio_debug( "point: %d, tno: %d, session: %d, adr: %d, control:%d, " + "address: %d:%d:%d, p: %d:%d:%d", + i_track, + pTrackDescriptors[i].tno, i_session, + pTrackDescriptors[i].adr, pTrackDescriptors[i].control, + pTrackDescriptors[i].address.minute, + pTrackDescriptors[i].address.second, + pTrackDescriptors[i].address.frame, + pTrackDescriptors[i].p.minute, + pTrackDescriptors[i].p.second, + pTrackDescriptors[i].p.frame ); + + /* track information has adr = 1 */ + if ( 0x01 != pTrackDescriptors[i].adr ) + continue; + + if( i_track == OSX_CDROM_LEADOUT_TRACK ) + i_leadout = i; + + if( i_track > CDIO_CD_MAX_TRACKS || i_track < CDIO_CD_MIN_TRACK_NO ) + continue; + + if (p_env->gen.i_first_track > i_track) + p_env->gen.i_first_track = i_track; + + if (p_env->i_last_track < i_track) + p_env->i_last_track = i_track; + + if (p_env->i_first_session > i_session) + p_env->i_first_session = i_session; + + if (p_env->i_last_session < i_session) + p_env->i_last_session = i_session; + } + + /* Now that we know what the first track number is, we can make sure + index positions are ordered starting at 0. + */ + for( i = 0; i < p_env->i_descriptors; i++ ) + { + track_t i_track = pTrackDescriptors[i].point; + + if( i_track > CDIO_CD_MAX_TRACKS || i_track < CDIO_CD_MIN_TRACK_NO ) + continue; + + /* Note what OSX calls a LBA we call an LSN. So below re we + really have have MSF -> LSN -> LBA. + */ + p_env->pp_lba[i_track - p_env->gen.i_first_track] = + cdio_lsn_to_lba(CDConvertMSFToLBA( pTrackDescriptors[i].p )); + set_track_flags(&(p_env->gen.track_flags[i_track]), + pTrackDescriptors[i].control); + } + + if( i_leadout == -1 ) + { + cdio_warn( "CD leadout not found" ); + free( p_env->pp_lba ); + free( (void *) p_env->pTOC ); + return false; + } + + /* Set leadout sector. + Note what OSX calls a LBA we call an LSN. So below re we + really have have MSF -> LSN -> LBA. + */ + p_env->pp_lba[TOTAL_TRACKS] = + cdio_lsn_to_lba(CDConvertMSFToLBA( pTrackDescriptors[i_leadout].p )); + p_env->gen.i_tracks = TOTAL_TRACKS; + } + + p_env->gen.toc_init = true; + + return( true ); + +} + +/*! + Return the starting LSN track number + i_track in obj. Track numbers start at 1. + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +static lsn_t +get_track_lba_osx(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if (!p_env->gen.toc_init) read_toc_osx (p_env) ; + if (!p_env->gen.toc_init) return CDIO_INVALID_LSN; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = p_env->i_last_track+1; + + if (i_track > p_env->i_last_track + 1 || i_track < p_env->gen.i_first_track) { + return CDIO_INVALID_LSN; + } else { + return p_env->pp_lba[i_track - p_env->gen.i_first_track]; + } +} + +/*! + Eject media . Return DRIVER_OP_SUCCESS if successful. + + The only way to cleanly unmount the disc under MacOS X (before + Tiger) is to use the 'disktool' command line utility. It uses the + non-public DiskArbitration API, which can not be used by Cocoa or + Carbon applications. + + Since Tiger (MacOS X 10.4), DiskArbitration is a public framework + and we can use it as needed. + + */ + +#ifndef HAVE_DISKARBITRATION +static driver_return_code_t +_eject_media_osx (void *user_data) { + + _img_private_t *p_env = user_data; + + FILE *p_file; + char *psz_drive; + char sz_cmd[32]; + + if( ( psz_drive = (char *)strstr( p_env->gen.source_name, "disk" ) ) != NULL && + strlen( psz_drive ) > 4 ) + { +#define EJECT_CMD "/usr/sbin/hdiutil eject %s" + snprintf( sz_cmd, sizeof(sz_cmd), EJECT_CMD, psz_drive ); +#undef EJECT_CMD + + if( ( p_file = popen( sz_cmd, "r" ) ) != NULL ) + { + char psz_result[0x200]; + int i_ret = fread( psz_result, 1, sizeof(psz_result) - 1, p_file ); + + if( i_ret == 0 && ferror( p_file ) != 0 ) + { + pclose( p_file ); + return DRIVER_OP_ERROR; + } + + pclose( p_file ); + + psz_result[ i_ret ] = 0; + + if( strstr( psz_result, "Disk Ejected" ) != NULL ) + { + return DRIVER_OP_SUCCESS; + } + } + } + + return DRIVER_OP_ERROR; +} +#else /* HAVE_DISKARBITRATION */ +typedef struct dacontext_s { + int result; + Boolean completed; + DASessionRef session; + CFRunLoopRef runloop; + CFRunLoopSourceRef cancel; +} dacontext_t; + +static void cancel_runloop(void *info) { /* do nothing */ } + +static CFRunLoopSourceContext cancelRunLoopSourceContext = { + .perform = cancel_runloop +}; + +static void media_eject_callback(DADiskRef disk, DADissenterRef dissenter, void *context) +{ + dacontext_t *dacontext = (dacontext_t *)context; + + if ( dissenter ) + { + CFStringRef status = DADissenterGetStatusString(dissenter); + if (status) + { + size_t cstr_size = CFStringGetLength(status); + char *cstr = malloc(cstr_size); + if ( CFStringGetCString( status, + cstr, cstr_size, + kCFStringEncodingASCII ) ) + CFRelease( status ); + + cdio_warn("%s", cstr); + + free(cstr); + } + } + + dacontext->result = (dissenter ? DRIVER_OP_ERROR : DRIVER_OP_SUCCESS); + dacontext->completed = TRUE; + CFRunLoopSourceSignal(dacontext->cancel); + CFRunLoopWakeUp(dacontext->runloop); +} + +static void media_unmount_callback(DADiskRef disk, DADissenterRef dissenter, void *context) +{ + dacontext_t *dacontext = (dacontext_t *)context; + + if (!dissenter) { + DADiskEject(disk, kDADiskEjectOptionDefault, media_eject_callback, context); + dacontext->result = dacontext->result == DRIVER_OP_UNINIT ? DRIVER_OP_SUCCESS : dacontext->result; + } + else { + dacontext->result = DRIVER_OP_ERROR; + dacontext->completed = TRUE; + CFRunLoopSourceSignal(dacontext->cancel); + CFRunLoopWakeUp(dacontext->runloop); + } +} + +static driver_return_code_t +_eject_media_osx (void *user_data) { + + _img_private_t *p_env = user_data; + char *psz_drive; + + DADiskRef disk; + dacontext_t dacontext; + CFDictionaryRef description; + + if( ( psz_drive = (char *)strstr( p_env->gen.source_name, "disk" ) ) == NULL || + strlen( psz_drive ) <= 4 ) + { + return DRIVER_OP_ERROR; + } + + if (p_env->gen.fd != -1) + close(p_env->gen.fd); + p_env->gen.fd = -1; + + dacontext.result = DRIVER_OP_UNINIT; + dacontext.completed = FALSE; + dacontext.runloop = CFRunLoopGetCurrent(); + dacontext.cancel = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &cancelRunLoopSourceContext); + + if (!dacontext.cancel) + { + return DRIVER_OP_ERROR; + } + + if (!(dacontext.session = DASessionCreate(kCFAllocatorDefault))) + { + CFRelease(dacontext.cancel); + return DRIVER_OP_ERROR; + } + + if ((disk = DADiskCreateFromBSDName(kCFAllocatorDefault, dacontext.session, psz_drive)) != NULL) + { + if ((description = DADiskCopyDescription(disk)) != NULL) + { + /* Does the device need to be unmounted first? */ + DASessionScheduleWithRunLoop(dacontext.session, dacontext.runloop, kCFRunLoopDefaultMode); + CFRunLoopAddSource(dacontext.runloop, dacontext.cancel, kCFRunLoopDefaultMode); + + if (CFDictionaryGetValueIfPresent(description, kDADiskDescriptionVolumePathKey, NULL)) + { + DADiskUnmount(disk, kDADiskUnmountOptionDefault, media_unmount_callback, &dacontext); + } + else + { + DADiskEject(disk, kDADiskEjectOptionDefault, media_eject_callback, &dacontext); + dacontext.result = dacontext.result == DRIVER_OP_UNINIT ? DRIVER_OP_SUCCESS : dacontext.result; + } + if (!dacontext.completed) + { + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 30.0, TRUE); /* timeout after 30 seconds */ + } + CFRunLoopRemoveSource(dacontext.runloop, dacontext.cancel, kCFRunLoopDefaultMode); + DASessionUnscheduleFromRunLoop(dacontext.session, dacontext.runloop, kCFRunLoopDefaultMode); + CFRelease(description); + } + CFRelease(disk); + } + + CFRunLoopSourceInvalidate(dacontext.cancel); + CFRelease(dacontext.cancel); + CFRelease(dacontext.session); + return dacontext.result; +} +#endif + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static lsn_t +get_disc_last_lsn_osx (void *user_data) +{ + return get_track_lba_osx(user_data, CDIO_CDROM_LEADOUT_TRACK); +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +_get_arg_osx (void *user_data, const char key[]) +{ + _img_private_t *p_env = user_data; + + if (!strcmp (key, "source")) { + return p_env->gen.source_name; + } else if (!strcmp (key, "access-mode")) { + switch (p_env->access_mode) { + case _AM_OSX: + return "OS X"; + case _AM_NONE: + return "no access method"; + } + } + return NULL; +} + +/*! + Return the media catalog number MCN. + */ +static char * +get_mcn_osx (const void *user_data) { + const _img_private_t *p_env = user_data; + dk_cd_read_mcn_t cd_read; + + memset( &cd_read, 0, sizeof(cd_read) ); + + if( ioctl( p_env->gen.fd, DKIOCCDREADMCN, &cd_read ) < 0 ) + { + cdio_debug( "could not read MCN, %s", strerror(errno) ); + return NULL; + } + return strdup((char*)cd_read.mcn); +} + + +/*! + Get format of track. +*/ +static track_format_t +get_track_format_osx(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + dk_cd_read_track_info_t cd_read; + CDTrackInfo a_track; + + if (!p_env->gen.toc_init) read_toc_osx (p_env) ; + + if (i_track > p_env->i_last_track || i_track < p_env->gen.i_first_track) + return TRACK_FORMAT_ERROR; + + memset( &cd_read, 0, sizeof(cd_read) ); + + cd_read.address = i_track; + cd_read.addressType = kCDTrackInfoAddressTypeTrackNumber; + + cd_read.buffer = &a_track; + cd_read.bufferLength = sizeof(CDTrackInfo); + + if( ioctl( p_env->gen.fd, DKIOCCDREADTRACKINFO, &cd_read ) == -1 ) + { + cdio_warn( "could not read trackinfo for track %d:\n%s", i_track, + strerror(errno)); + return TRACK_FORMAT_ERROR; + } + + cdio_debug( "%d: trackinfo trackMode: %x dataMode: %x", i_track, + a_track.trackMode, a_track.dataMode ); + + if (a_track.trackMode == CDIO_CDROM_DATA_TRACK) { + if (a_track.dataMode == CDROM_CDI_TRACK) { + return TRACK_FORMAT_CDI; + } else if (a_track.dataMode == CDROM_XA_TRACK) { + 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 +get_track_green_osx(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + CDTrackInfo a_track; + + if (!p_env->gen.toc_init) read_toc_osx (p_env) ; + + if ( i_track > p_env->i_last_track || i_track < p_env->gen.i_first_track ) + return false; + + else { + + dk_cd_read_track_info_t cd_read; + + memset( &cd_read, 0, sizeof(cd_read) ); + + cd_read.address = i_track; + cd_read.addressType = kCDTrackInfoAddressTypeTrackNumber; + + cd_read.buffer = &a_track; + cd_read.bufferLength = sizeof(CDTrackInfo); + + if( ioctl( p_env->gen.fd, DKIOCCDREADTRACKINFO, &cd_read ) == -1 ) { + cdio_warn( "could not read trackinfo for track %d:\n%s", i_track, + strerror(errno)); + return false; + } + return ((a_track.trackMode & CDIO_CDROM_DATA_TRACK) != 0); + } +} + +/* Set CD-ROM drive speed */ +static int +set_speed_osx (void *p_user_data, int i_speed) +{ + const _img_private_t *p_env = p_user_data; + + if (!p_env) return -1; + return ioctl(p_env->gen.fd, DKIOCCDSETSPEED, i_speed); +} + +#endif /* HAVE_DARWIN_CDROM */ + +/*! + Close tray on CD-ROM. + + @param psz_drive the CD-ROM drive to be closed. + +*/ + +/* FIXME: We don't use the device name because we don't how + to. + */ +#define CLOSE_TRAY_CMD "/usr/sbin/drutil tray close" +driver_return_code_t +close_tray_osx (const char *psz_drive) +{ +#ifdef HAVE_DARWIN_CDROM + FILE *p_file; + char sz_cmd[80]; + + if ( !psz_drive) return DRIVER_OP_UNINIT; + + /* Right now we really aren't making use of snprintf, but + possibly someday we will. + */ + snprintf( sz_cmd, sizeof(sz_cmd), CLOSE_TRAY_CMD ); + + if( ( p_file = popen( sz_cmd, "r" ) ) != NULL ) + { + char psz_result[0x200]; + int i_ret = fread( psz_result, 1, sizeof(psz_result) - 1, p_file ); + + if( i_ret == 0 && ferror( p_file ) != 0 ) + { + pclose( p_file ); + return DRIVER_OP_ERROR; + } + + pclose( p_file ); + + psz_result[ i_ret ] = 0; + + if( 0 == i_ret ) + { + return DRIVER_OP_SUCCESS; + } + } + + return DRIVER_OP_ERROR; +#else + return DRIVER_OP_NO_DRIVER; +#endif /*HAVE_DARWIN_CDROM*/ +} + +/*! + Return a string containing the default CD device if none is specified. + */ +char ** +cdio_get_devices_osx(void) +{ +#ifndef HAVE_DARWIN_CDROM + return NULL; +#else + io_object_t next_media; + mach_port_t master_port; + kern_return_t kern_result; + io_iterator_t media_iterator; + CFMutableDictionaryRef classes_to_match; + char **drives = NULL; + unsigned int num_drives=0; + + kern_result = IOMasterPort( MACH_PORT_NULL, &master_port ); + if( kern_result != KERN_SUCCESS ) + { + return( NULL ); + } + + classes_to_match = IOServiceMatching( kIOMediaClass ); + if( classes_to_match == NULL ) + { + return( NULL ); + } + + CFDictionarySetValue( classes_to_match, CFSTR(kIOMediaEjectableKey), + kCFBooleanTrue ); + + kern_result = IOServiceGetMatchingServices( master_port, + classes_to_match, + &media_iterator ); + if( kern_result != KERN_SUCCESS ) + { + return( NULL ); + } + + next_media = IOIteratorNext( media_iterator ); + if( next_media != 0 ) + { + char psz_buf[0x32]; + size_t dev_path_length; + CFTypeRef str_bsd_path; + + do + { + str_bsd_path = + IORegistryEntryCreateCFProperty( next_media, + CFSTR( kIOBSDNameKey ), + kCFAllocatorDefault, + 0 ); + if( str_bsd_path == NULL ) + { + IOObjectRelease( next_media ); + continue; + } + + /* Below, by appending 'r' to the BSD node name, we indicate + a raw disk. Raw disks receive I/O requests directly and + don't go through a buffer cache. */ + snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' ); + dev_path_length = strlen( psz_buf ); + + if( CFStringGetCString( str_bsd_path, + (char*)&psz_buf + dev_path_length, + sizeof(psz_buf) - dev_path_length, + kCFStringEncodingASCII ) ) + { + cdio_add_device_list(&drives, strdup(psz_buf), &num_drives); + } + CFRelease( str_bsd_path ); + IOObjectRelease( next_media ); + + } while( ( next_media = IOIteratorNext( media_iterator ) ) != 0 ); + } + IOObjectRelease( media_iterator ); + cdio_add_device_list(&drives, NULL, &num_drives); + return drives; +#endif /* HAVE_DARWIN_CDROM */ +} + +/*! + Return a string containing the default CD device if none is specified. + */ +char * +cdio_get_default_device_osx(void) +{ +#ifndef HAVE_DARWIN_CDROM + return NULL; +#else + io_object_t next_media; + kern_return_t kern_result; + io_iterator_t media_iterator; + CFMutableDictionaryRef classes_to_match; + + classes_to_match = IOServiceMatching( kIOMediaClass ); + if( classes_to_match == NULL ) + { + return( NULL ); + } + + CFDictionarySetValue( classes_to_match, CFSTR(kIOMediaEjectableKey), + kCFBooleanTrue ); + + kern_result = IOServiceGetMatchingServices( kIOMasterPortDefault, + classes_to_match, + &media_iterator ); + if( kern_result != KERN_SUCCESS ) + { + return( NULL ); + } + + next_media = IOIteratorNext( media_iterator ); + if( next_media != 0 ) + { + char psz_buf[0x32]; + size_t dev_path_length; + CFTypeRef str_bsd_path; + + do + { + str_bsd_path = IORegistryEntryCreateCFProperty( next_media, + CFSTR( kIOBSDNameKey ), + kCFAllocatorDefault, + 0 ); + if( str_bsd_path == NULL ) + { + IOObjectRelease( next_media ); + continue; + } + + snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' ); + dev_path_length = strlen( psz_buf ); + + if( CFStringGetCString( str_bsd_path, + (char*)&psz_buf + dev_path_length, + sizeof(psz_buf) - dev_path_length, + kCFStringEncodingASCII ) ) + { + CFRelease( str_bsd_path ); + IOObjectRelease( next_media ); + IOObjectRelease( media_iterator ); + return strdup( psz_buf ); + } + + CFRelease( str_bsd_path ); + IOObjectRelease( next_media ); + + } while( ( next_media = IOIteratorNext( media_iterator ) ) != 0 ); + } + IOObjectRelease( media_iterator ); + return NULL; +#endif /* HAVE_DARWIN_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_t * +cdio_open_am_osx (const char *psz_source_name, const char *psz_access_mode) +{ + + if (psz_access_mode != NULL) + cdio_warn ("there is only one access mode for OS X. Arg %s ignored", + psz_access_mode); + return cdio_open_osx(psz_source_name); +} + + +/*! + 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_t * +cdio_open_osx (const char *psz_orig_source) +{ +#ifdef HAVE_DARWIN_CDROM + CdIo_t *ret; + _img_private_t *_data; + char *psz_source; + + cdio_funcs_t _funcs = { + .eject_media = _eject_media_osx, + .free = _free_osx, + .get_arg = _get_arg_osx, + .get_cdtext = get_cdtext_osx, + .get_default_device = cdio_get_default_device_osx, + .get_devices = cdio_get_devices_osx, + .get_disc_last_lsn = get_disc_last_lsn_osx, + .get_discmode = get_discmode_osx, + .get_drive_cap = get_drive_cap_osx, + .get_first_track_num = get_first_track_num_generic, + .get_hwinfo = get_hwinfo_osx, + .get_mcn = get_mcn_osx, + .get_num_tracks = get_num_tracks_generic, + .get_track_channels = get_track_channels_generic, + .get_track_copy_permit = get_track_copy_permit_generic, + .get_track_format = get_track_format_osx, + .get_track_green = get_track_green_osx, + .get_track_lba = get_track_lba_osx, + .get_track_msf = NULL, + .get_track_preemphasis = get_track_preemphasis_generic, + .lseek = cdio_generic_lseek, + .read = cdio_generic_read, + .read_audio_sectors = read_audio_sectors_osx, + .read_data_sectors = read_data_sectors_osx, + .read_mode1_sector = read_mode1_sector_osx, + .read_mode1_sectors = read_mode1_sectors_osx, + .read_mode2_sector = read_mode2_sector_osx, + .read_mode2_sectors = read_mode2_sectors_osx, + .read_toc = read_toc_osx, + .run_mmc_cmd = run_mmc_cmd_osx, + .set_arg = _set_arg_osx, + .set_speed = set_speed_osx, + }; + + _data = calloc (1, sizeof (_img_private_t)); + _data->access_mode = _AM_OSX; + _data->MediaClass_service = 0; + _data->gen.init = false; + _data->gen.fd = -1; + _data->gen.toc_init = false; + _data->gen.b_cdtext_init = false; + _data->gen.b_cdtext_error = false; + + if (NULL == psz_orig_source) { + psz_source=cdio_get_default_device_osx(); + if (NULL == psz_source) { + cdio_generic_free(_data); + return NULL; + } + _set_arg_osx(_data, "source", psz_source); + free(psz_source); + } else { + if (cdio_is_device_generic(psz_orig_source)) + _set_arg_osx(_data, "source", psz_orig_source); + else { + /* The below would be okay if all device drivers worked this way. */ +#if 0 + cdio_info ("source %s is a not a device", psz_orig_source); +#endif + cdio_generic_free(_data); + return NULL; + } + } + + ret = cdio_new ((void *)_data, &_funcs); + if (ret == NULL) { + cdio_generic_free(_data); + return NULL; + } + ret->driver_id = DRIVER_OSX; + if (cdio_generic_init(_data, O_RDONLY | O_NONBLOCK) && init_osx(_data)) + return ret; + else { + cdio_generic_free (_data); + return NULL; + } + +#else + return NULL; +#endif /* HAVE_DARWIN_CDROM */ + +} + +bool +cdio_have_osx (void) +{ +#ifdef HAVE_DARWIN_CDROM + return true; +#else + return false; +#endif /* HAVE_DARWIN_CDROM */ +} diff --git a/lib/driver/portable.h b/lib/driver/portable.h new file mode 100644 index 00000000..f8d0ceab --- /dev/null +++ b/lib/driver/portable.h @@ -0,0 +1,77 @@ +/* + $Id: portable.h,v 1.4 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + This file contains definitions to fill in for differences or + deficiencies to OS or compiler irregularities. If this file is + included other routines can be more portable. +*/ + +#ifndef __CDIO_PORTABLE_H__ +#define __CDIO_PORTABLE_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if !defined(HAVE_FTRUNCATE) +# if defined ( WIN32 ) +# define ftruncate chsize +# endif +#endif /*HAVE_FTRUNCATE*/ + +#if !defined(HAVE_SNPRINTF) +# if defined ( MSVC ) +# define snprintf _snprintf +# endif +#endif /*HAVE_SNPRINTF*/ + +#if !defined(HAVE_VSNPRINTF) +# if defined ( MSVC ) +# define snprintf _vsnprintf +# endif +#endif /*HAVE_SNPRINTF*/ + +#if !defined(HAVE_DRAND48) && defined(HAVE_RAND) +# define drand48() (rand() / (double)RAND_MAX) +#endif + +#ifdef MSVC +# include + +# ifndef S_ISBLK +# define _S_IFBLK 0060000 /* Block Special */ +# define S_ISBLK(x) (x & _S_IFBLK) +# endif + +# ifndef S_ISCHR +# define _S_IFCHR 0020000 /* character special */ +# define S_ISCHR(x) (x & _S_IFCHR) +# endif +#endif /*MSVC*/ + +#ifdef HAVE_MEMSET +# define BZERO(ptr, size) memset(ptr, 0, size) +#elif HAVE_BZERO +# define BZERO(ptr, size) bzero(ptr, size) +#else +#error You need either memset or bzero +#endif + +#endif /* __CDIO_PORTABLE_H__ */ diff --git a/lib/driver/read.c b/lib/driver/read.c new file mode 100644 index 00000000..361c2b46 --- /dev/null +++ b/lib/driver/read.c @@ -0,0 +1,329 @@ +/* + $Id: read.c,v 1.12 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/** \file read.h + * + * \brief sector (block, frame)-related libcdio routines. + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include "cdio_private.h" +#include "cdio_assert.h" + +#ifdef HAVE_STRING_H +#include +#endif + +#define check_read_parms(p_cdio, p_buf, i_lsn) \ + if (!p_cdio) return DRIVER_OP_UNINIT; \ + if (!p_buf || CDIO_INVALID_LSN == i_lsn) \ + return DRIVER_OP_ERROR; + +#define check_lsn(i_lsn) \ + check_read_parms(p_cdio, p_buf, i_lsn); \ + { \ + lsn_t end_lsn = \ + cdio_get_track_lsn(p_cdio, CDIO_CDROM_LEADOUT_TRACK); \ + if ( i_lsn > end_lsn ) { \ + cdio_info("Trying to access past end of disk lsn: %ld, end lsn: %ld", \ + (long int) i_lsn, (long int) end_lsn); \ + return DRIVER_OP_ERROR; \ + } \ + } + +#define check_lsn_blocks(i_lsn, i_blocks) \ + check_read_parms(p_cdio, p_buf, i_lsn); \ + { \ + lsn_t end_lsn = \ + cdio_get_track_lsn(p_cdio, CDIO_CDROM_LEADOUT_TRACK); \ + if ( i_lsn > end_lsn ) { \ + cdio_info("Trying to access past end of disk lsn: %ld, end lsn: %ld", \ + (long int) i_lsn, (long int) end_lsn); \ + return DRIVER_OP_ERROR; \ + } \ + /* Care is used in the expression below to be correct with */ \ + /* respect to unsigned integers. */ \ + if ( i_lsn + i_blocks > end_lsn + 1 ) { \ + cdio_info("Request truncated to end disk; lsn: %ld, end lsn: %ld", \ + (long int) i_lsn, (long int) end_lsn); \ + i_blocks = end_lsn - i_lsn + 1; \ + } \ + } + +/*! + lseek - reposition read/write file offset + Returns (off_t) -1 on error. + Similar to (if not the same as) libc's lseek() +*/ +off_t +cdio_lseek (const CdIo_t *p_cdio, off_t offset, int whence) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + + if (p_cdio->op.lseek) + return (p_cdio->op.lseek) (p_cdio->env, offset, whence); + return DRIVER_OP_UNSUPPORTED; +} + +/*! Reads into buf the next size bytes. Similar to (if not the + same as) libc's read(). This is a "cooked" read, or one handled by + the OS. It probably won't work on audio data. For that use + cdio_read_audio_sector(s). + + @param p_cdio object to read from + @param p_buf place to read data into. The caller should make sure + this location can store at least i_size bytes. + @param i_size number of bytes to read + + @return (ssize_t) -1 on error. +*/ +ssize_t +cdio_read (const CdIo_t *p_cdio, void *p_buf, size_t i_size) +{ + if (!p_cdio) return DRIVER_OP_UNINIT; + + if (p_cdio->op.read) + return (p_cdio->op.read) (p_cdio->env, p_buf, i_size); + return DRIVER_OP_UNSUPPORTED; +} + +/*! + Reads an audio sector from cd device into data starting + from lsn. Returns DRIVER_OP_SUCCESS if no error. +*/ +driver_return_code_t +cdio_read_audio_sector (const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn) +{ + check_lsn(i_lsn); + if (p_cdio->op.read_audio_sectors) + return p_cdio->op.read_audio_sectors (p_cdio->env, p_buf, i_lsn, 1); + return DRIVER_OP_UNSUPPORTED; +} + +/*! + Reads audio sectors from cd device into data starting + from lsn. Returns DRIVER_OP_SUCCESS if no error. +*/ +driver_return_code_t +cdio_read_audio_sectors (const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn, + uint32_t i_blocks) +{ + check_lsn_blocks(i_lsn, i_blocks); + + if (0 == i_blocks) return DRIVER_OP_SUCCESS; + + if (p_cdio->op.read_audio_sectors) + return (p_cdio->op.read_audio_sectors) (p_cdio->env, p_buf, i_lsn, + i_blocks); + return DRIVER_OP_UNSUPPORTED; +} + +/*! + Reads an audio sector from cd device into data starting + from lsn. Returns DRIVER_OP_SUCCESS if no error. +*/ +driver_return_code_t +cdio_read_data_sectors (const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn, + uint16_t i_blocksize, uint32_t i_blocks) +{ + check_lsn(i_lsn); + + if (0 == i_blocks) return DRIVER_OP_SUCCESS; + + if (p_cdio->op.read_data_sectors) + return p_cdio->op.read_data_sectors (p_cdio->env, p_buf, i_lsn, + i_blocksize, i_blocks); + return DRIVER_OP_UNSUPPORTED; +} + + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +/*! + Reads a single mode1 form1 or form2 sector from cd device + into data starting from lsn. Returns DRIVER_OP_SUCCESS if no error. + */ +driver_return_code_t +cdio_read_mode1_sector (const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn, + bool b_form2) +{ + uint32_t size = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE ; + + check_lsn(i_lsn); + if (p_cdio->op.read_mode1_sector) { + return p_cdio->op.read_mode1_sector(p_cdio->env, p_buf, i_lsn, b_form2); + } else if (p_cdio->op.lseek && p_cdio->op.read) { + char buf[CDIO_CD_FRAMESIZE] = { 0, }; + if (0 > cdio_lseek(p_cdio, CDIO_CD_FRAMESIZE*i_lsn, SEEK_SET)) + return -1; + if (0 > cdio_read(p_cdio, buf, CDIO_CD_FRAMESIZE)) + return -1; + memcpy (p_buf, buf, size); + return DRIVER_OP_SUCCESS; + } + + return DRIVER_OP_UNSUPPORTED; +} + +/*! + Reads mode 1 sectors + + @param p_cdio object to read from + @param buf place to read data into + @param lsn sector to read + @param b_form2 true for reading mode 1 form 2 sectors or false for + mode 1 form 1 sectors. + @param i_blocks number of sectors to read +*/ +driver_return_code_t +cdio_read_mode1_sectors (const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn, + bool b_form2, uint32_t i_blocks) +{ + check_lsn_blocks(i_lsn, i_blocks); + + if (0 == i_blocks) return DRIVER_OP_SUCCESS; + + if (p_cdio->op.read_mode1_sectors) + return (p_cdio->op.read_mode1_sectors) (p_cdio->env, p_buf, i_lsn, b_form2, + i_blocks); + return DRIVER_OP_UNSUPPORTED; +} + +/*! + Reads a mode 2 sector + + @param p_cdio object to read from + @param buf place to read data into + @param lsn sector to read + @param b_form2 true for reading mode 2 form 2 sectors or false for + mode 2 form 1 sectors. +*/ +driver_return_code_t +cdio_read_mode2_sector (const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn, + bool b_form2) +{ + check_lsn(i_lsn); + if (p_cdio->op.read_mode2_sector) + return p_cdio->op.read_mode2_sector (p_cdio->env, p_buf, i_lsn, b_form2); + + /* fallback */ + if (p_cdio->op.read_mode2_sectors != NULL) + return cdio_read_mode2_sectors (p_cdio, p_buf, i_lsn, b_form2, 1); + return DRIVER_OP_UNSUPPORTED; +} + +/*! + Reads mode 2 sectors + + @param p_cdio object to read from + @param buf place to read data into + @param lsn sector to read + @param b_form2 true for reading mode2 form 2 sectors or false for + mode 2 form 1 sectors. + @param i_blocks number of sectors to read +*/ +driver_return_code_t +cdio_read_mode2_sectors (const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn, + bool b_form2, uint32_t i_blocks) +{ + check_lsn_blocks(i_lsn, i_blocks); + + if (0 == i_blocks) return DRIVER_OP_SUCCESS; + + if (p_cdio->op.read_mode2_sectors) + return (p_cdio->op.read_mode2_sectors) (p_cdio->env, p_buf, i_lsn, + b_form2, i_blocks); + return DRIVER_OP_UNSUPPORTED; + +} + + +/** The special case of reading a single block is a common one so we + provide a routine for that as a convenience. +*/ +driver_return_code_t +cdio_read_sector(const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn, + cdio_read_mode_t read_mode) +{ + return cdio_read_sectors(p_cdio, p_buf, i_lsn, read_mode, 1); +} + +/*! + Reads a number of sectors (AKA blocks). + + @param p_buf place to read data into. The caller should make sure + this location is large enough. See below for size information. + @param read_mode the kind of "mode" to use in reading. + @param i_lsn sector to read + @param i_blocks number of sectors to read + @return DRIVER_OP_SUCCESS (0) if no error, other (negative) enumerations + are returned on error. + + If read_mode is CDIO_MODE_AUDIO, + *p_buf should hold at least CDIO_FRAMESIZE_RAW * i_blocks bytes. + + If read_mode is CDIO_MODE_DATA, + *p_buf should hold at least i_blocks times either ISO_BLOCKSIZE, + M1RAW_SECTOR_SIZE or M2F2_SECTOR_SIZE depending on the kind of + sector getting read. If you don't know whether you have a Mode 1/2, + Form 1/ Form 2/Formless sector best to reserve space for the maximum + which is M2RAW_SECTOR_SIZE. + + If read_mode is CDIO_MODE_M2F1, + *p_buf should hold at least M2RAW_SECTOR_SIZE * i_blocks bytes. + + If read_mode is CDIO_MODE_M2F2, + *p_buf should hold at least CDIO_CD_FRAMESIZE * i_blocks bytes. + + +*/ +driver_return_code_t +cdio_read_sectors(const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn, + cdio_read_mode_t read_mode, uint32_t i_blocks) +{ + switch(read_mode) { + case CDIO_READ_MODE_AUDIO: + return cdio_read_audio_sectors (p_cdio, p_buf, i_lsn, i_blocks); + case CDIO_READ_MODE_M1F1: + return cdio_read_mode1_sectors (p_cdio, p_buf, i_lsn, false, i_blocks); + case CDIO_READ_MODE_M1F2: + return cdio_read_mode1_sectors (p_cdio, p_buf, i_lsn, true, i_blocks); + case CDIO_READ_MODE_M2F1: + return cdio_read_mode2_sectors (p_cdio, p_buf, i_lsn, false, i_blocks); + case CDIO_READ_MODE_M2F2: + return cdio_read_mode2_sectors (p_cdio, p_buf, i_lsn, true, i_blocks); + } + /* Can't happen. Just to shut up gcc. */ + return DRIVER_OP_ERROR; +} + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/driver/sector.c b/lib/driver/sector.c new file mode 100644 index 00000000..782e0de4 --- /dev/null +++ b/lib/driver/sector.c @@ -0,0 +1,268 @@ +/* + $Id: sector.c,v 1.5 2005/02/06 04:20:25 rocky Exp $ + + Copyright (C) 2004, 2005 Rocky Bernstein + 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 +#ifdef HAVE_STRING_H +#include +#endif + +#include + +static const char _rcsid[] = "$Id: sector.c,v 1.5 2005/02/06 04:20:25 rocky Exp $"; + +/*! String of bytes used to identify the beginning of a Mode 1 or + Mode 2 sector. */ +const uint8_t CDIO_SECTOR_SYNC_HEADER[CDIO_CD_SYNC_SIZE] = + {0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0}; + +/* Variables to hold debugger-helping enumerations */ +enum cdio_cd_enums; +enum m2_sector_enums; + +lba_t +cdio_lba_to_lsn (lba_t lba) +{ + if (CDIO_INVALID_LBA == lba) return CDIO_INVALID_LSN; + return lba - CDIO_PREGAP_SECTORS; +} + +/* + The below is adapted from cdparanoia code which claims it is + straight from the MMC3 spec. +*/ + +void +cdio_lsn_to_msf (lsn_t lsn, msf_t *msf) +{ + int m, s, f; + + cdio_assert (msf != 0); + + if ( lsn >= -CDIO_PREGAP_SECTORS ){ + m = (lsn + CDIO_PREGAP_SECTORS) / CDIO_CD_FRAMES_PER_MIN; + lsn -= m * CDIO_CD_FRAMES_PER_MIN; + s = (lsn + CDIO_PREGAP_SECTORS) / CDIO_CD_FRAMES_PER_SEC; + lsn -= s * CDIO_CD_FRAMES_PER_SEC; + f = lsn + CDIO_PREGAP_SECTORS; + } else { + m = (lsn + CDIO_CD_MAX_LSN) / CDIO_CD_FRAMES_PER_MIN; + lsn -= m * (CDIO_CD_FRAMES_PER_MIN); + s = (lsn+CDIO_CD_MAX_LSN) / CDIO_CD_FRAMES_PER_SEC; + lsn -= s * CDIO_CD_FRAMES_PER_SEC; + f = lsn + CDIO_CD_MAX_LSN; + } + + if (m > 99) { + cdio_warn ("number of minutes (%d) truncated to 99.", m); + m = 99; + } + + msf->m = cdio_to_bcd8 (m); + msf->s = cdio_to_bcd8 (s); + msf->f = cdio_to_bcd8 (f); +} + +/*! + Convert an LBA into a string representation of the MSF. + \warning cdio_lba_to_msf_str returns new allocated string */ +char * +cdio_lba_to_msf_str (lba_t lba) +{ + + if (CDIO_INVALID_LBA == lba) { + return strdup("*INVALID"); + } else { + msf_t msf; + msf.m = msf.s = msf.f = 0; + cdio_lba_to_msf (lba, &msf); + return cdio_msf_to_str(&msf); + } +} + +/*! + Convert an LSN into the corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. +*/ +lba_t +cdio_lsn_to_lba (lsn_t lsn) +{ + if (CDIO_INVALID_LSN == lsn) return CDIO_INVALID_LBA; + return lsn + CDIO_PREGAP_SECTORS; +} + +/*! + Convert an LBA into the corresponding MSF. +*/ +void +cdio_lba_to_msf (lba_t lba, msf_t *msf) +{ + cdio_assert (msf != 0); + cdio_lsn_to_msf(cdio_lba_to_lsn(lba), msf); +} + +/*! + Convert a MSF into the corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. +*/ +lba_t +cdio_msf_to_lba (const msf_t *msf) +{ + uint32_t lba = 0; + + cdio_assert (msf != 0); + + lba = cdio_from_bcd8 (msf->m); + lba *= CDIO_CD_SECS_PER_MIN; + + lba += cdio_from_bcd8 (msf->s); + lba *= CDIO_CD_FRAMES_PER_SEC; + + lba += cdio_from_bcd8 (msf->f); + + return lba; +} + +/*! + Convert a MSF into the corresponding LSN. + CDIO_INVALID_LSN is returned if there is an error. +*/ +lba_t +cdio_msf_to_lsn (const msf_t *msf) +{ + return cdio_lba_to_lsn(cdio_msf_to_lba (msf)); +} + +/*! + Convert an LBA into a string representation of the MSF. + \warning cdio_lba_to_msf_str returns new allocated string */ +char * +cdio_msf_to_str (const msf_t *msf) +{ + char buf[16]; + + snprintf (buf, sizeof (buf), "%2.2x:%2.2x:%2.2x", msf->m, msf->s, msf->f); + return strdup (buf); +} + +/*! + Convert a MSF - broken out as 3 integer components into the + corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. +*/ +lba_t +cdio_msf3_to_lba (unsigned int minutes, unsigned int seconds, + unsigned int frames) +{ + return ((minutes * CDIO_CD_SECS_PER_MIN + seconds) * CDIO_CD_FRAMES_PER_SEC + + frames); +} + +/*! + Convert a string of the form MM:SS:FF into the corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. +*/ +lba_t +cdio_mmssff_to_lba (const char *psz_mmssff) +{ + int psz_field; + lba_t ret; + char c; + + if (0 == strcmp (psz_mmssff, "0")) + return 0; + + c = *psz_mmssff++; + if(c >= '0' && c <= '9') + psz_field = (c - '0'); + else + return CDIO_INVALID_LBA; + while(':' != (c = *psz_mmssff++)) { + if(c >= '0' && c <= '9') + psz_field = psz_field * 10 + (c - '0'); + else + return CDIO_INVALID_LBA; + } + + ret = cdio_msf3_to_lba (psz_field, 0, 0); + + c = *psz_mmssff++; + if(c >= '0' && c <= '9') + psz_field = (c - '0'); + else + return CDIO_INVALID_LBA; + if(':' != (c = *psz_mmssff++)) { + if(c >= '0' && c <= '9') { + psz_field = psz_field * 10 + (c - '0'); + c = *psz_mmssff++; + if(c != ':') + return CDIO_INVALID_LBA; + } + else + return CDIO_INVALID_LBA; + } + + if(psz_field >= CDIO_CD_SECS_PER_MIN) + return CDIO_INVALID_LBA; + + ret += cdio_msf3_to_lba (0, psz_field, 0); + + c = *psz_mmssff++; + if (isdigit(c)) + psz_field = (c - '0'); + else + return -1; + if('\0' != (c = *psz_mmssff++)) { + if (isdigit(c)) { + psz_field = psz_field * 10 + (c - '0'); + c = *psz_mmssff++; + } + else + return CDIO_INVALID_LBA; + } + + if('\0' != c) + return CDIO_INVALID_LBA; + + if(psz_field >= CDIO_CD_FRAMES_PER_SEC) + return CDIO_INVALID_LBA; + + ret += psz_field; + + return ret; +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/driver/solaris.c b/lib/driver/solaris.c new file mode 100644 index 00000000..6ff40db0 --- /dev/null +++ b/lib/driver/solaris.c @@ -0,0 +1,1174 @@ +/* + $Id: solaris.c,v 1.12 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2002, 2003, 2004, 2005, 2006 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#include +#include +#include +#include +#include "cdio_assert.h" +#include "cdio_private.h" + +#define DEFAULT_CDIO_DEVICE "/vol/dev/aliases/cdrom0" + +#ifdef HAVE_SOLARIS_CDROM + +static const char _rcsid[] = "$Id: solaris.c,v 1.12 2008/04/22 15:29:12 karl Exp $"; + +#ifdef HAVE_GLOB_H +#include +#endif + +#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 +#include "cdtext_private.h" + +/* not defined in dkio.h yet */ +#define DK_DVDRW 0x13 + +/* reader */ + +typedef enum { + _AM_NONE, + _AM_SUN_CTRL_ATAPI, + _AM_SUN_CTRL_SCSI +#if FINISHED + _AM_READ_CD, + _AM_READ_10 +#endif +} access_mode_t; + + +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + + access_mode_t access_mode; + + /* Some of the more OS specific things. */ + /* Entry info for each track, add 1 for leadout. */ + struct cdrom_tocentry tocent[CDIO_CD_MAX_TRACKS+1]; + + /* Track information */ + struct cdrom_tochdr tochdr; +} _img_private_t; + +static track_format_t get_track_format_solaris(void *p_user_data, + track_t i_track); + +static access_mode_t +str_to_access_mode_solaris(const char *psz_access_mode) +{ + const access_mode_t default_access_mode = _AM_SUN_CTRL_SCSI; + + if (NULL==psz_access_mode) return default_access_mode; + + if (!strcmp(psz_access_mode, "ATAPI")) + return _AM_SUN_CTRL_SCSI; /* force ATAPI to be SCSI */ + else if (!strcmp(psz_access_mode, "SCSI")) + return _AM_SUN_CTRL_SCSI; + else { + cdio_warn ("unknown access type: %s. Default SCSI used.", + psz_access_mode); + return default_access_mode; + } +} + + +/*! + Pause playing CD through analog output + + @param p_cdio the CD object to be acted upon. +*/ +static driver_return_code_t +audio_pause_solaris (void *p_user_data) +{ + + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDROMPAUSE); +} + +/*! + Playing starting at given MSF through analog output + + @param p_cdio the CD object to be acted upon. +*/ +static driver_return_code_t +audio_play_msf_solaris (void *p_user_data, msf_t *p_start_msf, + msf_t *p_end_msf) +{ + + const _img_private_t *p_env = p_user_data; + + struct cdrom_msf solaris_msf; + solaris_msf.cdmsf_min0 = cdio_from_bcd8(p_start_msf->m); + solaris_msf.cdmsf_sec0 = cdio_from_bcd8(p_start_msf->s); + solaris_msf.cdmsf_frame0 = cdio_from_bcd8(p_start_msf->f); + solaris_msf.cdmsf_min1 = cdio_from_bcd8(p_end_msf->m); + solaris_msf.cdmsf_sec1 = cdio_from_bcd8(p_end_msf->s); + solaris_msf.cdmsf_frame1 = cdio_from_bcd8(p_end_msf->f); + + return ioctl(p_env->gen.fd, CDROMPLAYMSF, &solaris_msf); +} + +/*! + Playing CD through analog output at the desired track and index + + @param p_cdio the CD object to be acted upon. + @param p_track_index location to start/end. +*/ +static driver_return_code_t +audio_play_track_index_solaris (void *p_user_data, + cdio_track_index_t *p_track_index) +{ + + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDROMPLAYTRKIND, p_track_index); +} + +/*! + Read Audio Subchannel information + + @param p_cdio the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_read_subchannel_solaris (void *p_user_data, + cdio_subchannel_t *p_subchannel) +{ + const _img_private_t *p_env = p_user_data; + struct cdrom_subchnl subchannel; + int i_rc; + p_subchannel->format = CDIO_CDROM_MSF; + i_rc = ioctl(p_env->gen.fd, CDROMSUBCHNL, &subchannel); + if (0 == i_rc) { + p_subchannel->control = subchannel.cdsc_ctrl; + p_subchannel->track = subchannel.cdsc_trk; + p_subchannel->index = subchannel.cdsc_ind; + + p_subchannel->abs_addr.m = + cdio_to_bcd8(subchannel.cdsc_absaddr.msf.minute); + p_subchannel->abs_addr.s = + cdio_to_bcd8(subchannel.cdsc_absaddr.msf.second); + p_subchannel->abs_addr.f = + cdio_to_bcd8(subchannel.cdsc_absaddr.msf.frame); + p_subchannel->rel_addr.m = + cdio_to_bcd8(subchannel.cdsc_reladdr.msf.minute); + p_subchannel->rel_addr.s = + cdio_to_bcd8(subchannel.cdsc_reladdr.msf.second); + p_subchannel->rel_addr.f = + cdio_to_bcd8(subchannel.cdsc_reladdr.msf.frame); + p_subchannel->audio_status = subchannel.cdsc_audiostatus; + + return DRIVER_OP_SUCCESS; + } else { + cdio_info ("ioctl CDROMSUBCHNL failed: %s\n", strerror(errno)); + return DRIVER_OP_ERROR; + } +} + +/*! + Resume playing an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_resume_solaris (void *p_user_data) +{ + + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDROMRESUME, 0); +} + +/*! + Resume playing an audio CD. + + @param p_cdio the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_set_volume_solaris (void *p_user_data, + cdio_audio_volume_t *p_volume) { + + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDROMVOLCTRL, p_volume); +} + +/*! + Stop playing an audio CD. + + @param p_user_data the CD object to be acted upon. + +*/ +static driver_return_code_t +audio_stop_solaris (void *p_user_data) +{ + const _img_private_t *p_env = p_user_data; + return ioctl(p_env->gen.fd, CDROMSTOP); +} + +/*! + Initialize CD device. + */ +static bool +init_solaris (_img_private_t *p_env) +{ + + if (!cdio_generic_init(p_env, O_RDONLY)) return false; + + p_env->access_mode = _AM_SUN_CTRL_SCSI; + + return true; +} + +/*! + Run a SCSI MMC command. + + p_user_data internal CD structure. + i_timeout_ms time in milliseconds we will wait for the command + to complete. + i_cdb Size of p_cdb + p_cdb CDB bytes. + e_direction direction the transfer is to go. + i_buf Size of buffer + p_buf Buffer for data, both sending and receiving + */ +static driver_return_code_t +run_mmc_cmd_solaris( void *p_user_data, unsigned int i_timeout_ms, + unsigned int i_cdb, const mmc_cdb_t *p_cdb, + cdio_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + struct uscsi_cmd cgc; + + memset (&cgc, 0, sizeof (struct uscsi_cmd)); + cgc.uscsi_cdb = (caddr_t) p_cdb; + + cgc.uscsi_flags = SCSI_MMC_DATA_READ == e_direction ? + USCSI_READ : USCSI_WRITE; + + cgc.uscsi_timeout = msecs2secs(i_timeout_ms); + cgc.uscsi_bufaddr = p_buf; + cgc.uscsi_buflen = i_buf; + cgc.uscsi_cdblen = i_cdb; + + return ioctl(p_env->gen.fd, USCSICMD, &cgc); +} + +/*! + Reads audio sectors from CD device into data starting from lsn. + Returns 0 if no error. + + May have to check size of nblocks. There may be a limit that + can be read in one go, e.g. 25 blocks. +*/ + +static int +_read_audio_sectors_solaris (void *p_user_data, void *data, lsn_t i_lsn, + unsigned int i_blocks) +{ + struct cdrom_msf solaris_msf; + msf_t _msf; + struct cdrom_cdda cdda; + + _img_private_t *p_env = p_user_data; + + cdio_lba_to_msf (cdio_lsn_to_lba(i_lsn), &_msf); + solaris_msf.cdmsf_min0 = cdio_from_bcd8(_msf.m); + solaris_msf.cdmsf_sec0 = cdio_from_bcd8(_msf.s); + solaris_msf.cdmsf_frame0 = cdio_from_bcd8(_msf.f); + + if (p_env->gen.ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged < 75 + || (p_env->gen.ioctls_debugged < (30 * 75) + && p_env->gen.ioctls_debugged % 75 == 0) + || p_env->gen.ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %d", i_lsn); + + p_env->gen.ioctls_debugged++; + + if (i_blocks > 60) { + cdio_warn("%s:\n", + "we can't handle reading more than 60 blocks. Reset to 60"); + } + + cdda.cdda_addr = i_lsn; + cdda.cdda_length = i_blocks; + cdda.cdda_data = (caddr_t) data; + cdda.cdda_subcode = CDROM_DA_NO_SUBCODE; + + if (ioctl (p_env->gen.fd, CDROMCDDA, &cdda) == -1) { + perror ("ioctl(..,CDROMCDDA,..)"); + return DRIVER_OP_ERROR; + /* exit (EXIT_FAILURE); */ + } + + return DRIVER_OP_SUCCESS; +} + +/*! + Reads a single mode1 sector from cd device into data starting + from i_lsn. + */ +static driver_return_code_t +_read_mode1_sector_solaris (void *p_env, void *data, lsn_t i_lsn, + bool b_form2) +{ + +#if FIXED + do something here. +#else + return cdio_generic_read_form1_sector(p_env, data, i_lsn); +#endif +} + +/*! + Reads i_blocks of mode2 sectors from cd device into data starting + from i_lsn. + */ +static driver_return_code_t +_read_mode1_sectors_solaris (void *p_user_data, void *p_data, lsn_t i_lsn, + bool b_form2, unsigned int i_blocks) +{ + _img_private_t *p_env = p_user_data; + unsigned int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < i_blocks; i++) { + if ( (retval = _read_mode1_sector_solaris (p_env, + ((char *)p_data) + (blocksize * i), + i_lsn + i, b_form2)) ) + return retval; + } + return DRIVER_OP_SUCCESS; +} + +/*! + Reads a single mode2 sector from cd device into data starting from lsn. + */ +static driver_return_code_t +_read_mode2_sector_solaris (void *p_user_data, void *p_data, lsn_t i_lsn, + bool b_form2) +{ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + struct cdrom_msf solaris_msf; + msf_t _msf; + int offset = 0; + struct cdrom_cdxa cd_read; + + _img_private_t *p_env = p_user_data; + + cdio_lba_to_msf (cdio_lsn_to_lba(i_lsn), &_msf); + solaris_msf.cdmsf_min0 = cdio_from_bcd8(_msf.m); + solaris_msf.cdmsf_sec0 = cdio_from_bcd8(_msf.s); + solaris_msf.cdmsf_frame0 = cdio_from_bcd8(_msf.f); + + if (p_env->gen.ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged < 75 + || (p_env->gen.ioctls_debugged < (30 * 75) + && p_env->gen.ioctls_debugged % 75 == 0) + || p_env->gen.ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %2.2d:%2.2d:%2.2d", + solaris_msf.cdmsf_min0, solaris_msf.cdmsf_sec0, + solaris_msf.cdmsf_frame0); + + p_env->gen.ioctls_debugged++; + + /* Using CDROMXA ioctl will actually use the same uscsi command + * as ATAPI, except we don't need to be root + */ + offset = CDIO_CD_XA_SYNC_HEADER; + cd_read.cdxa_addr = i_lsn; + cd_read.cdxa_data = buf; + cd_read.cdxa_length = 1; + cd_read.cdxa_format = CDROM_XA_SECTOR_DATA; + if (ioctl (p_env->gen.fd, CDROMCDXA, &cd_read) == -1) { + perror ("ioctl(..,CDROMCDXA,..)"); + return 1; + /* exit (EXIT_FAILURE); */ + } + + if (b_form2) + memcpy (p_data, buf + (offset-CDIO_CD_SUBHEADER_SIZE), M2RAW_SECTOR_SIZE); + else + memcpy (((char *)p_data), buf + offset, CDIO_CD_FRAMESIZE); + + return DRIVER_OP_SUCCESS; +} + +/*! + Reads i_blocks of mode2 sectors from cd device into data starting + from i_lsn. + */ +static driver_return_code_t +_read_mode2_sectors_solaris (void *p_user_data, void *data, lsn_t i_lsn, + bool b_form2, unsigned int i_blocks) +{ + _img_private_t *p_env = p_user_data; + unsigned int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < i_blocks; i++) { + if ( (retval = _read_mode2_sector_solaris (p_env, + ((char *)data) + (blocksize * i), + i_lsn + i, b_form2)) ) + return retval; + } + return 0; +} + + +/*! + Return the size of the CD in logical block address (LBA) units. + @return the size. On error return CDIO_INVALID_LSN. + */ +static lsn_t +get_disc_last_lsn_solaris (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + struct cdrom_tocentry tocent; + uint32_t size; + + tocent.cdte_track = CDIO_CDROM_LEADOUT_TRACK; + tocent.cdte_format = CDIO_CDROM_LBA; + if (ioctl (p_env->gen.fd, CDROMREADTOCENTRY, &tocent) == -1) + { + perror ("ioctl(CDROMREADTOCENTRY)"); + exit (EXIT_FAILURE); + } + + size = tocent.cdte_addr.lba; + + return size; +} + +/*! + Set the arg "key" with "value" in the source device. + Currently "source" and "access-mode" are valid keys. + "source" sets the source device in I/O operations + "access-mode" sets the the method of CD access + + DRIVER_OP_SUCCESS is returned if no error was found, + and nonzero if there as an error. +*/ +static driver_return_code_t +_set_arg_solaris (void *p_user_data, const char key[], const char value[]) +{ + _img_private_t *p_env = p_user_data; + + if (!strcmp (key, "source")) + { + if (!value) return DRIVER_OP_ERROR; + free (p_env->gen.source_name); + p_env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + p_env->access_mode = str_to_access_mode_solaris(key); + } + else return DRIVER_OP_ERROR; + + return DRIVER_OP_SUCCESS; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return true if successful or false if an error. +*/ +static bool +read_toc_solaris (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + int i; + + /* read TOC header */ + if ( ioctl(p_env->gen.fd, CDROMREADTOCHDR, &p_env->tochdr) == -1 ) { + cdio_warn("%s: %s\n", + "error in ioctl CDROMREADTOCHDR", strerror(errno)); + return false; + } + + p_env->gen.i_first_track = p_env->tochdr.cdth_trk0; + p_env->gen.i_tracks = p_env->tochdr.cdth_trk1; + + /* read individual tracks */ + for (i=p_env->gen.i_first_track; i<=p_env->gen.i_tracks; i++) { + struct cdrom_tocentry *p_toc = + &(p_env->tocent[i-p_env->gen.i_first_track]); + + p_toc->cdte_track = i; + p_toc->cdte_format = CDIO_CDROM_MSF; + if ( ioctl(p_env->gen.fd, CDROMREADTOCENTRY, p_toc) == -1 ) { + cdio_warn("%s %d: %s\n", + "error in ioctl CDROMREADTOCENTRY for track", + i, strerror(errno)); + return false; + } + + set_track_flags(&(p_env->gen.track_flags[i]), p_toc->cdte_ctrl); + } + + /* read the lead-out track */ + p_env->tocent[p_env->tochdr.cdth_trk1].cdte_track = CDIO_CDROM_LEADOUT_TRACK; + p_env->tocent[p_env->tochdr.cdth_trk1].cdte_format = CDIO_CDROM_MSF; + + if (ioctl(p_env->gen.fd, CDROMREADTOCENTRY, + &p_env->tocent[p_env->tochdr.cdth_trk1]) == -1 ) { + cdio_warn("%s: %s\n", + "error in ioctl CDROMREADTOCENTRY for lead-out", + strerror(errno)); + return false; + } + + p_env->gen.toc_init = true; + return true; +} + +/*! + Eject media in CD drive. If successful, as a side effect we + also free obj. + */ +static driver_return_code_t +eject_media_solaris (void *p_user_data) { + + _img_private_t *p_env = p_user_data; + int ret; + + close(p_env->gen.fd); + p_env->gen.fd = -1; + if (p_env->gen.fd > -1) { + if ((ret = ioctl(p_env->gen.fd, CDROMEJECT)) != 0) { + cdio_generic_free((void *) p_env); + cdio_warn ("CDROMEJECT failed: %s\n", strerror(errno)); + return DRIVER_OP_ERROR; + } else { + return DRIVER_OP_SUCCESS; + } + } + return DRIVER_OP_ERROR; +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +get_arg_solaris (void *p_user_data, const char key[]) +{ + _img_private_t *p_env = p_user_data; + + if (!strcmp (key, "source")) { + return p_env->gen.source_name; + } else if (!strcmp (key, "access-mode")) { + switch (p_env->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; +} + +/*! + Get the block size used in read requests, via ioctl. + @return the blocksize if > 0; error if <= 0 + */ +static int +get_blocksize_solaris (void *p_user_data) { + + _img_private_t *p_env = p_user_data; + int ret; + int i_blocksize; + + if ( !p_env || p_env->gen.fd <=0 ) return DRIVER_OP_UNINIT; + if ((ret = ioctl(p_env->gen.fd, CDROMGBLKMODE, &i_blocksize)) != 0) { + cdio_warn ("CDROMGBLKMODE failed: %s\n", strerror(errno)); + return DRIVER_OP_ERROR; + } else { + return i_blocksize; + } +} + +/*! + Return a string containing the default CD device if none is specified. + */ +char * +cdio_get_default_device_solaris(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 = calloc(1, 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; + } + /* Check if it could be a Solaris media*/ + if((stat(DEFAULT_CDIO_DEVICE, &stb) == 0) && S_ISDIR(stb.st_mode)) { + device = calloc(1, strlen(DEFAULT_CDIO_DEVICE) + 4); + sprintf(device, "%s/s0", DEFAULT_CDIO_DEVICE); + return device; + } + return strdup(DEFAULT_CDIO_DEVICE); +} + +/*! + Get disc type associated with cd object. +*/ + +static discmode_t +get_discmode_solaris (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + track_t i_track; + discmode_t discmode=CDIO_DISC_MODE_NO_INFO; + struct dk_minfo media; + int ret; + + /* Get the media info */ + if((ret = ioctl(p_env->gen.fd, DKIOCGMEDIAINFO, &media)) != 0) { + cdio_warn ("DKIOCGMEDIAINFO failed: %s\n", strerror(errno)); + return CDIO_DISC_MODE_NO_INFO; + } + switch(media.dki_media_type) { + case DK_CDROM: + case DK_CDR: + case DK_CDRW: + /* Do cdrom detection */ + break; + case DK_DVDROM: return CDIO_DISC_MODE_DVD_ROM; + case DK_DVDR: discmode = CDIO_DISC_MODE_DVD_R; + break; + case DK_DVDRAM: discmode = CDIO_DISC_MODE_DVD_RAM; + break; + case DK_DVDRW: + case DK_DVDRW+1: discmode = CDIO_DISC_MODE_DVD_RW; + break; + default: /* no valid match */ + return CDIO_DISC_MODE_NO_INFO; + } + + /* + GNU/Linux ioctl(.., CDROM_DISC_STATUS) does not return "CD DATA + Form 2" for SVCD's even though they are are form 2. + Issue a SCSI MMC-2 FULL TOC command first to try get more + accurate information. + */ + discmode = mmc_get_discmode(p_env->gen.cdio); + if (CDIO_DISC_MODE_NO_INFO != discmode) + return discmode; + + if((discmode == CDIO_DISC_MODE_DVD_RAM || + discmode == CDIO_DISC_MODE_DVD_RW || + discmode == CDIO_DISC_MODE_DVD_R)) { + /* Fallback to uscsi if we can */ + if(geteuid() == 0) + return get_discmode_solaris(p_user_data); + return discmode; + } + + if (!p_env->gen.toc_init) + read_toc_solaris (p_env); + + if (!p_env->gen.toc_init) + return CDIO_DISC_MODE_NO_INFO; + + for (i_track = p_env->gen.i_first_track; + i_track < p_env->gen.i_first_track + p_env->tochdr.cdth_trk1 ; + i_track ++) { + track_format_t track_fmt=get_track_format_solaris(p_env, i_track); + + switch(track_fmt) { + case TRACK_FORMAT_AUDIO: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DA; + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_XA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_DATA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DATA; + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_ERROR: + default: + discmode = CDIO_DISC_MODE_ERROR; + } + } + return discmode; +} + +/*! + Return the session number of the last on the CD. + + @param p_cdio the CD object to be acted upon. + @param i_last_session pointer to the session number to be returned. +*/ +static driver_return_code_t +get_last_session_solaris (void *p_user_data, + /*out*/ lsn_t *i_last_session_lsn) +{ + const _img_private_t *p_env = p_user_data; + int i_rc; + + i_rc = ioctl(p_env->gen.fd, CDROMREADOFFSET, &i_last_session_lsn); + if (0 == i_rc) { + return DRIVER_OP_SUCCESS; + } else { + cdio_warn ("ioctl CDROMREADOFFSET failed: %s\n", strerror(errno)); + return DRIVER_OP_ERROR; + } +} + +/*! + Get format of track. +*/ +static track_format_t +get_track_format_solaris(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if ( !p_env ) return TRACK_FORMAT_ERROR; + if (!p_env->gen.init) init_solaris(p_env); + if (!p_env->gen.toc_init) read_toc_solaris (p_user_data) ; + + if ( (i_track > p_env->gen.i_tracks+p_env->gen.i_first_track) + || i_track < p_env->gen.i_first_track) + return TRACK_FORMAT_ERROR; + + i_track -= p_env->gen.i_first_track; + + /* This is pretty much copied from the "badly broken" cdrom_count_tracks + in linux/cdrom.c. + */ + if (p_env->tocent[i_track].cdte_ctrl & CDROM_DATA_TRACK) { + if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_CDI_TRACK) + return TRACK_FORMAT_CDI; + else if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_XA_TRACK) + 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 +get_track_green_solaris(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if ( !p_env ) return false; + if (!p_env->gen.init) init_solaris(p_env); + if (!p_env->gen.toc_init) read_toc_solaris (p_env) ; + + if (i_track >= p_env->gen.i_tracks+p_env->gen.i_first_track + || i_track < p_env->gen.i_first_track) + return false; + + i_track -= p_env->gen.i_first_track; + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cd-info for a while. + */ + return ((p_env->tocent[i_track].cdte_ctrl & 2) != 0); +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + track_num in obj. Track numbers usually start at something + greater than 0, usually 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 entry. +*/ +static bool +get_track_msf_solaris(void *p_user_data, track_t i_track, msf_t *msf) +{ + _img_private_t *p_env = p_user_data; + + if (NULL == msf) return false; + + if (!p_env->gen.init) init_solaris(p_env); + if (!p_env->gen.toc_init) read_toc_solaris (p_env) ; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) + i_track = p_env->gen.i_tracks + p_env->gen.i_first_track; + + if (i_track > (p_env->gen.i_tracks+p_env->gen.i_first_track) + || i_track < p_env->gen.i_first_track) { + return false; + } else { + struct cdrom_tocentry *msf0 = &p_env->tocent[i_track-1]; + msf->m = cdio_to_bcd8(msf0->cdte_addr.msf.minute); + msf->s = cdio_to_bcd8(msf0->cdte_addr.msf.second); + msf->f = cdio_to_bcd8(msf0->cdte_addr.msf.frame); + return true; + } +} + +/*! + Get the block size used in read requests, via ioctl. + @return the blocksize if > 0; error if <= 0 + */ +static driver_return_code_t +set_blocksize_solaris (void *p_user_data, uint16_t i_blocksize) { + + _img_private_t *p_env = p_user_data; + int ret; + + if ( !p_env || p_env->gen.fd <=0 ) return DRIVER_OP_UNINIT; + if ((ret = ioctl(p_env->gen.fd, CDROMSBLKMODE, i_blocksize)) != 0) { + cdio_warn ("CDROMSBLKMODE failed: %s\n", strerror(errno)); + return DRIVER_OP_ERROR; + } else { + return DRIVER_OP_SUCCESS; + } +} + +/* Set CD-ROM drive speed */ +static driver_return_code_t +set_speed_solaris (void *p_user_data, int i_speed) +{ + const _img_private_t *p_env = p_user_data; + + if (!p_env) return DRIVER_OP_UNINIT; + return ioctl(p_env->gen.fd, CDROMSDRVSPEED, i_speed); +} + +#else +/*! + Return a string containing the default VCD device if none is specified. + */ +char * +cdio_get_default_device_solaris(void) +{ + return strdup(DEFAULT_CDIO_DEVICE); +} + +#endif /* HAVE_SOLARIS_CDROM */ + +/*! + Close tray on CD-ROM. + + @param psz_device the CD-ROM drive to be closed. + +*/ +driver_return_code_t +close_tray_solaris (const char *psz_device) +{ +#ifdef HAVE_SOLARIS_CDROM + int i_rc; + int fd = open (psz_device, O_RDONLY|O_NONBLOCK); + + if ( fd > -1 ) { + i_rc = DRIVER_OP_SUCCESS; + if((i_rc = ioctl(fd, CDROMSTART)) != 0) { + cdio_warn ("ioctl CDROMSTART failed: %s\n", strerror(errno)); + i_rc = DRIVER_OP_ERROR; + } + close(fd); + } else + i_rc = DRIVER_OP_ERROR; + return i_rc; +#else + return DRIVER_OP_NO_DRIVER; +#endif /*HAVE_SOLARIS_CDROM*/ +} + +/*! + Return an array of strings giving possible CD devices. + */ +char ** +cdio_get_devices_solaris (void) +{ +#ifndef HAVE_SOLARIS_CDROM + return NULL; +#else + char volpath[256]; + struct stat st; + char **drives = NULL; + unsigned int i_files=0; +#ifdef HAVE_GLOB_H + unsigned int i; + glob_t globbuf; + + globbuf.gl_offs = 0; + glob("/vol/dev/aliases/cdrom*", GLOB_DOOFFS, NULL, &globbuf); + for (i=0; iaccess_mode = _AM_SUN_CTRL_SCSI; + _data->gen.init = false; + _data->gen.fd = -1; + _data->gen.toc_init = false; + _data->gen.b_cdtext_init = false; + _data->gen.b_cdtext_error = false; + + if (NULL == psz_orig_source) { + psz_source = cdio_get_default_device_solaris(); + if (NULL == psz_source) return NULL; + _set_arg_solaris(_data, "source", psz_source); + free(psz_source); + } else { + if (cdio_is_device_generic(psz_orig_source)) + _set_arg_solaris(_data, "source", psz_orig_source); + else { + /* The below would be okay if all device drivers worked this way. */ +#if 0 + cdio_info ("source %s is not a device", psz_orig_source); +#endif + free(_data); + return NULL; + } + } + + ret = cdio_new ( (void *) _data, &_funcs ); + if (ret == NULL) return NULL; + + ret->driver_id = DRIVER_SOLARIS; + + if (init_solaris(_data)) + return ret; + else { + cdio_generic_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/driver/track.c b/lib/driver/track.c new file mode 100644 index 00000000..96dd9b2d --- /dev/null +++ b/lib/driver/track.c @@ -0,0 +1,361 @@ +/* + $Id: track.c,v 1.7 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/*! Track-related routines. */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "cdio_private.h" + +const char *track_format2str[6] = + { + "audio", "CD-i", "XA", "data", "PSX", "error" + }; + +/* Variables to hold debugger-helping enumerations */ +enum cdio_track_enums; + +/*! + Return the number of the first track. + CDIO_INVALID_TRACK is returned on error. +*/ +track_t +cdio_get_first_track_num (const CdIo_t *p_cdio) +{ + if (NULL == p_cdio) return CDIO_INVALID_TRACK; + + if (p_cdio->op.get_first_track_num) { + return p_cdio->op.get_first_track_num (p_cdio->env); + } else { + return CDIO_INVALID_TRACK; + } +} + +/*! + Return the last track number. + CDIO_INVALID_TRACK is returned on error. +*/ +track_t +cdio_get_last_track_num (const CdIo_t *p_cdio) +{ + if (NULL == p_cdio) return CDIO_INVALID_TRACK; + { + const track_t i_first_track = cdio_get_first_track_num(p_cdio); + if ( CDIO_INVALID_TRACK != i_first_track ) { + const track_t i_tracks = cdio_get_num_tracks(p_cdio); + if ( CDIO_INVALID_TRACK != i_tracks ) + return i_first_track + i_tracks - 1; + } + return CDIO_INVALID_TRACK; + } +} + +/*! Return number of channels in track: 2 or 4; -2 if not + implemented or -1 for error. + Not meaningful if track is not an audio track. +*/ +int +cdio_get_track_channels(const CdIo_t *p_cdio, track_t i_track) +{ + if (p_cdio->op.get_track_channels) { + return p_cdio->op.get_track_channels (p_cdio->env, i_track); + } else { + return -2; + } +} + +/*! Return copy protection status on a track. Is this meaningful + if not an audio track? +*/ +track_flag_t +cdio_get_track_copy_permit(const CdIo_t *p_cdio, track_t i_track) +{ + if (p_cdio->op.get_track_copy_permit) { + return p_cdio->op.get_track_copy_permit (p_cdio->env, i_track); + } else { + return CDIO_TRACK_FLAG_UNKNOWN; + } +} + +/*! + Get format of track. +*/ +track_format_t +cdio_get_track_format(const CdIo_t *p_cdio, track_t i_track) +{ + if (!p_cdio) return TRACK_FORMAT_ERROR; + + if (p_cdio->op.get_track_format) { + return p_cdio->op.get_track_format (p_cdio->env, i_track); + } else { + return TRACK_FORMAT_ERROR; + } +} +/*! + Return the Joliet level recognized for p_cdio. +*/ +uint8_t +cdio_get_joliet_level(const CdIo_t *p_cdio) +{ + if (!p_cdio) return 0; + { + const generic_img_private_t *p_env + = (generic_img_private_t *) (p_cdio->env); + return p_env->i_joliet_level; + } +} + +/*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. +*/ +track_t +cdio_get_num_tracks (const CdIo_t *p_cdio) +{ + if (p_cdio == NULL) return CDIO_INVALID_TRACK; + + if (p_cdio->op.get_num_tracks) { + return p_cdio->op.get_num_tracks (p_cdio->env); + } else { + return CDIO_INVALID_TRACK; + } +} + +/*! Find the track which contans lsn. + CDIO_INVALID_TRACK is returned if the lsn outside of the CD or + if there was some error. + + If the lsn is before the pregap of the first track 0 is returned. + Otherwise we return the track that spans the lsn. +*/ +track_t +cdio_get_track(const CdIo_t *p_cdio, lsn_t lsn) +{ + if (!p_cdio) return CDIO_INVALID_TRACK; + + { + track_t i_low_track = cdio_get_first_track_num(p_cdio); + track_t i_high_track = cdio_get_last_track_num(p_cdio)+1; /* LEADOUT */ + + if (CDIO_INVALID_TRACK == i_low_track + || CDIO_INVALID_TRACK == i_high_track ) return CDIO_INVALID_TRACK; + + if (lsn < cdio_get_track_lsn(p_cdio, i_low_track)) + return 0; /* We're in the pre-gap of first track */ + + if (lsn > cdio_get_track_lsn(p_cdio, i_high_track)) + return CDIO_INVALID_TRACK; /* We're beyond the end. */ + + do { + const track_t i_mid = (i_low_track + i_high_track) / 2; + const lsn_t i_mid_lsn = cdio_get_track_lsn(p_cdio, i_mid); + if (lsn <= i_mid_lsn) i_high_track = i_mid - 1; + if (lsn >= i_mid_lsn) i_low_track = i_mid + 1; + } while ( i_low_track <= i_high_track ); + + return (i_low_track > i_high_track + 1) + ? i_high_track + 1 : i_high_track; + } +} + +/*! + 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_t *p_cdio, track_t i_track) +{ + if (p_cdio == NULL) { + return false; + } + + if (p_cdio->op.get_track_green) { + return p_cdio->op.get_track_green (p_cdio->env, i_track); + } else { + return false; + } +} + +/*! + Return the starting LBA for track number + track_num in cdio. Tracks numbers start at 1. + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + CDIO_INVALID_LBA is returned on error. +*/ +lba_t +cdio_get_track_lba(const CdIo_t *p_cdio, track_t i_track) +{ + if (!p_cdio) return CDIO_INVALID_LBA; + + if (p_cdio->op.get_track_lba) { + return p_cdio->op.get_track_lba (p_cdio->env, i_track); + } else { + msf_t msf; + if (p_cdio->op.get_track_msf) + if (cdio_get_track_msf(p_cdio, i_track, &msf)) + return cdio_msf_to_lba(&msf); + return CDIO_INVALID_LBA; + } +} + +/*! + Return the starting LSN for track number + i_track in cdio. Tracks numbers start at 1. + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + CDIO_INVALID_LSN is returned on error. +*/ +lsn_t +cdio_get_track_lsn(const CdIo_t *p_cdio, track_t i_track) +{ + if (p_cdio == NULL) return CDIO_INVALID_LSN; + + if (p_cdio->op.get_track_lba) { + return cdio_lba_to_lsn(p_cdio->op.get_track_lba (p_cdio->env, i_track)); + } else { + msf_t msf; + if (cdio_get_track_msf(p_cdio, i_track, &msf)) + return cdio_msf_to_lsn(&msf); + return CDIO_INVALID_LSN; + } +} + +/*! + Return the International Standard Recording Code (ISRC) for track number + i_track in p_cdio. Track numbers start at 1. + + Note: string is malloc'd so caller has to free() the returned + string when done with it. +*/ +char * +cdio_get_track_isrc (const CdIo_t *p_cdio, track_t i_track) +{ + if (p_cdio == NULL) return NULL; + + if (p_cdio->op.get_track_isrc) { + return p_cdio->op.get_track_isrc (p_cdio->env, i_track); + } else { + return NULL; + } +} + +/*! + Return the starting LBA for the pregap for track number + i_track in cdio. Track numbers start at 1. + CDIO_INVALID_LBA is returned on error. +*/ +lba_t +cdio_get_track_pregap_lba(const CdIo_t *p_cdio, track_t i_track) +{ + if (p_cdio == NULL) return CDIO_INVALID_LBA; + + if (p_cdio->op.get_track_pregap_lba) { + return p_cdio->op.get_track_pregap_lba (p_cdio->env, i_track); + } else { + return CDIO_INVALID_LBA; + } +} + +/*! + Return the starting LSN for the pregap for track number + i_track in cdio. Track numbers start at 1. + CDIO_INVALID_LSN is returned on error. +*/ +lsn_t +cdio_get_track_pregap_lsn(const CdIo_t *p_cdio, track_t i_track) +{ + return cdio_lba_to_lsn(cdio_get_track_pregap_lba(p_cdio, i_track)); +} + +/*! + Return the ending LSN for track number + i_track in cdio. CDIO_INVALID_LSN is returned on error. +*/ +lsn_t +cdio_get_track_last_lsn(const CdIo_t *p_cdio, track_t i_track) +{ + lsn_t lsn = cdio_get_track_lsn(p_cdio, i_track+1); + + if (CDIO_INVALID_LSN == lsn) return CDIO_INVALID_LSN; + /* Safe, we've always the leadout. */ + return lsn - 1; +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + i_track in cdio. Track numbers start at 1. + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +bool +cdio_get_track_msf(const CdIo_t *p_cdio, track_t i_track, /*out*/ msf_t *msf) +{ + if (!p_cdio) return false; + + if (p_cdio->op.get_track_msf) { + return p_cdio->op.get_track_msf (p_cdio->env, i_track, msf); + } else if (p_cdio->op.get_track_lba) { + lba_t lba = p_cdio->op.get_track_lba (p_cdio->env, i_track); + if (lba == CDIO_INVALID_LBA) return false; + cdio_lba_to_msf(lba, msf); + return true; + } else { + return false; + } +} + +/*! Return copy protection status on a track. Is this meaningful + if not an audio track? +*/ +track_flag_t +cdio_get_track_preemphasis(const CdIo *p_cdio, track_t i_track) +{ + if (p_cdio->op.get_track_preemphasis) { + return p_cdio->op.get_track_preemphasis (p_cdio->env, i_track); + } else { + return CDIO_TRACK_FLAG_UNKNOWN; + } +} + +/*! + Return the number of sectors between this track an the next. This + includes any pregap sectors before the start of the next track. + Tracks start at 1. + 0 is returned if there is an error. +*/ +unsigned int +cdio_get_track_sec_count(const CdIo_t *p_cdio, track_t i_track) +{ + const track_t i_tracks = cdio_get_num_tracks(p_cdio); + + if (i_track >=1 && i_track <= i_tracks) + return ( cdio_get_track_lba(p_cdio, i_track+1) + - cdio_get_track_lba(p_cdio, i_track) ); + return 0; +} diff --git a/lib/driver/utf8.c b/lib/driver/utf8.c new file mode 100644 index 00000000..6b7d3703 --- /dev/null +++ b/lib/driver/utf8.c @@ -0,0 +1,210 @@ +/* + $Id: utf8.c,v 1.5 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2006, 2008 Burkhard Plaum + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* UTF-8 support */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_JOLIET +#ifdef HAVE_STRING_H +# include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_ICONV +# include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +#include + +#include + + +struct cdio_charset_coverter_s + { + iconv_t ic; + }; + +cdio_charset_coverter_t * +cdio_charset_converter_create(const char * src_charset, + const char * dst_charset) + { + cdio_charset_coverter_t * ret; + ret = calloc(1, sizeof(*ret)); + ret->ic = iconv_open(dst_charset, src_charset); + return ret; + } + +#if 0 +static void bgav_hexdump(uint8_t * data, int len, int linebreak) + { + int i; + int bytes_written = 0; + int imax; + + while(bytes_written < len) + { + imax = (bytes_written + linebreak > len) ? len - bytes_written : linebreak; + for(i = 0; i < imax; i++) + fprintf(stderr, "%02x ", data[bytes_written + i]); + for(i = imax; i < linebreak; i++) + fprintf(stderr, " "); + for(i = 0; i < imax; i++) + { + if(!(data[bytes_written + i] & 0x80) && (data[bytes_written + i] >= 32)) + fprintf(stderr, "%c", data[bytes_written + i]); + else + fprintf(stderr, "."); + } + bytes_written += imax; + fprintf(stderr, "\n"); + } + } +#endif + +void cdio_charset_converter_destroy(cdio_charset_coverter_t*cnv) + { + iconv_close(cnv->ic); + free(cnv); + } + +#define BYTES_INCREMENT 16 + +static bool +do_convert(iconv_t cd, char * src, int src_len, + char ** dst, int *dst_len) + { + char * ret; + + char *inbuf; + char *outbuf; + int alloc_size; + int output_pos; + size_t inbytesleft; + size_t outbytesleft; + + if(src_len < 0) + src_len = strlen(src); +#if 0 + fprintf(stderr, "Converting:\n"); + bgav_hexdump(src, src_len, 16); +#endif + alloc_size = src_len + BYTES_INCREMENT; + + inbytesleft = src_len; + + /* We reserve space here to add a final '\0' */ + outbytesleft = alloc_size-1; + + ret = malloc(alloc_size); + + inbuf = src; + outbuf = ret; + + while(1) + { + + if(iconv(cd, &inbuf, &inbytesleft, + &outbuf, &outbytesleft) == (size_t)-1) + { + switch(errno) + { + case E2BIG: + output_pos = (int)(outbuf - ret); + + alloc_size += BYTES_INCREMENT; + outbytesleft += BYTES_INCREMENT; + + ret = realloc(ret, alloc_size); + if (ret == NULL) + { + fprintf(stderr, "Can't realloc(%d).\n", alloc_size); + return false; + } + outbuf = ret + output_pos; + break; + default: + fprintf(stderr, "Iconv failed: %s\n", strerror(errno)); + if (ret != NULL) + free(ret); + return false; + break; + } + } + if(!inbytesleft) + break; + } + /* Zero terminate */ + *outbuf = '\0'; + + /* Set return values */ + *dst = ret; + if(dst_len) + *dst_len = (int)(outbuf - ret); +#if 0 + fprintf(stderr, "Conversion done, src:\n"); + bgav_hexdump(src, src_len, 16); + fprintf(stderr, "dst:\n"); + bgav_hexdump((uint8_t*)(ret), (int)(outbuf - ret), 16); +#endif + return true; + } + +bool cdio_charset_convert(cdio_charset_coverter_t*cnv, + char * src, int src_len, + char ** dst, int * dst_len) + { + return do_convert(cnv->ic, src, src_len, dst, dst_len); + } + + + +bool cdio_charset_from_utf8(cdio_utf8_t * src, char ** dst, + int * dst_len, const char * dst_charset) + { + iconv_t ic; + bool result; + ic = iconv_open(dst_charset, "UTF-8"); + result = do_convert(ic, src, -1, dst, dst_len); + iconv_close(ic); + return result; + } + + + + +bool cdio_charset_to_utf8(char *src, size_t src_len, cdio_utf8_t **dst, + const char * src_charset) + { + iconv_t ic; + bool result; + ic = iconv_open("UTF-8", src_charset); + result = do_convert(ic, src, src_len, dst, NULL); + iconv_close(ic); + return result; + } +#endif /* HAVE_JOLIET */ diff --git a/lib/driver/util.c b/lib/driver/util.c new file mode 100644 index 00000000..629057a6 --- /dev/null +++ b/lib/driver/util.c @@ -0,0 +1,189 @@ +/* + $Id: util.c,v 1.6 2008/04/22 15:29:12 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2008 Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H // readlink +#include +#endif + +#ifdef HAVE_INTTYPES_H +#include "inttypes.h" +#endif + +#include "cdio_assert.h" +#include +#include + +static const char _rcsid[] = "$Id: util.c,v 1.6 2008/04/22 15:29:12 karl 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_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 = calloc (1, sizeof (char *) * (n+1)); + + n = 0; + while((p = strtok(n ? NULL : _str, _delim)) != NULL) + strv[n++] = strdup(p); + + free(_str); + + return strv; +} + +void * +_cdio_memdup (const void *mem, size_t count) +{ + void *new_mem = NULL; + + if (mem) + { + new_mem = calloc (1, 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 +cdio_to_bcd8 (uint8_t n) +{ + /*cdio_assert (n < 100);*/ + + return ((n/10)<<4) | (n%10); +} + +uint8_t +cdio_from_bcd8(uint8_t p) +{ + return (0xf & p)+(10*(p >> 4)); +} + +/*! + Follow symlinks until we have the real device file + (idea taken from libunieject). +*/ + +void cdio_follow_symlink (const char * src, char * dst) { +#ifdef HAVE_READLINK + char tmp_src[PATH_MAX+1]; + char tmp_dst[PATH_MAX+1]; + + int len; + + strcpy(tmp_src, src); + while(1) { + len = readlink(tmp_src, tmp_dst, PATH_MAX); + if(len < 0) { + strncpy(dst, tmp_src, PATH_MAX); + return; + } + else { + tmp_dst[len] = '\0'; + strncpy(tmp_src, tmp_dst, PATH_MAX); + } + } +#else + strncpy(dst, src, PATH_MAX); +#endif + +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/iso9660/.cvsignore b/lib/iso9660/.cvsignore new file mode 100644 index 00000000..ed5ff996 --- /dev/null +++ b/lib/iso9660/.cvsignore @@ -0,0 +1,9 @@ +.deps +.libs +Makefile +Makefile.in +*.o +*.lo +*.la +*.la.ver + diff --git a/lib/iso9660/.gitignore b/lib/iso9660/.gitignore new file mode 100644 index 00000000..f53478a0 --- /dev/null +++ b/lib/iso9660/.gitignore @@ -0,0 +1,3 @@ +/.deps +/Makefile +/Makefile.in diff --git a/lib/iso9660/Makefile.am b/lib/iso9660/Makefile.am new file mode 100644 index 00000000..56611332 --- /dev/null +++ b/lib/iso9660/Makefile.am @@ -0,0 +1,150 @@ +# $Id: Makefile.am,v 1.18 2008/10/20 01:25:15 rocky Exp $ +# +# Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 +# 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +######################################################## +# Things to make the libiso9660 library +######################################################## +# +# From libtool documentation amended with guidance from N. Boullis: +# +# 1. Start with version information of `0:0:0' for each libtool library. +# +# 2. It is probably not a good idea to update the version information +# several times between public releases, but rather once per public +# release. (This seems to be more an aesthetic consideration than +# a hard technical one.) +# +# 3. If the library source code has changed at all since the last +# update, then increment REVISION (`C:R:A' becomes `C:R+1:A'). +# +# 4. If any interfaces have been added, removed, or changed since the +# last update, increment CURRENT, and set REVISION to 0. +# +# 5. If any interfaces have been added since the last public release, +# then increment AGE. +# +# 6. If any interfaces have been removed or changed since the last +# public release, then set AGE to 0. A changed interface means an +# incompatibility with previous versions. + +libiso9660_la_CURRENT = 7 +libiso9660_la_REVISION = 0 +libiso9660_la_AGE = 0 + +EXTRA_DIST = libiso9660.sym + +noinst_HEADERS = iso9660_private.h + +lib_LTLIBRARIES = libiso9660.la + +if ENABLE_ROCK +rock_src = rock.c +else +rock_src = +endif + +libiso9660_la_SOURCES = \ + iso9660.c \ + iso9660_private.h \ + iso9660_fs.c \ + $(rock_src) \ + xa.c + +libiso9660_la_LIBADD = @LIBCDIO_LIBS@ +libiso9660_la_ldflags = -version-info $(libiso9660_la_CURRENT):$(libiso9660_la_REVISION):$(libiso9660_la_AGE) @LT_NO_UNDEFINED@ +libiso9660_la_dependencies = libcdio.la + +INCLUDES = $(LIBCDIO_CFLAGS) + +######################################################## +# Things to version the symbols in the libraries +######################################################## + +# An explanation of the versioning problem from Nicolas Boullis and +# the versioned symbol solution he uses below... +# +# Currently, libvcdinfo uses the cdio_open function from libcdio. +# Let's imagine a program foobar that uses both the vcdinfo_open +# function from libvcdinfo and the cdio_open function from libcdio. + +# Currently, libcdio has SONAME libcdio.so.0, libvcdinfo has SONAME +# libvcdinfo.so.0 and requires libcdio.so.0, and foobar requires both +# libvcdinfo.so.0 and libcdio.so.0. Everything looks fine. +# +# Now, for some reason, you decide to change the cdio_open function. +# That's your right, but you have to bump the CURRENT version and (if I +# understand it correctly, athough this is not that clear in libtool's +# documentation) set the AGE to 0. Anyway, this bumps the SONAME, which is +# sane since the interface changes incompatibly. + +# Now, you have a new libcdio with SONAME libcdio.so.1. But libvcdinfo and +# foobar still require libcdio.so.0. Everything is still fine. + +# Now, after some minor changes, the author of foobar recompiles foobar. +# Then, foobar now requires libvcdinfo.so.0 and libcdio.so.1. And foobar +# now segfaults... + +# What is happening? When you run foobar, if brings both libvcdinfo.so.0 +# and libcdio.so.1, but libvcdinfo.so.0 also brings libcdio.so.0. So you +# have both libcdio.so.0 and libcdio.so.1 that bring their symbols to the +# global namespace. Hence, you have to incompatible versions of the +# cdio_open function in the name space. When foobar calls cdio_open, it +# may choose the wrong function, and segfaults... + +# With versioned symbols, the cdio_open function from libcdio.so.0 may be +# known as (something that looks like) cdio_open@@CDIO_0. An the cdio_open +# function from libcdio.so.1 as cdio_open@@CDIO_1. Both versions of +# libcdio would still be brought in by the most recent foobar, but foobar +# (and libvcdinfo) know which versioned function to use and then use the +# good one. + + +# This is some simple versioning where every symbol is versioned with +# something that looks like the SONAME of the library. More complex (and +# better) versioning is possible; it is for example what is used by glibc. +# But good complex versioning is something that requires much more +# work... + + +# The below is a impliments symbol versioning. First of all, I +# compute MAJOR as CURENT - AGE; that is what is used within libtool +# (at least on GNU/Linux systems) for the number in the SONAME. The +# nm command gives the list of symbols known in each of the object +# files that will be part of the shared library. And the sed command +# extracts from this list those symbols that will be shared. (This sed +# command comes from libtool.) + +libiso9660_la_MAJOR = $(shell expr $(libiso9660_la_CURRENT) - $(libiso9660_la_AGE)) +if BUILD_VERSIONED_LIBS +libiso9660_la_LDFLAGS = $(libiso9660_la_ldflags) -Wl,--version-script=libiso9660.la.ver +libiso9660_la_DEPENDENCIES = $(libcdio9660_la_dependencies) libiso9660.la.ver + +libiso9660.la.ver: $(libiso9660_la_OBJECTS) $(srcdir)/libiso9660.sym + echo 'ISO9660_$(libiso9660_la_MAJOR) {' > $@ + objs=`for obj in $(libiso9660_la_OBJECTS); do sed -ne "s/^pic_object='\(.*\)'$$/\1/p" $$obj; done`; + if test -n "$$objs" ; then \ + nm $${objs} | sed -n -e 's/^.*[ ][ABCDGIRSTW][ABCDGIRSTW]*[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$$/\1/p' | sort -u | { first=true; while read symbol; do if grep -q "^$${symbol}\$$" $(srcdir)/libiso9660.sym; then if test $$first = true; then echo " global:"; first=false; fi; echo " $${symbol};"; fi; done; } >> $@; \ + nm $${objs} | sed -n -e 's/^.*[ ][ABCDGIRSTW][ABCDGIRSTW]*[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$$/\1/p' | sort -u | { first=true; while read symbol; do if grep -q "^$${symbol}\$$" $(srcdir)/libiso9660.sym; then :; else if test $$first = true; then echo " local:"; first=false; fi; echo " $${symbol};"; fi; done; } >> $@; \ + fi + echo '};' >> $@ + +MOSTLYCLEANFILES = libiso9660.la.ver +else +libiso9660_la_LDFLAGS = $(libiso9660_la_ldflags) +libiso9660_la_DEPENDENCIES = $(libcdio9660_la_dependencies) +endif diff --git a/lib/iso9660/iso9660.c b/lib/iso9660/iso9660.c new file mode 100644 index 00000000..0f58bfbc --- /dev/null +++ b/lib/iso9660/iso9660.c @@ -0,0 +1,1241 @@ +/* + $Id: iso9660.c,v 1.41 2008/06/25 08:01:54 rocky Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 + Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/*! String inside frame which identifies an ISO 9660 filesystem. This + string is the "id" field of an iso9660_pvd_t or an iso9660_svd_t. + Note should come *before* #include which does + a #define of this name. +*/ +const char ISO_STANDARD_ID[] = {'C', 'D', '0', '0', '1'}; + +/* Private headers */ +#include "iso9660_private.h" +#include "cdio_assert.h" + +/* Public headers */ +#include +#include +#include + +#include +#include +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +#ifndef HAVE_SETENV +static int +setenv(const char *envname, const char *envval, int overwrite) +{ + return -1; +} +#endif + +#ifndef HAVE_UNSETENV +static int +unsetenv(const char *envname) +{ + return -1; +} +#endif + +#ifndef HAVE_TIMEGM +static time_t +timegm(struct tm *tm) +{ + time_t ret; + char *tz; + + tz = getenv("TZ"); + setenv("TZ", "", 1); + tzset(); + ret = mktime(tm); + if (tz) + setenv("TZ", tz, 1); + else + unsetenv("TZ"); + tzset(); + return ret; +} +#endif + +#ifndef HAVE_GMTIME_R +static struct tm * +gmtime_r(const time_t *timer, struct tm *result) +{ + struct tm *tmp = gmtime(timer); + + if (tmp) { + *result = *tmp; + return result; + } + return tmp; +} +#endif + +#ifndef HAVE_LOCALTIME_R +static struct tm * +localtime_r(const time_t *timer, struct tm *result) +{ + struct tm *tmp = localtime(timer); + + if (tmp) { + *result = *tmp; + return result; + } + return tmp; +} +#endif + +static const char _rcsid[] = "$Id: iso9660.c,v 1.41 2008/06/25 08:01:54 rocky Exp $"; + +/* Variables to hold debugger-helping enumerations */ +enum iso_enum1_s iso_enums1; +enum iso_flag_enum_s iso_flag_enums; +enum iso_vd_enum_s iso_vd_enums; +enum iso_extension_enum_s iso_extension_enums; + +/* some parameters... */ +#define SYSTEM_ID "CD-RTOS CD-BRIDGE" +#define VOLUME_SET_ID "" + +/*! + Change trailing blanks in str to nulls. Str has a maximum size of + n characters. +*/ +static char * +strip_trail (const char str[], size_t n) +{ + static char buf[1025]; + int j; + + cdio_assert (n < 1024); + + strncpy (buf, str, n); + buf[n] = '\0'; + + for (j = strlen (buf) - 1; j >= 0; j--) + { + if (buf[j] != ' ') + break; + + buf[j] = '\0'; + } + + return buf; +} + +static void +pathtable_get_size_and_entries(const void *pt, unsigned int *size, + unsigned int *entries); + +/*! + Get time structure from structure in an ISO 9660 directory index + record. Even though tm_wday and tm_yday fields are not explicitly in + idr_date, the are calculated from the other fields. + + If tm is to reflect the localtime set b_localtime true, otherwise + tm will reported in GMT. +*/ +bool +iso9660_get_dtime (const iso9660_dtime_t *idr_date, bool b_localtime, + /*out*/ struct tm *p_tm) +{ + if (!idr_date) return false; + + /* + Section 9.1.5 of ECMA 119 says: + If all seven numbers are zero, it shall mean that the date and + time are not specified. + + HACK: However we've seen it happen that everything except gmtoff + is zero and the expected date is the beginning of the epoch. So + we accept 6 numbers being zero. I'm also not sure if using the + beginning of the Epoch is also the right thing to do either. + */ + + if ( 0 == idr_date->dt_year && 0 == idr_date->dt_month && + 0 == idr_date->dt_day && 0 == idr_date->dt_hour && + 0 == idr_date->dt_minute && 0 == idr_date->dt_second ) { + time_t t = 0; + struct tm temp_tm; + localtime_r(&t, &temp_tm); + + memcpy(p_tm, &temp_tm, sizeof(struct tm)); + return true; + } + + memset(p_tm, 0, sizeof(struct tm)); + + p_tm->tm_year = idr_date->dt_year; + p_tm->tm_mon = idr_date->dt_month - 1; + p_tm->tm_mday = idr_date->dt_day; + p_tm->tm_hour = idr_date->dt_hour; + p_tm->tm_min = idr_date->dt_minute; + p_tm->tm_sec = idr_date->dt_second - idr_date->dt_gmtoff * (15 * 60); + p_tm->tm_isdst = -1; /* information not available */ + + /* Recompute tm_wday and tm_yday via mktime. mktime will also renormalize + date values to account for the timezone offset. */ + { + time_t t = 0; + struct tm temp_tm; + + t = timegm(p_tm); + + if (b_localtime) + localtime_r(&t, &temp_tm); + else + gmtime_r(&t, &temp_tm); + + memcpy(p_tm, &temp_tm, sizeof(struct tm)); + } + + + return true; +} + +/* + A note regarding the strange strtol() testing below as pointed out SMS. + From man strtol: + + If an underflow occurs, strtol() returns LONG_MIN. If an overflow + occurs, strtol() returns LONG_MAX. In both cases, errno is set to + ERANGE. + + This implies that one should only look at errno if the value is + LONG_MIN or LONG_MAX. +*/ + +#define set_ltime_field(TM_FIELD, LT_FIELD, ADD_CONSTANT) \ + { \ + char num[10]; long tmp; \ + memcpy(num, p_ldate->LT_FIELD, sizeof(p_ldate->LT_FIELD)); \ + num[sizeof(p_ldate->LT_FIELD)] = '\0'; \ + errno = 0; \ + tmp = strtol(num, \ + (char **)NULL, 10); \ + if ( tmp < INT_MIN || tmp > INT_MAX || \ + ((unsigned long)tmp + ADD_CONSTANT) > INT_MAX || \ + (tmp + ADD_CONSTANT) < INT_MIN ) \ + return false; \ + p_tm->TM_FIELD = tmp + ADD_CONSTANT; \ + } + +/*! + Get "long" time in format used in ISO 9660 primary volume descriptor + from a Unix time structure. +*/ +bool +iso9660_get_ltime (const iso9660_ltime_t *p_ldate, + /*out*/ struct tm *p_tm) +{ + if (!p_tm) return false; + memset(p_tm, 0, sizeof(struct tm)); + set_ltime_field(tm_year, lt_year, -1900); + set_ltime_field(tm_mon, lt_month, -1); + set_ltime_field(tm_mday, lt_day, 0); + set_ltime_field(tm_hour, lt_hour, 0); + set_ltime_field(tm_min, lt_minute, 0); + set_ltime_field(tm_sec, lt_second, 0); + p_tm->tm_isdst= -1; /* information not available */ +#ifndef HAVE_TM_GMTOFF + p_tm->tm_sec += p_ldate->lt_gmtoff * (15 * 60); +#endif + + /* Recompute tm_wday and tm_yday via mktime. mktime will also renormalize + date values to account for the timezone offset. */ + { + time_t t; + struct tm temp_tm; + + t = mktime(p_tm); + + localtime_r(&t, &temp_tm); + + memcpy(p_tm, &temp_tm, sizeof(struct tm)); + } + p_tm->tm_isdst= -1; /* information not available */ +#ifdef HAVE_TM_GMTOFF + p_tm->tm_gmtoff = -p_ldate->lt_gmtoff * (15 * 60); +#endif + return true; +} + + +/*! + Set time in format used in ISO 9660 directory index record + from a Unix time structure. */ +void +iso9660_set_dtime (const struct tm *p_tm, /*out*/ iso9660_dtime_t *p_idr_date) +{ + memset (p_idr_date, 0, 7); + + if (!p_tm) return; + + p_idr_date->dt_year = p_tm->tm_year; + p_idr_date->dt_month = p_tm->tm_mon + 1; + p_idr_date->dt_day = p_tm->tm_mday; + p_idr_date->dt_hour = p_tm->tm_hour; + p_idr_date->dt_minute = p_tm->tm_min; + p_idr_date->dt_second = p_tm->tm_sec; + +#ifdef HAVE_TM_GMTOFF + /* The ISO 9660 timezone is in the range -48..+52 and each unit + represents a 15-minute interval. */ + p_idr_date->dt_gmtoff = p_tm->tm_gmtoff / (15 * 60); + + if (p_idr_date->dt_gmtoff < -48 ) { + + cdio_warn ("Converted ISO 9660 timezone %d is less than -48. Adjusted", + p_idr_date->dt_gmtoff); + p_idr_date->dt_gmtoff = -48; + } else if (p_idr_date->dt_gmtoff > 52) { + cdio_warn ("Converted ISO 9660 timezone %d is over 52. Adjusted", + p_idr_date->dt_gmtoff); + p_idr_date->dt_gmtoff = 52; + } +#else + p_idr_date->dt_gmtoff = 0; + + if (p_tm->tm_isdst > 0) p_idr_date->dt_gmtoff -= 4; +#endif +} + +/*! + Set "long" time in format used in ISO 9660 primary volume descriptor + from a Unix time structure. */ +void +iso9660_set_ltime (const struct tm *p_tm, /*out*/ iso9660_ltime_t *pvd_date) +{ + char *_pvd_date = (char *) pvd_date; + + memset (_pvd_date, '0', 16); + pvd_date->lt_gmtoff = (iso712_t) 0; /* Start out with time zone GMT. */ + + if (!p_tm) return; + + snprintf(_pvd_date, 17, + "%4.4d%2.2d%2.2d" "%2.2d%2.2d%2.2d" "%2.2d", + p_tm->tm_year + 1900, p_tm->tm_mon + 1, p_tm->tm_mday, + p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec, + 0 /* 1/100 secs */ ); + +#ifndef HAVE_TM_GMTOFF + /* Adjust for daylight savings time. */ + if (p_tm->tm_isdst > 0) _pvd_date[16] -= (iso712_t) 4; + +#else + /* Set time zone in 15-minute interval encoding. */ + pvd_date->lt_gmtoff -= p_tm->tm_gmtoff / (15 * 60); + if (pvd_date->lt_gmtoff < -48 ) { + + cdio_warn ("Converted ISO 9660 timezone %d is less than -48. Adjusted", + (int) pvd_date->lt_gmtoff); + pvd_date->lt_gmtoff = -48; + } else if (pvd_date->lt_gmtoff > 52) { + cdio_warn ("Converted ISO 9660 timezone %d is over 52. Adjusted", + (int) pvd_date->lt_gmtoff); + pvd_date->lt_gmtoff = 52; + } +#endif + +} + +/*! + Convert an ISO-9660 file name which is in the format usually stored + in a ISO 9660 directory entry into what's usually listed as the + file name in a listing. Lowercase name, and remove trailing ;1's + or .;1's and turn the other ;'s into version numbers. + + @param psz_oldname the ISO-9660 filename to be translated. + @param psz_newname returned string. The caller allocates this and + it should be at least the size of psz_oldname. + @return length of the translated string is returned. It will be no greater + than the length of psz_oldname. +*/ +int +iso9660_name_translate(const char *psz_oldname, char *psz_newname) +{ + return iso9660_name_translate_ext(psz_oldname, psz_newname, 0); +} + +/*! + Convert an ISO-9660 file name which is in the format usually stored + in a ISO 9660 directory entry into what's usually listed as the + file name in a listing. Lowercase name if no Joliet Extension + interpretation. Remove trailing ;1's or .;1's and turn the other + ;'s into version numbers. + + @param psz_oldname the ISO-9660 filename to be translated. + @param psz_newname returned string. The caller allocates this and + it should be at least the size of psz_oldname. + @param i_joliet_level 0 if not using Joliet Extension. Otherwise the + Joliet level. + @return length of the translated string is returned. It will be no greater + than the length of psz_oldname. +*/ +int +iso9660_name_translate_ext(const char *psz_oldname, char *psz_newname, + uint8_t i_joliet_level) +{ + int len = strlen(psz_oldname); + int i; + + if (0 == len) return 0; + for (i = 0; i < len; i++) { + unsigned char c = psz_oldname[i]; + if (!c) + break; + + /* Lower case, unless we have Joliet extensions. */ + if (!i_joliet_level && isupper(c)) c = tolower(c); + + /* Drop trailing '.;1' (ISO 9660:1988 7.5.1 requires period) */ + if (c == '.' && i == len - 3 + && psz_oldname[i + 1] == ';' && psz_oldname[i + 2] == '1') + break; + + /* Drop trailing ';1' */ + if (c == ';' && i == len - 2 && psz_oldname[i + 1] == '1') + break; + + /* Convert remaining ';' to '.' */ + if (c == ';') + c = '.'; + + psz_newname[i] = c; + } + psz_newname[i] = '\0'; + return i; +} + +/*! + Pad string src with spaces to size len and copy this to dst. If + len is less than the length of src, dst will be truncated to the + first len characters of src. + + src can also be scanned to see if it contains only ACHARs, DCHARs, + 7-bit ASCII chars depending on the enumeration _check. + + In addition to getting changed, dst is the return value. + Note: this string might not be NULL terminated. + */ +char * +iso9660_strncpy_pad(char dst[], const char src[], size_t len, + enum strncpy_pad_check _check) +{ + size_t rlen; + + cdio_assert (dst != NULL); + cdio_assert (src != NULL); + cdio_assert (len > 0); + + switch (_check) + { + int idx; + case ISO9660_NOCHECK: + break; + + case ISO9660_7BIT: + for (idx = 0; src[idx]; idx++) + if ((int8_t) src[idx] < 0) + { + cdio_warn ("string '%s' fails 7bit constraint (pos = %d)", + src, idx); + break; + } + break; + + case ISO9660_ACHARS: + for (idx = 0; src[idx]; idx++) + if (!iso9660_is_achar (src[idx])) + { + cdio_warn ("string '%s' fails a-character constraint (pos = %d)", + src, idx); + break; + } + break; + + case ISO9660_DCHARS: + for (idx = 0; src[idx]; idx++) + if (!iso9660_is_dchar (src[idx])) + { + cdio_warn ("string '%s' fails d-character constraint (pos = %d)", + src, idx); + break; + } + break; + + default: + cdio_assert_not_reached (); + break; + } + + rlen = strlen (src); + + if (rlen > len) + cdio_warn ("string '%s' is getting truncated to %d characters", + src, (unsigned int) len); + + strncpy (dst, src, len); + if (rlen < len) + memset(dst+rlen, ' ', len-rlen); + return dst; +} + +/*! + Return true if c is a DCHAR - a valid ISO-9660 level 1 character. + These are the ASCSII capital letters A-Z, the digits 0-9 and an + underscore. +*/ +bool +iso9660_is_dchar (int c) +{ + if (!IN (c, 0x30, 0x5f) + || IN (c, 0x3a, 0x40) + || IN (c, 0x5b, 0x5e)) + return false; + + return true; +} + + +/*! + Return true if c is an ACHAR - + These are the DCHAR's plus some ASCII symbols including the space + symbol. +*/ +bool +iso9660_is_achar (int c) +{ + if (!IN (c, 0x20, 0x5f) + || IN (c, 0x23, 0x24) + || c == 0x40 + || IN (c, 0x5b, 0x5e)) + return false; + + return true; +} + +void +iso9660_set_evd(void *pd) +{ + iso_volume_descriptor_t ied; + + cdio_assert (sizeof(iso_volume_descriptor_t) == ISO_BLOCKSIZE); + + cdio_assert (pd != NULL); + + memset(&ied, 0, sizeof(ied)); + + ied.type = to_711(ISO_VD_END); + iso9660_strncpy_pad (ied.id, ISO_STANDARD_ID, sizeof(ied.id), + ISO9660_DCHARS); + ied.version = to_711(ISO_VERSION); + + memcpy(pd, &ied, sizeof(ied)); +} + +void +iso9660_set_pvd(void *pd, + const char volume_id[], + const char publisher_id[], + const char preparer_id[], + const char application_id[], + uint32_t iso_size, + const void *root_dir, + uint32_t path_table_l_extent, + uint32_t path_table_m_extent, + uint32_t path_table_size, + const time_t *pvd_time + ) +{ + iso9660_pvd_t ipd; + struct tm temp_tm; + + cdio_assert (sizeof(iso9660_pvd_t) == ISO_BLOCKSIZE); + + cdio_assert (pd != NULL); + cdio_assert (volume_id != NULL); + cdio_assert (application_id != NULL); + + memset(&ipd,0,sizeof(ipd)); /* paranoia? */ + + /* magic stuff ... thatis CD XA marker... */ + strncpy(((char*)&ipd)+ISO_XA_MARKER_OFFSET, ISO_XA_MARKER_STRING, + sizeof(ISO_XA_MARKER_STRING)); + + ipd.type = to_711(ISO_VD_PRIMARY); + iso9660_strncpy_pad (ipd.id, ISO_STANDARD_ID, 5, ISO9660_DCHARS); + ipd.version = to_711(ISO_VERSION); + + iso9660_strncpy_pad (ipd.system_id, SYSTEM_ID, 32, ISO9660_ACHARS); + iso9660_strncpy_pad (ipd.volume_id, volume_id, 32, ISO9660_DCHARS); + + ipd.volume_space_size = to_733(iso_size); + + ipd.volume_set_size = to_723(1); + ipd.volume_sequence_number = to_723(1); + ipd.logical_block_size = to_723(ISO_BLOCKSIZE); + + ipd.path_table_size = to_733(path_table_size); + ipd.type_l_path_table = to_731(path_table_l_extent); + ipd.type_m_path_table = to_732(path_table_m_extent); + + /* root_directory_record doesn't contain the 1-byte filename, + so we add one for that. */ + cdio_assert (sizeof(ipd.root_directory_record) == 33); + memcpy(&(ipd.root_directory_record), root_dir, + sizeof(ipd.root_directory_record)); + ipd.root_directory_filename='\0'; + ipd.root_directory_record.length = sizeof(ipd.root_directory_record)+1; + iso9660_strncpy_pad (ipd.volume_set_id, VOLUME_SET_ID, + ISO_MAX_VOLUMESET_ID, ISO9660_DCHARS); + + iso9660_strncpy_pad (ipd.publisher_id, publisher_id, ISO_MAX_PUBLISHER_ID, + ISO9660_ACHARS); + iso9660_strncpy_pad (ipd.preparer_id, preparer_id, ISO_MAX_PREPARER_ID, + ISO9660_ACHARS); + iso9660_strncpy_pad (ipd.application_id, application_id, + ISO_MAX_APPLICATION_ID, ISO9660_ACHARS); + + iso9660_strncpy_pad (ipd.copyright_file_id , "", 37, ISO9660_DCHARS); + iso9660_strncpy_pad (ipd.abstract_file_id , "", 37, ISO9660_DCHARS); + iso9660_strncpy_pad (ipd.bibliographic_file_id, "", 37, ISO9660_DCHARS); + + gmtime_r(pvd_time, &temp_tm); + iso9660_set_ltime (&temp_tm, &(ipd.creation_date)); + gmtime_r(pvd_time, &temp_tm); + iso9660_set_ltime (&temp_tm, &(ipd.modification_date)); + iso9660_set_ltime (NULL, &(ipd.expiration_date)); + iso9660_set_ltime (NULL, &(ipd.effective_date)); + + ipd.file_structure_version = to_711(1); + + /* we leave ipd.application_data = 0 */ + + memcpy(pd, &ipd, sizeof(ipd)); /* copy stuff to arg ptr */ +} + +unsigned int +iso9660_dir_calc_record_size(unsigned int namelen, unsigned int su_len) +{ + unsigned int length; + + length = sizeof(iso9660_dir_t); + length += namelen; + if (length % 2) /* pad to word boundary */ + length++; + length += su_len; + if (length % 2) /* pad to word boundary again */ + length++; + + return length; +} + +void +iso9660_dir_add_entry_su(void *dir, + const char filename[], + uint32_t extent, + uint32_t size, + uint8_t file_flags, + const void *su_data, + unsigned int su_size, + const time_t *entry_time) +{ + iso9660_dir_t *idr = dir; + uint8_t *dir8 = dir; + unsigned int offset = 0; + uint32_t dsize = from_733(idr->size); + int length, su_offset; + struct tm temp_tm; + cdio_assert (sizeof(iso9660_dir_t) == 33); + + if (!dsize && !idr->length) + dsize = ISO_BLOCKSIZE; /* for when dir lacks '.' entry */ + + cdio_assert (dsize > 0 && !(dsize % ISO_BLOCKSIZE)); + cdio_assert (dir != NULL); + cdio_assert (extent > 17); + cdio_assert (filename != NULL); + cdio_assert (strlen(filename) <= MAX_ISOPATHNAME); + + length = sizeof(iso9660_dir_t); + length += strlen(filename); + length = _cdio_ceil2block (length, 2); /* pad to word boundary */ + su_offset = length; + length += su_size; + length = _cdio_ceil2block (length, 2); /* pad to word boundary again */ + + /* find the last entry's end */ + { + unsigned int ofs_last_rec = 0; + + offset = 0; + while (offset < dsize) + { + if (!dir8[offset]) + { + offset++; + continue; + } + + offset += dir8[offset]; + ofs_last_rec = offset; + } + + cdio_assert (offset == dsize); + + offset = ofs_last_rec; + } + + /* be sure we don't cross sectors boundaries */ + offset = _cdio_ofs_add (offset, length, ISO_BLOCKSIZE); + offset -= length; + + cdio_assert (offset + length <= dsize); + + idr = (iso9660_dir_t *) &dir8[offset]; + + cdio_assert (offset+length < dsize); + + memset(idr, 0, length); + + idr->length = to_711(length); + idr->extent = to_733(extent); + idr->size = to_733(size); + + gmtime_r(entry_time, &temp_tm); + iso9660_set_dtime (&temp_tm, &(idr->recording_time)); + + idr->file_flags = to_711(file_flags); + + idr->volume_sequence_number = to_723(1); + + idr->filename_len = to_711(strlen(filename) + ? strlen(filename) : 1); /* working hack! */ + + memcpy(idr->filename, filename, from_711(idr->filename_len)); + memcpy(&dir8[offset] + su_offset, su_data, su_size); +} + +void +iso9660_dir_init_new (void *dir, + uint32_t self, + uint32_t ssize, + uint32_t parent, + uint32_t psize, + const time_t *dir_time) +{ + iso9660_dir_init_new_su (dir, self, ssize, NULL, 0, parent, psize, NULL, + 0, dir_time); +} + +void +iso9660_dir_init_new_su (void *dir, + uint32_t self, + uint32_t ssize, + const void *ssu_data, + unsigned int ssu_size, + uint32_t parent, + uint32_t psize, + const void *psu_data, + unsigned int psu_size, + const time_t *dir_time) +{ + cdio_assert (ssize > 0 && !(ssize % ISO_BLOCKSIZE)); + cdio_assert (psize > 0 && !(psize % ISO_BLOCKSIZE)); + cdio_assert (dir != NULL); + + memset (dir, 0, ssize); + + /* "\0" -- working hack due to padding */ + iso9660_dir_add_entry_su (dir, "\0", self, ssize, ISO_DIRECTORY, ssu_data, + ssu_size, dir_time); + + iso9660_dir_add_entry_su (dir, "\1", parent, psize, ISO_DIRECTORY, psu_data, + psu_size, dir_time); +} + +/* Zero's out pathable. Do this first. */ +void +iso9660_pathtable_init (void *pt) +{ + cdio_assert (sizeof (iso_path_table_t) == 8); + + cdio_assert (pt != NULL); + + memset (pt, 0, ISO_BLOCKSIZE); /* fixme */ +} + +/*! + Returns POSIX mode bitstring for a given file. +*/ +mode_t +iso9660_get_posix_filemode(const iso9660_stat_t *p_iso_dirent) +{ + mode_t mode = 0; + + if (yep == p_iso_dirent->rr.b3_rock) { + return iso9660_get_posix_filemode_from_rock(&p_iso_dirent->rr); + } else if (p_iso_dirent->b_xa) { + return iso9660_get_posix_filemode_from_xa(p_iso_dirent->xa.attributes); + } + return mode; +} + +static const iso_path_table_t * +pathtable_get_entry (const void *pt, unsigned int entrynum) +{ + const uint8_t *tmp = pt; + unsigned int offset = 0; + unsigned int count = 0; + + cdio_assert (pt != NULL); + + while (from_711 (*tmp)) + { + if (count == entrynum) + break; + + cdio_assert (count < entrynum); + + offset += sizeof (iso_path_table_t); + offset += from_711 (*tmp); + if (offset % 2) + offset++; + tmp = (uint8_t *)pt + offset; + count++; + } + + if (!from_711 (*tmp)) + return NULL; + + return (const void *) tmp; +} + +void +pathtable_get_size_and_entries (const void *pt, + unsigned int *size, + unsigned int *entries) +{ + const uint8_t *tmp = pt; + unsigned int offset = 0; + unsigned int count = 0; + + cdio_assert (pt != NULL); + + while (from_711 (*tmp)) + { + offset += sizeof (iso_path_table_t); + offset += from_711 (*tmp); + if (offset % 2) + offset++; + tmp = (uint8_t *)pt + offset; + count++; + } + + if (size) + *size = offset; + + if (entries) + *entries = count; +} + +unsigned int +iso9660_pathtable_get_size (const void *pt) +{ + unsigned int size = 0; + pathtable_get_size_and_entries (pt, &size, NULL); + return size; +} + +uint16_t +iso9660_pathtable_l_add_entry (void *pt, + const char name[], + uint32_t extent, + uint16_t parent) +{ + iso_path_table_t *ipt = + (iso_path_table_t *)((char *)pt + iso9660_pathtable_get_size (pt)); + size_t name_len = strlen (name) ? strlen (name) : 1; + unsigned int entrynum = 0; + + cdio_assert (iso9660_pathtable_get_size (pt) < ISO_BLOCKSIZE); /*fixme */ + + memset (ipt, 0, sizeof (iso_path_table_t) + name_len); /* paranoia */ + + ipt->name_len = to_711 (name_len); + ipt->extent = to_731 (extent); + ipt->parent = to_721 (parent); + memcpy (ipt->name, name, name_len); + + pathtable_get_size_and_entries (pt, NULL, &entrynum); + + if (entrynum > 1) + { + const iso_path_table_t *ipt2 + = pathtable_get_entry (pt, entrynum - 2); + + cdio_assert (ipt2 != NULL); + + cdio_assert (from_721 (ipt2->parent) <= parent); + } + + return entrynum; +} + +uint16_t +iso9660_pathtable_m_add_entry (void *pt, + const char name[], + uint32_t extent, + uint16_t parent) +{ + iso_path_table_t *ipt = + (iso_path_table_t *)((char *)pt + iso9660_pathtable_get_size (pt)); + size_t name_len = strlen (name) ? strlen (name) : 1; + unsigned int entrynum = 0; + + cdio_assert (iso9660_pathtable_get_size(pt) < ISO_BLOCKSIZE); /* fixme */ + + memset(ipt, 0, sizeof (iso_path_table_t) + name_len); /* paranoia */ + + ipt->name_len = to_711 (name_len); + ipt->extent = to_732 (extent); + ipt->parent = to_722 (parent); + memcpy (ipt->name, name, name_len); + + pathtable_get_size_and_entries (pt, NULL, &entrynum); + + if (entrynum > 1) + { + const iso_path_table_t *ipt2 + = pathtable_get_entry (pt, entrynum - 2); + + cdio_assert (ipt2 != NULL); + + cdio_assert (from_722 (ipt2->parent) <= parent); + } + + return entrynum; +} + +/*! + Check that pathname is a valid ISO-9660 directory name. + + A valid directory name should not start out with a slash (/), + dot (.) or null byte, should be less than 37 characters long, + have no more than 8 characters in a directory component + which is separated by a /, and consist of only DCHARs. + */ +bool +iso9660_dirname_valid_p (const char pathname[]) +{ + const char *p = pathname; + int len; + + cdio_assert (pathname != NULL); + + if (*p == '/' || *p == '.' || *p == '\0') + return false; + + if (strlen (pathname) > MAX_ISOPATHNAME) + return false; + + len = 0; + for (; *p; p++) + if (iso9660_is_dchar (*p)) + { + len++; + if (len > 8) + return false; + } + else if (*p == '/') + { + if (!len) + return false; + len = 0; + } + else + return false; /* unexpected char */ + + if (!len) + return false; /* last char may not be '/' */ + + return true; +} + +/*! + Check that pathname is a valid ISO-9660 pathname. + + A valid pathname contains a valid directory name, if one appears and + the filename portion should be no more than 8 characters for the + file prefix and 3 characters in the extension (or portion after a + dot). There should be exactly one dot somewhere in the filename + portion and the filename should be composed of only DCHARs. + + True is returned if pathname is valid. + */ +bool +iso9660_pathname_valid_p (const char pathname[]) +{ + const char *p = NULL; + + cdio_assert (pathname != NULL); + + if ((p = strrchr (pathname, '/'))) + { + bool rc; + char *_tmp = strdup (pathname); + + *strrchr (_tmp, '/') = '\0'; + + rc = iso9660_dirname_valid_p (_tmp); + + free (_tmp); + + if (!rc) + return false; + + p++; + } + else + p = pathname; + + if (strlen (pathname) > (MAX_ISOPATHNAME - 6)) + return false; + + { + int len = 0; + int dots = 0; + + for (; *p; p++) + if (iso9660_is_dchar (*p)) + { + len++; + if (dots == 0 ? len > 8 : len > 3) + return false; + } + else if (*p == '.') + { + dots++; + if (dots > 1) + return false; + if (!len) + return false; + len = 0; + } + else + return false; + + if (dots != 1) + return false; + } + + return true; +} + +/*! + Take pathname and a version number and turn that into a ISO-9660 + pathname. (That's just the pathname followd by ";" and the version + number. For example, mydir/file.ext -> mydir/file.ext;1 for version + 1. The resulting ISO-9660 pathname is returned. +*/ +char * +iso9660_pathname_isofy (const char pathname[], uint16_t version) +{ + char tmpbuf[1024] = { 0, }; + + cdio_assert (strlen (pathname) < (sizeof (tmpbuf) - sizeof (";65535"))); + + snprintf (tmpbuf, sizeof(tmpbuf), "%s;%d", pathname, version); + + return strdup (tmpbuf); +} + +/*! + Return the PVD's application ID. + NULL is returned if there is some problem in getting this. +*/ +char * +iso9660_get_application_id(iso9660_pvd_t *p_pvd) +{ + if (NULL==p_pvd) return NULL; + return strdup(strip_trail(p_pvd->application_id, ISO_MAX_APPLICATION_ID)); +} + +#if FIXME +lsn_t +iso9660_get_dir_extent(const iso9660_dir_t *idr) +{ + if (NULL == idr) return 0; + return from_733(idr->extent); +} +#endif + +uint8_t +iso9660_get_dir_len(const iso9660_dir_t *idr) +{ + if (NULL == idr) return 0; + return idr->length; +} + +#if FIXME +uint8_t +iso9660_get_dir_size(const iso9660_dir_t *idr) +{ + if (NULL == idr) return 0; + return from_733(idr->size); +} +#endif + +uint8_t +iso9660_get_pvd_type(const iso9660_pvd_t *pvd) +{ + if (NULL == pvd) return 255; + return(pvd->type); +} + +const char * +iso9660_get_pvd_id(const iso9660_pvd_t *pvd) +{ + if (NULL == pvd) return "ERR"; + return(pvd->id); +} + +int +iso9660_get_pvd_space_size(const iso9660_pvd_t *pvd) +{ + if (NULL == pvd) return 0; + return from_733(pvd->volume_space_size); +} + +int +iso9660_get_pvd_block_size(const iso9660_pvd_t *pvd) +{ + if (NULL == pvd) return 0; + return from_723(pvd->logical_block_size); +} + +/*! Return the primary volume id version number (of pvd). + If there is an error 0 is returned. + */ +int +iso9660_get_pvd_version(const iso9660_pvd_t *pvd) +{ + if (NULL == pvd) return 0; + return pvd->version; +} + +/*! Return the LSN of the root directory for pvd. + If there is an error CDIO_INVALID_LSN is returned. + */ +lsn_t +iso9660_get_root_lsn(const iso9660_pvd_t *pvd) +{ + if (NULL == pvd) + return CDIO_INVALID_LSN; + else { + const iso9660_dir_t *idr = &(pvd->root_directory_record); + if (NULL == idr) return CDIO_INVALID_LSN; + return(from_733 (idr->extent)); + } +} + +/*! + Return a string containing the preparer id with trailing + blanks removed. +*/ +char * +iso9660_get_preparer_id(const iso9660_pvd_t *pvd) +{ + if (NULL==pvd) return NULL; + return strdup(strip_trail(pvd->preparer_id, ISO_MAX_PREPARER_ID)); +} + +/*! + Return a string containing the publisher id with trailing + blanks removed. +*/ +char * +iso9660_get_publisher_id(const iso9660_pvd_t *pvd) +{ + if (NULL==pvd) return NULL; + return strdup(strip_trail(pvd->publisher_id, ISO_MAX_PUBLISHER_ID)); +} + +/*! + Return a string containing the PVD's system id with trailing + blanks removed. +*/ +char * +iso9660_get_system_id(const iso9660_pvd_t *pvd) +{ + if (NULL==pvd) return NULL; + return strdup(strip_trail(pvd->system_id, ISO_MAX_SYSTEM_ID)); +} + +/*! + Return the PVD's volume ID. +*/ +char * +iso9660_get_volume_id(const iso9660_pvd_t *pvd) +{ + if (NULL == pvd) return NULL; + return strdup(strip_trail(pvd->volume_id, ISO_MAX_VOLUME_ID)); +} + +/*! + Return the PVD's volumeset ID. + NULL is returned if there is some problem in getting this. +*/ +char * +iso9660_get_volumeset_id(const iso9660_pvd_t *pvd) +{ + if ( NULL == pvd ) return NULL; + return strdup(strip_trail(pvd->volume_set_id, ISO_MAX_VOLUMESET_ID)); +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/iso9660/iso9660_fs.c b/lib/iso9660/iso9660_fs.c new file mode 100644 index 00000000..e2fb54b4 --- /dev/null +++ b/lib/iso9660/iso9660_fs.c @@ -0,0 +1,1567 @@ +/* + $Id: iso9660_fs.c,v 1.47 2008/04/18 16:02:09 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* iso9660 filesystem-based routines */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif + +#ifdef HAVE_ERRNO_H +# include +#endif + +#ifdef HAVE_LANGINFO_CODESET +#include +#endif + +#include +#include +#include +#include +#include + +/* Private headers */ +#include "cdio_assert.h" +#include "_cdio_stdio.h" +#include "cdio_private.h" + +#include + +static const char _rcsid[] = "$Id: iso9660_fs.c,v 1.47 2008/04/18 16:02:09 karl Exp $"; + +/* Implementation of iso9660_t type */ +struct _iso9660_s { + CdioDataSource_t *stream; /* Stream pointer */ + bool_3way_t b_xa; /* true if has XA attributes. */ + bool_3way_t b_mode2; /* true if has mode 2, false for mode 1. */ + uint8_t i_joliet_level; /* 0 = no Joliet extensions. + 1-3: Joliet level. */ + iso9660_pvd_t pvd; + iso9660_svd_t svd; + iso_extension_mask_t iso_extension_mask; /* What extensions we + tolerate. */ + uint32_t i_datastart; /* Usually 0 when i_framesize is ISO_BLOCKSIZE. + This is the normal condition. But in a fuzzy + read we may be reading a CD-image + and not a true ISO 9660 image this might be + CDIO_CD_SYNC_SIZE + */ + uint32_t i_framesize; /* Usually ISO_BLOCKSIZE (2048), but in a + fuzzy read, we may be reading a CD-image + and not a true ISO 9660 image this might + be CDIO_CD_FRAMESIZE_RAW (2352) or + M2RAW_SECTOR_SIZE (2336). + */ + int i_fuzzy_offset; /* Adjustment in bytes to make ISO_STANDARD_ID + ("CD001") come out as ISO_PVD_SECTOR + (frame 16). Normally this should be 0 + for an ISO 9660 image, but if one is + say reading a BIN/CUE or cdrdao BIN/TOC + without having the CUE or TOC and + trying to extract an ISO-9660 + filesystem inside that it may be + different. + */ +}; + +static long int iso9660_seek_read_framesize (const iso9660_t *p_iso, + void *ptr, lsn_t start, + long int size, + uint16_t i_framesize); + +/* Adjust the p_iso's i_datastart, i_byte_offset and i_framesize + based on whether we find a frame header or not. +*/ +static void +adjust_fuzzy_pvd( iso9660_t *p_iso ) +{ + long int i_byte_offset; + + if (!p_iso) return; + + i_byte_offset = (ISO_PVD_SECTOR * p_iso->i_framesize) + + p_iso->i_fuzzy_offset + p_iso->i_datastart; + + /* If we have a raw 2352-byte frame then we should expect to see a sync + frame and a header. + */ + if (CDIO_CD_FRAMESIZE_RAW == p_iso->i_framesize) { + const int pre_user_data=CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE + + CDIO_CD_SUBHEADER_SIZE; + char buf[pre_user_data]; + + i_byte_offset -= pre_user_data; + + if ( DRIVER_OP_SUCCESS != cdio_stream_seek (p_iso->stream, i_byte_offset, + SEEK_SET) ) + return; + if (sizeof(buf) == cdio_stream_read (p_iso->stream, buf, sizeof(buf), 1)) { + /* Does the sector frame header suggest Mode 1 format? */ + if (!memcmp(CDIO_SECTOR_SYNC_HEADER, buf+CDIO_CD_SUBHEADER_SIZE, + CDIO_CD_SYNC_SIZE)) { + if (buf[14+CDIO_CD_SUBHEADER_SIZE] != 0x16) { + cdio_warn ("Expecting the PVD sector header MSF to be 0x16, is: %x", + buf[14]); + } + if (buf[15+CDIO_CD_SUBHEADER_SIZE] != 0x1) { + cdio_warn ("Expecting the PVD sector mode to be Mode 1 is: %x", + buf[15]); + } + p_iso->b_mode2 = nope; + p_iso->b_xa = nope; + } else if (!memcmp(CDIO_SECTOR_SYNC_HEADER, buf, CDIO_CD_SYNC_SIZE)) { + /* Frame header indicates Mode 2 Form 1*/ + if (buf[14] != 0x16) { + cdio_warn ("Expecting the PVD sector header MSF to be 0x16, is: %x", + buf[14]); + } + if (buf[15] != 0x2) { + cdio_warn ("Expecting the PVD sector mode to be Mode 2 is: %x", + buf[15]); + } + p_iso->b_mode2 = yep; + /* Do do: check Mode 2 Form 2? */ + } else { + /* Has no frame header */ + p_iso->i_framesize = M2RAW_SECTOR_SIZE; + p_iso->i_fuzzy_offset = (CDIO_CD_FRAMESIZE_RAW - M2RAW_SECTOR_SIZE) + * ISO_PVD_SECTOR + p_iso->i_fuzzy_offset + p_iso->i_datastart; + p_iso->i_datastart = 0; + } + } + } + + +} + +/*! + Open an ISO 9660 image for reading in either fuzzy mode or not. +*/ +static iso9660_t * +iso9660_open_ext_private (const char *psz_path, + iso_extension_mask_t iso_extension_mask, + uint16_t i_fuzz, bool b_fuzzy) +{ + iso9660_t *p_iso = (iso9660_t *) calloc(1, sizeof(iso9660_t)) ; + bool b_have_superblock; + + if (!p_iso) return NULL; + + p_iso->stream = cdio_stdio_new( psz_path ); + if (NULL == p_iso->stream) + goto error; + + p_iso->i_framesize = ISO_BLOCKSIZE; + + b_have_superblock = (b_fuzzy) + ? iso9660_ifs_fuzzy_read_superblock(p_iso, iso_extension_mask, i_fuzz) + : iso9660_ifs_read_superblock(p_iso, iso_extension_mask) ; + + if ( ! b_have_superblock ) goto error; + + /* Determine if image has XA attributes. */ + + p_iso->b_xa = strncmp ((char *) &(p_iso->pvd) + ISO_XA_MARKER_OFFSET, + ISO_XA_MARKER_STRING, + sizeof (ISO_XA_MARKER_STRING)) + ? nope : yep; + + p_iso->iso_extension_mask = iso_extension_mask; + return p_iso; + + error: + if (p_iso && p_iso->stream) { + cdio_stdio_destroy(p_iso->stream); + free(p_iso); + } + return NULL; +} + +/*! + Open an ISO 9660 image for reading. Maybe in the future we will have + a mode. NULL is returned on error. +*/ +iso9660_t * +iso9660_open (const char *psz_path /*, mode*/) +{ + return iso9660_open_ext(psz_path, ISO_EXTENSION_NONE); +} + +/*! + Open an ISO 9660 image for reading. Maybe in the future we will have + a mode. NULL is returned on error. +*/ +iso9660_t * +iso9660_open_ext (const char *psz_path, + iso_extension_mask_t iso_extension_mask) +{ + return iso9660_open_ext_private(psz_path, iso_extension_mask, 0, false); +} + + +/*! + Open an ISO 9660 image for reading. Maybe in the future we will have + a mode. NULL is returned on error. +*/ +iso9660_t * +iso9660_open_fuzzy (const char *psz_path, uint16_t i_fuzz /*, mode*/) +{ + return iso9660_open_fuzzy_ext(psz_path, ISO_EXTENSION_NONE, i_fuzz); +} + +/*! + Open an ISO 9660 image for reading. Maybe in the future we will have + a mode. NULL is returned on error. +*/ +iso9660_t * +iso9660_open_fuzzy_ext (const char *psz_path, + iso_extension_mask_t iso_extension_mask, + uint16_t i_fuzz) +{ + return iso9660_open_ext_private(psz_path, iso_extension_mask, i_fuzz, + true); +} + +/*! + Close previously opened ISO 9660 image. + True is unconditionally returned. If there was an error false would + be returned. +*/ +bool +iso9660_close (iso9660_t *p_iso) +{ + if (NULL != p_iso) { + cdio_stdio_destroy(p_iso->stream); + free(p_iso); + } + return true; +} + +static bool +check_pvd (const iso9660_pvd_t *p_pvd, cdio_log_level_t log_level) +{ + if ( ISO_VD_PRIMARY != from_711(p_pvd->type) ) { + cdio_log (log_level, "unexpected PVD type %d", p_pvd->type); + return false; + } + + if (strncmp (p_pvd->id, ISO_STANDARD_ID, strlen (ISO_STANDARD_ID))) + { + cdio_log (log_level, "unexpected ID encountered (expected `" + ISO_STANDARD_ID "', got `%.5s'", p_pvd->id); + return false; + } + return true; +} + +/*! + Return the application ID. NULL is returned in psz_app_id if there + is some problem in getting this. +*/ +bool +iso9660_ifs_get_application_id(iso9660_t *p_iso, + /*out*/ cdio_utf8_t **p_psz_app_id) +{ + if (!p_iso) { + *p_psz_app_id = NULL; + return false; + } + +#ifdef HAVE_JOLIET + if (p_iso->i_joliet_level) { + /* TODO: check that we haven't reached the maximum size. + If we have, perhaps we've truncated and if we can get + longer results *and* have the same character using + the PVD, do that. + */ + if ( cdio_charset_to_utf8(p_iso->svd.application_id, + ISO_MAX_APPLICATION_ID, + p_psz_app_id, "UCS-2BE")) + return true; + } +#endif /*HAVE_JOLIET*/ + *p_psz_app_id = iso9660_get_application_id( &(p_iso->pvd) ); + return *p_psz_app_id != NULL && strlen(*p_psz_app_id); +} + +/*! + Return the Joliet level recognaized for p_iso. +*/ +uint8_t iso9660_ifs_get_joliet_level(iso9660_t *p_iso) +{ + if (!p_iso) return 0; + return p_iso->i_joliet_level; +} + +/*! + Return a string containing the preparer id with trailing + blanks removed. +*/ +bool +iso9660_ifs_get_preparer_id(iso9660_t *p_iso, + /*out*/ cdio_utf8_t **p_psz_preparer_id) +{ + if (!p_iso) { + *p_psz_preparer_id = NULL; + return false; + } + +#ifdef HAVE_JOLIET + if (p_iso->i_joliet_level) { + /* TODO: check that we haven't reached the maximum size. + If we have, perhaps we've truncated and if we can get + longer results *and* have the same character using + the PVD, do that. + */ + if ( cdio_charset_to_utf8(p_iso->svd.preparer_id, ISO_MAX_PREPARER_ID, + p_psz_preparer_id, "UCS-2BE") ) + return true; + } +#endif /*HAVE_JOLIET*/ + *p_psz_preparer_id = iso9660_get_preparer_id( &(p_iso->pvd) ); + return *p_psz_preparer_id != NULL && strlen(*p_psz_preparer_id); +} + +/*! + Return a string containing the PVD's publisher id with trailing + blanks removed. +*/ +bool iso9660_ifs_get_publisher_id(iso9660_t *p_iso, + /*out*/ cdio_utf8_t **p_psz_publisher_id) +{ + if (!p_iso) { + *p_psz_publisher_id = NULL; + return false; + } + +#ifdef HAVE_JOLIET + if (p_iso->i_joliet_level) { + /* TODO: check that we haven't reached the maximum size. + If we have, perhaps we've truncated and if we can get + longer results *and* have the same character using + the PVD, do that. + */ + if( cdio_charset_to_utf8(p_iso->svd.publisher_id, ISO_MAX_PUBLISHER_ID, + p_psz_publisher_id, "UCS-2BE") ) + return true; + } +#endif /*HAVE_JOLIET*/ + *p_psz_publisher_id = iso9660_get_publisher_id( &(p_iso->pvd) ); + return *p_psz_publisher_id != NULL && strlen(*p_psz_publisher_id); +} + + +/*! + Return a string containing the PVD's publisher id with trailing + blanks removed. +*/ +bool iso9660_ifs_get_system_id(iso9660_t *p_iso, + /*out*/ cdio_utf8_t **p_psz_system_id) +{ + if (!p_iso) { + *p_psz_system_id = NULL; + return false; + } + +#ifdef HAVE_JOLIET + if (p_iso->i_joliet_level) { + /* TODO: check that we haven't reached the maximum size. + If we have, perhaps we've truncated and if we can get + longer results *and* have the same character using + the PVD, do that. + */ + if ( cdio_charset_to_utf8(p_iso->svd.system_id, ISO_MAX_SYSTEM_ID, + p_psz_system_id, "UCS-2BE") ) + return true; + } +#endif /*HAVE_JOLIET*/ + *p_psz_system_id = iso9660_get_system_id( &(p_iso->pvd) ); + return *p_psz_system_id != NULL && strlen(*p_psz_system_id); +} + + +/*! + Return a string containing the PVD's publisher id with trailing + blanks removed. +*/ +bool iso9660_ifs_get_volume_id(iso9660_t *p_iso, + /*out*/ cdio_utf8_t **p_psz_volume_id) +{ + if (!p_iso) { + *p_psz_volume_id = NULL; + return false; + } + +#ifdef HAVE_JOLIET + if (p_iso->i_joliet_level) { + /* TODO: check that we haven't reached the maximum size. + If we have, perhaps we've truncated and if we can get + longer results *and* have the same character using + the PVD, do that. + */ + if ( cdio_charset_to_utf8(p_iso->svd.volume_id, ISO_MAX_VOLUME_ID, + p_psz_volume_id, "UCS-2BE") ) + return true; + } +#endif /* HAVE_JOLIET */ + *p_psz_volume_id = iso9660_get_volume_id( &(p_iso->pvd) ); + return *p_psz_volume_id != NULL && strlen(*p_psz_volume_id); +} + + +/*! + Return a string containing the PVD's publisher id with trailing + blanks removed. +*/ +bool iso9660_ifs_get_volumeset_id(iso9660_t *p_iso, + /*out*/ cdio_utf8_t **p_psz_volumeset_id) +{ + if (!p_iso) { + *p_psz_volumeset_id = NULL; + return false; + } + +#ifdef HAVE_JOLIET + if (p_iso->i_joliet_level) { + /* TODO: check that we haven't reached the maximum size. + If we have, perhaps we've truncated and if we can get + longer results *and* have the same character using + the PVD, do that. + */ + if ( cdio_charset_to_utf8(p_iso->svd.volume_set_id, + ISO_MAX_VOLUMESET_ID, + p_psz_volumeset_id, + "UCS-2BE") ) + return true; + } +#endif /*HAVE_JOLIET*/ + *p_psz_volumeset_id = iso9660_get_volumeset_id( &(p_iso->pvd) ); + return *p_psz_volumeset_id != NULL && strlen(*p_psz_volumeset_id); +} + + +/*! + Read the Primary Volume Descriptor for an ISO 9660 image. + True is returned if read, and false if there was an error. +*/ +static bool +iso9660_ifs_read_pvd_loglevel (const iso9660_t *p_iso, + /*out*/ iso9660_pvd_t *p_pvd, + cdio_log_level_t log_level) +{ + if (0 == iso9660_iso_seek_read (p_iso, p_pvd, ISO_PVD_SECTOR, 1)) { + cdio_log ( log_level, "error reading PVD sector (%d)", ISO_PVD_SECTOR ); + return false; + } + return check_pvd(p_pvd, log_level); +} + +/*! + Read the Primary Volume Descriptor for an ISO 9660 image. + True is returned if read, and false if there was an error. +*/ +bool +iso9660_ifs_read_pvd (const iso9660_t *p_iso, /*out*/ iso9660_pvd_t *p_pvd) +{ + return iso9660_ifs_read_pvd_loglevel(p_iso, p_pvd, CDIO_LOG_WARN); +} + + +/*! + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental Volume + Descriptor if (Joliet) extensions are acceptable. +*/ +bool +iso9660_ifs_read_superblock (iso9660_t *p_iso, + iso_extension_mask_t iso_extension_mask) +{ + iso9660_svd_t *p_svd; /* Secondary volume descriptor. */ + + if (!p_iso || !iso9660_ifs_read_pvd(p_iso, &(p_iso->pvd))) + return false; + + p_svd = &(p_iso->svd); + p_iso->i_joliet_level = 0; + + if (0 != iso9660_iso_seek_read (p_iso, p_svd, ISO_PVD_SECTOR+1, 1)) { + if ( ISO_VD_SUPPLEMENTARY == from_711(p_svd->type) ) { + if (p_svd->escape_sequences[0] == 0x25 + && p_svd->escape_sequences[1] == 0x2f) { + switch (p_svd->escape_sequences[2]) { + case 0x40: + if (iso_extension_mask & ISO_EXTENSION_JOLIET_LEVEL1) + p_iso->i_joliet_level = 1; + break; + case 0x43: + if (iso_extension_mask & ISO_EXTENSION_JOLIET_LEVEL2) + p_iso->i_joliet_level = 2; + break; + case 0x45: + if (iso_extension_mask & ISO_EXTENSION_JOLIET_LEVEL3) + p_iso->i_joliet_level = 3; + break; + default: + cdio_info("Supplementary Volume Descriptor found, but not Joliet"); + } + if (p_iso->i_joliet_level > 0) { + cdio_info("Found Extension: Joliet Level %d", p_iso->i_joliet_level); + } + } + } + } + + return true; +} + +/*! + Read the Super block of an ISO 9660 image but determine framesize + and datastart and a possible additional offset. Generally here we are + not reading an ISO 9660 image but a CD-Image which contains an ISO 9660 + filesystem. +*/ +bool +iso9660_ifs_fuzzy_read_superblock (iso9660_t *p_iso, + iso_extension_mask_t iso_extension_mask, + uint16_t i_fuzz) +{ + /* Got some work to do to find ISO_STANDARD_ID ("CD001") */ + unsigned int i; + + for (i=0; ii_framesize = framesizes[k]; + p_iso->i_datastart = (ISO_BLOCKSIZE == framesizes[k]) ? + 0 : CDIO_CD_SYNC_SIZE; + p_iso->i_fuzzy_offset = 0; + if (0 == iso9660_seek_read_framesize (p_iso, frame, lsn, 1, + p_iso->i_framesize)) { + return false; + } + + q = memchr(frame, 'C', p_iso->i_framesize); + for ( p=q; p && p < frame + p_iso->i_framesize ; p=q+1 ) { + q = memchr(p, 'C', p_iso->i_framesize - (p - frame)); + if ( !q || (pvd = strstr(q, ISO_STANDARD_ID)) ) + break; + } + + if (pvd) { + /* Yay! Found something */ + p_iso->i_fuzzy_offset = (pvd - frame - 1) - + ((ISO_PVD_SECTOR-lsn)*p_iso->i_framesize) ; + /* But is it *really* a PVD? */ + if ( iso9660_ifs_read_pvd_loglevel(p_iso, &(p_iso->pvd), + CDIO_LOG_DEBUG) ) { + adjust_fuzzy_pvd(p_iso); + return true; + } + + } + } + } + } + return false; +} + + +/*! + Read the Primary Volume Descriptor for of CD. +*/ +bool +iso9660_fs_read_pvd(const CdIo_t *p_cdio, /*out*/ iso9660_pvd_t *p_pvd) +{ + /* A bit of a hack, we'll assume track 1 contains ISO_PVD_SECTOR.*/ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + driver_return_code_t driver_return = + cdio_read_data_sectors (p_cdio, buf, ISO_PVD_SECTOR, ISO_BLOCKSIZE, 1); + + if (DRIVER_OP_SUCCESS != driver_return) { + cdio_warn ("error reading PVD sector (%d) error %d", ISO_PVD_SECTOR, + driver_return); + return false; + } + + /* The size of a PVD or SVD is smaller than a sector. So we + allocated a bigger block above (buf) and now we'll copy just + the part we need to save. + */ + cdio_assert (sizeof(buf) >= sizeof (iso9660_pvd_t)); + memcpy(p_pvd, buf, sizeof(iso9660_pvd_t)); + + return check_pvd(p_pvd, CDIO_LOG_WARN); +} + + +/*! + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental Volume + Descriptor if (Joliet) extensions are acceptable. +*/ +bool +iso9660_fs_read_superblock (CdIo_t *p_cdio, + iso_extension_mask_t iso_extension_mask) +{ + if (!p_cdio) return false; + + { + generic_img_private_t *p_env = (generic_img_private_t *) p_cdio->env; + iso9660_pvd_t *p_pvd = &(p_env->pvd); + iso9660_svd_t *p_svd = &(p_env->svd); + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + driver_return_code_t driver_return; + + if ( !iso9660_fs_read_pvd(p_cdio, p_pvd) ) + return false; + + p_env->i_joliet_level = 0; + + driver_return = + cdio_read_data_sectors ( p_cdio, buf, ISO_PVD_SECTOR+1, ISO_BLOCKSIZE, + 1 ); + + if (DRIVER_OP_SUCCESS == driver_return) { + /* The size of a PVD or SVD is smaller than a sector. So we + allocated a bigger block above (buf) and now we'll copy just + the part we need to save. + */ + cdio_assert (sizeof(buf) >= sizeof (iso9660_svd_t)); + memcpy(p_svd, buf, sizeof(iso9660_svd_t)); + + if ( ISO_VD_SUPPLEMENTARY == from_711(p_svd->type) ) { + if (p_svd->escape_sequences[0] == 0x25 + && p_svd->escape_sequences[1] == 0x2f) { + switch (p_svd->escape_sequences[2]) { + case 0x40: + if (iso_extension_mask & ISO_EXTENSION_JOLIET_LEVEL1) + p_env->i_joliet_level = 1; + break; + case 0x43: + if (iso_extension_mask & ISO_EXTENSION_JOLIET_LEVEL2) + p_env->i_joliet_level = 2; + break; + case 0x45: + if (iso_extension_mask & ISO_EXTENSION_JOLIET_LEVEL3) + p_env->i_joliet_level = 3; + break; + default: + cdio_info("Supplementary Volume Descriptor found, but not Joliet"); + } + if (p_env->i_joliet_level > 0) { + cdio_info("Found Extension: Joliet Level %d", + p_env->i_joliet_level); + } + } + } + } + } + + return true; +} + +/*! + Seek to a position and then read n blocks. Size read is returned. +*/ +static long int +iso9660_seek_read_framesize (const iso9660_t *p_iso, void *ptr, + lsn_t start, long int size, + uint16_t i_framesize) +{ + long int ret; + long int i_byte_offset; + + if (!p_iso) return 0; + i_byte_offset = (start * p_iso->i_framesize) + p_iso->i_fuzzy_offset + + p_iso->i_datastart; + + ret = cdio_stream_seek (p_iso->stream, i_byte_offset, SEEK_SET); + if (ret!=0) return 0; + return cdio_stream_read (p_iso->stream, ptr, i_framesize, size); +} + +/*! + Seek to a position and then read n blocks. Size read is returned. +*/ +long int +iso9660_iso_seek_read (const iso9660_t *p_iso, void *ptr, lsn_t start, + long int size) +{ + return iso9660_seek_read_framesize(p_iso, ptr, start, size, ISO_BLOCKSIZE); +} + + + +static iso9660_stat_t * +_iso9660_dir_to_statbuf (iso9660_dir_t *p_iso9660_dir, bool_3way_t b_xa, + uint8_t i_joliet_level) +{ + uint8_t dir_len= iso9660_get_dir_len(p_iso9660_dir); + iso711_t i_fname; + unsigned int stat_len; + iso9660_stat_t *p_stat; + + if (!dir_len) return NULL; + + i_fname = from_711(p_iso9660_dir->filename_len); + + /* .. string in statbuf is one longer than in p_iso9660_dir's listing '\1' */ + stat_len = sizeof(iso9660_stat_t)+i_fname+2; + + p_stat = calloc(1, stat_len); + if (!p_stat) + { + cdio_warn("Couldn't calloc(1, %d)", stat_len); + return NULL; + } + p_stat->type = (p_iso9660_dir->file_flags & ISO_DIRECTORY) + ? _STAT_DIR : _STAT_FILE; + p_stat->lsn = from_733 (p_iso9660_dir->extent); + p_stat->size = from_733 (p_iso9660_dir->size); + p_stat->secsize = _cdio_len2blocks (p_stat->size, ISO_BLOCKSIZE); + p_stat->rr.b3_rock = dunno; /*FIXME should do based on mask */ + p_stat->b_xa = false; + + { + char rr_fname[256] = ""; + + int i_rr_fname = +#ifdef HAVE_ROCK + get_rock_ridge_filename(p_iso9660_dir, rr_fname, p_stat); +#else + 0; +#endif + + if (i_rr_fname > 0) { + if (i_rr_fname > i_fname) { + /* realloc gives valgrind errors */ + iso9660_stat_t *p_stat_new = + calloc(1, sizeof(iso9660_stat_t)+i_rr_fname+2); + if (!p_stat_new) + { + cdio_warn("Couldn't calloc(1, %d)", sizeof(iso9660_stat_t)+i_rr_fname+2); + return NULL; + } + memcpy(p_stat_new, p_stat, stat_len); + free(p_stat); + p_stat = p_stat_new; + } + strncpy(p_stat->filename, rr_fname, i_rr_fname+1); + } else { + if ('\0' == p_iso9660_dir->filename[0] && 1 == i_fname) + strncpy (p_stat->filename, ".", sizeof(".")); + else if ('\1' == p_iso9660_dir->filename[0] && 1 == i_fname) + strncpy (p_stat->filename, "..", sizeof("..")); +#ifdef HAVE_JOLIET + else if (i_joliet_level) { + int i_inlen = i_fname; + cdio_utf8_t *p_psz_out = NULL; + if (cdio_charset_to_utf8(p_iso9660_dir->filename, i_inlen, + &p_psz_out, "UCS-2BE")) { + strncpy(p_stat->filename, p_psz_out, i_fname); + free(p_psz_out); + } + else { + return NULL; + } + } +#endif /*HAVE_JOLIET*/ + else { + strncpy (p_stat->filename, p_iso9660_dir->filename, i_fname); + } + } + } + + + iso9660_get_dtime(&(p_iso9660_dir->recording_time), true, &(p_stat->tm)); + + if (dir_len < sizeof (iso9660_dir_t)) { + free(p_stat->rr.psz_symlink); + free(p_stat); + return NULL; + } + + + { + int su_length = iso9660_get_dir_len(p_iso9660_dir) + - sizeof (iso9660_dir_t); + su_length -= i_fname; + + if (su_length % 2) + su_length--; + + if (su_length < 0 || su_length < sizeof (iso9660_xa_t)) + return p_stat; + + if (nope == b_xa) { + return p_stat; + } else { + iso9660_xa_t *xa_data = + (void *) (((char *) p_iso9660_dir) + + (iso9660_get_dir_len(p_iso9660_dir) - su_length)); + cdio_log_level_t loglevel = (yep == b_xa) + ? CDIO_LOG_WARN : CDIO_LOG_INFO; + + if (xa_data->signature[0] != 'X' + || xa_data->signature[1] != 'A') + { + cdio_log (loglevel, + "XA signature not found in ISO9660's system use area;" + " ignoring XA attributes for this file entry."); + cdio_debug ("%d %d %d, '%c%c' (%d, %d)", + iso9660_get_dir_len(p_iso9660_dir), + i_fname, + su_length, + xa_data->signature[0], xa_data->signature[1], + xa_data->signature[0], xa_data->signature[1]); + return p_stat; + } + p_stat->b_xa = true; + p_stat->xa = *xa_data; + } + } + return p_stat; + +} + +/*! + Return the directory name stored in the iso9660_dir_t + + A string is allocated: the caller must deallocate. This routine + can return NULL if memory allocation fails. + */ +char * +iso9660_dir_to_name (const iso9660_dir_t *iso9660_dir) +{ + uint8_t len=iso9660_get_dir_len(iso9660_dir); + + if (!len) return NULL; + + cdio_assert (len >= sizeof (iso9660_dir_t)); + + /* (iso9660_dir->file_flags & ISO_DIRECTORY) */ + + if (iso9660_dir->filename[0] == '\0') + return strdup("."); + else if (iso9660_dir->filename[0] == '\1') + return strdup(".."); + else { + return strdup(iso9660_dir->filename); + } +} + +/* + Return a pointer to a ISO 9660 stat buffer or NULL if there's an error +*/ +static iso9660_stat_t * +_fs_stat_root (CdIo_t *p_cdio) +{ + + if (!p_cdio) return NULL; + + { + iso_extension_mask_t iso_extension_mask = ISO_EXTENSION_ALL; + generic_img_private_t *p_env = (generic_img_private_t *) p_cdio->env; + iso9660_dir_t *p_iso9660_dir; + iso9660_stat_t *p_stat; + bool_3way_t b_xa; + + if (!p_env->i_joliet_level) + iso_extension_mask &= ~ISO_EXTENSION_JOLIET; + + /* FIXME try also with Joliet.*/ + if ( !iso9660_fs_read_superblock (p_cdio, iso_extension_mask) ) { + cdio_warn("Could not read ISO-9660 Superblock."); + return NULL; + } + + switch(cdio_get_discmode(p_cdio)) { + case CDIO_DISC_MODE_CD_XA: + b_xa = yep; + break; + case CDIO_DISC_MODE_CD_DATA: + b_xa = nope; + break; + default: + b_xa = dunno; + } + +#ifdef HAVE_JOLIET + p_iso9660_dir = p_env->i_joliet_level + ? &(p_env->svd.root_directory_record) + : &(p_env->pvd.root_directory_record) ; +#else + p_iso9660_dir = &(p_env->pvd.root_directory_record) ; +#endif + + p_stat = _iso9660_dir_to_statbuf (p_iso9660_dir, b_xa, + p_env->i_joliet_level); + return p_stat; + } + +} + +static iso9660_stat_t * +_ifs_stat_root (iso9660_t *p_iso) +{ + iso9660_stat_t *p_stat; + iso9660_dir_t *p_iso9660_dir; + +#ifdef HAVE_JOLIET + p_iso9660_dir = p_iso->i_joliet_level + ? &(p_iso->svd.root_directory_record) + : &(p_iso->pvd.root_directory_record) ; +#else + p_iso9660_dir = &(p_iso->pvd.root_directory_record) ; +#endif + + p_stat = _iso9660_dir_to_statbuf (p_iso9660_dir, p_iso->b_xa, + p_iso->i_joliet_level); + return p_stat; +} + +static iso9660_stat_t * +_fs_stat_traverse (const CdIo_t *p_cdio, const iso9660_stat_t *_root, + char **splitpath) +{ + unsigned offset = 0; + uint8_t *_dirbuf = NULL; + iso9660_stat_t *p_stat; + generic_img_private_t *p_env = (generic_img_private_t *) p_cdio->env; + + if (!splitpath[0]) + { + unsigned int len=sizeof(iso9660_stat_t) + strlen(_root->filename)+1; + p_stat = calloc(1, len); + memcpy(p_stat, _root, len); + p_stat->rr.psz_symlink = calloc(1, p_stat->rr.i_symlink_max); + memcpy(p_stat->rr.psz_symlink, _root->rr.psz_symlink, + p_stat->rr.i_symlink_max); + return p_stat; + } + + if (_root->type == _STAT_FILE) + return NULL; + + cdio_assert (_root->type == _STAT_DIR); + + if (_root->size != ISO_BLOCKSIZE * _root->secsize) + { + cdio_warn ("bad size for ISO9660 directory (%ud) should be (%lu)!", + (unsigned) _root->size, + (unsigned long int) ISO_BLOCKSIZE * _root->secsize); + } + + _dirbuf = calloc(1, _root->secsize * ISO_BLOCKSIZE); + if (!_dirbuf) + { + cdio_warn("Couldn't calloc(1, %d)", _root->secsize * ISO_BLOCKSIZE); + return NULL; + } + + if (cdio_read_data_sectors (p_cdio, _dirbuf, _root->lsn, ISO_BLOCKSIZE, + _root->secsize)) + return NULL; + + while (offset < (_root->secsize * ISO_BLOCKSIZE)) + { + iso9660_dir_t *p_iso9660_dir = (void *) &_dirbuf[offset]; + iso9660_stat_t *p_stat; + int cmp; + + if (!iso9660_get_dir_len(p_iso9660_dir)) + { + offset++; + continue; + } + + p_stat = _iso9660_dir_to_statbuf (p_iso9660_dir, dunno, + p_env->i_joliet_level); + + cmp = strcmp(splitpath[0], p_stat->filename); + + if ( 0 != cmp && 0 == p_env->i_joliet_level + && yep != p_stat->rr.b3_rock ) { + char *trans_fname = NULL; + unsigned int i_trans_fname=strlen(p_stat->filename); + int trans_len; + + if (i_trans_fname) { + trans_fname = calloc(1, i_trans_fname+1); + if (!trans_fname) { + cdio_warn("can't allocate %lu bytes", + (long unsigned int) strlen(p_stat->filename)); + return NULL; + } + trans_len = iso9660_name_translate_ext(p_stat->filename, trans_fname, + p_env->i_joliet_level); + cmp = strcmp(splitpath[0], trans_fname); + free(trans_fname); + } + } + + if (!cmp) { + iso9660_stat_t *ret_stat + = _fs_stat_traverse (p_cdio, p_stat, &splitpath[1]); + free(p_stat->rr.psz_symlink); + free(p_stat); + free (_dirbuf); + return ret_stat; + } + + free(p_stat->rr.psz_symlink); + free(p_stat); + + offset += iso9660_get_dir_len(p_iso9660_dir); + } + + cdio_assert (offset == (_root->secsize * ISO_BLOCKSIZE)); + + /* not found */ + free (_dirbuf); + return NULL; +} + +static iso9660_stat_t * +_fs_iso_stat_traverse (iso9660_t *p_iso, const iso9660_stat_t *_root, + char **splitpath) +{ + unsigned offset = 0; + uint8_t *_dirbuf = NULL; + int ret; + + if (!splitpath[0]) + { + iso9660_stat_t *p_stat; + unsigned int len=sizeof(iso9660_stat_t) + strlen(_root->filename)+1; + p_stat = calloc(1, len); + if (!p_stat) + { + cdio_warn("Couldn't calloc(1, %d)", len); + return NULL; + } + memcpy(p_stat, _root, len); + p_stat->rr.psz_symlink = calloc(1, p_stat->rr.i_symlink_max); + memcpy(p_stat->rr.psz_symlink, _root->rr.psz_symlink, + p_stat->rr.i_symlink_max); + return p_stat; + } + + if (_root->type == _STAT_FILE) + return NULL; + + cdio_assert (_root->type == _STAT_DIR); + + if (_root->size != ISO_BLOCKSIZE * _root->secsize) + { + cdio_warn ("bad size for ISO9660 directory (%ud) should be (%lu)!", + (unsigned) _root->size, + (unsigned long int) ISO_BLOCKSIZE * _root->secsize); + } + + _dirbuf = calloc(1, _root->secsize * ISO_BLOCKSIZE); + if (!_dirbuf) + { + cdio_warn("Couldn't calloc(1, %d)", _root->secsize * ISO_BLOCKSIZE); + return NULL; + } + + ret = iso9660_iso_seek_read (p_iso, _dirbuf, _root->lsn, _root->secsize); + if (ret!=ISO_BLOCKSIZE*_root->secsize) return NULL; + + while (offset < (_root->secsize * ISO_BLOCKSIZE)) + { + iso9660_dir_t *p_iso9660_dir = (void *) &_dirbuf[offset]; + iso9660_stat_t *p_stat; + int cmp; + + if (!iso9660_get_dir_len(p_iso9660_dir)) + { + offset++; + continue; + } + + p_stat = _iso9660_dir_to_statbuf (p_iso9660_dir, p_iso->b_xa, + p_iso->i_joliet_level); + + cmp = strcmp(splitpath[0], p_stat->filename); + + if ( 0 != cmp && 0 == p_iso->i_joliet_level + && yep != p_stat->rr.b3_rock ) { + char *trans_fname = NULL; + unsigned int i_trans_fname=strlen(p_stat->filename); + int trans_len; + + if (i_trans_fname) { + trans_fname = calloc(1, i_trans_fname+1); + if (!trans_fname) { + cdio_warn("can't allocate %lu bytes", + (long unsigned int) strlen(p_stat->filename)); + return NULL; + } + trans_len = iso9660_name_translate_ext(p_stat->filename, trans_fname, + p_iso->i_joliet_level); + cmp = strcmp(splitpath[0], trans_fname); + free(trans_fname); + } + } + + if (!cmp) { + iso9660_stat_t *ret_stat + = _fs_iso_stat_traverse (p_iso, p_stat, &splitpath[1]); + free(p_stat->rr.psz_symlink); + free(p_stat); + free (_dirbuf); + return ret_stat; + } + + free(p_stat->rr.psz_symlink); + free(p_stat); + + offset += iso9660_get_dir_len(p_iso9660_dir); + } + + cdio_assert (offset == (_root->secsize * ISO_BLOCKSIZE)); + + /* not found */ + free (_dirbuf); + return NULL; +} + +/*! + Get file status for psz_path into stat. NULL is returned on error. + */ +iso9660_stat_t * +iso9660_fs_stat (CdIo_t *p_cdio, const char psz_path[]) +{ + iso9660_stat_t *p_root; + char **p_psz_splitpath; + iso9660_stat_t *p_stat; + + if (!p_cdio) return NULL; + if (!psz_path) return NULL; + + p_root = _fs_stat_root (p_cdio); + + if (!p_root) return NULL; + + p_psz_splitpath = _cdio_strsplit (psz_path, '/'); + p_stat = _fs_stat_traverse (p_cdio, p_root, p_psz_splitpath); + free(p_root); + _cdio_strfreev (p_psz_splitpath); + + return p_stat; +} + +typedef iso9660_stat_t * (stat_root_t) (void *p_image); +typedef iso9660_stat_t * (stat_traverse_t) + (const void *p_image, const iso9660_stat_t *_root, char **splitpath); + +/*! + Get file status for psz_path into stat. NULL is returned on error. + pathname version numbers in the ISO 9660 + name are dropped, i.e. ;1 is removed and if level 1 ISO-9660 names + are lowercased. + */ +static iso9660_stat_t * +fs_stat_translate (void *p_image, stat_root_t stat_root, + stat_traverse_t stat_traverse, + const char psz_path[]) +{ + iso9660_stat_t *p_root; + char **p_psz_splitpath; + iso9660_stat_t *p_stat; + + if (!p_image) return NULL; + if (!psz_path) return NULL; + + p_root = stat_root (p_image); + if (!p_root) return NULL; + + p_psz_splitpath = _cdio_strsplit (psz_path, '/'); + p_stat = stat_traverse (p_image, p_root, p_psz_splitpath); + free(p_root); + _cdio_strfreev (p_psz_splitpath); + + return p_stat; +} + +iso9660_stat_t * +iso9660_fs_stat_translate (CdIo_t *p_cdio, const char psz_path[], + bool b_mode2) +{ + return fs_stat_translate(p_cdio, (stat_root_t *) _fs_stat_root, + (stat_traverse_t *) _fs_stat_traverse, + psz_path); +} + +/*! + Get file status for psz_path into stat. NULL is returned on error. + pathname version numbers in the ISO 9660 + name are dropped, i.e. ;1 is removed and if level 1 ISO-9660 names + are lowercased. + */ +iso9660_stat_t * +iso9660_ifs_stat_translate (iso9660_t *p_iso, const char psz_path[]) +{ + return fs_stat_translate(p_iso, (stat_root_t *) _ifs_stat_root, + (stat_traverse_t *) _fs_iso_stat_traverse, + psz_path); +} + + +/*! + Get file status for psz_path into stat. NULL is returned on error. + */ +iso9660_stat_t * +iso9660_ifs_stat (iso9660_t *p_iso, const char psz_path[]) +{ + iso9660_stat_t *p_root; + char **splitpath; + iso9660_stat_t *stat; + + if (!p_iso) return NULL; + if (!psz_path) return NULL; + + p_root = _ifs_stat_root (p_iso); + if (!p_root) return NULL; + + splitpath = _cdio_strsplit (psz_path, '/'); + stat = _fs_iso_stat_traverse (p_iso, p_root, splitpath); + free(p_root); + _cdio_strfreev (splitpath); + + return stat; +} + +/*! + Read psz_path (a directory) and return a list of iso9660_stat_t + of the files inside that. The caller must free the returned result. + + b_mode2 is historical. It is not used. +*/ +CdioList_t * +iso9660_fs_readdir (CdIo_t *p_cdio, const char psz_path[], bool b_mode2) +{ + generic_img_private_t *p_env; + iso9660_stat_t *p_stat; + + if (!p_cdio) return NULL; + if (!psz_path) return NULL; + + p_env = (generic_img_private_t *) p_cdio->env; + + p_stat = iso9660_fs_stat (p_cdio, psz_path); + if (!p_stat) return NULL; + + if (p_stat->type != _STAT_DIR) { + free(p_stat->rr.psz_symlink); + free(p_stat); + return NULL; + } + + { + unsigned offset = 0; + uint8_t *_dirbuf = NULL; + CdioList_t *retval = _cdio_list_new (); + + if (p_stat->size != ISO_BLOCKSIZE * p_stat->secsize) + { + cdio_warn ("bad size for ISO9660 directory (%ud) should be (%lu)!", + (unsigned) p_stat->size, + (unsigned long int) ISO_BLOCKSIZE * p_stat->secsize); + } + + _dirbuf = calloc(1, p_stat->secsize * ISO_BLOCKSIZE); + if (!_dirbuf) + { + cdio_warn("Couldn't calloc(1, %d)", p_stat->secsize * ISO_BLOCKSIZE); + return NULL; + } + + if (cdio_read_data_sectors (p_cdio, _dirbuf, p_stat->lsn, + ISO_BLOCKSIZE, p_stat->secsize)) + return NULL; + + while (offset < (p_stat->secsize * ISO_BLOCKSIZE)) + { + iso9660_dir_t *p_iso9660_dir = (void *) &_dirbuf[offset]; + iso9660_stat_t *p_iso9660_stat; + + if (!iso9660_get_dir_len(p_iso9660_dir)) + { + offset++; + continue; + } + + p_iso9660_stat = _iso9660_dir_to_statbuf(p_iso9660_dir, dunno, + p_env->i_joliet_level); + _cdio_list_append (retval, p_iso9660_stat); + + offset += iso9660_get_dir_len(p_iso9660_dir); + } + + cdio_assert (offset == (p_stat->secsize * ISO_BLOCKSIZE)); + + free (_dirbuf); + free (p_stat); + return retval; + } +} + +/*! + Read psz_path (a directory) and return a list of iso9660_stat_t + of the files inside that. The caller must free the returned result. +*/ +CdioList_t * +iso9660_ifs_readdir (iso9660_t *p_iso, const char psz_path[]) +{ + iso9660_stat_t *p_stat; + + if (!p_iso) return NULL; + if (!psz_path) return NULL; + + p_stat = iso9660_ifs_stat (p_iso, psz_path); + if (!p_stat) return NULL; + + if (p_stat->type != _STAT_DIR) { + free(p_stat->rr.psz_symlink); + free(p_stat); + return NULL; + } + + { + long int ret; + unsigned offset = 0; + uint8_t *_dirbuf = NULL; + CdioList_t *retval = _cdio_list_new (); + + if (p_stat->size != ISO_BLOCKSIZE * p_stat->secsize) + { + cdio_warn ("bad size for ISO9660 directory (%ud) should be (%lu)!", + (unsigned int) p_stat->size, + (unsigned long int) ISO_BLOCKSIZE * p_stat->secsize); + } + + _dirbuf = calloc(1, p_stat->secsize * ISO_BLOCKSIZE); + if (!_dirbuf) + { + cdio_warn("Couldn't calloc(1, %d)", p_stat->secsize * ISO_BLOCKSIZE); + return NULL; + } + + ret = iso9660_iso_seek_read (p_iso, _dirbuf, p_stat->lsn, p_stat->secsize); + if (ret != ISO_BLOCKSIZE*p_stat->secsize) return NULL; + + while (offset < (p_stat->secsize * ISO_BLOCKSIZE)) + { + iso9660_dir_t *p_iso9660_dir = (void *) &_dirbuf[offset]; + iso9660_stat_t *p_iso9660_stat; + + if (!iso9660_get_dir_len(p_iso9660_dir)) + { + offset++; + continue; + } + + p_iso9660_stat = _iso9660_dir_to_statbuf(p_iso9660_dir, p_iso->b_xa, + p_iso->i_joliet_level); + + if (p_iso9660_stat) + _cdio_list_append (retval, p_iso9660_stat); + + offset += iso9660_get_dir_len(p_iso9660_dir); + } + + free (_dirbuf); + + if (offset != (p_stat->secsize * ISO_BLOCKSIZE)) { + free (p_stat); + _cdio_list_free (retval, true); + return NULL; + } + + free (p_stat); + return retval; + } +} + +typedef CdioList_t * (iso9660_readdir_t) + (void *p_image, const char * psz_path); + +static iso9660_stat_t * +find_lsn_recurse (void *p_image, iso9660_readdir_t iso9660_readdir, + const char psz_path[], lsn_t lsn, + /*out*/ char **ppsz_full_filename) +{ + CdioList_t *entlist = iso9660_readdir (p_image, psz_path); + CdioList_t *dirlist = _cdio_list_new (); + CdioListNode_t *entnode; + + cdio_assert (entlist != NULL); + + /* iterate over each entry in the directory */ + + _CDIO_LIST_FOREACH (entnode, entlist) + { + iso9660_stat_t *statbuf = _cdio_list_node_data (entnode); + const char *psz_filename = (char *) statbuf->filename; + const unsigned int len = strlen(psz_path) + strlen(psz_filename)+2; + + if (*ppsz_full_filename != NULL) free(*ppsz_full_filename); + *ppsz_full_filename = calloc(1, len); + snprintf (*ppsz_full_filename, len, "%s%s/", psz_path, psz_filename); + + if (statbuf->type == _STAT_DIR + && strcmp ((char *) statbuf->filename, ".") + && strcmp ((char *) statbuf->filename, "..")) { + _cdio_list_append (dirlist, strdup(*ppsz_full_filename)); + } + + if (statbuf->lsn == lsn) { + unsigned int len=sizeof(iso9660_stat_t)+strlen(statbuf->filename)+1; + iso9660_stat_t *ret_stat = calloc(1, len); + if (!ret_stat) + { + cdio_warn("Couldn't calloc(1, %d)", len); + return NULL; + } + memcpy(ret_stat, statbuf, len); + _cdio_list_free (entlist, true); + _cdio_list_free (dirlist, true); + return ret_stat; + } + + } + + _cdio_list_free (entlist, true); + + /* now recurse/descend over directories encountered */ + + _CDIO_LIST_FOREACH (entnode, dirlist) + { + char *psz_path_prefix = _cdio_list_node_data (entnode); + iso9660_stat_t *ret_stat; + free(*ppsz_full_filename); + *ppsz_full_filename = NULL; + ret_stat = find_lsn_recurse (p_image, iso9660_readdir, + psz_path_prefix, lsn, + ppsz_full_filename); + + if (NULL != ret_stat) { + _cdio_list_free (dirlist, true); + return ret_stat; + } + } + + if (*ppsz_full_filename != NULL) { + free(*ppsz_full_filename); + *ppsz_full_filename = NULL; + } + _cdio_list_free (dirlist, true); + return NULL; +} + +/*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + Returns stat_t of entry if we found lsn, or NULL otherwise. + */ +iso9660_stat_t * +iso9660_fs_find_lsn(CdIo_t *p_cdio, lsn_t i_lsn) +{ + char *psz_full_filename = NULL; + return find_lsn_recurse (p_cdio, (iso9660_readdir_t *) iso9660_fs_readdir, + "/", i_lsn, &psz_full_filename); +} + +/*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + Returns stat_t of entry if we found lsn, or NULL otherwise. + */ +iso9660_stat_t * +iso9660_fs_find_lsn_with_path(CdIo_t *p_cdio, lsn_t i_lsn, + /*out*/ char **ppsz_full_filename) +{ + return find_lsn_recurse (p_cdio, (iso9660_readdir_t *) iso9660_fs_readdir, + "/", i_lsn, ppsz_full_filename); +} + +/*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + Returns stat_t of entry if we found lsn, or NULL otherwise. + */ +iso9660_stat_t * +iso9660_ifs_find_lsn(iso9660_t *p_iso, lsn_t i_lsn) +{ + char *psz_full_filename = NULL; + return find_lsn_recurse (p_iso, (iso9660_readdir_t *) iso9660_ifs_readdir, + "/", i_lsn, &psz_full_filename); +} + +/*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + Returns stat_t of entry if we found lsn, or NULL otherwise. + */ +iso9660_stat_t * +iso9660_ifs_find_lsn_with_path(iso9660_t *p_iso, lsn_t i_lsn, + /*out*/ char **ppsz_full_filename) +{ + return find_lsn_recurse (p_iso, (iso9660_readdir_t *) iso9660_ifs_readdir, + "/", i_lsn, ppsz_full_filename); +} + +/*! + Return true if ISO 9660 image has extended attrributes (XA). +*/ +bool +iso9660_ifs_is_xa (const iso9660_t * p_iso) +{ + if (!p_iso) return false; + return yep == p_iso->b_xa; +} diff --git a/lib/iso9660/iso9660_private.h b/lib/iso9660/iso9660_private.h new file mode 100644 index 00000000..79b368ac --- /dev/null +++ b/lib/iso9660/iso9660_private.h @@ -0,0 +1,83 @@ +/* + $Id: iso9660_private.h,v 1.5 2008/04/18 16:02:09 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2008 Rocky Bernstein + Copyright (C) 2000 Herbert Valerio Riedel + + See also iso9660.h by Eric Youngdale (1993). + + Copyright 1993 Yggdrasil Computing, Incorporated + Copyright (c) 1999,2000 J. Schilling + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __CDIO_ISO9660_PRIVATE_H__ +#define __CDIO_ISO9660_PRIVATE_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#define ISO_VERSION 1 + +PRAGMA_BEGIN_PACKED + +typedef struct iso_volume_descriptor_s { + uint8_t type; /**< 7.1.1 */ + char id[5]; /**< "CD001" (ISO_STANDARD_ID) */ + uint8_t version; /**< 7.1.1 */ + char data[2041]; +} GNUC_PACKED iso_volume_descriptor_t; + +#define iso_volume_descriptor_t_SIZEOF ISO_BLOCKSIZE + +#define iso9660_pvd_t_SIZEOF ISO_BLOCKSIZE + +/* + * XXX JS: The next structure has an odd length! + * Some compilers (e.g. on Sun3/mc68020) padd the structures to even length. + * For this reason, we cannot use sizeof (struct iso_path_table) or + * sizeof (struct iso_directory_record) to compute on disk sizes. + * Instead, we use offsetof(..., name) and add the name size. + * See mkisofs.h + */ + +/** We use this to help us look up the parent inode numbers. */ +typedef struct iso_path_table_s { + uint8_t name_len; /**< 7.1.1 */ + uint8_t xa_len; /**< 7.1.1 */ + uint32_t extent; /**< 7.3.1/7.3.2 */ + uint16_t parent; /**< 7.2.1/7.2.2 */ + char name[EMPTY_ARRAY_SIZE]; +} GNUC_PACKED iso_path_table_t; + +#define iso_path_table_t_SIZEOF 8 + +#define iso9660_dir_t_SIZEOF 33 + +PRAGMA_END_PACKED + +#endif /* __CDIO_ISO9660_PRIVATE_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/iso9660/libiso9660.sym b/lib/iso9660/libiso9660.sym new file mode 100644 index 00000000..7f9d3ad3 --- /dev/null +++ b/lib/iso9660/libiso9660.sym @@ -0,0 +1,81 @@ +iso_enums1 +iso_extension_enums +iso_flag_enums +iso_vd_enums +iso_rock_enums +iso_rock_nm_flag +iso_rock_sl_flag +iso_rock_tf_flag +iso9660_close +iso9660_dir_add_entry_su +iso9660_dir_calc_record_size +iso9660_dir_init_new +iso9660_dir_init_new_su +iso9660_dir_to_name +iso9660_dirname_valid_p +iso9660_find_fs_lsn +iso9660_fs_find_lsn +iso9660_fs_find_lsn_with_path +iso9660_fs_read_pvd +iso9660_fs_read_superblock +iso9660_fs_readdir +iso9660_fs_stat +iso9660_fs_stat_translate +iso9660_get_application_id +iso9660_get_dir_len +iso9660_get_dtime +iso9660_get_ltime +iso9660_get_posix_filemode +iso9660_get_posix_filemode_from_rock +iso9660_get_posix_filemode_from_xa +iso9660_get_preparer_id +iso9660_get_publisher_id +iso9660_get_pvd_block_size +iso9660_get_pvd_id +iso9660_get_pvd_space_size +iso9660_get_pvd_type +iso9660_get_pvd_version +iso9660_get_rock_attr_str +iso9660_get_root_lsn +iso9660_get_system_id +iso9660_get_volume_id +iso9660_get_volumeset_id +iso9660_get_xa_attr_str +iso9660_ifs_find_lsn +iso9660_ifs_find_lsn_with_path +iso9660_ifs_fuzzy_read_superblock +iso9660_ifs_get_application_id +iso9660_ifs_get_joliet_level +iso9660_ifs_get_preparer_id +iso9660_ifs_get_publisher_id +iso9660_ifs_get_system_id +iso9660_ifs_get_volume_id +iso9660_ifs_get_volumeset_id +iso9660_ifs_is_xa +iso9660_ifs_read_pvd +iso9660_ifs_read_superblock +iso9660_ifs_readdir +iso9660_ifs_stat +iso9660_ifs_stat_translate +iso9660_is_achar +iso9660_is_dchar +iso9660_iso_seek_read +iso9660_name_translate +iso9660_name_translate_ext +iso9660_open +iso9660_open_ext +iso9660_open_fuzzy +iso9660_open_fuzzy_ext +iso9660_pathname_isofy +iso9660_pathname_valid_p +iso9660_pathtable_get_size +iso9660_pathtable_init +iso9660_pathtable_l_add_entry +iso9660_pathtable_m_add_entry +iso9660_set_dtime +iso9660_set_evd +iso9660_set_ltime +iso9660_set_pvd +iso9660_strncpy_pad +iso9660_xa_init +ISO_STANDARD_ID diff --git a/lib/iso9660/rock.c b/lib/iso9660/rock.c new file mode 100644 index 00000000..415d836a --- /dev/null +++ b/lib/iso9660/rock.c @@ -0,0 +1,619 @@ +/* + $Id: rock.c,v 1.18 2008/04/18 16:02:09 karl Exp $ + + Copyright (C) 2005, 2008 Rocky Bernstein + Adapted from GNU/Linux fs/isofs/rock.c (C) 1992, 1993 Eric Youngdale + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* Rock Ridge Extensions to iso9660 */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif + +#ifdef HAVE_STDLIB_H +# include +#endif + +#ifdef HAVE_SYS_STAT_H +# include +#endif + +#include +#include +#include + +#define CDIO_MKDEV(ma,mi) ((ma)<<16 | (mi)) + +enum iso_rock_enums iso_rock_enums; +iso_rock_nm_flag_t iso_rock_nm_flag; +iso_rock_sl_flag_t iso_rock_sl_flag; +iso_rock_tf_flag_t iso_rock_tf_flag; + +/* Our own realloc routine tailored for the iso9660_stat_t symlink + field. I can't figure out how to make realloc() work without + valgrind complaint. +*/ +static bool +realloc_symlink(/*in/out*/ iso9660_stat_t *p_stat, uint8_t i_grow) +{ + if (!p_stat->rr.i_symlink) { + const uint16_t i_max = 2*i_grow+1; + p_stat->rr.psz_symlink = (char *) calloc(1, i_max); + p_stat->rr.i_symlink_max = i_max; + return (NULL != p_stat->rr.psz_symlink); + } else { + unsigned int i_needed = p_stat->rr.i_symlink + i_grow ; + if ( i_needed <= p_stat->rr.i_symlink_max) + return true; + else { + char * psz_newsymlink = (char *) calloc(1, 2*i_needed); + if (!psz_newsymlink) return false; + p_stat->rr.i_symlink_max = 2*i_needed; + memcpy(psz_newsymlink, p_stat->rr.psz_symlink, p_stat->rr.i_symlink); + free(p_stat->rr.psz_symlink); + p_stat->rr.psz_symlink = psz_newsymlink; + return true; + } + } +} + +/* These functions are designed to read the system areas of a directory record + * and extract relevant information. There are different functions provided + * depending upon what information we need at the time. One function fills + * out an inode structure, a second one extracts a filename, a third one + * returns a symbolic link name, and a fourth one returns the extent number + * for the file. */ + +#define SIG(A,B) ((A) | ((B) << 8)) /* isonum_721() */ + + +/* This is a way of ensuring that we have something in the system + use fields that is compatible with Rock Ridge */ +#define CHECK_SP(FAIL) \ + if(rr->u.SP.magic[0] != 0xbe) FAIL; \ + if(rr->u.SP.magic[1] != 0xef) FAIL; \ + p_stat->rr.s_rock_offset = rr->u.SP.skip; +/* We define a series of macros because each function must do exactly the + same thing in certain places. We use the macros to ensure that everything + is done correctly */ + +#define CONTINUE_DECLS \ + int cont_extent = 0, cont_offset = 0, cont_size = 0; \ + void *buffer = NULL + +#define CHECK_CE \ + { cont_extent = from_733(*rr->u.CE.extent); \ + cont_offset = from_733(*rr->u.CE.offset); \ + cont_size = from_733(*rr->u.CE.size); } + +#define SETUP_ROCK_RIDGE(DE,CHR,LEN) \ + { \ + LEN= sizeof(iso9660_dir_t) + DE->filename_len; \ + if(LEN & 1) LEN++; \ + CHR = ((unsigned char *) DE) + LEN; \ + LEN = *((unsigned char *) DE) - LEN; \ + if (0xff != p_stat->rr.s_rock_offset) \ + { \ + LEN -= p_stat->rr.s_rock_offset; \ + CHR += p_stat->rr.s_rock_offset; \ + if (LEN<0) LEN=0; \ + } \ + } + +/* Copy a long or short time from the iso_rock_tf_t into + the specified field of a iso_rock_statbuf_t. + non-paramater variables are p_stat, rr, and cnt. +*/ +#define add_time(FLAG, TIME_FIELD) \ + if (rr->u.TF.flags & FLAG) { \ + p_stat->rr.TIME_FIELD.b_used = true; \ + p_stat->rr.TIME_FIELD.b_longdate = \ + (0 != (rr->u.TF.flags & ISO_ROCK_TF_LONG_FORM)); \ + if (p_stat->rr.TIME_FIELD.b_longdate) { \ + memcpy(&(p_stat->rr.TIME_FIELD.t.ltime), \ + &(rr->u.TF.time_bytes[cnt]), \ + sizeof(iso9660_ltime_t)); \ + cnt += sizeof(iso9660_ltime_t); \ + } else { \ + memcpy(&(p_stat->rr.TIME_FIELD.t.dtime), \ + &(rr->u.TF.time_bytes[cnt]), \ + sizeof(iso9660_dtime_t)); \ + cnt += sizeof(iso9660_dtime_t); \ + } \ + } + +/*! + Get + @return length of name field; 0: not found, -1: to be ignored +*/ +int +get_rock_ridge_filename(iso9660_dir_t * p_iso9660_dir, + /*out*/ char * psz_name, + /*in/out*/ iso9660_stat_t *p_stat) +{ + int len; + unsigned char *chr; + int symlink_len = 0; + CONTINUE_DECLS; + int i_namelen = 0; + int truncate=0; + + if (!p_stat || nope == p_stat->rr.b3_rock) return 0; + *psz_name = 0; + + SETUP_ROCK_RIDGE(p_iso9660_dir, chr, len); + /*repeat:*/ + { + iso_extension_record_t * rr; + int sig; + int rootflag; + + while (len > 1){ /* There may be one byte for padding somewhere */ + rr = (iso_extension_record_t *) chr; + if (rr->len == 0) goto out; /* Something got screwed up here */ + sig = *chr+(*(chr+1) << 8); + chr += rr->len; + len -= rr->len; + + switch(sig){ + case SIG('S','P'): + CHECK_SP(goto out); + break; + case SIG('C','E'): + { + iso711_t i_fname = from_711(p_iso9660_dir->filename_len); + if ('\0' == p_iso9660_dir->filename[0] && 1 == i_fname) + break; + if ('\1' == p_iso9660_dir->filename[0] && 1 == i_fname) + break; + } + CHECK_CE; + break; + case SIG('E','R'): + p_stat->rr.b3_rock = yep; + cdio_debug("ISO 9660 Extensions: "); + { + int p; + for(p=0;pu.ER.len_id;p++) cdio_debug("%c",rr->u.ER.data[p]); + } + break; + case SIG('N','M'): + /* Alternate name */ + p_stat->rr.b3_rock = yep; + if (truncate) break; + if (rr->u.NM.flags & ISO_ROCK_NM_PARENT) { + i_namelen = sizeof(".."); + strncat(psz_name, "..", i_namelen); + } else if (rr->u.NM.flags & ISO_ROCK_NM_CURRENT) { + i_namelen = sizeof("."); + strncat(psz_name, ".", i_namelen); + break; + } + + if (rr->u.NM.flags & ~1) { + cdio_info("Unsupported NM flag settings (%d)",rr->u.NM.flags); + break; + } + if((strlen(psz_name) + rr->len - 5) >= 254) { + truncate = 1; + break; + } + strncat(psz_name, rr->u.NM.name, rr->len - 5); + i_namelen += rr->len - 5; + break; + case SIG('P','X'): + /* POSIX file attributes */ + p_stat->rr.st_mode = from_733(rr->u.PX.st_mode); + p_stat->rr.st_nlinks = from_733(rr->u.PX.st_nlinks); + p_stat->rr.st_uid = from_733(rr->u.PX.st_uid); + p_stat->rr.st_gid = from_733(rr->u.PX.st_gid); + p_stat->rr.b3_rock = yep; + break; + case SIG('S','L'): + { + /* Symbolic link */ + uint8_t slen; + iso_rock_sl_part_t * p_sl; + iso_rock_sl_part_t * p_oldsl; + slen = rr->len - 5; + p_sl = &rr->u.SL.link; + p_stat->rr.i_symlink = symlink_len; + while (slen > 1){ + rootflag = 0; + switch(p_sl->flags &~1){ + case 0: + realloc_symlink(p_stat, p_sl->len); + memcpy(&(p_stat->rr.psz_symlink[p_stat->rr.i_symlink]), + p_sl->text, p_sl->len); + p_stat->rr.i_symlink += p_sl->len; + break; + case 4: + realloc_symlink(p_stat, 1); + p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '.'; + /* continue into next case. */ + case 2: + realloc_symlink(p_stat, 1); + p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '.'; + break; + case 8: + rootflag = 1; + realloc_symlink(p_stat, 1); + p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '/'; + break; + default: + cdio_warn("Symlink component flag not implemented"); + } + slen -= p_sl->len + 2; + p_oldsl = p_sl; + p_sl = (iso_rock_sl_part_t *) (((char *) p_sl) + p_sl->len + 2); + + if (slen < 2) { + if (((rr->u.SL.flags & 1) != 0) && ((p_oldsl->flags & 1) == 0)) + p_stat->rr.i_symlink += 1; + break; + } + + /* + * If this component record isn't continued, then append a '/'. + */ + if (!rootflag && (p_oldsl->flags & 1) == 0) { + realloc_symlink(p_stat, 1); + p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '/'; + } + } + } + symlink_len = p_stat->rr.i_symlink; + realloc_symlink(p_stat, 1); + p_stat->rr.psz_symlink[symlink_len]='\0'; + break; + case SIG('R','E'): + free(buffer); + return -1; + case SIG('T','F'): + /* Time stamp(s) for a file */ + { + int cnt = 0; + add_time(ISO_ROCK_TF_CREATE, create); + add_time(ISO_ROCK_TF_MODIFY, modify); + add_time(ISO_ROCK_TF_ACCESS, access); + add_time(ISO_ROCK_TF_ATTRIBUTES, attributes); + add_time(ISO_ROCK_TF_BACKUP, backup); + add_time(ISO_ROCK_TF_EXPIRATION, expiration); + add_time(ISO_ROCK_TF_EFFECTIVE, effective); + p_stat->rr.b3_rock = yep; + break; + } + default: + break; + } + } + } + free(buffer); + return i_namelen; /* If 0, this file did not have a NM field */ + out: + free(buffer); + return 0; +} + +static int +parse_rock_ridge_stat_internal(iso9660_dir_t *p_iso9660_dir, + iso9660_stat_t *p_stat, int regard_xa) +{ + int len; + unsigned char * chr; + int symlink_len = 0; + CONTINUE_DECLS; + + if (nope == p_stat->rr.b3_rock) return 0; + + SETUP_ROCK_RIDGE(p_iso9660_dir, chr, len); + if (regard_xa) + { + chr+=14; + len-=14; + if (len<0) len=0; + } + + /* repeat:*/ + { + int sig; + iso_extension_record_t * rr; + int rootflag; + + while (len > 1){ /* There may be one byte for padding somewhere */ + rr = (iso_extension_record_t *) chr; + if (rr->len == 0) goto out; /* Something got screwed up here */ + sig = from_721(*chr); + chr += rr->len; + len -= rr->len; + + switch(sig){ + case SIG('S','P'): + CHECK_SP(goto out); + break; + case SIG('C','E'): + CHECK_CE; + break; + case SIG('E','R'): + p_stat->rr.b3_rock = yep; + cdio_debug("ISO 9660 Extensions: "); + { int p; + for(p=0;pu.ER.len_id;p++) cdio_debug("%c",rr->u.ER.data[p]); + } + break; + case SIG('P','X'): + p_stat->rr.st_mode = from_733(rr->u.PX.st_mode); + p_stat->rr.st_nlinks = from_733(rr->u.PX.st_nlinks); + p_stat->rr.st_uid = from_733(rr->u.PX.st_uid); + p_stat->rr.st_gid = from_733(rr->u.PX.st_gid); + break; + case SIG('P','N'): + /* Device major,minor number */ + { int32_t high, low; + high = from_733(rr->u.PN.dev_high); + low = from_733(rr->u.PN.dev_low); + /* + * The Rock Ridge standard specifies that if sizeof(dev_t) <= 4, + * then the high field is unused, and the device number is completely + * stored in the low field. Some writers may ignore this subtlety, + * and as a result we test to see if the entire device number is + * stored in the low field, and use that. + */ + if((low & ~0xff) && high == 0) { + p_stat->rr.i_rdev = CDIO_MKDEV(low >> 8, low & 0xff); + } else { + p_stat->rr.i_rdev = CDIO_MKDEV(high, low); + } + } + break; + case SIG('T','F'): + /* Time stamp(s) for a file */ + { + int cnt = 0; + add_time(ISO_ROCK_TF_CREATE, create); + add_time(ISO_ROCK_TF_MODIFY, modify); + add_time(ISO_ROCK_TF_ACCESS, access); + add_time(ISO_ROCK_TF_ATTRIBUTES, attributes); + add_time(ISO_ROCK_TF_BACKUP, backup); + add_time(ISO_ROCK_TF_EXPIRATION, expiration); + add_time(ISO_ROCK_TF_EFFECTIVE, effective); + p_stat->rr.b3_rock = yep; + break; + } + case SIG('S','L'): + { + /* Symbolic link */ + uint8_t slen; + iso_rock_sl_part_t * p_sl; + iso_rock_sl_part_t * p_oldsl; + slen = rr->len - 5; + p_sl = &rr->u.SL.link; + p_stat->rr.i_symlink = symlink_len; + while (slen > 1){ + rootflag = 0; + switch(p_sl->flags &~1){ + case 0: + realloc_symlink(p_stat, p_sl->len); + memcpy(&(p_stat->rr.psz_symlink[p_stat->rr.i_symlink]), + p_sl->text, p_sl->len); + p_stat->rr.i_symlink += p_sl->len; + break; + case 4: + realloc_symlink(p_stat, 1); + p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '.'; + /* continue into next case. */ + case 2: + realloc_symlink(p_stat, 1); + p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '.'; + break; + case 8: + rootflag = 1; + realloc_symlink(p_stat, 1); + p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '/'; + p_stat->rr.i_symlink++; + break; + default: + cdio_warn("Symlink component flag not implemented"); + } + slen -= p_sl->len + 2; + p_oldsl = p_sl; + p_sl = (iso_rock_sl_part_t *) (((char *) p_sl) + p_sl->len + 2); + + if (slen < 2) { + if (((rr->u.SL.flags & 1) != 0) && ((p_oldsl->flags & 1) == 0)) + p_stat->rr.i_symlink += 1; + break; + } + + /* + * If this component record isn't continued, then append a '/'. + */ + if (!rootflag && (p_oldsl->flags & 1) == 0) { + realloc_symlink(p_stat, 1); + p_stat->rr.psz_symlink[p_stat->rr.i_symlink++] = '/'; + } + } + } + symlink_len = p_stat->rr.i_symlink; + realloc_symlink(p_stat, 1); + p_stat->rr.psz_symlink[symlink_len]='\0'; + break; + case SIG('R','E'): + cdio_warn("Attempt to read p_stat for relocated directory"); + goto out; +#if FINISHED + case SIG('C','L'): + { + iso9660_stat_t * reloc; + ISOFS_I(p_stat)->i_first_extent = from_733(rr->u.CL.location); + reloc = isofs_iget(p_stat->rr.i_sb, p_stat->rr.i_first_extent, 0); + if (!reloc) + goto out; + p_stat->rr.st_mode = reloc->st_mode; + p_stat->rr.st_nlinks = reloc->st_nlinks; + p_stat->rr.st_uid = reloc->st_uid; + p_stat->rr.st_gid = reloc->st_gid; + p_stat->rr.i_rdev = reloc->i_rdev; + p_stat->rr.i_symlink = reloc->i_symlink; + p_stat->rr.i_blocks = reloc->i_blocks; + p_stat->rr.i_atime = reloc->i_atime; + p_stat->rr.i_ctime = reloc->i_ctime; + p_stat->rr.i_mtime = reloc->i_mtime; + iput(reloc); + } + break; +#endif + default: + break; + } + } + } + out: + free(buffer); + return 0; +} + +int +parse_rock_ridge_stat(iso9660_dir_t *p_iso9660_dir, + /*out*/ iso9660_stat_t *p_stat) +{ + int result; + + if (!p_stat) return 0; + + result = parse_rock_ridge_stat_internal(p_iso9660_dir, p_stat, 0); + /* if Rock-Ridge flag was reset and we didn't look for attributes + * behind eventual XA attributes, have a look there */ + if (0xFF == p_stat->rr.s_rock_offset && nope != p_stat->rr.b3_rock) { + result = parse_rock_ridge_stat_internal(p_iso9660_dir, p_stat, 14); + } + return result; +} + +#define BUF_COUNT 16 +#define BUF_SIZE sizeof("drwxrwxrwx") + +/* Return a pointer to a internal free buffer */ +static char * +_getbuf (void) +{ + static char _buf[BUF_COUNT][BUF_SIZE]; + static int _i = -1; + + _i++; + _i %= BUF_COUNT; + + memset (_buf[_i], 0, BUF_SIZE); + + return _buf[_i]; +} + +/*! + Returns a string which interpreting the POSIX mode st_mode. + For example: + \verbatim + drwxrws--- + -rw-rw-r-- + lrwxrwxrwx + \endverbatim + + A description of the characters in the string follows + The 1st character is either "b" for a block device, + "c" for a character device, "d" if the entry is a directory, "l" for + a symbolic link, "p" for a pipe or FIFO, "s" for a "socket", + or "-" if none of the these. + + The 2nd to 4th characters refer to permissions for a user while the + the 5th to 7th characters refer to permissions for a group while, and + the 8th to 10h characters refer to permissions for everyone. + + In each of these triplets the first character (2, 5, 8) is "r" if + the entry is allowed to be read. + + The second character of a triplet (3, 6, 9) is "w" if the entry is + allowed to be written. + + The third character of a triplet (4, 7, 10) is "x" if the entry is + executable but not user (for character 4) or group (for characters + 6) settable and "s" if the item has the corresponding user/group set. + + For a directory having an executable property on ("x" or "s") means + the directory is allowed to be listed or "searched". If the execute + property is not allowed for a group or user but the corresponding + group/user is set "S" indicates this. If none of these properties + holds the "-" indicates this. +*/ +const char * +iso9660_get_rock_attr_str(posix_mode_t st_mode) +{ + char *result = _getbuf(); + + if (S_ISBLK(st_mode)) + result[ 0] = 'b'; + else if (S_ISDIR(st_mode)) + result[ 0] = 'd'; + else if (S_ISCHR(st_mode)) + result[ 0] = 'c'; + else if (S_ISLNK(st_mode)) + result[ 0] = 'l'; + else if (S_ISFIFO(st_mode)) + result[ 0] = 'p'; + else if (S_ISSOCK(st_mode)) + result[ 0] = 's'; + /* May eventually fill in others.. */ + else + result[ 0] = '-'; + + result[ 1] = (st_mode & ISO_ROCK_IRUSR) ? 'r' : '-'; + result[ 2] = (st_mode & ISO_ROCK_IWUSR) ? 'w' : '-'; + + if (st_mode & ISO_ROCK_ISUID) + result[ 3] = (st_mode & ISO_ROCK_IXUSR) ? 's' : 'S'; + else + result[ 3] = (st_mode & ISO_ROCK_IXUSR) ? 'x' : '-'; + + result[ 4] = (st_mode & ISO_ROCK_IRGRP) ? 'r' : '-'; + result[ 5] = (st_mode & ISO_ROCK_IWGRP) ? 'w' : '-'; + + if (st_mode & ISO_ROCK_ISGID) + result[ 6] = (st_mode & ISO_ROCK_IXGRP) ? 's' : 'S'; + else + result[ 6] = (st_mode & ISO_ROCK_IXGRP) ? 'x' : '-'; + + result[ 7] = (st_mode & ISO_ROCK_IROTH) ? 'r' : '-'; + result[ 8] = (st_mode & ISO_ROCK_IWOTH) ? 'w' : '-'; + result[ 9] = (st_mode & ISO_ROCK_IXOTH) ? 'x' : '-'; + + result[11] = '\0'; + + return result; +} + +/*! + Returns POSIX mode bitstring for a given file. +*/ +mode_t +iso9660_get_posix_filemode_from_rock(const iso_rock_statbuf_t *rr) +{ + return (mode_t) rr->st_mode; +} + + diff --git a/lib/iso9660/xa.c b/lib/iso9660/xa.c new file mode 100644 index 00000000..cdc0d5e6 --- /dev/null +++ b/lib/iso9660/xa.c @@ -0,0 +1,183 @@ +/* + $Id: xa.c,v 1.8 2008/04/24 07:28:00 rocky Exp $ + + Copyright (C) 2003, 2005, 2008 Rocky Bernstein + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +/*! String inside frame which identifies XA attributes. Note should + come *before* public headers which does a #define of + this name. +*/ +const char ISO_XA_MARKER_STRING[] = {'C', 'D', '-', 'X', 'A', '0', '0', '1'}; + +/* Public headers */ +#include +#include +#include + +/* Private headers */ +#include "cdio_assert.h" + +/** The below variable is trickery to force enum symbol values to be + recorded in debug symbol tables. It is used to allow one to refer + to the enumeration value names in the typedefs above in a debugger + and debugger expressions. +*/ +xa_misc_enum_t debugger_xa_misc_enum; + +#define BUF_COUNT 16 +#define BUF_SIZE 80 + +/* Return a pointer to a internal free buffer */ +static char * +_getbuf (void) +{ + static char _buf[BUF_COUNT][BUF_SIZE]; + static int _num = -1; + + _num++; + _num %= BUF_COUNT; + + memset (_buf[_num], 0, BUF_SIZE); + + return _buf[_num]; +} + +/*! + Returns a string which interpreting the extended attribute xa_attr. + For example: + \verbatim + d---1xrxrxr + ---2--r-r-r + -a--1xrxrxr + \endverbatim + + A description of the characters in the string follows + The 1st character is either "d" if the entry is a directory, or "-" if not. + The 2nd character is either "a" if the entry is CDDA (audio), or "-" if not. + The 3rd character is either "i" if the entry is interleaved, or "-" if not. + The 4th character is either "2" if the entry is mode2 form2 or "-" if not. + The 5th character is either "1" if the entry is mode2 form1 or "-" if not. + Note that an entry will either be in mode2 form1 or mode form2. That + is you will either see "2-" or "-1" in the 4th & 5th positions. + + The 6th and 7th characters refer to permissions for a user while the + the 8th and 9th characters refer to permissions for a group while, and + the 10th and 11th characters refer to permissions for a others. + + In each of these pairs the first character (6, 8, 10) is "x" if the + entry is executable. For a directory this means the directory is + allowed to be listed or "searched". + The second character of a pair (7, 9, 11) is "r" if the entry is allowed + to be read. +*/ + +const char * +iso9660_get_xa_attr_str (uint16_t xa_attr) +{ + char *result = _getbuf(); + + xa_attr = uint16_from_be (xa_attr); + + result[ 0] = (xa_attr & XA_ATTR_DIRECTORY) ? 'd' : '-'; + result[ 1] = (xa_attr & XA_ATTR_CDDA) ? 'a' : '-'; + result[ 2] = (xa_attr & XA_ATTR_INTERLEAVED) ? 'i' : '-'; + result[ 3] = (xa_attr & XA_ATTR_MODE2FORM2) ? '2' : '-'; + result[ 4] = (xa_attr & XA_ATTR_MODE2FORM1) ? '1' : '-'; + + result[ 5] = (xa_attr & XA_PERM_XUSR) ? 'x' : '-'; + result[ 6] = (xa_attr & XA_PERM_RUSR) ? 'r' : '-'; + + result[ 7] = (xa_attr & XA_PERM_XGRP) ? 'x' : '-'; + result[ 8] = (xa_attr & XA_PERM_RGRP) ? 'r' : '-'; + + /* Hack alert: wonder if this should be ROTH and XOTH? */ + result[ 9] = (xa_attr & XA_PERM_XSYS) ? 'x' : '-'; + result[10] = (xa_attr & XA_PERM_RSYS) ? 'r' : '-'; + + result[11] = '\0'; + + return result; +} + +iso9660_xa_t * +iso9660_xa_init (iso9660_xa_t *_xa, uint16_t uid, uint16_t gid, uint16_t attr, + uint8_t filenum) +{ + cdio_assert (_xa != NULL); + + _xa->user_id = uint16_to_be (uid); + _xa->group_id = uint16_to_be (gid); + _xa->attributes = uint16_to_be (attr); + + _xa->signature[0] = 'X'; + _xa->signature[1] = 'A'; + + _xa->filenum = filenum; + + _xa->reserved[0] + = _xa->reserved[1] + = _xa->reserved[2] + = _xa->reserved[3] + = _xa->reserved[4] = 0x00; + + return _xa; +} + +/*! + Returns POSIX mode bitstring for a given file. +*/ +posix_mode_t +iso9660_get_posix_filemode_from_xa(uint16_t i_perms) +{ + posix_mode_t mode = 0; + + if (i_perms & XA_PERM_RUSR) mode |= S_IRUSR; + if (i_perms & XA_PERM_XUSR) mode |= S_IXUSR; + +#ifdef S_IRGRP + if (i_perms & XA_PERM_RGRP) mode |= S_IRGRP; +#endif +#ifdef S_IXGRP + if (i_perms & XA_PERM_XGRP) mode |= S_IXGRP; +#endif + +#ifdef S_IROTH + if (i_perms & XA_PERM_ROTH) mode |= S_IROTH; +#endif +#ifdef S_IXOTH + if (i_perms & XA_PERM_XOTH) mode |= S_IXOTH; +#endif + + if (i_perms & XA_ATTR_DIRECTORY) mode |= S_IFDIR; + + return mode; +} + diff --git a/lib/paranoia/.cvsignore b/lib/paranoia/.cvsignore new file mode 100644 index 00000000..c17b1b9e --- /dev/null +++ b/lib/paranoia/.cvsignore @@ -0,0 +1,8 @@ +.deps +.libs +Makefile +Makefile.in +*.o +*.lo +*.la +*.la.ver diff --git a/lib/paranoia/.gitignore b/lib/paranoia/.gitignore new file mode 100644 index 00000000..f53478a0 --- /dev/null +++ b/lib/paranoia/.gitignore @@ -0,0 +1,3 @@ +/.deps +/Makefile +/Makefile.in diff --git a/lib/paranoia/Makefile.am b/lib/paranoia/Makefile.am new file mode 100644 index 00000000..318419e7 --- /dev/null +++ b/lib/paranoia/Makefile.am @@ -0,0 +1,154 @@ +# $Id: Makefile.am,v 1.10 2008/10/20 01:25:15 rocky Exp $ +# +# Copyright (C) 2004, 2006, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +######################################################## +# Things to make the libcdio_paranoia library +######################################################## + +# +# From libtool documentation amended with guidance from N. Boullis: +# +# 1. Start with version information of `0:0:0' for each libtool library. +# +# 2. It is probably not a good idea to update the version information +# several times between public releases, but rather once per public +# release. (This seems to be more an aesthetic consideration than +# a hard technical one.) +# +# 3. If the library source code has changed at all since the last +# update, then increment REVISION (`C:R:A' becomes `C:R+1:A'). +# +# 4. If any interfaces have been added, removed, or changed since the +# last update, increment CURRENT, and set REVISION to 0. +# +# 5. If any interfaces have been added since the last public release, +# then increment AGE. +# +# 6. If any interfaces have been removed or changed since the last +# public release, then set AGE to 0. A changed interface means an +# incompatibility with previous versions. + +EXTRA_DIST = libcdio_paranoia.sym + +libcdio_paranoia_la_CURRENT = 0 +libcdio_paranoia_la_REVISION = 3 +libcdio_paranoia_la_AGE = 0 + +noinst_HEADERS = gap.h isort.h overlap.h p_block.h + +libcdio_paranoia_sources = gap.c isort.c overlap.c overlap.h \ + p_block.c paranoia.c + +lib_LTLIBRARIES = libcdio_paranoia.la + +libcdio_paranoia_la_SOURCES = $(libcdio_paranoia_sources) +libcdio_paranoia_la_ldflags = -version-info $(libcdio_paranoia_la_CURRENT):$(libcdio_paranoia_la_REVISION):$(libcdio_paranoia_la_AGE) @LT_NO_UNDEFINED@ + +INCLUDES = $(LIBCDIO_CFLAGS) + +FLAGS=@LIBCDIO_CFLAGS@ @TYPESIZES@ @CFLAGS@ -I.. -I../.. +OPT=$(FLAGS) +DEBUG=$(FLAGS) + +## SUFFIXES = .t +## TFILES = isort.t gap.t p_block.t paranoia.t +##test: $(TFILES) +##.c.t: +## $(CC) -g -DTEST $(DEBUG) -o $@ $< $(LIBS) +## $@ +##debug: +## $(MAKE) libcdio_paranoia.a CFLAGS="$(DEBUG)" + +LIBS = $(LIBCDIO_LIBS) $(LIBCDIO_CDDA_LIBS) + + +######################################################## +# Things to version the symbols in the libraries +######################################################## + +# An explanation of the versioning problem from Nicolas Boullis and +# the versioned symbol solution he uses below... +# +# Currently, libvcdinfo uses the cdio_open function from libcdio. +# Let's imagine a program foobar that uses both the vcdinfo_open +# function from libvcdinfo and the cdio_open function from libcdio. + +# Currently, libcdio has SONAME libcdio.so.0, libvcdinfo has SONAME +# libvcdinfo.so.0 and requires libcdio.so.0, and foobar requires both +# libvcdinfo.so.0 and libcdio.so.0. Everything looks fine. +# +# Now, for some reason, you decide to change the cdio_open function. +# That's your right, but you have to bump the CURRENT version and (if I +# understand it correctly, athough this is not that clear in libtool's +# documentation) set the AGE to 0. Anyway, this bumps the SONAME, which is +# sane since the interface changes incompatibly. + +# Now, you have a new libcdio with SONAME libcdio.so.1. But libvcdinfo and +# foobar still require libcdio.so.0. Everything is still fine. + +# Now, after some minor changes, the author of foobar recompiles foobar. +# Then, foobar now requires libvcdinfo.so.0 and libcdio.so.1. And foobar +# now segfaults... + +# What is happening? When you run foobar, if brings both libvcdinfo.so.0 +# and libcdio.so.1, but libvcdinfo.so.0 also brings libcdio.so.0. So you +# have both libcdio.so.0 and libcdio.so.1 that bring their symbols to the +# global namespace. Hence, you have to incompatible versions of the +# cdio_open function in the name space. When foobar calls cdio_open, it +# may choose the wrong function, and segfaults... + +# With versioned symbols, the cdio_open function from libcdio.so.0 may be +# known as (something that looks like) cdio_open@@CDIO_0. An the cdio_open +# function from libcdio.so.1 as cdio_open@@CDIO_1. Both versions of +# libcdio would still be brought in by the most recent foobar, but foobar +# (and libvcdinfo) know which versioned function to use and then use the +# good one. + + +# This is some simple versioning where every symbol is versioned with +# something that looks like the SONAME of the library. More complex (and +# better) versioning is possible; it is for example what is used by glibc. +# But good complex versioning is something that requires much more +# work... + + +# The below is a impliments symbol versioning. First of all, I +# compute MAJOR as CURENT - AGE; that is what is used within libtool +# (at least on GNU/Linux systems) for the number in the SONAME. The +# nm command gives the list of symbols known in each of the object +# files that will be part of the shared library. And the sed command +# extracts from this list those symbols that will be shared. (This sed +# command comes from libtool.) + +libcdio_paranoia_la_MAJOR = $(shell expr $(libcdio_paranoia_la_CURRENT) - $(libcdio_paranoia_la_AGE)) +if BUILD_VERSIONED_LIBS +libcdio_paranoia_la_LDFLAGS = $(libcdio_paranoia_la_ldflags) -Wl,--version-script=libcdio_paranoia.la.ver +libcdio_paranoia_la_DEPENDENCIES = libcdio_paranoia.la.ver + +libcdio_paranoia.la.ver: $(libcdio_paranoia_la_OBJECTS) $(srcdir)/libcdio_paranoia.sym + echo 'CDIO_PARANOIA_$(libcdio_paranoia_la_MAJOR) { ' > $@ + objs=`for obj in $(libcdio_paranoia_la_OBJECTS); do sed -ne "s/^pic_object='\(.*\)'$$/\1/p" $$obj; done`; \ + if test -n "$$objs" ; then \ + nm $${objs} | sed -n -e 's/^.*[ ][ABCDGIRSTW][ABCDGIRSTW]*[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$$/\1/p' | sort -u | { first=true; while read symbol; do if grep -q "^$${symbol}\$$" $(srcdir)/libcdio_paranoia.sym; then if test $$first = true; then echo " global:"; first=false; fi; echo " $${symbol};"; fi; done; } >> $@; \ + nm $${objs} | sed -n -e 's/^.*[ ][ABCDGIRSTW][ABCDGIRSTW]*[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$$/\1/p' | sort -u | { first=true; while read symbol; do if grep -q "^$${symbol}\$$" $(srcdir)/libcdio_paranoia.sym; then :; else if test $$first = true; then echo " local:"; first=false; fi; echo " $${symbol};"; fi; done; } >> $@; \ + fi + echo '};' >> $@ +else +libcdio_paranoia_la_LDFLAGS = $(libcdio_paranoia_la_ldflags) +endif + +MOSTLYCLEANFILES = libcdio_paranoia.la.ver diff --git a/lib/paranoia/gap.c b/lib/paranoia/gap.c new file mode 100644 index 00000000..c79305fa --- /dev/null +++ b/lib/paranoia/gap.c @@ -0,0 +1,550 @@ +/* + $Id: gap.c,v 1.4 2008/04/17 17:39:48 karl Exp $ + + Copyright (C) 2004, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/*** + * Gap analysis support code for paranoia + * + ***/ + +#include "config.h" +#include "p_block.h" +#include +#include "gap.h" +#include + +/**** Gap analysis code ***************************************************/ + +/* =========================================================================== + * i_paranoia_overlap_r (internal) + * + * This function seeks backward through two vectors (starting at the given + * offsets) to determine how many consecutive samples agree. It returns + * the number of matching samples, which may be 0. + * + * Unlike its sibling, i_paranoia_overlap_f, this function doesn't need to + * be given the size of the vectors (all vectors stop at offset 0). + * + * This function is used by i_analyze_rift_r() below to find where a + * leading rift ends. + */ +long int +i_paranoia_overlap_r(int16_t *buffA,int16_t *buffB, + long offsetA, long offsetB) +{ + long beginA=offsetA; + long beginB=offsetB; + + /* Start at the given offsets and work our way backwards until we hit + * the beginning of one of the vectors. + */ + for( ; beginA>=0 && beginB>=0; beginA--,beginB-- ) + if (buffA[beginA] != buffB[beginB]) break; + + /* These values will either point to the first mismatching sample, or + * -1 if we hit the beginning of a vector. So increment to point to the + * last matching sample. + * + * ??? Why? This would appear to return one less sample than actually + * matched. E.g., no matching samples returns -1! Is this a bug? + */ + beginA++; + beginB++; + + return(offsetA-beginA); +} + + +/* =========================================================================== + * i_paranoia_overlap_f (internal) + * + * This function seeks forward through two vectors (starting at the given + * offsets) to determine how many consecutive samples agree. It returns + * the number of matching samples, which may be 0. + * + * Unlike its sibling, i_paranoia_overlap_r, this function needs to given + * the size of the vectors. + * + * This function is used by i_analyze_rift_f() below to find where a + * trailing rift ends. + */ +long int +i_paranoia_overlap_f(int16_t *buffA,int16_t *buffB, + long offsetA, long offsetB, + long sizeA,long sizeB) +{ + long endA=offsetA; + long endB=offsetB; + + /* Start at the given offsets and work our way forward until we hit + * the end of one of the vectors. + */ + for(;endA or <- */ + + +/* =========================================================================== + * i_analyze_rift_f (internal) + * + * This function examines a trailing rift to see how far forward the rift goes + * and to determine what kind of rift it is. This function is called by + * i_stage2_each() when a trailing rift is detected. (aoffset,boffset) are + * the offsets into (A,B) of the first mismatching sample. + * + * This function returns: + * matchA > 0 if there are (matchA) samples missing from A + * matchA < 0 if there are (-matchA) duplicate samples (stuttering) in A + * matchB > 0 if there are (matchB) samples missing from B + * matchB < 0 if there are (-matchB) duplicate samples in B + * matchC != 0 if there are (matchC) samples of garbage, after which + * both A and B are in sync again + */ +void +i_analyze_rift_f(int16_t *A,int16_t *B, + long sizeA, long sizeB, + long aoffset, long boffset, + long *matchA,long *matchB,long *matchC) +{ + + long apast=sizeA-aoffset; + long bpast=sizeB-boffset; + long i; + + *matchA=0, *matchB=0, *matchC=0; + + /* Look forward to see where we regain agreement between vectors + * A and B (of at least MIN_WORDS_RIFT samples). We look for one of + * the following possible matches: + * + * edge + * v + * (1) (... A matching run)|(aoffset matches ...) + * (... B matching run)| (rift) |(boffset+i matches ...) + * + * (2) (... A matching run)| (rift) |(aoffset+i matches ...) + * (... B matching run)|(boffset matches ...) + * + * (3) (... A matching run)| (rift) |(aoffset+i matches ...) + * (... B matching run)| (rift) |(boffset+i matches ...) + * + * Anything that doesn't match one of these three is too corrupt to + * for us to recover from. E.g.: + * + * (... A matching run)| (rift) |(eventual match ...) + * (... B matching run)| (big rift) |(eventual match ...) + * + * We won't find the eventual match, since we wouldn't be sure how + * to fix the rift. + */ + + for(i=0;;i++){ + /* Search for whatever case we hit first, so as to end up with the + * smallest rift. + * + * ??? Why do we start at 0? It should never match. + */ + + /* Don't search for (1) past the end of B */ + if (i=MIN_WORDS_RIFT){ + *matchA=i; + break; + } + + /* Don't search for (2) or (3) past the end of A */ + if (i=MIN_WORDS_RIFT){ + *matchB=i; + break; + } + + /* Don't search for (3) past the end of B */ + if (i=MIN_WORDS_RIFT){ + *matchC=i; + break; + } + }else + + /* Stop searching when we've reached the end of both vectors. + * In theory we could stop when there aren't MIN_WORDS_RIFT samples + * left in both vectors, but this case should happen fairly rarely. + */ + if(i>=bpast)break; + + /* Try the search again with a larger tentative rift. */ + } + + if(*matchA==0 && *matchB==0 && *matchC==0)return; + + if(*matchC)return; + + /* For case (1) or (2), we need to determine whether the rift contains + * samples dropped by the other vector (that should be inserted), or + * whether the rift contains a stutter (that should be dropped). To + * distinguish, we check the contents of the rift against the good samples + * just before the rift. If the contents match, then the rift contains + * a stutter. + * + * A stutter in the second vector: + * (...good samples... 1234)|(567 ...newly matched run...) + * (...good samples... 1234)| (1234) | (567 ...newly matched run) + * + * Samples missing from the first vector: + * (...good samples... 1234)|(901 ...newly matched run...) + * (...good samples... 1234)| (5678) |(901 ...newly matched run...) + * + * Of course, there's no theoretical guarantee that a non-stutter + * truly represents missing samples, but given that we're dealing with + * verified fragments in stage 2, we can have some confidence that this + * is the case. + */ + if(*matchA){ + /* For case (1), we need to determine whether A dropped samples at the + * rift or whether B stuttered. + * + * If the rift doesn't match the good samples in A (and hence in B), + * it's not a stutter, and the rift should be inserted into A. + */ + if(i_stutter_or_gap(A,B,aoffset-*matchA,boffset,*matchA)) + return; + + /* It is a stutter, so we need to signal that we need to remove + * (matchA) bytes from B. + */ + *matchB = -*matchA; + *matchA=0; + return; + + }else{ + /* Case (2) is the inverse of case (1) above. */ + if(i_stutter_or_gap(B,A,boffset-*matchB,aoffset,*matchB)) + return; + + *matchA = -*matchB; + *matchB=0; + return; + } +} + + +/* riftv must be first even val of rift moving back */ + +/* =========================================================================== + * i_analyze_rift_r (internal) + * + * This function examines a leading rift to see how far back the rift goes + * and to determine what kind of rift it is. This function is called by + * i_stage2_each() when a leading rift is detected. (aoffset,boffset) are + * the offsets into (A,B) of the first mismatching sample. + * + * This function returns: + * matchA > 0 if there are (matchA) samples missing from A + * matchA < 0 if there are (-matchA) duplicate samples (stuttering) in A + * matchB > 0 if there are (matchB) samples missing from B + * matchB < 0 if there are (-matchB) duplicate samples in B + * matchC != 0 if there are (matchC) samples of garbage, after which + * both A and B are in sync again + */ +void +i_analyze_rift_r(int16_t *A,int16_t *B, + long sizeA, long sizeB, + long aoffset, long boffset, + long *matchA,long *matchB,long *matchC) +{ + + long apast=aoffset+1; + long bpast=boffset+1; + long i; + + *matchA=0, *matchB=0, *matchC=0; + + /* Look backward to see where we regain agreement between vectors + * A and B (of at least MIN_WORDS_RIFT samples). We look for one of + * the following possible matches: + * + * edge + * v + * (1) (... aoffset matches)|(A matching run ...) + * (... boffset-i matches)| (rift) |(B matching run ...) + * + * (2) (... aoffset-i matches)| (rift) |(A matching run ...) + * (... boffset matches)|(B matching run ...) + * + * (3) (... aoffset-i matches)| (rift) |(A matching run ...) + * (... boffset-i matches)| (rift) |(B matching run ...) + * + * Anything that doesn't match one of these three is too corrupt to + * for us to recover from. E.g.: + * + * (... eventual match)| (rift) |(A matching run ...) + * (... eventual match) | (big rift) |(B matching run ...) + * + * We won't find the eventual match, since we wouldn't be sure how + * to fix the rift. + */ + + for(i=0;;i++){ + /* Search for whatever case we hit first, so as to end up with the + * smallest rift. + * + * ??? Why do we start at 0? It should never match. + */ + + /* Don't search for (1) past the beginning of B */ + if (i=MIN_WORDS_RIFT){ + *matchA=i; + break; + } + + /* Don't search for (2) or (3) past the beginning of A */ + if (i=MIN_WORDS_RIFT){ + *matchB=i; + break; + } + + /* Don't search for (3) past the beginning of B */ + if (i=MIN_WORDS_RIFT){ + *matchC=i; + break; + } + }else + + /* Stop searching when we've reached the end of both vectors. + * In theory we could stop when there aren't MIN_WORDS_RIFT samples + * left in both vectors, but this case should happen fairly rarely. + */ + if(i>=bpast)break; + + /* Try the search again with a larger tentative rift. */ + } + + if(*matchA==0 && *matchB==0 && *matchC==0)return; + + if(*matchC)return; + + /* For case (1) or (2), we need to determine whether the rift contains + * samples dropped by the other vector (that should be inserted), or + * whether the rift contains a stutter (that should be dropped). To + * distinguish, we check the contents of the rift against the good samples + * just after the rift. If the contents match, then the rift contains + * a stutter. + * + * A stutter in the second vector: + * (...newly matched run... 234)|(5678 ...good samples...) + * (...newly matched run... 234)| (5678) |(5678 ...good samples...) + * + * Samples missing from the first vector: + * (...newly matched run... 890)|(5678 ...good samples...) + * (...newly matched run... 890)| (1234) |(5678 ...good samples...) + * + * Of course, there's no theoretical guarantee that a non-stutter + * truly represents missing samples, but given that we're dealing with + * verified fragments in stage 2, we can have some confidence that this + * is the case. + */ + + if(*matchA){ + /* For case (1), we need to determine whether A dropped samples at the + * rift or whether B stuttered. + * + * If the rift doesn't match the good samples in A (and hence in B), + * it's not a stutter, and the rift should be inserted into A. + * + * ???BUG??? It's possible for aoffset+1+*matchA to be > sizeA, in + * which case the comparison in i_stutter_or_gap() will extend beyond + * the bounds of A. Thankfully, this isn't writing data and thus + * trampling memory, but it's still a memory access error that should + * be fixed. + * + * This bug is not fixed yet. + */ + if(i_stutter_or_gap(A,B,aoffset+1,boffset-*matchA+1,*matchA)) + return; + + /* It is a stutter, so we need to signal that we need to remove + * (matchA) bytes from B. + */ + *matchB = -*matchA; + *matchA=0; + return; + + }else{ + /* Case (2) is the inverse of case (1) above. */ + if(i_stutter_or_gap(B,A,boffset+1,aoffset-*matchB+1,*matchB)) + return; + + *matchA = -*matchB; + *matchB=0; + return; + } +} + + +/* =========================================================================== + * analyze_rift_silence_f (internal) + * + * This function examines the fragment and root from the rift onward to + * see if they have a rift's worth of silence (or if they end with silence). + * It sets (*matchA) to -1 if A's rift is silence, (*matchB) to -1 if B's + * rift is silence, and sets them to 0 otherwise. + * + * Note that, unlike every other function in cdparanoia, this function + * considers any repeated value to be silence (which, in effect, it is). + * All other functions only consider repeated zeroes to be silence. + * + * ??? Is this function name just a misnomer, as it's really looking for + * repeated garbage? + * + * This function is called by i_stage2_each() if it runs into a trailing rift + * that i_analyze_rift_f couldn't diagnose. This checks for another variant: + * where one vector has silence and the other doesn't. We then assume + * that the silence (and anything following it) is garbage. + * + * Note that while this function checks both A and B for silence, the caller + * assumes that only one or the other has silence. + */ +void +analyze_rift_silence_f(int16_t *A,int16_t *B,long sizeA,long sizeB, + long aoffset, long boffset, + long *matchA, long *matchB) +{ + *matchA=-1; + *matchB=-1; + + /* Search for MIN_WORDS_RIFT samples, or to the end of the vector, + * whichever comes first. + */ + sizeA=min(sizeA,aoffset+MIN_WORDS_RIFT); + sizeB=min(sizeB,boffset+MIN_WORDS_RIFT); + + aoffset++; + boffset++; + + /* Check whether A has only "silence" within the search range. Note + * that "silence" here is a single, repeated value (zero or not). + */ + while(aoffset + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _GAP_H_ +#define _GAP_H_ + +extern long i_paranoia_overlap_r(int16_t *buffA,int16_t *buffB, + long offsetA, long offsetB); +extern long i_paranoia_overlap_f(int16_t *buffA,int16_t *buffB, + long offsetA, long offsetB, + long sizeA,long sizeB); +extern int i_stutter_or_gap(int16_t *A, int16_t *B,long offA, long offB, + long gap); +extern void i_analyze_rift_f(int16_t *A,int16_t *B, + long sizeA, long sizeB, + long aoffset, long boffset, + long *matchA,long *matchB,long *matchC); +extern void i_analyze_rift_r(int16_t *A,int16_t *B, + long sizeA, long sizeB, + long aoffset, long boffset, + long *matchA,long *matchB,long *matchC); + +extern void analyze_rift_silence_f(int16_t *A,int16_t *B,long sizeA,long sizeB, + long aoffset, long boffset, + long *matchA, long *matchB); +#endif /*_GAP_H*/ diff --git a/lib/paranoia/isort.c b/lib/paranoia/isort.c new file mode 100644 index 00000000..49a808f4 --- /dev/null +++ b/lib/paranoia/isort.c @@ -0,0 +1,302 @@ +/* + $Id: isort.c,v 1.7 2008/04/17 17:39:48 karl Exp $ + + Copyright (C) 2004, 2005, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* sorted vector abstraction for paranoia */ + +/* Old isort got a bit complex. This re-constrains complexity to + give a go at speed through a more alpha-6-like mechanism. */ + + +/* "Sort" is a bit of a misnomer in this implementation. It's actually + * basically a hash table of sample values (with a linked-list collision + * resolution), which lets you quickly determine where in a vector a + * particular sample value occurs. + * + * Collisions aren't due to hash collisions, as the table has one bucket + * for each possible sample value. Instead, the "collisions" represent + * multiple occurrences of a given value. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include "p_block.h" +#include "isort.h" + + +/* =========================================================================== + * sort_alloc() + * + * Allocates and initializes a new, empty sort_info object, which can be + * used to index up to (size) samples from a vector. + */ + +sort_info_t * +sort_alloc(long size) +{ + sort_info_t *ret=calloc(1, sizeof(sort_info_t)); + + ret->vector=NULL; + ret->sortbegin=-1; + ret->size=-1; + ret->maxsize=size; + + ret->head=calloc(65536,sizeof(sort_link_t *)); + ret->bucketusage=calloc(1, 65536*sizeof(long)); + ret->revindex=calloc(size,sizeof(sort_link_t)); + ret->lastbucket=0; + + return(ret); +} + + +/* =========================================================================== + * sort_unsortall() (internal) + * + * This function resets the index for further use with a different vector + * or range, without the overhead of an unnecessary free/alloc. + */ + +void +sort_unsortall(sort_info_t *i) +{ + /* If there were few enough different samples encountered (and hence few + * enough buckets used), we can just zero out those buckets. If there + * were many (2000 is picked somewhat arbitrarily), it's faster simply to + * zero out all buckets with a memset() rather than walking the data + * structure and zeroing them out one by one. + */ + if (i->lastbucket>2000) { /* a guess */ + memset(i->head,0,65536*sizeof(sort_link_t *)); + } else { + long b; + for (b=0; blastbucket; b++) + i->head[i->bucketusage[b]]=NULL; + } + + i->lastbucket=0; + i->sortbegin=-1; + + /* Curiously, this function preserves the vector association created + * by sort_setup(), but it is used only internally by sort_setup, so + * preserving this association is unnecessary. + */ +} + + +/* =========================================================================== + * sort_free() + * + * Releases all memory consumed by a sort_info object. + */ + +void +sort_free(sort_info_t *i) +{ + free(i->revindex); + free(i->head); + free(i->bucketusage); + free(i); +} + + +/* =========================================================================== + * sort_sort() (internal) + * + * This function builds the index to allow for fast searching for sample + * values within a portion (sortlo - sorthi) of the object's associated + * vector. It is called internally and only when needed. + */ + +static void +sort_sort(sort_info_t *i,long sortlo,long sorthi) +{ + long j; + + /* We walk backward through the range to index because we insert new + * samples at the head of each bucket's list. At the end, they'll be + * sorted from first to last occurrence. + */ + for (j=sorthi-1; j>=sortlo; j--) { + /* i->vector[j] = the signed 16-bit sample to index. + * hv = pointer to the head of the sorted list of occurences + * of this sample + * l = the node to associate with this sample + * + * We add 32768 to convert the signed 16-bit integer to an unsigned + * range from 0 to 65535. + * + * Note that l is located within i->revindex at a position + * corresponding to the sample's position in the vector. This allows + * ipos() to determine the sample position from a returned sort_link. + */ + sort_link_t **hv = i->head+i->vector[j]+32768; + sort_link_t *l = i->revindex+j; + + /* If this is the first time we've encountered this sample, add its + * bucket to the list of buckets used. This list is used only for + * resetting the index quickly. + */ + if(*hv==NULL){ + i->bucketusage[i->lastbucket] = i->vector[j]+32768; + i->lastbucket++; + } + + /* Point the new node at the old head, then assign the new node as + * the new head. + */ + l->next=*hv; + *hv=l; + } + + /* Mark the index as initialized. + */ + i->sortbegin=0; +} + + +/* =========================================================================== + * sort_setup() + * + * This function initializes a previously allocated sort_info_t. The + * sort_info_t is associated with a vector of samples of length + * (size), whose position begins at (*abspos) within the CD's stream + * of samples. Only the range of samples between (sortlo, sorthi) + * will eventually be indexed for fast searching. (sortlo, sorthi) + * are absolute sample positions. + * + * ???: Why is abspos a pointer? Why not just store a copy? + * + * Note: size *must* be <= the size given to the preceding sort_alloc(), + * but no error checking is done here. + */ + +void +sort_setup(sort_info_t *i, int16_t *vector, long int *abspos, + long int size, long int sortlo, long int sorthi) +{ + /* Reset the index if it has already been built. + */ + if (i->sortbegin!=-1) + sort_unsortall(i); + + i->vector=vector; + i->size=size; + i->abspos=abspos; + + /* Convert the absolute (sortlo, sorthi) to offsets within the vector. + * Note that the index will not be built until sort_getmatch() is called. + * Here we're simply hanging on to the range to index until then. + */ + i->lo = min(size, max(sortlo - *abspos, 0)); + i->hi = max(0, min(sorthi - *abspos, size)); +} + +/* =========================================================================== + * sort_getmatch() + * + * This function returns a sort_link_t pointer which refers to the + * first sample equal to (value) in the vector. It only searches for + * hits within (overlap) samples of (post), where (post) is an offset + * within the vector. The caller can determine the position of the + * matched sample using ipos(sort_info *, sort_link *). + * + * This function returns NULL if no matches were found. + */ + +sort_link_t * +sort_getmatch(sort_info_t *i, long post, long overlap, int value) +{ + sort_link_t *ret; + + /* If the vector hasn't been indexed yet, index it now. + */ + if (i->sortbegin==-1) + sort_sort(i,i->lo,i->hi); + /* Now we reuse lo and hi */ + + /* We'll only return samples within (overlap) samples of (post). + * Clamp the boundaries to search to the boundaries of the array, + * convert the signed sample to an unsigned offset, and store the + * state so that future calls to sort_nextmatch do the right thing. + * + * Reusing lo and hi this way is awful. + */ + post=max(0,min(i->size,post)); + i->val=value+32768; + i->lo=max(0,post-overlap); /* absolute position */ + i->hi=min(i->size,post+overlap); /* absolute position */ + + /* Walk through the linked list of samples with this value, until + * we find the first one within the bounds specified. If there + * aren't any, return NULL. + */ + ret=i->head[i->val]; + + while (ret) { + /* ipos() calculates the offset (in terms of the original vector) + * of this hit. + */ + + if (ipos(i,ret)lo) { + ret=ret->next; + } else { + if (ipos(i,ret)>=i->hi) + ret=NULL; + break; + } + } + /*i->head[i->val]=ret;*/ + return(ret); +} + + +/* =========================================================================== + * sort_nextmatch() + * + * This function returns a sort_link_t pointer which refers to the next sample + * matching the criteria previously passed to sort_getmatch(). See + * sort_getmatch() for details. + * + * This function returns NULL if no further matches were found. + */ + +sort_link_t * +sort_nextmatch(sort_info_t *i, sort_link_t *prev) +{ + sort_link_t *ret=prev->next; + + /* If there aren't any more hits, or we've passed the boundary requested + * of sort_getmatch(), we're done. + */ + if (!ret || ipos(i,ret)>=i->hi) + return(NULL); + + return(ret); +} + diff --git a/lib/paranoia/isort.h b/lib/paranoia/isort.h new file mode 100644 index 00000000..8479f840 --- /dev/null +++ b/lib/paranoia/isort.h @@ -0,0 +1,161 @@ +/* + $Id: isort.h,v 1.6 2008/04/17 17:39:48 karl Exp $ + + Copyright (C) 2004, 2005, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _ISORT_H_ +#define _ISORT_H_ + +typedef struct sort_link{ + struct sort_link *next; +} sort_link_t; + +typedef struct sort_info { + int16_t *vector; /* vector (storage doesn't belong to us) */ + + long *abspos; /* pointer for side effects */ + long size; /* vector size */ + + long maxsize; /* maximum vector size */ + + long sortbegin; /* range of contiguous sorted area */ + long lo,hi; /* current post, overlap range */ + int val; /* ...and val */ + + /* sort structs */ + sort_link_t **head; /* sort buckets (65536) */ + + long *bucketusage; /* of used buckets (65536) */ + long lastbucket; + sort_link_t *revindex; + +} sort_info_t; + +/*! ======================================================================== + * sort_alloc() + * + * Allocates and initializes a new, empty sort_info object, which can + * be used to index up to (size) samples from a vector. + */ +extern sort_info_t *sort_alloc(long int size); + +/*! ======================================================================== + * sort_unsortall() (internal) + * + * This function resets the index for further use with a different + * vector or range, without the overhead of an unnecessary free/alloc. + */ +extern void sort_unsortall(sort_info_t *i); + +/*! ======================================================================== + * sort_setup() + * + * This function initializes a previously allocated sort_info_t. The + * sort_info_t is associated with a vector of samples of length + * (size), whose position begins at (*abspos) within the CD's stream + * of samples. Only the range of samples between (sortlo, sorthi) + * will eventually be indexed for fast searching. (sortlo, sorthi) + * are absolute sample positions. + * + * ???: Why is abspos a pointer? Why not just store a copy? + * + * Note: size *must* be <= the size given to the preceding sort_alloc(), + * but no error checking is done here. + */ +extern void sort_setup(sort_info_t *i, int16_t *vector, long int *abspos, + long int size, long int sortlo, long int sorthi); + +/* ========================================================================= + * sort_free() + * + * Releases all memory consumed by a sort_info object. + */ +extern void sort_free(sort_info_t *i); + +/*! ======================================================================== + * sort_getmatch() + * + * This function returns a sort_link_t pointer which refers to the + * first sample equal to (value) in the vector. It only searches for + * hits within (overlap) samples of (post), where (post) is an offset + * within the vector. The caller can determine the position of the + * matched sample using ipos(sort_info *, sort_link *). + * + * This function returns NULL if no matches were found. + */ +extern sort_link_t *sort_getmatch(sort_info_t *i, long post, long overlap, + int value); + +/*! ======================================================================== + * sort_nextmatch() + * + * This function returns a sort_link_t pointer which refers to the + * next sample matching the criteria previously passed to + * sort_getmatch(). See sort_getmatch() for details. + * + * This function returns NULL if no further matches were found. + */ +extern sort_link_t *sort_nextmatch(sort_info_t *i, sort_link_t *prev); + +/* =========================================================================== + * is() + * + * This macro returns the size of the vector indexed by the given sort_info_t. + */ +#define is(i) (i->size) + +/* =========================================================================== + * ib() + * + * This macro returns the absolute position of the first sample in the vector + * indexed by the given sort_info_t. + */ +#define ib(i) (*i->abspos) + +/* =========================================================================== + * ie() + * + * This macro returns the absolute position of the sample after the last + * sample in the vector indexed by the given sort_info_t. + */ +#define ie(i) (i->size+*i->abspos) + +/* =========================================================================== + * iv() + * + * This macro returns the vector indexed by the given sort_info_t. + */ +#define iv(i) (i->vector) + +/* =========================================================================== + * ipos() + * + * This macro returns the relative position (offset) within the indexed vector + * at which the given match was found. + * + * It uses a little-known and frightening aspect of C pointer arithmetic: + * subtracting a pointer is not an arithmetic subtraction, but rather the + * additive inverse. In other words, since + * q = p + n returns a pointer to the nth object in p, + * q - p = p + n - p, and + * q - p = n, not the difference of the two addresses. + */ +#define ipos(i,l) (l-i->revindex) + +#endif /* _ISORT_H_ */ + diff --git a/lib/paranoia/libcdio_paranoia.sym b/lib/paranoia/libcdio_paranoia.sym new file mode 100644 index 00000000..20f8f1af --- /dev/null +++ b/lib/paranoia/libcdio_paranoia.sym @@ -0,0 +1,9 @@ +cdio_paranoia_init +cdio_paranoia_free +cdio_paranoia_modeset +cdio_paranoia_seek +cdio_paranoia_read +cdio_paranoia_read_limited +cdio_paranoia_overlapset +cdio_paranoia_set_range +paranoia_cb_mode2str diff --git a/lib/paranoia/overlap.c b/lib/paranoia/overlap.c new file mode 100644 index 00000000..764b1154 --- /dev/null +++ b/lib/paranoia/overlap.c @@ -0,0 +1,256 @@ +/* + $Id: overlap.c,v 1.6 2008/04/17 17:39:48 karl Exp $ + + Copyright (C) 2004, 2005, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/*** + * + * Statistic code and cache management for overlap settings + * + ***/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#ifdef HAVE_STRING_H +#include +#endif +#include +#include "p_block.h" +#include "overlap.h" +#include "isort.h" + +/**** Internal cache management *****************************************/ + +void +paranoia_resetcache(cdrom_paranoia_t *p) +{ + c_block_t *c=c_first(p); + v_fragment_t *v; + + while(c){ + free_c_block(c); + c=c_first(p); + } + + v=v_first(p); + while(v){ + free_v_fragment(v); + v=v_first(p); + } +} + +void +paranoia_resetall(cdrom_paranoia_t *p) +{ + p->root.returnedlimit=0; + p->dyndrift=0; + p->root.lastsector=0; + + if(p->root.vector){ + i_cblock_destructor(p->root.vector); + p->root.vector=NULL; + } + + paranoia_resetcache(p); +} + +void +i_paranoia_trim(cdrom_paranoia_t *p, long int beginword, long int endword) +{ + root_block *root=&(p->root); + if(root->vector!=NULL){ + long target=beginword-MAX_SECTOR_OVERLAP*CD_FRAMEWORDS; + long rbegin=cb(root->vector); + long rend=ce(root->vector); + + if(rbegin>beginword) + goto rootfree; + + if(rbegin+MAX_SECTOR_OVERLAP*CD_FRAMEWORDSrend) + goto rootfree; + + { + long int offset=target-rbegin; + c_removef(root->vector,offset); + } + } + + { + c_block_t *c=c_first(p); + while(c){ + c_block_t *next=c_next(c); + if(ce(c)vector); + root->vector=NULL; + root->returnedlimit=-1; + root->lastsector=0; + +} + +/**** Statistical and heuristic[al? :-] management ************************/ + +/* =========================================================================== + * offset_adjust_settings (internal) + * + * This function is called by offset_add_value() every time 10 samples have + * been accumulated. This function updates the internal statistics for + * paranoia (dynoverlap, dyndrift) that compensate for jitter and drift. + * + * (dynoverlap) influences how far stage 1 and stage 2 search for matching + * runs. In low-jitter conditions, it will be very small (or even 0), + * narrowing our search. In high-jitter conditions, it will be much larger, + * widening our search at the cost of speed. + * + * ???: To be studied further. + */ +void +offset_adjust_settings(cdrom_paranoia_t *p, + void(*callback)(long int, paranoia_cb_mode_t)) +{ + if(p->stage2.offpoints>=10){ + /* drift: look at the average offset value. If it's over one + sector, frob it. We just want a little hysteresis [sp?]*/ + long av=(p->stage2.offpoints?p->stage2.offaccum/p->stage2.offpoints:0); + + if(abs(av)>p->dynoverlap/4){ + av=(av/MIN_SECTOR_EPSILON)*MIN_SECTOR_EPSILON; + + if(callback)(*callback)(ce(p->root.vector),PARANOIA_CB_DRIFT); + p->dyndrift+=av; + + /* Adjust all the values in the cache otherwise we get a + (potentially unstable) feedback loop */ + { + c_block_t *c=c_first(p); + v_fragment_t *v=v_first(p); + + while(v && v->one){ + /* safeguard beginning bounds case with a hammer */ + if(fb(v)one)one=NULL; + }else{ + fb(v)-=av; + } + v=v_next(v); + } + while(c){ + long adj=min(av,cb(c)); + c_set(c,cb(c)-adj); + c=c_next(c); + } + } + + p->stage2.offaccum=0; + p->stage2.offmin=0; + p->stage2.offmax=0; + p->stage2.offpoints=0; + p->stage2.newpoints=0; + p->stage2.offdiff=0; + } + } + + if(p->stage1.offpoints>=10){ + /* dynoverlap: we arbitrarily set it to 4x the running difference + value, unless min/max are more */ + + p->dynoverlap=(p->stage1.offpoints?p->stage1.offdiff/ + p->stage1.offpoints*3:CD_FRAMEWORDS); + + if(p->dynoverlap<-p->stage1.offmin*1.5) + p->dynoverlap=-p->stage1.offmin*1.5; + + if(p->dynoverlapstage1.offmax*1.5) + p->dynoverlap=p->stage1.offmax*1.5; + + if(p->dynoverlapdynoverlap=MIN_SECTOR_EPSILON; + if(p->dynoverlap>MAX_SECTOR_OVERLAP*CD_FRAMEWORDS) + p->dynoverlap=MAX_SECTOR_OVERLAP*CD_FRAMEWORDS; + + if(callback)(*callback)(p->dynoverlap,PARANOIA_CB_OVERLAP); + + if(p->stage1.offpoints>600){ /* bit of a bug; this routine is + called too often due to the overlap + mesh alg we use in stage 1 */ + p->stage1.offpoints/=1.2; + p->stage1.offaccum/=1.2; + p->stage1.offdiff/=1.2; + } + p->stage1.offmin=0; + p->stage1.offmax=0; + p->stage1.newpoints=0; + } +} + + +/* =========================================================================== + * offset_add_value (internal) + * + * This function adds the given jitter detected (value) to the statistics + * for the given stage (o). It is called whenever jitter has been identified + * by stage 1 or 2. After every 10 samples, we update the overall jitter- + * compensation settings (e.g. dynoverlap). This allows us to narrow our + * search for matching runs (in both stages) in low-jitter conditions + * and also widen our search appropriately when there is jitter. + * + * ???BUG???: + * Note that there is a bug in the way that this is called by try_sort_sync(). + * Silence looks like zero jitter, and dynoverlap may be incorrectly reduced + * when there's lots of silence but also jitter. + * + * See the bug notes in try_sort_sync() for details. + */ +void +offset_add_value(cdrom_paranoia_t *p,offsets *o,long value, + void(*callback)(long int, paranoia_cb_mode_t)) +{ + if(o->offpoints!=-1){ + + /* Track the average magnitude of jitter (in either direction) */ + o->offdiff+=abs(value); + o->offpoints++; + o->newpoints++; + + /* Track the net value of the jitter (to track drift) */ + o->offaccum+=value; + + /* Track the largest jitter we've encountered in each direction */ + if(valueoffmin)o->offmin=value; + if(value>o->offmax)o->offmax=value; + + /* After 10 samples, update dynoverlap, etc. */ + if(o->newpoints>=10)offset_adjust_settings(p,callback); + } +} + diff --git a/lib/paranoia/overlap.h b/lib/paranoia/overlap.h new file mode 100644 index 00000000..e2b72016 --- /dev/null +++ b/lib/paranoia/overlap.h @@ -0,0 +1,33 @@ +/* + $Id: overlap.h,v 1.2 2008/04/17 17:39:48 karl Exp $ + + Copyright (C) 2004, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _OVERLAP_H_ +#define _OVERLAP_H_ + +extern void offset_add_value(cdrom_paranoia_t *p,offsets *o,long value, + void(*callback)(long int, paranoia_cb_mode_t)); +extern void offset_clear_settings(offsets *o); +extern void offset_adjust_settings(cdrom_paranoia_t *p, + void(*callback)(long, paranoia_cb_mode_t)); +extern void i_paranoia_trim(cdrom_paranoia_t *p,long beginword,long endword); +extern void paranoia_resetall(cdrom_paranoia_t *p); +extern void paranoia_resetcache(cdrom_paranoia_t *p); + +#endif /*_OVERLAP_H_*/ diff --git a/lib/paranoia/p_block.c b/lib/paranoia/p_block.c new file mode 100644 index 00000000..a7bdb34f --- /dev/null +++ b/lib/paranoia/p_block.c @@ -0,0 +1,459 @@ +/* + $Id: p_block.c,v 1.13 2008/04/17 17:39:48 karl Exp $ + + Copyright (C) 2004, 2005, 2007, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#ifdef HAVE_STRING_H +#include +#endif +#include +#include "p_block.h" +#include +#include + +linked_list_t *new_list(void *(*newp)(void),void (*freep)(void *)) +{ + linked_list_t *ret=calloc(1,sizeof(linked_list_t)); + ret->new_poly=newp; + ret->free_poly=freep; + return(ret); +} + +linked_element *add_elem(linked_list_t *l,void *elem) +{ + + linked_element *ret=calloc(1,sizeof(linked_element)); + ret->stamp=l->current++; + ret->ptr=elem; + ret->list=l; + + if(l->head) + l->head->prev=ret; + else + l->tail=ret; + ret->next=l->head; + ret->prev=NULL; + l->head=ret; + l->active++; + + return(ret); +} + +linked_element * +new_elem(linked_list_t *p_list) +{ + void *p_new=p_list->new_poly(); + return(add_elem(p_list,p_new)); +} + +void +free_elem(linked_element *e,int free_ptr) +{ + linked_list_t *l=e->list; + if(free_ptr)l->free_poly(e->ptr); + + if(e==l->head) + l->head=e->next; + if(e==l->tail) + l->tail=e->prev; + + if(e->prev) + e->prev->next=e->next; + if(e->next) + e->next->prev=e->prev; + + l->active--; + free(e); +} + +void +free_list(linked_list_t *list,int free_ptr) +{ + while(list->head) + free_elem(list->head,free_ptr); + free(list); +} + +void *get_elem(linked_element *e) +{ + return(e->ptr); +} + +linked_list_t *copy_list(linked_list_t *list) +{ + linked_list_t *new=new_list(list->new_poly,list->free_poly); + linked_element *i=list->tail; + + while(i){ + add_elem(new,i->ptr); + i=i->prev; + } + return(new); +} + +/**** C_block stuff ******************************************************/ + +static c_block_t * +i_cblock_constructor(cdrom_paranoia_t *p) +{ + c_block_t *ret=calloc(1,sizeof(c_block_t)); + return(ret); +} + +void +i_cblock_destructor(c_block_t *c) +{ + if(c){ + if(c->vector)free(c->vector); + if(c->flags)free(c->flags); + c->e=NULL; + free(c); + } +} + +c_block_t * +new_c_block(cdrom_paranoia_t *p) +{ + linked_element *e=new_elem(p->cache); + c_block_t *c=e->ptr; + c->e=e; + c->p=p; + return(c); +} + +void free_c_block(c_block_t *c) +{ + /* also rid ourselves of v_fragments that reference this block */ + v_fragment_t *v=v_first(c->p); + + while(v){ + v_fragment_t *next=v_next(v); + if(v->one==c)free_v_fragment(v); + v=next; + } + + free_elem(c->e,1); +} + +static v_fragment_t * +i_vfragment_constructor(void) +{ + v_fragment_t *ret=calloc(1,sizeof(v_fragment_t)); + return(ret); +} + +static void +i_v_fragment_destructor(v_fragment_t *v) +{ + free(v); +} + +v_fragment_t * +new_v_fragment(cdrom_paranoia_t *p, c_block_t *one, + long int begin, long int end, int last) +{ + linked_element *e=new_elem(p->fragments); + v_fragment_t *b=e->ptr; + + b->e=e; + b->p=p; + + b->one=one; + b->begin=begin; + b->vector=one->vector+begin-one->begin; + b->size=end-begin; + b->lastsector=last; + +#if TRACE_PARANOIA + fprintf(stderr, "- Verified [%ld-%ld] (0x%04X...0x%04X)%s\n", + begin, end, + b->vector[0]&0xFFFF, b->vector[b->size-1]&0xFFFF, + last ? " *" : ""); +#endif + + return(b); +} + +void free_v_fragment(v_fragment_t *v) +{ + free_elem(v->e,1); +} + +c_block_t * +c_first(cdrom_paranoia_t *p) +{ + if(p->cache->head) + return(p->cache->head->ptr); + return(NULL); +} + +c_block_t * +c_last(cdrom_paranoia_t *p) +{ + if(p->cache->tail) + return(p->cache->tail->ptr); + return(NULL); +} + +c_block_t * +c_next(c_block_t *c) +{ + if(c->e->next) + return(c->e->next->ptr); + return(NULL); +} + +c_block_t * +c_prev(c_block_t *c) +{ + if(c->e->prev) + return(c->e->prev->ptr); + return(NULL); +} + +v_fragment_t * +v_first(cdrom_paranoia_t *p) +{ + if(p->fragments->head){ + return(p->fragments->head->ptr); + } + return(NULL); +} + +v_fragment_t * +v_last(cdrom_paranoia_t *p) +{ + if(p->fragments->tail) + return(p->fragments->tail->ptr); + return(NULL); +} + +v_fragment_t * +v_next(v_fragment_t *v) +{ + if(v->e->next) + return(v->e->next->ptr); + return(NULL); +} + +v_fragment_t * +v_prev(v_fragment_t *v) +{ + if(v->e->prev) + return(v->e->prev->ptr); + return(NULL); +} + +void +recover_cache(cdrom_paranoia_t *p) +{ + linked_list_t *l=p->cache; + + /* Are we at/over our allowed cache size? */ + while(l->active>p->cache_limit) + /* cull from the tail of the list */ + free_c_block(c_last(p)); + +} + +int16_t * +v_buffer(v_fragment_t *v) +{ + if(!v->one)return(NULL); + if(!cv(v->one))return(NULL); + return(v->vector); +} + +/* alloc a c_block not on a cache list */ +c_block_t * +c_alloc(int16_t *vector, long begin, long size) +{ + c_block_t *c=calloc(1,sizeof(c_block_t)); + c->vector=vector; + c->begin=begin; + c->size=size; + return(c); +} + +void c_set(c_block_t *v,long begin){ + v->begin=begin; +} + +/* pos here is vector position from zero */ +void +c_insert(c_block_t *v,long pos,int16_t *b,long size) +{ + int vs=cs(v); + if(pos<0 || pos>vs)return; + + if(v->vector) { + v->vector = realloc(v->vector,sizeof(int16_t)*(size+vs)); + } else { + v->vector = calloc(1, sizeof(int16_t)*size); + } + + if(posvector+pos+size,v->vector+pos, + (vs-pos)*sizeof(int16_t)); + memcpy(v->vector+pos,b,size*sizeof(int16_t)); + + v->size+=size; +} + +void +c_remove(c_block_t *v, long cutpos, long cutsize) +{ + int vs=cs(v); + if(cutpos<0 || cutpos>vs)return; + if(cutpos+cutsize>vs)cutsize=vs-cutpos; + if(cutsize<0)cutsize=vs-cutpos; + if(cutsize<1)return; + + memmove(v->vector+cutpos,v->vector+cutpos+cutsize, + (vs-cutpos-cutsize)*sizeof(int16_t)); + + v->size-=cutsize; +} + +void +c_overwrite(c_block_t *v,long pos,int16_t *b,long size) +{ + int vs=cs(v); + + if(pos<0)return; + if(pos+size>vs)size=vs-pos; + + memcpy(v->vector+pos,b,size*sizeof(int16_t)); +} + +void +c_append(c_block_t *v, int16_t *vector, long size) +{ + int vs=cs(v); + + /* update the vector */ + if(v->vector) + v->vector=realloc(v->vector,sizeof(int16_t)*(size+vs)); + else { + v->vector=calloc(1, sizeof(int16_t)*size); + } + memcpy(v->vector+vs,vector,sizeof(int16_t)*size); + + v->size+=size; +} + +void +c_removef(c_block_t *v, long cut) +{ + c_remove(v,0,cut); + v->begin+=cut; +} + + + +/**** Initialization *************************************************/ + +/*! Get the beginning and ending sector bounds given cursor position. + + There are a couple of subtle differences between this and the + cdda_firsttrack_sector and cdda_lasttrack_sector. If the cursor is + an a sector later than cdda_firsttrack_sector, that sectur will be + used. As for the difference between cdda_lasttrack_sector, if the CD + is mixed and there is a data track after the cursor but before the + last audio track, the end of the audio sector before that is used. +*/ +void +i_paranoia_firstlast(cdrom_paranoia_t *p) +{ + track_t i, j; + cdrom_drive_t *d=p->d; + const track_t i_first_track = cdio_get_first_track_num(d->p_cdio); + const track_t i_last_track = cdio_get_last_track_num(d->p_cdio); + + p->current_lastsector = p->current_firstsector = -1; + + i = cdda_sector_gettrack(d, p->cursor); + + if ( CDIO_INVALID_TRACK != i ) { + if ( 0 == i ) i++; + j = i; + /* In the below loops, We assume the cursor already is on an audio + sector. Not sure if this is correct if p->cursor is in the pregap + before the first track. + */ + for ( ; i < i_last_track; i++) + if( !cdda_track_audiop(d,i) ) { + p->current_lastsector=cdda_track_lastsector(d,i-1); + break; + } + + i = j; + for ( ; i >= i_first_track; i-- ) + if( !cdda_track_audiop(d,i) ) { + p->current_firstsector = cdda_track_firstsector(d,i+1); + break; + } + } + + if (p->current_lastsector == -1) + p->current_lastsector = cdda_disc_lastsector(d); + + if(p->current_firstsector == -1) + p->current_firstsector = cdda_disc_firstsector(d); + +} + +cdrom_paranoia_t * +paranoia_init(cdrom_drive_t *d) +{ + cdrom_paranoia_t *p=calloc(1,sizeof(cdrom_paranoia_t)); + + p->cache=new_list((void *)&i_cblock_constructor, + (void *)&i_cblock_destructor); + + p->fragments=new_list((void *)&i_vfragment_constructor, + (void *)&i_v_fragment_destructor); + + p->readahead=150; + p->sortcache=sort_alloc(p->readahead*CD_FRAMEWORDS); + p->d=d; + p->dynoverlap=MAX_SECTOR_OVERLAP*CD_FRAMEWORDS; + p->cache_limit=JIGGLE_MODULO; + p->enable=PARANOIA_MODE_FULL; + p->cursor=cdda_disc_firstsector(d); + p->lastread=LONG_MAX; + + /* One last one... in case data and audio tracks are mixed... */ + i_paranoia_firstlast(p); + + return(p); +} + +void paranoia_set_range(cdrom_paranoia_t *p, long start, long end) +{ + p->cursor = start; + p->current_firstsector = start; + p->current_lastsector = end; +} diff --git a/lib/paranoia/p_block.h b/lib/paranoia/p_block.h new file mode 100644 index 00000000..8bf17bf3 --- /dev/null +++ b/lib/paranoia/p_block.h @@ -0,0 +1,213 @@ +/* + $Id: p_block.h,v 1.6 2008/04/17 17:39:48 karl Exp $ + + Copyright (C) 2004, 2005, 2008 Rocky Bernstein + Copyright (C) by Monty (xiphmont@mit.edu) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _P_BLOCK_H_ +#define _P_BLOCK_H_ + +#include +#include + +#define MIN_WORDS_OVERLAP 64 /* 16 bit words */ +#define MIN_WORDS_SEARCH 64 /* 16 bit words */ +#define MIN_WORDS_RIFT 16 /* 16 bit words */ +#define MAX_SECTOR_OVERLAP 32 /* sectors */ +#define MIN_SECTOR_EPSILON 128 /* words */ +#define MIN_SECTOR_BACKUP 16 /* sectors */ +#define JIGGLE_MODULO 15 /* sectors */ +#define MIN_SILENCE_BOUNDARY 1024 /* 16 bit words */ + +#define min(x,y) ((x)>(y)?(y):(x)) +#define max(x,y) ((x)<(y)?(y):(x)) + +#include "isort.h" + +typedef struct { + /* linked list */ + struct linked_element *head; + struct linked_element *tail; + + void *(*new_poly)(); + void (*free_poly)(void *poly); + long current; + long active; + +} linked_list_t; + +typedef struct linked_element{ + void *ptr; + struct linked_element *prev; + struct linked_element *next; + + linked_list_t *list; + int stamp; +} linked_element; + +extern linked_list_t *new_list(void *(*new_fn)(void),void (*free)(void *)); +extern linked_element *new_elem(linked_list_t *list); +extern linked_element *add_elem(linked_list_t *list,void *elem); +extern void free_list(linked_list_t *list,int free_ptr); /* unlink or free */ +extern void free_elem(linked_element *e,int free_ptr); /* unlink or free */ +extern void *get_elem(linked_element *e); + +/* This is a shallow copy; it doesn't copy contained structures */ +extern linked_list_t *copy_list(linked_list_t *p_list); + +typedef struct c_block { + /* The buffer */ + int16_t *vector; + long begin; + long size; + + /* auxiliary support structures */ + unsigned char *flags; /* 1 known boundaries in read data + 2 known blanked data + 4 matched sample + 8 reserved + 16 reserved + 32 reserved + 64 reserved + 128 reserved + */ + + /* end of session cases */ + long lastsector; + cdrom_paranoia_t *p; + struct linked_element *e; + +} c_block_t; + +extern void free_c_block(c_block_t *c); +extern void i_cblock_destructor(c_block_t *c); +extern c_block_t *new_c_block(cdrom_paranoia_t *p); + +typedef struct v_fragment_s { + c_block_t *one; + + long begin; + long size; + int16_t *vector; + + /* end of session cases */ + long lastsector; + + /* linked list */ + cdrom_paranoia_t *p; + struct linked_element *e; + +} v_fragment_t; + +extern void free_v_fragment(v_fragment_t *c); +extern v_fragment_t *new_v_fragment(cdrom_paranoia_t *p, c_block_t *one, + long int begin, long int end, + int lastsector); +extern int16_t *v_buffer(v_fragment_t *v); + +extern c_block_t *c_first(cdrom_paranoia_t *p); +extern c_block_t *c_last(cdrom_paranoia_t *p); +extern c_block_t *c_next(c_block_t *c); +extern c_block_t *c_prev(c_block_t *c); + +extern v_fragment_t *v_first(cdrom_paranoia_t *p); +extern v_fragment_t *v_last(cdrom_paranoia_t *p); +extern v_fragment_t *v_next(v_fragment_t *v); +extern v_fragment_t *v_prev(v_fragment_t *v); + +typedef struct root_block{ + long returnedlimit; + long lastsector; + cdrom_paranoia_t *p; + + c_block_t *vector; /* doesn't use any sorting */ + int silenceflag; + long silencebegin; +} root_block; + +typedef struct offsets{ + + long offpoints; + long newpoints; + long offaccum; + long offdiff; + long offmin; + long offmax; + +} offsets; + +struct cdrom_paranoia_s { + cdrom_drive_t *d; + + root_block root; /* verified/reconstructed cached data */ + linked_list_t *cache; /* our data as read from the cdrom */ + long int cache_limit; + linked_list_t *fragments; /* fragments of blocks that have been 'verified' */ + sort_info_t *sortcache; + + int readahead; /* sectors of readahead in each readop */ + int jitter; + long lastread; + + paranoia_cb_mode_t enable; + long int cursor; + long int current_lastsector; + long int current_firstsector; + + /* statistics for drift/overlap */ + struct offsets stage1; + struct offsets stage2; + + long dynoverlap; + long dyndrift; + + /* statistics for verification */ + +}; + +extern c_block_t *c_alloc(int16_t *vector,long begin,long size); +extern void c_set(c_block_t *v,long begin); +extern void c_insert(c_block_t *v,long pos,int16_t *b,long size); +extern void c_remove(c_block_t *v,long cutpos,long cutsize); +extern void c_overwrite(c_block_t *v,long pos,int16_t *b,long size); +extern void c_append(c_block_t *v, int16_t *vector, long size); +extern void c_removef(c_block_t *v, long cut); + +#define ce(v) (v->begin+v->size) +#define cb(v) (v->begin) +#define cs(v) (v->size) + +/* pos here is vector position from zero */ + +extern void recover_cache(cdrom_paranoia_t *p); +extern void i_paranoia_firstlast(cdrom_paranoia_t *p); + +#define cv(c) (c->vector) + +#define fe(f) (f->begin+f->size) +#define fb(f) (f->begin) +#define fs(f) (f->size) +#define fv(f) (v_buffer(f)) + +#ifndef DO_NOT_WANT_PARANOIA_COMPATIBILITY +/** For compatibility with good ol' paranoia */ +#define linked_list linked_list_t +#endif /*DO_NOT_WANT_PARANOIA_COMPATIBILITY*/ + +#define CDP_COMPILE +#endif /*_P_BLOCK_H_*/ + diff --git a/lib/paranoia/paranoia.c b/lib/paranoia/paranoia.c new file mode 100644 index 00000000..2d73f583 --- /dev/null +++ b/lib/paranoia/paranoia.c @@ -0,0 +1,3025 @@ +/* + $Id: paranoia.c,v 1.27 2008/04/17 17:39:48 karl Exp $ + + Copyright (C) 2004, 2005, 2006, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/*** + * Toplevel file for the paranoia abstraction over the cdda lib + * + ***/ + +/* immediate todo:: */ +/* Allow disabling of root fixups? */ +/* Dupe bytes are creeping into cases that require greater overlap + than a single fragment can provide. We need to check against a + larger area* (+/-32 sectors of root?) to better eliminate + dupes. Of course this leads to other problems... Is it actually a + practically solvable problem? */ +/* Bimodal overlap distributions break us. */ +/* scratch detection/tolerance not implemented yet */ + +/*************************************************************** + + Da new shtick: verification now a two-step assymetric process. + + A single 'verified/reconstructed' data segment cache, and then the + multiple fragment cache + + verify a newly read block against previous blocks; do it only this + once. We maintain a list of 'verified sections' from these matches. + + We then glom these verified areas into a new data buffer. + Defragmentation fixups are allowed here alone. + + We also now track where read boundaries actually happened; do not + verify across matching boundaries. + + **************************************************************/ + +/*************************************************************** + + Silence. "It's BAAAAAAaaack." + + audio is now treated as great continents of values floating on a + mantle of molten silence. Silence is not handled by basic + verification at all; we simply anchor sections of nonzero audio to a + position and fill in everything else as silence. We also note the + audio that interfaces with silence; an edge must be 'wet'. + + **************************************************************/ + +/* =========================================================================== + * Let's translate the above vivid metaphor into something a mere mortal + * can understand: + * + * Non-silent audio is "solid." Silent audio is "wet" and fluid. The reason + * to treat silence as fluid is that if there's a long enough span of + * silence, we can't reliably detect jitter or dropped samples within that + * span (since all silence looks alike). Non-silent audio, on the other + * hand, is distinctive and can be reliably reassembled. + * + * So we treat long spans of silence specially. We only consider an edge + * of a non-silent region ("continent" or "island") to be "wet" if it borders + * a long span of silence. Short spans of silence are merely damp and can + * be reliably placed within a continent. + * + * We position ("anchor") the non-silent regions somewhat arbitrarily (since + * they may be jittered and we have no way to verify their exact position), + * and fill the intervening space with silence. + * + * See i_silence_match() for the gory details. + * =========================================================================== + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#include +#ifdef HAVE_STRING_H +#include +#endif +#include +#include +#include "../cdda_interface/smallft.h" +#include "p_block.h" +#include +#include "overlap.h" +#include "gap.h" +#include "isort.h" + +const char *paranoia_cb_mode2str[] = { + "read", + "verify", + "fixup edge", + "fixup atom", + "scratch", + "repair", + "skip", + "drift", + "backoff", + "overlap", + "fixup dropped", + "fixup duplicated", + "read error" +}; + +/** The below variables are trickery to force the above enum symbol + values to be recorded in debug symbol tables. They are used to + allow one to refer to the enumeration value names in the typedefs + above in a debugger and debugger expressions +*/ + +paranoia_mode_t debug_paranoia_mode; +paranoia_cb_mode_t debug_paranoia_cb_mode; + +static inline long +re(root_block *root) +{ + if (!root)return(-1); + if (!root->vector)return(-1); + return(ce(root->vector)); +} + +static inline long +rb(root_block *root) +{ + if (!root)return(-1); + if (!root->vector)return(-1); + return(cb(root->vector)); +} + +static inline +long rs(root_block *root) +{ + if (!root)return(-1); + if (!root->vector)return(-1); + return(cs(root->vector)); +} + +static inline int16_t * +rv(root_block *root){ + if (!root)return(NULL); + if (!root->vector)return(NULL); + return(cv(root->vector)); +} + +#define rc(r) (r->vector) + +/** + Flags indicating the status of a read samples. + + Imagine the below enumeration values are #defines to be used in a + bitmask rather than distinct values of an enum. + + The variable part of the declaration is trickery to force the enum + symbol values to be recorded in debug symbol tables. They are used + to allow one refer to the enumeration value names in a debugger + and in debugger expressions. +*/ +enum { + FLAGS_EDGE =0x1, /**< first/last N words of frame */ + FLAGS_UNREAD =0x2, /**< unread, hence missing and unmatchable */ + FLAGS_VERIFIED=0x4 /**< block read and verified */ +} paranoia_read_flags; + + +/**** matching and analysis code *****************************************/ + +/* =========================================================================== + * i_paranoia_overlap() (internal) + * + * This function is called when buffA[offsetA] == buffB[offsetB]. This + * function searches backward and forward to see how many consecutive + * samples also match. + * + * This function is called by do_const_sync() when we're not doing any + * verification. Its more complicated sibling is i_paranoia_overlap2. + * + * This function returns the number of consecutive matching samples. + * If (ret_begin) or (ret_end) are not NULL, it fills them with the + * offsets of the first and last matching samples in A. + */ +static inline long +i_paranoia_overlap(int16_t *buffA,int16_t *buffB, + long offsetA, long offsetB, + long sizeA,long sizeB, + long *ret_begin, long *ret_end) +{ + long beginA=offsetA,endA=offsetA; + long beginB=offsetB,endB=offsetB; + + /* Scan backward to extend the matching run in that direction. */ + for(; beginA>=0 && beginB>=0; beginA--,beginB--) + if (buffA[beginA] != buffB[beginB]) break; + beginA++; + beginB++; + + /* Scan forward to extend the matching run in that direction. */ + for(; endA=0 && beginB>=0; beginA--,beginB--) { + if (buffA[beginA] != buffB[beginB]) break; + + /* don't allow matching across matching sector boundaries */ + /* Stop if both samples were at the edges of a low-level read. + * ???: What implications does this have? + * ???: Why do we include the first sample for which this is true? + */ + if ((flagsA[beginA]&flagsB[beginB]&FLAGS_EDGE)) { + beginA--; + beginB--; + break; + } + + /* don't allow matching through known missing data */ + if ((flagsA[beginA]&FLAGS_UNREAD) || (flagsB[beginB]&FLAGS_UNREAD)) + break; + } + beginA++; + beginB++; + + /* Scan forward to extend the matching run in that direction. */ + for (; endAflags; + long ret=0; + + /* If we're doing any verification whatsoever, we have flags in stage + * 1, and will take them into account. Otherwise (e.g. in stage 2), + * we just do the simple equality test for samples on both sides of + * the initial match. + */ + if (flagB==NULL) + ret=i_paranoia_overlap(cv(A), iv(B), posA, posB, + cs(A), is(B), begin, end); + else + if ((flagB[posB]&FLAGS_UNREAD)==0) + ret=i_paranoia_overlap2(cv(A), iv(B), flagA, flagB, + posA, posB, cs(A), is(B), + begin, end); + + /* Small matching runs could just be coincidental. We only consider this + * a real match if it's long enough. + */ + if (ret > MIN_WORDS_SEARCH) { + *offset=+(posA+cb(A))-(posB+ib(B)); + + /* Note that try_sort_sync()'s swaps A & B when it calls this function, + * so while we adjust begin & end to be relative to A here, that means + * it's relative to B in try_sort_sync(). + */ + *begin+=cb(A); + *end+=cb(A); + return(ret); + } + + return(0); +} + + +/* =========================================================================== + * try_sort_sync() (internal) + * + * Starting from the sample in B with the absolute position (post), look + * for a matching run in A. This search will look in A for a first + * matching sample within (p->dynoverlap) samples around (post). If it + * finds one, it will then determine how many consecutive samples match + * both A and B from that point, looking backwards and forwards. If + * this search produces a matching run longer than MIN_WORDS_SEARCH, we + * consider it a match. + * + * When used by stage 1, the "post" is planted with respect to the old + * c_block being compare to the new c_block. In stage 2, the "post" is + * planted with respect to the verified root. + * + * This function returns 1 if a match is found and 0 if not. When a match + * is found, (begin) and (end) are set to the boundaries of the run, and + * (offset) is set to the difference in position of the run in A and B. + * (begin) and (end) are the absolute positions of the samples in + * B. (offset) transforms A to B's frame of reference. I.e., an offset of + * 2 would mean that A's absolute 3 is equivalent to B's 5. + */ + +/* post is w.r.t. B. in stage one, we post from old. In stage 2 we + post from root. Begin, end, offset count from B's frame of + reference */ + +static inline long int +try_sort_sync(cdrom_paranoia_t *p, + sort_info_t *A, unsigned char *Aflags, + c_block_t *B, + long int post, + long int *begin, + long int *end, + long *offset, + void (*callback)(long int, paranoia_cb_mode_t)) +{ + + long int dynoverlap=p->dynoverlap; + sort_link_t *ptr=NULL; + unsigned char *Bflags=B->flags; + + /* block flag matches FLAGS_UNREAD (and hence unmatchable) */ + if (Bflags==NULL || (Bflags[post-cb(B)]&FLAGS_UNREAD)==0){ + /* always try absolute offset zero first! */ + { + long zeropos=post-ib(A); + if (zeropos>=0 && zeroposstage1),*offset,callback); + + return(1); + } + } + } + } + } else + return(0); + + /* If the samples with the same absolute position didn't match, it's + * either a bad sample, or the two c_blocks are jittered with respect + * to each other. Now we search through A for samples that do have + * the same value as B's post. The search looks from first to last + * occurrence witin (dynoverlap) samples of (post). + */ + ptr=sort_getmatch(A,post-ib(A),dynoverlap,cv(B)[post-cb(B)]); + + while (ptr){ + + /* We've found a matching sample, so try to grow the matching run in + * both directions. If we find a long enough run (longer than + * MIN_WORDS_SEARCH), we've found a match. + */ + if (do_const_sync(B,A,Aflags, + post-cb(B),ipos(A,ptr), + begin,end,offset)){ + + /* ???BUG??? Jitter cannot be accurately detected when there are + * large regions of silence. Silence all looks alike, so if + * there is actually jitter but lots of silence, jitter (offset) + * will be incorrectly identified as 0. When the incorrect zero + * jitter is passed to offset_add_value, it eventually reduces + * dynoverlap so much that it's impossible for stage 2 to merge + * jittered fragments into the root (it doesn't search far enough). + * + * A potential solution (tested, but not committed) is to check + * for silence in do_const_sync and simply not call + * offset_add_value if the match is all silence. + * + * This bug is not fixed yet. + */ + /* ???: To be studied. */ + offset_add_value(p,&(p->stage1),*offset,callback); + return(1); + } + + /* The matching sample was just a fluke -- there weren't enough adjacent + * samples that matched to consider a matching run. So now we check + * for the next occurrence of that value in A. + */ + ptr=sort_nextmatch(A,ptr); + } + + /* We didn't find any matches. */ + *begin=-1; + *end=-1; + *offset=-1; + return(0); +} + + +/* =========================================================================== + * STAGE 1 MATCHING + * + * ???: Insert high-level explanation here. + * =========================================================================== + */ + +/* Top level of the first stage matcher */ + +/* We match each analysis point of new to the preexisting blocks +recursively. We can also optionally maintain a list of fragments of +the preexisting block that didn't match anything, and match them back +afterward. */ + +#define OVERLAP_ADJ (MIN_WORDS_OVERLAP/2-1) + + +/* =========================================================================== + * stage1_matched() (internal) + * + * This function is called whenever stage 1 verification finds two identical + * runs of samples from different reads. The runs must be more than + * MIN_WORDS_SEARCH samples long. They may be jittered (i.e. their absolute + * positions on the CD may not match due to inaccurate seeking) with respect + * to each other, but they have been verified to have no dropped samples + * within them. + * + * This function provides feedback via the callback mechanism and marks the + * runs as verified. The details of the marking are somehwat subtle and + * are described near the relevant code. + * + * Subsequent portions of the stage 1 code will build a verified fragment + * from this run. The verified fragment will eventually be merged + * into the verified root (and its absolute position determined) in + * stage 2. + */ +static inline void +stage1_matched(c_block_t *old, c_block_t *new, + long matchbegin,long matchend, + long matchoffset, + void (*callback)(long int, paranoia_cb_mode_t)) +{ + long i; + long oldadjbegin=matchbegin-cb(old); + long oldadjend=matchend-cb(old); + long newadjbegin=matchbegin-matchoffset-cb(new); + long newadjend=matchend-matchoffset-cb(new); + + + /* Provide feedback via the callback about the samples we've just + * verified. + * + * ???: How can matchbegin ever be < cb(old)? + * + * ???: Why do edge samples get logged only when there's jitter + * between the matched runs (matchoffset != 0)? + */ + if ( matchbegin-matchoffset<=cb(new) + || matchbegin<=cb(old) + || (new->flags[newadjbegin]&FLAGS_EDGE) + || (old->flags[oldadjbegin]&FLAGS_EDGE) ) { + if ( matchoffset && callback ) + (*callback)(matchbegin,PARANOIA_CB_FIXUP_EDGE); + } else + if (callback) + (*callback)(matchbegin,PARANOIA_CB_FIXUP_ATOM); + + if ( matchend-matchoffset>=ce(new) || + (new->flags[newadjend]&FLAGS_EDGE) || + matchend>=ce(old) || + (old->flags[oldadjend]&FLAGS_EDGE) ) { + if ( matchoffset && callback ) + (*callback)(matchend,PARANOIA_CB_FIXUP_EDGE); + } else + if (callback) + (*callback)(matchend, PARANOIA_CB_FIXUP_ATOM); + + +#if TRACE_PARANOIA & 1 + fprintf(stderr, "- Matched [%ld-%ld] against [%ld-%ld]\n", + newadjbegin+cb(new), newadjend+cb(new), + oldadjbegin+cb(old), oldadjend+cb(old)); +#endif + + /* Mark verified samples as "verified," but trim the verified region + * by OVERLAP_ADJ samples on each side. There are several significant + * implications of this trimming: + * + * 1) Why we trim at all: We have to trim to distinguish between two + * adjacent verified runs and one long verified run. We encounter this + * situation when samples have been dropped: + * + * matched portion of read 1 ....)(.... matched portion of read 1 + * read 2 adjacent run .....)(..... read 2 adjacent run + * || + * dropped samples in read 2 + * + * So at this point, the fact that we have two adjacent runs means + * that we have not yet verified that the two runs really are adjacent. + * (In fact, just the opposite: there are two runs because they were + * matched by separate runs, indicating that some samples didn't match + * across the length of read 2.) + * + * If we verify that they are actually adjacent (e.g. if the two runs + * are simply a result of matching runs from different reads, not from + * dropped samples), we will indeed mark them as one long merged run. + * + * 2) Why we trim by this amount: We want to ensure that when we + * verify the relationship between these two runs, we do so with + * an overlapping fragment at least OVERLAP samples long. Following + * from the above example: + * + * (..... matched portion of read 3 .....) + * read 2 adjacent run .....)(..... read 2 adjacent run + * + * Assuming there were no dropped samples between the adjacent runs, + * the matching portion of read 3 will need to be at least OVERLAP + * samples long to mark the two runs as one long verified run. + * If there were dropped samples, read 3 wouldn't match across the + * two runs, proving our caution worthwhile. + * + * 3) Why we partially discard the work we've done: We don't. + * When subsequently creating verified fragments from this run, + * we compensate for this trimming. Thus the verified fragment will + * contain the full length of verified samples. Only the c_blocks + * will reflect this trimming. + * + * ???: The comment below indicates that the sort cache is updated in + * some way, but this does not appear to be the case. + */ + + /* Mark the verification flags. Don't mark the first or + last OVERLAP/2 elements so that overlapping fragments + have to overlap by OVERLAP to actually merge. We also + remove elements from the sort such that later sorts do + not have to sift through already matched data */ + + newadjbegin+=OVERLAP_ADJ; + newadjend-=OVERLAP_ADJ; + for(i=newadjbegin;iflags[i]|=FLAGS_VERIFIED; /* mark verified */ + + oldadjbegin+=OVERLAP_ADJ; + oldadjend-=OVERLAP_ADJ; + for(i=oldadjbegin;iflags[i]|=FLAGS_VERIFIED; /* mark verified */ +} + + +/* =========================================================================== + * i_iterate_stage1 (internal) + * + * This function is called by i_stage1() to compare newly read samples with + * previously read samples, searching for contiguous runs of identical + * samples. Matching runs indicate that at least two reads of the CD + * returned identical data, with no dropped samples in that run. + * The runs may be jittered (i.e. their absolute positions on the CD may + * not be accurate due to inaccurate seeking) at this point. Their + * positions will be determined in stage 2. + * + * This function compares the new c_block (which has been indexed in + * p->sortcache) to a previous c_block. It is called for each previous + * c_block. It searches for runs of identical samples longer than + * MIN_WORDS_SEARCH. Samples in matched runs are marked as verified. + * + * Subsequent stage 1 code builds verified fragments from the runs of + * verified samples. These fragments are merged into the verified root + * in stage 2. + * + * This function returns the number of distinct runs verified in the new + * c_block when compared against this old c_block. + */ +static long int +i_iterate_stage1(cdrom_paranoia_t *p, c_block_t *old, c_block_t *new, + void(*callback)(long int, paranoia_cb_mode_t)) +{ + long matchbegin = -1; + long matchend = -1; + long matchoffset; + + /* ???: Why do we limit our search only to the samples with overlapping + * absolute positions? It could be because it eliminates some further + * bounds checking. + * + * Why do we "no longer try to spread the ... search" as mentioned below? + */ + /* we no longer try to spread the stage one search area by dynoverlap */ + long searchend = min(ce(old), ce(new)); + long searchbegin = max(cb(old), cb(new)); + long searchsize = searchend-searchbegin; + sort_info_t *i = p->sortcache; + long ret = 0; + long int j; + + long tried = 0; + long matched = 0; + + if (searchsize<=0) + return(0); + + /* match return values are in terms of the new vector, not old */ + + /* ???: Why 23? */ + + for (j=searchbegin; jflags[j-cb(new)] & (FLAGS_VERIFIED|FLAGS_UNREAD)) == 0) { + tried++; + + /* Starting from the sample in the old c_block with the absolute + * position j, look for a matching run in the new c_block. This + * search will look a certain distance around j, and if successful + * will extend the matching run as far backward and forward as + * it can. + * + * The search will only return 1 if it finds a matching run long + * enough to be deemed significant. + */ + if (try_sort_sync(p, i, new->flags, old, j, + &matchbegin, &matchend, &matchoffset, + callback) == 1) { + + matched+=matchend-matchbegin; + + /* purely cosmetic: if we're matching zeros, don't use the + callback because they will appear to be all skewed */ + { + long j = matchbegin-cb(old); + long end = matchend-cb(old); + for (; j j) + j = matchend-1; + } + } + } /* end for */ + +#ifdef NOISY + fprintf(stderr,"iterate_stage1: search area=%ld[%ld-%ld] tried=%ld matched=%ld spans=%ld\n", + searchsize,searchbegin,searchend,tried,matched,ret); +#endif + + return(ret); +} + + +/* =========================================================================== + * i_stage1() (internal) + * + * Compare newly read samples against previously read samples, searching + * for contiguous runs of identical samples. Matching runs indicate that + * at least two reads of the CD returned identical data, with no dropped + * samples in that run. The runs may be jittered (i.e. their absolute + * positions on the CD may not be accurate due to inaccurate seeking) at + * this point. Their positions will be determined in stage 2. + * + * This function compares a new c_block against all other c_blocks in memory, + * searching for sufficiently long runs of identical samples. Since each + * c_block represents a separate call to read_c_block, this ensures that + * multiple reads have returned identical data. (Additionally, read_c_block + * varies the reads so that multiple reads are unlikely to produce identical + * errors, so any matches between reads are considered verified. See + * i_read_c_block for more details.) + * + * Each time we find such a run (longer than MIN_WORDS_SEARCH), we mark + * the samples as "verified" in both c_blocks. Runs of verified samples in + * the new c_block are promoted into verified fragments, which will later + * be merged into the verified root in stage 2. + * + * In reality, not all the verified samples are marked as "verified." + * See stage1_matched() for an explanation. + * + * This function returns the number of verified fragments created by the + * stage 1 matching. + */ +static long int +i_stage1(cdrom_paranoia_t *p, c_block_t *p_new, + void (*callback)(long int, paranoia_cb_mode_t)) +{ + long size=cs(p_new); + c_block_t *ptr=c_last(p); + int ret=0; + long int begin=0; + long int end; + +#if TRACE_PARANOIA & 1 + long int block_count = 0; + fprintf(stderr, + "Verifying block %ld:[%ld-%ld] against previously read blocks...\n", + p->cache->active, + cb(p_new), ce(p_new)); +#endif + + /* We're going to be comparing the new c_block against the other + * c_blocks in memory. Initialize the "sort cache" index to allow + * for fast searching through the new c_block. (The index will + * actually be built the first time we search.) + */ + if (ptr) + sort_setup( p->sortcache, cv(p_new), &cb(p_new), cs(p_new), cb(p_new), + ce(p_new) ); + + /* Iterate from oldest to newest c_block, comparing the new c_block + * to each, looking for a sufficiently long run of identical samples + * (longer than MIN_WORDS_SEARCH), which will be marked as "verified" + * in both c_blocks. + * + * Since the new c_block is already in the list (at the head), don't + * compare it against itself. + */ + while ( ptr && ptr != p_new ) { +#if TRACE_PARANOIA & 1 + block_count++; + fprintf(stderr, + "- Verifying against block %ld:[%ld-%ld] dynoverlap=%ld\n", + block_count, cb(ptr), ce(ptr), p->dynoverlap); +#endif + + if (callback) + (*callback)(cb(p_new), PARANOIA_CB_VERIFY); + i_iterate_stage1(p,ptr,p_new,callback); + + ptr=c_prev(ptr); + } + + /* parse the verified areas of p_new into v_fragments */ + + /* Find each run of contiguous verified samples in the new c_block + * and create a verified fragment from each run. + */ + begin=0; + while (beginflags[begin]&FLAGS_VERIFIED) break; + for (end=begin; end < size; end++) + if ((p_new->flags[end]&FLAGS_VERIFIED)==0) break; + if (begin>=size) break; + + ret++; + + /* We create a new verified fragment from the contiguous run + * of verified samples. + * + * We expand the "verified" range by OVERLAP_ADJ on each side + * to compensate for trimming done to the verified range by + * stage1_matched(). The samples were actually verified, and + * hence belong in the verified fragment. See stage1_matched() + * for an explanation of the trimming. + */ + new_v_fragment(p,p_new,cb(p_new)+max(0,begin-OVERLAP_ADJ), + cb(p_new)+min(size,end+OVERLAP_ADJ), + (end+OVERLAP_ADJ>=size && p_new->lastsector)); + + begin=end; + } + + /* Return the number of distinct verified fragments we found with + * stage 1 matching. + */ + return(ret); +} + + +/* =========================================================================== + * STAGE 2 MATCHING + * + * ???: Insert high-level explanation here. + * =========================================================================== + */ + +typedef struct sync_result { + long offset; + long begin; + long end; +} sync_result_t; + +/* Reconcile v_fragments to root buffer. Free if matched, fragment/fixup root + if necessary. + + Do *not* match using zero posts +*/ + +/* =========================================================================== + * i_iterate_stage2 (internal) + * + * This function searches for a sufficiently long run of identical samples + * between the passed verified fragment and the verified root. The search + * is similar to that performed by i_iterate_stage1. Of course, what we do + * as a result of a match is different. + * + * Our search is slightly different in that we refuse to match silence to + * silence. All silence looks alike, and it would result in too many false + * positives here, so we handle silence separately. + * + * Also, because we're trying to determine whether this fragment as a whole + * overlaps with the root at all, we narrow our search (since it should match + * immediately or not at all). This is in contrast to stage 1, where we + * search the entire vector looking for all possible matches. + * + * This function returns 0 if no match was found (including failure to find + * one due to silence), or 1 if we found a match. + * + * When a match is found, the sync_result_t is set to the boundaries of + * matching run (begin/end, in terms of the root) and how far out of sync + * the fragment is from the canonical root (offset). Note that this offset + * is opposite in sign from the notion of offset used by try_sort_sync() + * and stage 1 generally. + */ +static long int +i_iterate_stage2(cdrom_paranoia_t *p, + v_fragment_t *v, + sync_result_t *r, + void(*callback)(long int, paranoia_cb_mode_t)) +{ + root_block *root=&(p->root); + long matchbegin=-1,matchend=-1,offset; + long fbv,fev; + +#if TRACE_PARANOIA & 2 + fprintf(stderr, "- Comparing fragment [%ld-%ld] to root [%ld-%ld]...", + fb(v), fe(v), rb(root), re(root)); +#endif + +#ifdef NOISY + fprintf(stderr,"Stage 2 search: fbv=%ld fev=%ld\n",fb(v),fe(v)); +#endif + + /* Quickly check whether there could possibly be any overlap between + * the verified fragment and the root. Our search will allow up to + * (p->dynoverlap) jitter between the two, so we expand the fragment + * search area by p->dynoverlap on both sides and see if that expanded + * area overlaps with the root. + * + * We could just as easily expand root's boundaries by p->dynoverlap + * instead and achieve the same result. + */ + if (min(fe(v) + p->dynoverlap,re(root)) - + max(fb(v) - p->dynoverlap,rb(root)) <= 0) + return(0); + + if (callback) + (*callback)(fb(v), PARANOIA_CB_VERIFY); + + /* We're going to try to match the fragment to the root while allowing + * for p->dynoverlap jitter, so we'll actually be looking at samples + * in the fragment whose position claims to be up to p->dynoverlap + * outside the boundaries of the root. But, of course, don't extend + * past the edges of the fragment. + */ + fbv = max(fb(v), rb(root)-p->dynoverlap); + + /* Skip past leading zeroes in the fragment, and bail if there's nothing + * but silence. We handle silence later separately. + */ + while (fbvdynoverlap outside the boundaries + * of the root, but don't extend past the edges of the fragment. + * + * However, we also limit the search to no more than 256 samples. + * Unlike stage 1, we're not trying to find all possible matches within + * two runs -- rather, we're trying to see if the fragment as a whole + * overlaps with the root. If we can't find a match within 256 samples, + * there's probably no match to be found (because this fragment doesn't + * overlap with the root). + * + * ??? Is this why? Why 256? + */ + fev = min(min(fbv+256, re(root)+p->dynoverlap), fe(v)); + + { + /* Because we'll allow for up to (p->dynoverlap) jitter between the + * fragment and the root, we expand the search area (fbv to fev) by + * p->dynoverlap on both sides. But, because we're iterating through + * root, we need to constrain the search area not to extend beyond + * the root's boundaries. + */ + long searchend=min(fev+p->dynoverlap,re(root)); + long searchbegin=max(fbv-p->dynoverlap,rb(root)); + sort_info_t *i=p->sortcache; + long j; + + /* Initialize the "sort cache" index to allow for fast searching + * through the verified fragment between (fbv,fev). (The index will + * actually be built the first time we search.) + */ + sort_setup(i, fv(v), &fb(v), fs(v), fbv, fev); + + /* ??? Why 23? */ + for(j=searchbegin; jbegin=matchbegin; + r->end=matchend; + r->offset=-offset; + if (offset)if (callback)(*callback)(r->begin,PARANOIA_CB_FIXUP_EDGE); + return(1); + } + } + } + + return(0); +} + + +/* =========================================================================== + * i_silence_test() (internal) + * + * If the entire root is silent, or there's enough trailing silence + * to be significant (MIN_SILENCE_BOUNDARY samples), mark the beginning + * of the silence and "light" the silence flag. This flag will remain lit + * until i_silence_match() appends some non-silent samples to the root. + * + * We do this because if there's a long enough span of silence, we can't + * reliably detect jitter or dropped samples within that span. See + * i_silence_match() for details on how we recover from this situation. + */ +static void +i_silence_test(root_block *root) +{ + int16_t *vec=rv(root); + long end=re(root)-rb(root)-1; + long j; + + /* Look backward from the end of the root to find the first non-silent + * sample. + */ + for(j=end-1;j>=0;j--) + if (vec[j]!=0) break; + + /* If the entire root is silent, or there's enough trailing silence + * to be significant, mark the beginning of the silence and "light" + * the silence flag. + */ + if (j<0 || end-j>MIN_SILENCE_BOUNDARY) { + /* ???BUG???: + * + * The original code appears to have a bug, as it points to the + * last non-zero sample, and silence matching appears to treat + * silencebegin as the first silent sample. As a result, in certain + * situations, the last non-zero sample can get clobbered. + * + * This bug has been tentatively fixed, since it allows more regression + * tests to pass. The original code was: + * if (j<0)j=0; + */ + j++; + + root->silenceflag=1; + root->silencebegin=rb(root)+j; + + /* ???: To be studied. */ + if (root->silencebeginreturnedlimit) + root->silencebegin=root->returnedlimit; + } +} + + +/* =========================================================================== + * i_silence_match() (internal) + * + * This function is merges verified fragments into the verified root in cases + * where there is a problematic amount of silence (MIN_SILENCE_BOUNDARY + * samples) at the end of the root. + * + * We need a special approach because if there's a long enough span of + * silence, we can't reliably detect jitter or dropped samples within that + * span (since all silence looks alike). + * + * Only fragments that begin with MIN_SILENCE_BOUNDARY samples are eligible + * to be merged in this case. Fragments that are too far beyond the edge + * of the root to possibly overlap are also disregarded. + * + * Our first approach is to assume that such fragments have no jitter (since + * we can't establish otherwise) and merge them. However, if it's clear + * that there must be jitter (i.e. because non-silent samples overlap when + * we assume no jitter), we assume the fragment has the minimum possible + * jitter and then merge it. + * + * This function extends silence fairly aggressively, so it must be called + * with fragments in ascending order (beginning position) in case there are + * small non-silent regions within the silence. + */ +static long int +i_silence_match(root_block *root, v_fragment_t *v, + void(*callback)(long int, paranoia_cb_mode_t)) +{ + + cdrom_paranoia_t *p=v->p; + int16_t *vec=fv(v); + long end=fs(v),begin; + long j; + +#if TRACE_PARANOIA & 2 + fprintf(stderr, "- Silence matching fragment [%ld-%ld] to root [%ld-%ld]" + " silencebegin=%ld\n", + fb(v), fe(v), rb(root), re(root), root->silencebegin); +#endif + + /* See how much leading silence this fragment has. If there are fewer than + * MIN_SILENCE_BOUNDARY leading silent samples, we don't do this special + * silence matching. + * + * This fragment could actually belong here, but we can't be sure unless + * it has enough silence on its leading edge. This fragment will likely + * stick around until we do successfully extend the root, at which point + * it will be merged using the usual method. + */ + if (enddynoverlap samples of the end of root). + */ + if (fb(v)>=re(root) && fb(v)-p->dynoverlapsilencebegin); + end = min(j,re(root)); + + /* If there is an overlap, we assume that both the root and the fragment + * are jitter-free (since there's no way for us to tell otherwise). + */ + if (beginre(root)){ + long int voff = begin-fb(v); + + /* Truncate the overlapping silence from the end of the root. + */ + c_remove(rc(root),begin-rb(root),-1); + + /* Append the fragment to the root, starting from the point of overlap. + */ + c_append(rc(root),vec+voff,fs(v)-voff); + +#if TRACE_PARANOIA & 2 + fprintf(stderr, "* Adding [%ld-%ld] to root (no jitter)\n", + begin, re(root)); +#endif + } + + /* Record the fact that we merged this fragment assuming zero jitter. + */ + offset_add_value(p,&p->stage2,0,callback); + + } else { + + /* We weren't able to merge the fragment assuming zero jitter. + * + * Check whether the fragment's leading silence ends before the root's + * trailing silence begins. If it does, we assume that the root is + * jittered forward. + */ + if (jre(root)) { + + /* Truncate the trailing silence from the root. + */ + c_remove(rc(root),root->silencebegin-rb(root),-1); + + /* Append the non-silent tail of the fragment to the root. + */ + c_append(rc(root),vec+voff,fs(v)-voff); + +#if TRACE_PARANOIA & 2 + fprintf(stderr, "* Adding [%ld-%ld] to root (jitter=%ld)\n", + root->silencebegin, re(root), end-begin); +#endif + } + + /* Record the fact that we merged this fragment assuming (end-begin) + * jitter. + */ + offset_add_value(p,&p->stage2,end-begin,callback); + + } else + + /* We only get here if the fragment is past the end of the root, + * which means it must be farther than (dynoverlap) away, due to our + * root extension above. + */ + + /* We weren't able to merge this fragment into the root after all. + */ + return(0); + } + + + /* We only get here if we merged the fragment into the root. Update + * the root's silence flag. + * + * Note that this is the only place silenceflag is reset. In other words, + * once i_silence_test() lights the silence flag, it can only be reset + * by i_silence_match(). + */ + root->silenceflag = 0; + + /* Now see if the new, extended root ends in silence. + */ + i_silence_test(root); + + + /* Since we merged the fragment, we can free it now. But first we propagate + * its lastsector flag. + */ + if (v->lastsector) root->lastsector=1; + free_v_fragment(v); + return(1); +} + + +/* =========================================================================== + * i_stage2_each (internal) + * + * This function (which is entirely too long) attempts to merge the passed + * verified fragment into the verified root. + * + * First this function looks for a run of identical samples between + * the root and the fragment. If it finds a long enough run, it then + * checks for "rifts" (see below) and fixes the root and/or fragment as + * necessary. Finally, if the fragment will extend the tail of the root, + * we merge the fragment and extend the root. + * + * Most of the ugliness in this function has to do with handling "rifts", + * which are points of disagreement between the root and the verified + * fragment. This can happen when a drive consistently drops a few samples + * or stutters and repeats a few samples. It has to be consistent enough + * to result in a verified fragment (i.e. it happens twice), but inconsistent + * enough (e.g. due to the jiggled reads) not to happen every time. + * + * This function returns 1 if the fragment was successfully merged into the + * root, and 0 if not. + */ +static long int +i_stage2_each(root_block *root, v_fragment_t *v, + void(*callback)(long int, paranoia_cb_mode_t)) +{ + + cdrom_paranoia_t *p=v->p; + + /* ??? Why do we round down to an even dynoverlap? */ + long dynoverlap=p->dynoverlap/2*2; + + /* If this fragment has already been merged & freed, abort. */ + if (!v || !v->one) return(0); + + /* If there's no verified root yet, abort. */ + if (!rv(root)){ + return(0); + } else { + sync_result_t r; + + /* Search for a sufficiently long run of identical samples between + * the verified fragment and the verified root. There's a little + * bit of subtlety in the search when silence is involved. + */ + if (i_iterate_stage2(p,v,&r,callback)){ + + /* Convert the results of the search to be relative to the root. */ + long int begin=r.begin-rb(root); + long int end=r.end-rb(root); + + /* Convert offset into a value that will transform a relative + * position in the root to the corresponding relative position in + * the fragment. I.e., if offset = -2, then the sample at relative + * position 2 in the root is at relative position 0 in the fragment. + * + * While a bit opaque, this does reduce the number of calculations + * below. + */ + long int offset=r.begin+r.offset-fb(v)-begin; + long int temp; + c_block_t *l=NULL; + + /* we have a match! We don't rematch off rift, we chase the + match all the way to both extremes doing rift analysis. */ + +#if TRACE_PARANOIA & 2 + fprintf(stderr, "matched [%ld-%ld], offset=%ld\n", + r.begin, r.end, r.offset); + int traced = 0; +#endif +#ifdef NOISY + fprintf(stderr,"Stage 2 match\n"); +#endif + + /* Now that we've found a sufficiently long run of identical samples + * between the fragment and the root, we need to check for rifts. + * + * A "rift", as mentioned above, is a disagreement between the + * fragment and the root. When there's a rift, the matching run + * found by i_iterate_stage2() will obviously stop where the root + * and the fragment disagree. + * + * So we detect rifts by checking whether the matching run extends + * to the ends of the fragment and root. If the run does extend to + * the ends of the fragment and root, then all overlapping samples + * agreed, and there's no rift. If, however, the matching run + * stops with samples left over in both the root and the fragment, + * that means the root and fragment disagreed at that point. + * Leftover samples at the beginning of the match indicate a + * leading rift, and leftover samples at the end of the match indicate + * a trailing rift. + * + * Once we detect a rift, we attempt to fix it, depending on the + * nature of the disagreement. See i_analyze_rift_[rf] for details + * on how we determine what kind of rift it is. See below for + * how we attempt to fix the rifts. + */ + + /* First, check for a leading rift, fix it if possible, and then + * extend the match forward until either we hit the limit of the + * overlapping samples, or until we encounter another leading rift. + * Keep doing this until we hit the beginning of the overlap. + * + * Note that while we do fix up leading rifts, we don't extend + * the root backward (earlier samples) -- only forward (later + * samples). + */ + + /* If the beginning of the match didn't reach the beginning of + * either the fragment or the root, we have a leading rift to be + * examined. + * + * Remember that (begin) is the offset into the root, and (begin+offset) + * is the equivalent offset into the fragment. If neither one is at + * zero, then they both have samples before the match, and hence a + * rift. + */ + while ((begin+offset>0 && begin>0)){ + long matchA=0,matchB=0,matchC=0; + + /* (begin) is the offset into the root of the first matching sample, + * (beginL) is the offset into the fragment of the first matching + * sample. These samples are at the edge of the rift. + */ + long beginL=begin+offset; + +#if TRACE_PARANOIA & 2 + if ((traced & 1) == 0) { + fprintf(stderr, "- Analyzing leading rift...\n"); + traced |= 1; + } +#endif + + /* The first time we encounter a leading rift, allocate a + * scratch copy of the verified fragment which we'll use if + * we need to fix up the fragment before merging it into + * the root. + */ + if (l==NULL){ + int16_t *buff=malloc(fs(v)*sizeof(int16_t)); + l=c_alloc(buff,fb(v),fs(v)); + memcpy(buff,fv(v),fs(v)*sizeof(int16_t)); + } + + /* Starting at the first mismatching sample, see how far back the + * rift goes, and determine what kind of rift it is. Note that + * we're searching through the fixed up copy of the fragment. + * + * matchA > 0 if there are samples missing from the root + * matchA < 0 if there are duplicate samples (stuttering) in the root + * matchB > 0 if there are samples missing from the fragment + * matchB < 0 if there are duplicate samples in the fragment + * matchC != 0 if there's a section of garbage, after which + * the fragment and root agree and are in sync + */ + i_analyze_rift_r(rv(root),cv(l), + rs(root),cs(l), + begin-1,beginL-1, + &matchA,&matchB,&matchC); + +#ifdef NOISY + fprintf(stderr,"matching rootR: matchA:%ld matchB:%ld matchC:%ld\n", + matchA,matchB,matchC); +#endif + + /* ??? The root.returnedlimit checks below are presently a mystery. */ + + if (matchA){ + /* There's a problem with the root */ + + if (matchA>0){ + /* There were (matchA) samples dropped from the root. We'll add + * them back from the fixed up fragment. + */ + if (callback) + (*callback)(begin+rb(root)-1,PARANOIA_CB_FIXUP_DROPPED); + if (rb(root)+beginroot.returnedlimit) + break; + else{ + + /* At the edge of the rift in the root, insert the missing + * samples from the fixed up fragment. They're the (matchA) + * samples immediately preceding the edge of the rift in the + * fragment. + */ + c_insert(rc(root),begin,cv(l)+beginL-matchA, + matchA); + + /* We just inserted (matchA) samples into the root, so update + * our begin/end offsets accordingly. Also adjust the + * (offset) to compensate (since we use it to find samples in + * the fragment, and the fragment hasn't changed). + */ + offset-=matchA; + begin+=matchA; + end+=matchA; + } + + } else { + /* There were (-matchA) duplicate samples (stuttering) in the + * root. We'll drop them. + */ + if (callback) + (*callback)(begin+rb(root)-1,PARANOIA_CB_FIXUP_DUPED); + if (rb(root)+begin+matchAroot.returnedlimit) + break; + else{ + + /* Remove the (-matchA) samples immediately preceding the + * edge of the rift in the root. + */ + c_remove(rc(root),begin+matchA,-matchA); + + /* We just removed (-matchA) samples from the root, so update + * our begin/end offsets accordingly. Also adjust the offset + * to compensate. Remember that matchA < 0, so we're actually + * subtracting from begin/end. + */ + offset-=matchA; + begin+=matchA; + end+=matchA; + } + } + } else if (matchB){ + /* There's a problem with the fragment */ + + if (matchB>0){ + /* There were (matchB) samples dropped from the fragment. We'll + * add them back from the root. + */ + if (callback) + (*callback)(begin+rb(root)-1,PARANOIA_CB_FIXUP_DROPPED); + + /* At the edge of the rift in the fragment, insert the missing + * samples from the root. They're the (matchB) samples + * immediately preceding the edge of the rift in the root. + * Note that we're fixing up the scratch copy of the fragment. + */ + c_insert(l,beginL,rv(root)+begin-matchB, + matchB); + + /* We just inserted (matchB) samples into the fixed up fragment, + * so update (offset), since we use it to find samples in the + * fragment based on the root's unchanged offsets. + */ + offset+=matchB; + + } else { + /* There were (-matchB) duplicate samples (stuttering) in the + * fixed up fragment. We'll drop them. + */ + if (callback) + (*callback)(begin+rb(root)-1,PARANOIA_CB_FIXUP_DUPED); + + /* Remove the (-matchB) samples immediately preceding the edge + * of the rift in the fixed up fragment. + */ + c_remove(l,beginL+matchB,-matchB); + + /* We just removed (-matchB) samples from the fixed up fragment, + * so update (offset), since we use it to find samples in the + * fragment based on the root's unchanged offsets. + */ + offset+=matchB; + } + + } else if (matchC){ + + /* There are (matchC) samples that simply disagree between the + * fragment and the root. On the other side of the mismatch, the + * fragment and root agree again. We can't classify the mismatch + * as either a stutter or dropped samples, and we have no way of + * telling whether the fragment or the root is right. + * + * The original comment indicated that we set "disagree" flags + * in the root, but it seems to be historical. + */ + + if (rb(root)+begin-matchCroot.returnedlimit) + break; + + /* Overwrite the mismatching (matchC) samples in root with the + * samples from the fixed up fragment. + * + * ??? Do we think the fragment is more likely correct, is this + * just arbitrary, or is there some other reason for overwriting + * the root? + */ + c_overwrite(rc(root),begin-matchC, + cv(l)+beginL-matchC,matchC); + + } else { + + /* We may have had a mismatch because we ran into leading silence. + * + * ??? To be studied: why would this cause a mismatch? Neither + * i_analyze_rift_r nor i_iterate_stage2() nor i_paranoia_overlap() + * appear to take silence into consideration in this regard. + * It could be due to our skipping of silence when searching for + * a match. + * + * Since we don't extend the root in that direction, we don't + * do anything, just move on to trailing rifts. + */ + + /* If the rift was too complex to fix (see i_analyze_rift_r), + * we just stop and leave the leading edge where it is. + */ + + /*RRR(*callback)(post,PARANOIA_CB_XXX);*/ + break; + } + + /* Recalculate the offset of the edge of the rift in the fixed + * up fragment, in case it changed. + * + * ??? Why is this done here rather than in the (matchB) case above, + * which should be the only time beginL will change. + */ + beginL=begin+offset; + + /* Now that we've fixed up the root or fragment as necessary, see + * how far we can extend the matching run. This function is + * overkill, as it tries to extend the matching run in both + * directions (and rematches what we already matched), but it works. + */ + i_paranoia_overlap(rv(root),cv(l), + begin,beginL, + rs(root),cs(l), + &begin,&end); + + } /* end while (leading rift) */ + + + /* Second, check for a trailing rift, fix it if possible, and then + * extend the match forward until either we hit the limit of the + * overlapping samples, or until we encounter another trailing rift. + * Keep doing this until we hit the end of the overlap. + */ + + /* If the end of the match didn't reach the end of either the fragment + * or the root, we have a trailing rift to be examined. + * + * Remember that (end) is the offset into the root, and (end+offset) + * is the equivalent offset into the fragment. If neither one is + * at the end of the vector, then they both have samples after the + * match, and hence a rift. + * + * (temp) is the size of the (potentially fixed-up) fragment. If + * there was a leading rift, (l) is the fixed up fragment, and + * (offset) is now relative to it. + */ + temp=l ? cs(l) : fs(v); + while (end+offset 0 if there are samples missing from the root + * matchA < 0 if there are duplicate samples (stuttering) in the root + * matchB > 0 if there are samples missing from the fragment + * matchB < 0 if there are duplicate samples in the fragment + * matchC != 0 if there's a section of garbage, after which + * the fragment and root agree and are in sync + */ + i_analyze_rift_f(rv(root),cv(l), + rs(root),cs(l), + end,endL, + &matchA,&matchB,&matchC); + +#ifdef NOISY + fprintf(stderr,"matching rootF: matchA:%ld matchB:%ld matchC:%ld\n", + matchA,matchB,matchC); +#endif + + /* ??? The root.returnedlimit checks below are presently a mystery. */ + + if (matchA){ + /* There's a problem with the root */ + + if (matchA>0){ + /* There were (matchA) samples dropped from the root. We'll add + * them back from the fixed up fragment. + */ + if (callback)(*callback)(end+rb(root),PARANOIA_CB_FIXUP_DROPPED); + if (end+rb(root)root.returnedlimit) + break; + + /* At the edge of the rift in the root, insert the missing + * samples from the fixed up fragment. They're the (matchA) + * samples immediately preceding the edge of the rift in the + * fragment. + */ + c_insert(rc(root),end,cv(l)+endL,matchA); + + /* Although we just inserted samples into the root, we did so + * after (begin) and (end), so we needn't update those offsets. + */ + + } else { + /* There were (-matchA) duplicate samples (stuttering) in the + * root. We'll drop them. + */ + if (callback)(*callback)(end+rb(root),PARANOIA_CB_FIXUP_DUPED); + if (end+rb(root)root.returnedlimit) + break; + + /* Remove the (-matchA) samples immediately following the edge + * of the rift in the root. + */ + c_remove(rc(root),end,-matchA); + + /* Although we just removed samples from the root, we did so + * after (begin) and (end), so we needn't update those offsets. + */ + + } + } else if (matchB){ + /* There's a problem with the fragment */ + + if (matchB>0){ + /* There were (matchB) samples dropped from the fragment. We'll + * add them back from the root. + */ + if (callback)(*callback)(end+rb(root),PARANOIA_CB_FIXUP_DROPPED); + + /* At the edge of the rift in the fragment, insert the missing + * samples from the root. They're the (matchB) samples + * immediately following the dge of the rift in the root. + * Note that we're fixing up the scratch copy of the fragment. + */ + c_insert(l,endL,rv(root)+end,matchB); + + /* Although we just inserted samples into the fragment, we did so + * after (begin) and (end), so (offset) hasn't changed either. + */ + + } else { + /* There were (-matchB) duplicate samples (stuttering) in the + * fixed up fragment. We'll drop them. + */ + if (callback)(*callback)(end+rb(root),PARANOIA_CB_FIXUP_DUPED); + + /* Remove the (-matchB) samples immediately following the edge + * of the rift in the fixed up fragment. + */ + c_remove(l,endL,-matchB); + + /* Although we just removed samples from the fragment, we did so + * after (begin) and (end), so (offset) hasn't changed either. + */ + } + } else if (matchC){ + + /* There are (matchC) samples that simply disagree between the + * fragment and the root. On the other side of the mismatch, the + * fragment and root agree again. We can't classify the mismatch + * as either a stutter or dropped samples, and we have no way of + * telling whether the fragment or the root is right. + * + * The original comment indicated that we set "disagree" flags + * in the root, but it seems to be historical. + */ + + if (end+rb(root)root.returnedlimit) + break; + + /* Overwrite the mismatching (matchC) samples in root with the + * samples from the fixed up fragment. + * + * ??? Do we think the fragment is more likely correct, is this + * just arbitrary, or is there some other reason for overwriting + * the root? + */ + c_overwrite(rc(root),end,cv(l)+endL,matchC); + + } else { + + /* We may have had a mismatch because we ran into trailing silence. + * + * ??? To be studied: why would this cause a mismatch? Neither + * i_analyze_rift_f nor i_iterate_stage2() nor i_paranoia_overlap() + * appear to take silence into consideration in this regard. + * It could be due to our skipping of silence when searching for + * a match. + */ + + /* At this point we have a trailing rift. We check whether + * one of the vectors (fragment or root) has trailing silence. + */ + analyze_rift_silence_f(rv(root),cv(l), + rs(root),cs(l), + end,endL, + &matchA,&matchB); + if (matchA){ + + /* The contents of the root's trailing rift are silence. The + * fragment's are not (otherwise there wouldn't be a rift). + * We therefore assume that the root has garbage from this + * point forward and truncate it. + * + * This will have the effect of eliminating the trailing + * rift, causing the fragment's samples to be appended to + * the root. + * + * ??? Does this have any negative side effects? Why is this + * a good idea? + */ + /* ??? TODO: returnedlimit */ + /* Can only do this if we haven't already returned data */ + if (end+rb(root)>=p->root.returnedlimit){ + c_remove(rc(root),end,-1); + } + + } else if (matchB){ + + /* The contents of the fragment's trailing rift are silence. + * The root's are not (otherwise there wouldn't be a rift). + * We therefore assume that the fragment has garbage from this + * point forward. + * + * We needn't actually truncate the fragment, because the root + * has already been fixed up from this fragment as much as + * possible, and the truncated fragment wouldn't extend the + * root. Therefore, we can consider this (truncated) fragment + * to be already merged into the root. So we dispose of it and + * return a success. + */ + if (l)i_cblock_destructor(l); + free_v_fragment(v); + return(1); + + } else { + + /* If the rift was too complex to fix (see i_analyze_rift_f), + * we just stop and leave the trailing edge where it is. + */ + + /*RRR(*callback)(post,PARANOIA_CB_XXX);*/ + } + break; + } + + /* Now that we've fixed up the root or fragment as necessary, see + * how far we can extend the matching run. This function is + * overkill, as it tries to extend the matching run in both + * directions (and rematches what we already matched), but it works. + */ + i_paranoia_overlap(rv(root),cv(l), + begin,beginL, + rs(root),cs(l), + NULL,&end); + + /* ???BUG??? (temp) never gets updated within the loop, even if the + * fragment gets fixed up. In contrast, rs(root) is inherently + * updated when the verified root gets fixed up. + * + * This bug is not fixed yet. + */ + + } /* end while (trailing rift) */ + + + /* Third and finally, if the overlapping verified fragment extends + * our range forward (later samples), we append ("glom") the new + * samples to the end of the root. + * + * Note that while we did fix up leading rifts, we don't extend + * the root backward (earlier samples) -- only forward (later + * samples). + * + * This is generally fine, since the verified root is supposed to + * slide from earlier samples to later samples across multiple calls + * to paranoia_read(). + * + * ??? But, is this actually right? Because of this, we don't + * extend the root to hold the earliest read sample, if we happened + * to initialize the root with a later sample due to jitter. + * There are probably some ugly side effects from extending the root + * backward, in the general case, but it may not be so dire if we're + * near sample 0. To be investigated. + */ + { + long sizeA=rs(root); + long sizeB; + long vecbegin; + int16_t *vector; + + /* If there were any rifts, we'll use the fixed up fragment (l), + * otherwise, we use the original fragment (v). + */ + if (l){ + sizeB=cs(l); + vector=cv(l); + vecbegin=cb(l); + } else { + sizeB=fs(v); + vector=fv(v); + vecbegin=fb(v); + } + + /* Convert the fragment-relative offset (sizeB) into an offset + * relative to the root (A), and see if the offset is past the + * end of the root (> sizeA). If it is, this fragment will extend + * our root. + * + * ??? Why do we check for v->lastsector separately? + */ + if (sizeB-offset>sizeA || v->lastsector){ + if (v->lastsector){ + root->lastsector=1; + } + + /* ??? Why would end be < sizeA? Why do we truncate root? */ + if (endstage2,offset+vecbegin-rb(root),callback); + } + } + if (l)i_cblock_destructor(l); + free_v_fragment(v); + return(1); + + } else { /* !i_iterate_stage2(...) */ +#if TRACE_PARANOIA & 2 + fprintf(stderr, "no match"); +#endif + + /* We were unable to merge this fragment into the root. + * + * Check whether the fragment should have overlapped with the root, + * even taking possible jitter into account. (I.e., If the fragment + * ends so far before the end of the root that even (dynoverlap) + * samples of jitter couldn't push it beyond the end of the root, + * it should have overlapped.) + * + * It is, however, possible that we failed to match using the normal + * tests because we're dealing with silence, which we handle + * separately. + * + * If the fragment should have overlapped, and we're not dealing + * with the special silence case, we don't know what to make of + * this fragment, and we just discard it. + */ + if (fe(v)+dynoverlapsilenceflag){ + /* It *should* have matched. No good; free it. */ + free_v_fragment(v); +#if TRACE_PARANOIA & 2 + fprintf(stderr, ", discarding fragment."); +#endif + } + +#if TRACE_PARANOIA & 2 + fprintf(stderr, "\n"); +#endif + + /* otherwise, we likely want this for an upcoming match */ + /* we don't free the sort info (if it was collected) */ + return(0); + + } + } /* endif rv(root) */ +} + +static int +i_init_root(root_block *root, v_fragment_t *v,long int begin, + void(*callback)(long int, paranoia_cb_mode_t)) +{ + if (fb(v)<=begin && fe(v)>begin){ + + root->lastsector=v->lastsector; + root->returnedlimit=begin; + + if (rv(root)){ + i_cblock_destructor(rc(root)); + rc(root)=NULL; + } + + { + int16_t *buff=malloc(fs(v)*sizeof(int16_t)); + memcpy(buff,fv(v),fs(v)*sizeof(int16_t)); + root->vector=c_alloc(buff,fb(v),fs(v)); + } + + /* Check whether the new root has a long span of trailing silence. + */ + i_silence_test(root); + +#if TRACE_PARANOIA & 2 + fprintf(stderr, + "* Assigning fragment [%ld-%ld] to root, silencebegin=%ld\n", + rb(root), re(root), root->silencebegin); +#endif + + return(1); + } else + return(0); +} + +static int +vsort(const void *a,const void *b) +{ + return((*(v_fragment_t **)a)->begin-(*(v_fragment_t **)b)->begin); +} + + +/* =========================================================================== + * i_stage2 (internal) + * + * This function attempts to extend the verified root by merging verified + * fragments into it. It keeps extending the tail end of the root until + * it runs out of matching fragments. See i_stage2_each (and + * i_iterate_stage2) for details of fragment matching and merging. + * + * This function is called by paranoia_read_limited when the verified root + * doesn't contain sufficient data to satisfy the request for samples. + * If this function fails to extend the verified root far enough (having + * exhausted the currently available verified fragments), the caller + * will then read the device again to try and establish more verified + * fragments. + * + * We first try to merge all the fragments in ascending order using the + * standard method (i_stage2_each()), and then we try to merge the + * remaining fragments using silence matching (i_silence_match()) + * if the root has a long span of trailing silence. See the initial + * comments on silence and i_silence_match() for an explanation of this + * distinction. + * + * This function returns the number of verified fragments successfully + * merged into the verified root. + */ +static int +i_stage2(cdrom_paranoia_t *p, long int beginword, long int endword, + void (*callback)(long int, paranoia_cb_mode_t)) +{ + + int flag=1,ret=0; + root_block *root=&(p->root); + +#ifdef NOISY + fprintf(stderr,"Fragments:%ld\n",p->fragments->active); + fflush(stderr); +#endif + + /* even when the 'silence flag' is lit, we try to do non-silence + matching in the event that there are still audio vectors with + content to be sunk before the silence */ + + /* This flag is not the silence flag. Rather, it indicates whether + * we succeeded in adding a verified fragment to the verified root. + * In short, we keep adding fragments until we no longer find a + * match. + */ + while (flag) { + + /* Convert the linked list of verified fragments into an array, + * to be sorted in order of beginning sample position + */ + v_fragment_t *first=v_first(p); + long active=p->fragments->active,count=0; + v_fragment_t **list = calloc(active, sizeof(v_fragment_t *)); + + while (first){ + v_fragment_t *next=v_next(first); + list[count++]=first; + first=next; + } + + /* Reset the flag so that if we don't match any fragments, we + * stop looping. Then, proceed only if there are any fragments + * to match. + */ + flag=0; + if (count){ + + /* Sort the array of verified fragments in order of beginning + * sample position. + */ + qsort(list,active,sizeof(v_fragment_t *),&vsort); + + /* We don't check for the silence flag yet, because even if the + * verified root ends in silence (and thus the silence flag is set), + * there may be a non-silent region at the beginning of the verified + * root, into which we can merge the verified fragments. + */ + + /* Iterate through the verified fragments, starting at the fragment + * with the lowest beginning sample position. + */ + for(count=0;countone){ + + /* If we don't have a verified root yet, just promote the first + * fragment (with lowest beginning sample) to be the verified + * root. + * + * ??? It seems that this could be fairly arbitrary if jitter + * is an issue. If we've verified two fragments allegedly + * beginning at "0" (which are actually slightly offset due to + * jitter), the root might not begin at the earliest read + * sample. Additionally, because subsequent fragments are + * only merged at the tail end of the root, this situation + * won't be fixed by merging the earlier samples. + * + * Practically, this ends up not being critical since most + * drives insert some extra silent samples at the beginning + * of the stream. Missing a few of them doesn't cause any + * real lost data. But it is non-deterministic. + */ + if (rv(root)==NULL){ + if (i_init_root(&(p->root),first,beginword,callback)){ + free_v_fragment(first); + + /* Consider this a merged fragment, so set the flag + * to keep looping. + */ + flag=1; + ret++; + } + } else { + + /* Try to merge this fragment with the verified root, + * extending the tail of the root. + */ + if (i_stage2_each(root,first,callback)){ + + /* If we successfully merged the fragment, set the flag + * to keep looping. + */ + ret++; + flag=1; + } + } + } + } + + /* If the verified root ends in a long span of silence, iterate + * through the remaining unmerged fragments to see if they can be + * merged using our special silence matching. + */ + if (!flag && p->root.silenceflag){ + for(count=0;countone){ + if (rv(root)!=NULL){ + + /* Try to merge the fragment into the root. This will only + * succeed if the fragment overlaps and begins with sufficient + * silence to be a presumed match. + * + * Note that the fragments must be passed to i_silence_match() + * in ascending order, as they are here. + */ + if (i_silence_match(root,first,callback)){ + + /* If we successfully merged the fragment, set the flag + * to keep looping. + */ + ret++; + flag=1; + } + } + } + } /* end for */ + } + } /* end if(count) */ + free(list); + + /* If we were able to extend the verified root at all during this pass + * through the loop, loop again to see if we can merge any remaining + * fragments with the extended root. + */ + +#if TRACE_PARANOIA & 2 + if (flag) + fprintf(stderr, + "- Root updated, comparing remaining fragments again.\n"); +#endif + + } /* end while */ + + /* Return the number of fragments we successfully merged into the + * verified root. + */ + return(ret); +} + + +static void +i_end_case(cdrom_paranoia_t *p,long endword, + void(*callback)(long int, paranoia_cb_mode_t)) +{ + + root_block *root=&p->root; + + /* have an 'end' flag; if we've just read in the last sector in a + session, set the flag. If we verify to the end of a fragment + which has the end flag set, we're done (set a done flag). Pad + zeroes to the end of the read */ + + if (root->lastsector==0)return; + if (endwordroot); + c_block_t *graft=NULL; + int vflag=0; + int gend=0; + long post; + +#ifdef NOISY + fprintf(stderr,"\nskipping\n"); +#endif + + if (rv(root)==NULL){ + post=0; + } else { + post=re(root); + } + if (post==-1)post=0; + + if (callback)(*callback)(post,PARANOIA_CB_SKIP); + +#if TRACE_PARANOIA + fprintf(stderr, "Skipping [%ld-", post); +#endif + + /* We want to add a sector. Look for a c_block that spans, + preferrably a verified area */ + + { + c_block_t *c=c_first(p); + while (c){ + long cbegin=cb(c); + long cend=ce(c); + if (cbegin<=post && cend>post){ + long vend=post; + + if (c->flags[post-cbegin]&FLAGS_VERIFIED){ + /* verified area! */ + while (vendflags[vend-cbegin]&FLAGS_VERIFIED))vend++; + if (!vflag || vend>vflag){ + graft=c; + gend=vend; + } + vflag=1; + } else { + /* not a verified area */ + if (!vflag){ + while (vendflags[vend-cbegin]&FLAGS_VERIFIED)==0)vend++; + if (graft==NULL || gend>vend){ + /* smallest unverified area */ + graft=c; + gend=vend; + } + } + } + } + c=c_next(c); + } + + if (graft){ + long cbegin=cb(graft); + long cend=ce(graft); + + while (gendflags[gend-cbegin]&FLAGS_VERIFIED))gend++; + gend=min(gend+OVERLAP_ADJ,cend); + + if (rv(root)==NULL){ + int16_t *buff=malloc(cs(graft)); + memcpy(buff,cv(graft),cs(graft)); + rc(root)=c_alloc(buff,cb(graft),cs(graft)); + } else { + c_append(rc(root),cv(graft)+post-cbegin, + gend-post); + } + +#if TRACE_PARANOIA + fprintf(stderr, "%d], filled with %s data from block [%ld-%ld]\n", + gend, (graft->flags[post-cbegin]&FLAGS_VERIFIED) + ? "verified" : "unverified", cbegin, cend); +#endif + + root->returnedlimit=re(root); + return; + } + } + + /* No? Fine. Great. Write in some zeroes :-P */ + { + void *temp=calloc(CDIO_CD_FRAMESIZE_RAW,sizeof(int16_t)); + + if (rv(root)==NULL){ + rc(root)=c_alloc(temp,post,CDIO_CD_FRAMESIZE_RAW); + } else { + c_append(rc(root),temp,CDIO_CD_FRAMESIZE_RAW); + free(temp); + } + +#if TRACE_PARANOIA + fprintf(stderr, "%ld], filled with zero\n", re(root)); +#endif + + root->returnedlimit=re(root); + } +} + +/**** toplevel ****************************************/ + +void +paranoia_free(cdrom_paranoia_t *p) +{ + paranoia_resetall(p); + sort_free(p->sortcache); + free_list(p->cache, 1); + free_list(p->fragments, 1); + free(p); +} + +/*! + Set the kind of repair you want to on for reading. + The modes are listed above + + @param p paranoia type + @mode mode paranoia mode flags built from values in + paranoia_mode_t, e.g. + PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP +*/ +void +paranoia_modeset(cdrom_paranoia_t *p, int mode_flags) +{ + p->enable=mode_flags; +} + +/*! + reposition reading offset. + + @param p paranoia type + @param seek byte offset to seek to + @param whence like corresponding parameter in libc's lseek, e.g. + SEEK_SET or SEEK_END. +*/ +lsn_t +paranoia_seek(cdrom_paranoia_t *p, off_t seek, int whence) +{ + long sector; + long ret; + switch(whence){ + case SEEK_SET: + sector=seek; + break; + case SEEK_END: + sector=cdda_disc_lastsector(p->d)+seek; + break; + default: + sector=p->cursor+seek; + break; + } + + if (cdda_sector_gettrack(p->d,sector)==-1)return(-1); + + i_cblock_destructor(p->root.vector); + p->root.vector=NULL; + p->root.lastsector=0; + p->root.returnedlimit=0; + + ret=p->cursor; + p->cursor=sector; + + i_paranoia_firstlast(p); + + /* Evil hack to fix pregap patch for NEC drives! To be rooted out in a10 */ + p->current_firstsector=sector; + + return(ret); +} + + + +/* =========================================================================== + * read_c_block() (internal) + * + * This funtion reads many (p->readahead) sectors, encompassing at least + * the requested words. + * + * It returns a c_block which encapsulates these sectors' data and sector + * number. The sectors come come from multiple low-level read requests. + * + * This function reads many sectors in order to exhaust any caching on the + * drive itself, as caching would simply return the same incorrect data + * over and over. Paranoia depends on truly re-reading portions of the + * disc to make sure the reads are accurate and correct any inaccuracies. + * + * Which precise sectors are read varies ("jiggles") between calls to + * read_c_block, to prevent consistent errors across multiple reads + * from being misinterpreted as correct data. + * + * The size of each low-level read is determined by the underlying driver + * (p->d->nsectors), which allows the driver to specify how many sectors + * can be read in a single request. Historically, the Linux kernel could + * only read 8 sectors at a time, with likely dropped samples between each + * read request. Other operating systems may have different limitations. + * + * This function is called by paranoia_read_limited(), which breaks the + * c_block of read data into runs of samples that are likely to be + * contiguous, verifies them and stores them in verified fragments, and + * eventually merges the fragments into the verified root. + * + * This function returns the last c_block read or NULL on error. + */ + +static c_block_t * +i_read_c_block(cdrom_paranoia_t *p,long beginword,long endword, + void(*callback)(long, paranoia_cb_mode_t)) +{ + +/* why do it this way? We need to read lots of sectors to kludge + around stupid read ahead buffers on cheap drives, as well as avoid + expensive back-seeking. We also want to 'jiggle' the start address + to try to break borderline drives more noticeably (and make broken + drives with unaddressable sectors behave more often). */ + + long readat,firstread; + long totaltoread=p->readahead; + long sectatonce=p->d->nsectors; + long driftcomp=(float)p->dyndrift/CD_FRAMEWORDS+.5; + c_block_t *new=NULL; + root_block *root=&p->root; + int16_t *buffer=NULL; + unsigned char *flags=NULL; + long sofar; + long dynoverlap=(p->dynoverlap+CD_FRAMEWORDS-1)/CD_FRAMEWORDS; + long anyflag=0; + + + /* Calculate the first sector to read. This calculation takes + * into account the need to jitter the starting point of the read + * to reveal consistent errors as well as the low reliability of + * the edge words of a read. + * + * ???: Document more clearly how dynoverlap and MIN_SECTOR_BACKUP + * are calculated and used. + */ + + /* What is the first sector to read? want some pre-buffer if + we're not at the extreme beginning of the disc */ + + if (p->enable&(PARANOIA_MODE_VERIFY|PARANOIA_MODE_OVERLAP)){ + + /* we want to jitter the read alignment boundary */ + long target; + if (rv(root)==NULL || rb(root)>beginword) + target=p->cursor-dynoverlap; + else + target=re(root)/(CD_FRAMEWORDS)-dynoverlap; + + if (target+MIN_SECTOR_BACKUP>p->lastread && target<=p->lastread) + target=p->lastread-MIN_SECTOR_BACKUP; + + /* we want to jitter the read alignment boundary, as some + drives, beginning from a specific point, will tend to + lose bytes between sectors in the same place. Also, as + our vectors are being made up of multiple reads, we want + the overlap boundaries to move.... */ + + readat=(target&(~((long)JIGGLE_MODULO-1)))+p->jitter; + if (readat>target)readat-=JIGGLE_MODULO; + p->jitter++; + if (p->jitter>=JIGGLE_MODULO) + p->jitter=0; + + } else { + readat=p->cursor; + } + + readat+=driftcomp; + + /* Create a new, empty c_block and add it to the head of the + * list of c_blocks in memory. It will be empty until the end of + * this subroutine. + */ + if (p->enable&(PARANOIA_MODE_OVERLAP|PARANOIA_MODE_VERIFY)) { + flags=calloc(totaltoread*CD_FRAMEWORDS, 1); + new=new_c_block(p); + recover_cache(p); + } else { + /* in the case of root it's just the buffer */ + paranoia_resetall(p); + new=new_c_block(p); + } + + buffer=calloc(totaltoread*CDIO_CD_FRAMESIZE_RAW, 1); + sofar=0; + firstread=-1; + +#if TRACE_PARANOIA + fprintf(stderr, "Reading [%ld-%ld] from media\n", + readat*CD_FRAMEWORDS, (readat+totaltoread)*CD_FRAMEWORDS); +#endif + + /* Issue each of the low-level reads until we've read enough sectors + * to exhaust the drive's cache. + * + * p->readahead = total number of sectors to read + * p->d->nsectors = number of sectors to read per request + * + * The driver determines this latter number, which is the maximum + * number of sectors the kernel can reliably read per request. In + * old Linux kernels, there was a hard limit of 8 sectors per read. + * While this limit has since been removed, certain motherboards + * can't handle DMA requests larger than 64K. And other operating + * systems may have similar limitations. So the method of splitting + * up reads is still useful. + */ + + /* actual read loop */ + + while (sofarcurrent_firstsector){ + secread-=p->current_firstsector-adjread; + adjread=p->current_firstsector; + } + if (adjread+secread-1>p->current_lastsector) + secread=p->current_lastsector-adjread+1; + + if (sofar+secread>totaltoread)secread=totaltoread-sofar; + + if (secread>0){ + + if (firstread<0) firstread = adjread; + + /* Issue the low-level read to the driver. + */ + + thisread = cdda_read(p->d, buffer+sofar*CD_FRAMEWORDS, adjread, secread); + +#if TRACE_PARANOIA & 1 + fprintf(stderr, "- Read [%ld-%ld] (0x%04X...0x%04X)%s", + adjread*CD_FRAMEWORDS, (adjread+thisread)*CD_FRAMEWORDS, + buffer[sofar*CD_FRAMEWORDS] & 0xFFFF, + buffer[(sofar+thisread)*CD_FRAMEWORDS - 1] & 0xFFFF, + thisread < secread ? "" : "\n"); +#endif + + /* If the low-level read returned too few sectors, pad the result + * with null data and mark it as invalid (FLAGS_UNREAD). We pad + * because we're going to be appending further reads to the current + * c_block. + * + * ???: Why not re-read? It might be to keep you from getting + * hung up on a bad sector. Or it might be to avoid interrupting + * the streaming as much as possible. + */ + if ( thisread < secread) { + + if (thisread<0) thisread=0; + +#if TRACE_PARANOIA & 1 + fprintf(stderr, " -- couldn't read [%ld-%ld]\n", + (adjread+thisread)*CD_FRAMEWORDS, + (adjread+secread)*CD_FRAMEWORDS); +#endif + + /* Uhhh... right. Make something up. But don't make us seek + backward! */ + + if (callback) + (*callback)((adjread+thisread)*CD_FRAMEWORDS, PARANOIA_CB_READERR); + memset(buffer+(sofar+thisread)*CD_FRAMEWORDS,0, + CDIO_CD_FRAMESIZE_RAW*(secread-thisread)); + if (flags) + memset(flags+(sofar+thisread)*CD_FRAMEWORDS, FLAGS_UNREAD, + CD_FRAMEWORDS*(secread-thisread)); + } + if (thisread!=0)anyflag=1; + + + /* Because samples are likely to be dropped between read requests, + * mark the samples near the the boundaries of the read requests + * as suspicious (FLAGS_EDGE). This means that any span of samples + * against which these adjacent read requests are compared must + * overlap beyond the edges and into the more trustworthy data. + * Such overlapping spans are accordingly at least MIN_WORDS_OVERLAP + * words long (and naturally longer if any samples were dropped + * between the read requests). + * + * (EEEEE...overlapping span...EEEEE) + * (read 1 ...........EEEEE) (EEEEE...... read 2 ......EEEEE) ... + * dropped samples --^ + */ + if (flags && sofar!=0){ + /* Don't verify across overlaps that are too close to one + another */ + int i=0; + for(i=-MIN_WORDS_OVERLAP/2;ilastread=adjread+secread; + + if (adjread+secread-1==p->current_lastsector) + new->lastsector=-1; + + if (callback)(*callback)((adjread+secread-1)*CD_FRAMEWORDS,PARANOIA_CB_READ); + + sofar+=secread; + readat=adjread+secread; + } else /* secread <= 0 */ + if (readatcurrent_firstsector) + readat+=sectatonce; /* due to being before the readable area */ + else + break; /* due to being past the readable area */ + + + /* Keep issuing read requests until we've read enough sectors to + * exhaust the drive's cache. + */ + + } /* end while */ + + + /* If we managed to read any sectors at all (anyflag), fill in the + * previously allocated c_block with the read data. Otherwise, free + * our buffers, dispose of the c_block, and return NULL. + */ + if (anyflag) { + new->vector=buffer; + new->begin=firstread*CD_FRAMEWORDS-p->dyndrift; + new->size=sofar*CD_FRAMEWORDS; + new->flags=flags; + +#if TRACE_PARANOIA + fprintf(stderr, "- Read block %ld:[%ld-%ld] from media\n", + p->cache->active, cb(new), ce(new)); +#endif + } else { + if (new)free_c_block(new); + free(buffer); + free(flags); + new=NULL; + } + return(new); +} + + +/** ========================================================================== + * cdio_paranoia_read(), cdio_paranoia_read_limited() + * + * These functions "read" the next sector of audio data and returns + * a pointer to a full sector of verified samples (2352 bytes). + * + * The returned buffer is *not* to be freed by the caller. It will + * persist only until the next call to paranoia_read() for this p +*/ + +int16_t * +cdio_paranoia_read(cdrom_paranoia_t *p, + void(*callback)(long, paranoia_cb_mode_t)) +{ + return paranoia_read_limited(p, callback, 20); +} + +/* I added max_retry functionality this way in order to avoid + breaking any old apps using the nerw libs. cdparanoia 9.8 will + need the updated libs, but nothing else will require it. */ +int16_t * +cdio_paranoia_read_limited(cdrom_paranoia_t *p, + void(*callback)(long int, paranoia_cb_mode_t), + int max_retries) +{ + long int beginword = p->cursor*(CD_FRAMEWORDS); + long int endword = beginword+CD_FRAMEWORDS; + long int retry_count= 0; + long int lastend = -2; + root_block *root = &p->root; + + if (beginword > p->root.returnedlimit) + p->root.returnedlimit=beginword; + lastend=re(root); + + + /* Since paranoia reads and verifies chunks of data at a time + * (which it needs to counteract dropped samples and inaccurate + * seeking), the requested samples may already be in memory, + * in the verified "root". + * + * The root is where paranoia stores samples that have been + * verified and whose position has been accurately determined. + */ + + /* First, is the sector we want already in the root? */ + while (rv(root)==NULL || + rb(root)>beginword || + (re(root)enable&(PARANOIA_MODE_VERIFY|PARANOIA_MODE_OVERLAP)) || + re(root)enable&(PARANOIA_MODE_VERIFY|PARANOIA_MODE_OVERLAP)){ + + /* We need to make sure our memory consumption doesn't grow + * to the size of the whole CD. But at the same time, we + * need to hang onto some of the verified data (even perhaps + * data that's already been returned by paranoia_read()) in + * order to verify and accurately position future samples. + * + * Therefore, we free some of the verified data that we + * no longer need. + */ + i_paranoia_trim(p,beginword,endword); + recover_cache(p); + + if (rb(root)!=-1 && p->root.lastsector) + i_end_case(p, endword+(MAX_SECTOR_OVERLAP*CD_FRAMEWORDS), + callback); + else + + /* Merge as many verified fragments into the verified root + * as we need to satisfy the pending request. We may + * not have all the fragments we need, in which case we'll + * read data from the CD further below. + */ + i_stage2(p, beginword, + endword+(MAX_SECTOR_OVERLAP*CD_FRAMEWORDS), + callback); + } else + i_end_case(p,endword+(MAX_SECTOR_OVERLAP*CD_FRAMEWORDS), + callback); /* only trips if we're already done */ + +#if TRACE_PARANOIA + fprintf(stderr, "- Root is now [%ld-%ld] silencebegin=%ld\n", + rb(root), re(root), root->silencebegin); +#endif + + /* If we were able to fill the verified root with data already + * in memory, we don't need to read any more data from the drive. + */ + if (!(rb(root)==-1 || rb(root)>beginword || + re(root)enable&(PARANOIA_MODE_OVERLAP|PARANOIA_MODE_VERIFY)){ + + /* If we need to verify these samples, send them to + * stage 1 verification, which will add verified samples + * to the set of verified fragments. Verified fragments + * will be merged into the verified root during stage 2 + * overlap analysis. + */ + if (p->enable&PARANOIA_MODE_VERIFY) + i_stage1(p,new,callback); + + /* If we're only doing overlapping reads (no stage 1 + * verification), consider each low-level read in the + * c_block to be a verified fragment. We exclude the + * edges from these fragments to enforce the requirement + * that we overlap the reads by the minimum amount. + * These fragments will be merged into the verified + * root during stage 2 overlap analysis. + */ + else{ + /* just make v_fragments from the boundary information. */ + long begin=0,end=0; + + while (beginflags[begin]&FLAGS_EDGE))begin++; + end=begin+1; + while (endflags[end]&FLAGS_EDGE)==0)end++; + { + new_v_fragment(p,new,begin+cb(new), + end+cb(new), + (new->lastsector && cb(new)+end==ce(new))); + } + begin=end; + } + } + + } else { + + /* If we're not doing any overlapping reads or verification + * of data, skip over the stage 1 and stage 2 verification and + * promote this c_block directly to the current "verified" root. + */ + + if (p->root.vector)i_cblock_destructor(p->root.vector); + free_elem(new->e,0); + p->root.vector=new; + + i_end_case(p,endword+(MAX_SECTOR_OVERLAP*CD_FRAMEWORDS), + callback); + + } + } + } + + /* Are we doing lots of retries? **************************************/ + + /* ???: To be studied + */ + + /* Check unaddressable sectors first. There's no backoff here; + jiggle and minimum backseek handle that for us */ + + if (rb(root)!=-1 && lastend+588dynoverlap==MAX_SECTOR_OVERLAP*CD_FRAMEWORDS || + retry_count==max_retries){ + if (!(p->enable&PARANOIA_MODE_NEVERSKIP)) + verify_skip_case(p,callback); + retry_count=0; + } else { + if (p->stage1.offpoints!=-1){ /* hack */ + p->dynoverlap*=1.5; + if (p->dynoverlap>MAX_SECTOR_OVERLAP*CD_FRAMEWORDS) + p->dynoverlap=MAX_SECTOR_OVERLAP*CD_FRAMEWORDS; + if (callback) + (*callback)(p->dynoverlap,PARANOIA_CB_OVERLAP); + } + } + } + } + + /* Having read data from the drive and placed it into verified + * fragments, we now loop back to try to extend the root with + * the newly loaded data. Alternatively, if the root already + * contains the needed data, we'll just fall through. + */ + + } /* end while */ + p->cursor++; + + /* Return a pointer into the verified root. Thus, the caller + * must NOT free the returned pointer! + */ + return(rv(root)+(beginword-rb(root))); +} + +/* a temporary hack */ +void +cdio_paranoia_overlapset(cdrom_paranoia_t *p, long int overlap) +{ + p->dynoverlap=overlap*CD_FRAMEWORDS; + p->stage1.offpoints=-1; +} diff --git a/lib/udf/.cvsignore b/lib/udf/.cvsignore new file mode 100644 index 00000000..8608ee91 --- /dev/null +++ b/lib/udf/.cvsignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +.libs +.deps +*.lo +libudf.la + diff --git a/lib/udf/.gitignore b/lib/udf/.gitignore new file mode 100644 index 00000000..f53478a0 --- /dev/null +++ b/lib/udf/.gitignore @@ -0,0 +1,3 @@ +/.deps +/Makefile +/Makefile.in diff --git a/lib/udf/Makefile.am b/lib/udf/Makefile.am new file mode 100644 index 00000000..6c8f6b38 --- /dev/null +++ b/lib/udf/Makefile.am @@ -0,0 +1,58 @@ +# $Id: Makefile.am,v 1.9 2008/06/25 08:01:54 rocky Exp $ +# +# Copyright (C) 2003, 2004, 2006, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +######################################################## +# Things to make the libudf library +######################################################## +# +# From libtool documentation amended with guidance from N. Boullis: +# +# 1. Start with version information of `0:0:0' for each libtool library. +# +# 2. It is probably not a good idea to update the version information +# several times between public releases, but rather once per public +# release. (This seems to be more an aesthetic consideration than +# a hard technical one.) +# +# 3. If the library source code has changed at all since the last +# update, then increment REVISION (`C:R:A' becomes `C:R+1:A'). +# +# 4. If any interfaces have been added, removed, or changed since the +# last update, increment CURRENT, and set REVISION to 0. +# +# 5. If any interfaces have been added since the last public release, +# then increment AGE. +# +# 6. If any interfaces have been removed or changed since the last +# public release, then set AGE to 0. A changed interface means an +# incompatibility with previous versions. + +libudf_la_CURRENT = 0 +libudf_la_REVISION = 0 +libudf_la_AGE = 0 + +EXTRA_DIST = libudf.sym + +noinst_HEADERS = udf_fs.h udf_private.h + +lib_LTLIBRARIES = libudf.la + +libudf_la_SOURCES = udf.c udf_file.c udf_fs.c udf_time.c filemode.c + +libudf_la_LIBADD = @LIBCDIO_LIBS@ @LT_NO_UNDEFINED@ + +INCLUDES = $(LIBCDIO_CFLAGS) diff --git a/lib/udf/filemode.c b/lib/udf/filemode.c new file mode 100644 index 00000000..97849785 --- /dev/null +++ b/lib/udf/filemode.c @@ -0,0 +1,291 @@ +/* + $Id: filemode.c,v 1.4 2008/04/18 16:02:10 karl Exp $ + filemode.c -- make a string describing file modes + + Copyright (C) 2005, 2008 Rocky Bernstein + Copyright (C) 1985, 1990, 1993, 1998-2000 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#if HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#include + +#if !S_IRUSR +# if S_IREAD +# define S_IRUSR S_IREAD +# else +# define S_IRUSR 00400 +# endif +#endif + +#if !S_IWUSR +# if S_IWRITE +# define S_IWUSR S_IWRITE +# else +# define S_IWUSR 00200 +# endif +#endif + +#if !S_IXUSR +# if S_IEXEC +# define S_IXUSR S_IEXEC +# else +# define S_IXUSR 00100 +# endif +#endif + +#if !S_IRGRP +# define S_IRGRP (S_IRUSR >> 3) +#endif +#if !S_IWGRP +# define S_IWGRP (S_IWUSR >> 3) +#endif +#if !S_IXGRP +# define S_IXGRP (S_IXUSR >> 3) +#endif +#if !S_IROTH +# define S_IROTH (S_IRUSR >> 6) +#endif +#if !S_IWOTH +# define S_IWOTH (S_IWUSR >> 6) +#endif +#if !S_IXOTH +# define S_IXOTH (S_IXUSR >> 6) +#endif + +#ifdef STAT_MACROS_BROKEN +# undef S_ISBLK +# undef S_ISCHR +# undef S_ISDIR +# undef S_ISFIFO +# undef S_ISLNK +# undef S_ISMPB +# undef S_ISMPC +# undef S_ISNWK +# undef S_ISREG +# undef S_ISSOCK +#endif /* STAT_MACROS_BROKEN. */ + +#if !defined S_ISBLK && defined S_IFBLK +# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined S_ISCHR && defined S_IFCHR +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#if !defined S_ISDIR && defined S_IFDIR +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined S_ISREG && defined S_IFREG +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined S_ISFIFO && defined S_IFIFO +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined S_ISLNK && defined S_IFLNK +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#if !defined S_ISSOCK && defined S_IFSOCK +# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#if !defined S_ISMPB && defined S_IFMPB /* V7 */ +# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +#endif +#if !defined S_ISNWK && defined S_IFNWK /* HP/UX */ +# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +#endif +#if !defined S_ISDOOR && defined S_IFDOOR /* Solaris 2.5 and up */ +# define S_ISDOOR(m) (((m) & S_IFMT) == S_IFDOOR) +#endif +#if !defined S_ISCTG && defined S_IFCTG /* MassComp */ +# define S_ISCTG(m) (((m) & S_IFMT) == S_IFCTG) +#endif + + + +/* Set the 's' and 't' flags in file attributes string CHARS, + according to the file mode BITS. */ + +static void +setst (mode_t bits, char *chars) +{ +#ifdef S_ISUID + if (bits & S_ISUID) + { + if (chars[3] != 'x') + /* Set-uid, but not executable by owner. */ + chars[3] = 'S'; + else + chars[3] = 's'; + } +#endif +#ifdef S_ISGID + if (bits & S_ISGID) + { + if (chars[6] != 'x') + /* Set-gid, but not executable by group. */ + chars[6] = 'S'; + else + chars[6] = 's'; + } +#endif +#ifdef S_ISVTX + if (bits & S_ISVTX) + { + if (chars[9] != 'x') + /* Sticky, but not executable by others. */ + chars[9] = 'T'; + else + chars[9] = 't'; + } +#endif +} + +/* Return a character indicating the type of file described by + file mode BITS: + 'd' for directories + 'D' for doors + 'b' for block special files + 'c' for character special files + 'n' for network special files + 'm' for multiplexor files + 'M' for an off-line (regular) file + 'l' for symbolic links + 's' for sockets + 'p' for fifos + 'C' for contigous data files + '-' for regular files + '?' for any other file type. */ + +static char +ftypelet (mode_t bits) +{ +#ifdef S_ISBLK + if (S_ISBLK (bits)) + return 'b'; +#endif + if (S_ISCHR (bits)) + return 'c'; + if (S_ISDIR (bits)) + return 'd'; + if (S_ISREG (bits)) + return '-'; +#ifdef S_ISFIFO + if (S_ISFIFO (bits)) + return 'p'; +#endif +#ifdef S_ISLNK + if (S_ISLNK (bits)) + return 'l'; +#endif +#ifdef S_ISSOCK + if (S_ISSOCK (bits)) + return 's'; +#endif +#ifdef S_ISMPC + if (S_ISMPC (bits)) + return 'm'; +#endif +#ifdef S_ISNWK + if (S_ISNWK (bits)) + return 'n'; +#endif +#ifdef S_ISDOOR + if (S_ISDOOR (bits)) + return 'D'; +#endif +#ifdef S_ISCTG + if (S_ISCTG (bits)) + return 'C'; +#endif + + /* The following two tests are for Cray DMF (Data Migration + Facility), which is a HSM file system. A migrated file has a + `st_dm_mode' that is different from the normal `st_mode', so any + tests for migrated files should use the former. */ + +#ifdef S_ISOFD + if (S_ISOFD (bits)) + /* off line, with data */ + return 'M'; +#endif +#ifdef S_ISOFL + /* off line, with no data */ + if (S_ISOFL (bits)) + return 'M'; +#endif + return '?'; +} + +/*! udf_mode_string - fill in string STR with an ls-style ASCII + representation of the st_mode field of file stats block STATP. + 10 characters are stored in STR; no terminating null is added. + The characters stored in STR are: + + 0 File type. 'd' for directory, 'c' for character + special, 'b' for block special, 'm' for multiplex, + 'l' for symbolic link, 's' for socket, 'p' for fifo, + '-' for regular, '?' for any other file type + + 1 'r' if the owner may read, '-' otherwise. + + 2 'w' if the owner may write, '-' otherwise. + + 3 'x' if the owner may execute, 's' if the file is + set-user-id, '-' otherwise. + 'S' if the file is set-user-id, but the execute + bit isn't set. + + 4 'r' if group members may read, '-' otherwise. + + 5 'w' if group members may write, '-' otherwise. + + 6 'x' if group members may execute, 's' if the file is + set-group-id, '-' otherwise. + 'S' if it is set-group-id but not executable. + + 7 'r' if any user may read, '-' otherwise. + + 8 'w' if any user may write, '-' otherwise. + + 9 'x' if any user may execute, 't' if the file is "sticky" + (will be retained in swap space after execution), '-' + otherwise. + 'T' if the file is sticky but not executable. */ + +char * +udf_mode_string (mode_t i_mode, char *psz_str) +{ + psz_str[ 0] = ftypelet (i_mode); + psz_str[ 1] = i_mode & S_IRUSR ? 'r' : '-'; + psz_str[ 2] = i_mode & S_IWUSR ? 'w' : '-'; + psz_str[ 3] = i_mode & S_IXUSR ? 'x' : '-'; + psz_str[ 4] = i_mode & S_IRGRP ? 'r' : '-'; + psz_str[ 5] = i_mode & S_IWGRP ? 'w' : '-'; + psz_str[ 6] = i_mode & S_IXGRP ? 'x' : '-'; + psz_str[ 7] = i_mode & S_IROTH ? 'r' : '-'; + psz_str[ 8] = i_mode & S_IWOTH ? 'w' : '-'; + psz_str[ 9] = i_mode & S_IXOTH ? 'x' : '-'; + psz_str[10] = '\0'; + setst (i_mode, psz_str); + return psz_str; +} diff --git a/lib/udf/libudf.sym b/lib/udf/libudf.sym new file mode 100644 index 00000000..75be7864 --- /dev/null +++ b/lib/udf/libudf.sym @@ -0,0 +1,29 @@ +debug_ecma_167_enums1 +debug_ecma_167_timezone_enum +debug_file_characteristics +debug_icbtag_file_type_enum +debug_tagid +debug_udf_enums1 +debug_udf_time_enum +VSD_STD_ID_BEA01 +VSD_STD_ID_BOOT2 +VSD_STD_ID_CD001 +VSD_STD_ID_CDW01 +VSD_STD_ID_NSR03 +VSD_STD_ID_TEA01 +udf_close +udf_dirent_free +udf_get_file_entry +udf_get_file_length +udf_get_fileid_descriptor +udf_get_filename +udf_get_link_count +udf_get_part_number +udf_get_posix_filemode +udf_opendir +udf_readdir +udf_is_dir +udf_open +udf_read_sectors +udf_stamp_to_time +udf_time_to_stamp diff --git a/lib/udf/udf.c b/lib/udf/udf.c new file mode 100644 index 00000000..b4cb00b4 --- /dev/null +++ b/lib/udf/udf.c @@ -0,0 +1,124 @@ +/* + $Id: udf.c,v 1.13 2008/04/24 07:28:00 rocky Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* Access routines */ + +#include +#include "udf_private.h" + +#ifdef HAVE_STRING_H +# include +#endif + +#ifdef HAVE_SYS_STAT_H +# include +#endif + +/** The below variables are trickery to force enum symbol values to be + recorded in debug symbol tables. They are used to allow one to refer + to the enumeration value names in the typedefs above in a debugger + and debugger expressions +*/ +tag_id_t debug_tagid; +file_characteristics_t debug_file_characteristics; +icbtag_file_type_enum_t debug_icbtag_file_type_enum; +icbtag_flag_enum_t debug_flag_enum; +ecma_167_enum1_t debug_ecma_167_enum1; +ecma_167_timezone_enum_t debug_ecma_167_timezone_enum; +udf_enum1_t debug_udf_enum1; + + +/*! + Returns POSIX mode bitstring for a given file. +*/ +mode_t +udf_get_posix_filemode(const udf_dirent_t *p_udf_dirent) +{ + udf_file_entry_t udf_fe; + mode_t mode = 0; + + if (udf_get_file_entry(p_udf_dirent, &udf_fe)) { + uint16_t i_flags; + uint32_t i_perms; + + i_perms = uint32_from_le(udf_fe.permissions); + i_flags = uint16_from_le(udf_fe.icb_tag.flags); + + if (i_perms & FE_PERM_U_READ) mode |= S_IRUSR; + if (i_perms & FE_PERM_U_WRITE) mode |= S_IWUSR; + if (i_perms & FE_PERM_U_EXEC) mode |= S_IXUSR; + +#ifdef S_IRGRP + if (i_perms & FE_PERM_G_READ) mode |= S_IRGRP; + if (i_perms & FE_PERM_G_WRITE) mode |= S_IWGRP; + if (i_perms & FE_PERM_G_EXEC) mode |= S_IXGRP; +#endif + +#ifdef S_IROTH + if (i_perms & FE_PERM_O_READ) mode |= S_IROTH; + if (i_perms & FE_PERM_O_WRITE) mode |= S_IWOTH; + if (i_perms & FE_PERM_O_EXEC) mode |= S_IXOTH; +#endif + + switch (udf_fe.icb_tag.file_type) { + case ICBTAG_FILE_TYPE_DIRECTORY: + mode |= S_IFDIR; + break; + case ICBTAG_FILE_TYPE_REGULAR: + mode |= S_IFREG; + break; +#ifdef S_IFLNK + case ICBTAG_FILE_TYPE_SYMLINK: + mode |= S_IFLNK; + break; +#endif + case ICBTAG_FILE_TYPE_CHAR: + mode |= S_IFCHR; + break; +#ifdef S_IFSOCK + case ICBTAG_FILE_TYPE_SOCKET: + mode |= S_IFSOCK; + break; +#endif + case ICBTAG_FILE_TYPE_BLOCK: + mode |= S_IFBLK; + break; + default: ; + }; + +#ifdef S_ISUID + if (i_flags & ICBTAG_FLAG_SETUID) mode |= S_ISUID; + if (i_flags & ICBTAG_FLAG_SETGID) mode |= S_ISGID; + if (i_flags & ICBTAG_FLAG_STICKY) mode |= S_ISVTX; +#endif + } + + return mode; + +} + +/*! + Return the partition number of the the opened udf handle. -1 + Is returned if we have an error. +*/ +int16_t udf_get_part_number(const udf_t *p_udf) +{ + if (!p_udf) return -1; + return p_udf->i_partition; +} + diff --git a/lib/udf/udf_file.c b/lib/udf/udf_file.c new file mode 100644 index 00000000..7afe1b42 --- /dev/null +++ b/lib/udf/udf_file.c @@ -0,0 +1,251 @@ +/* + $Id: udf_file.c,v 1.14 2008/04/18 16:02:10 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* Access routines */ + +#include +#include "udf_private.h" +#include "udf_fs.h" + +#ifdef HAVE_STRING_H +# include +#endif + +#include /* Remove when adding cdio/logging.h */ + +/* Useful defines */ + +#define MIN(a, b) (aalloc_descs[offset] + +const char * +udf_get_filename(const udf_dirent_t *p_udf_dirent) +{ + if (!p_udf_dirent) return NULL; + if (!p_udf_dirent->psz_name) return ".."; + return p_udf_dirent->psz_name; +} + +/* Get UDF File Entry. However we do NOT get the variable-length extended + attributes. */ +bool +udf_get_file_entry(const udf_dirent_t *p_udf_dirent, + /*out*/ udf_file_entry_t *p_udf_fe) +{ + if (!p_udf_dirent) return false; + memcpy(p_udf_fe, &p_udf_dirent->fe, sizeof(udf_file_entry_t)); + return true; +} + +/*! + Return the file id descriptor of the given file. +*/ +bool udf_get_fileid_descriptor(const udf_dirent_t *p_udf_dirent, + /*out*/ udf_fileid_desc_t *p_udf_fid) +{ + + if (!p_udf_dirent) return false; + if (!p_udf_dirent->fid) { + /* FIXME do something about trying to get the descriptor. */ + return false; + } + memcpy(p_udf_fid, p_udf_dirent->fid, sizeof(udf_fileid_desc_t)); + return true; +} + + +/*! + Return the number of hard links of the file. Return 0 if error. +*/ +uint16_t udf_get_link_count(const udf_dirent_t *p_udf_dirent) +{ + if (p_udf_dirent) { + return uint16_from_le(p_udf_dirent->fe.link_count); + } + return 0; /* Error. Non-error case handled above. */ +} + +/*! + Return the file length the file. Return 2147483647L if error. +*/ +uint64_t udf_get_file_length(const udf_dirent_t *p_udf_dirent) +{ + if (p_udf_dirent) { + return uint64_from_le(p_udf_dirent->fe.info_len); + } + return 2147483647L; /* Error. Non-error case handled above. */ +} + +/*! + Return true if the file is a directory. +*/ +bool +udf_is_dir(const udf_dirent_t *p_udf_dirent) +{ + return p_udf_dirent->b_dir; +} + +/* + * Translate a file offset into a logical block and then into a physical + * block. + */ +static lba_t +offset_to_lba(const udf_dirent_t *p_udf_dirent, off_t i_offset, + /*out*/ lba_t *pi_lba, /*out*/ uint32_t *pi_max_size) +{ + udf_t *p_udf = p_udf_dirent->p_udf; + const udf_file_entry_t *p_udf_fe = (udf_file_entry_t *) + &p_udf_dirent->fe; + const udf_icbtag_t *p_icb_tag = &p_udf_fe->icb_tag; + const uint16_t strat_type= uint16_from_le(p_icb_tag->strat_type); + + switch (strat_type) { + case 4096: + printf("Cannot deal with strategy4096 yet!\n"); + return CDIO_INVALID_LBA; + break; + case ICBTAG_STRATEGY_TYPE_4: + { + uint32_t icblen = 0; + lba_t lsector; + int ad_offset, ad_num = 0; + uint16_t addr_ilk = uint16_from_le(p_icb_tag->flags&ICBTAG_FLAG_AD_MASK); + + switch (addr_ilk) { + case ICBTAG_FLAG_AD_SHORT: + { + udf_short_ad_t *p_icb; + /* + * The allocation descriptor field is filled with short_ad's. + * If the offset is beyond the current extent, look for the + * next extent. + */ + do { + i_offset -= icblen; + ad_offset = sizeof(udf_short_ad_t) * ad_num; + if (ad_offset > uint32_from_le(p_udf_fe->i_alloc_descs)) { + printf("File offset out of bounds\n"); + return CDIO_INVALID_LBA; + } + p_icb = (udf_short_ad_t *) + GETICB( uint32_from_le(p_udf_fe->i_extended_attr) + + ad_offset ); + icblen = p_icb->len; + ad_num++; + } while(i_offset >= icblen); + + lsector = (i_offset / UDF_BLOCKSIZE) + p_icb->pos; + + *pi_max_size = p_icb->len; + } + break; + case ICBTAG_FLAG_AD_LONG: + { + /* + * The allocation descriptor field is filled with long_ad's + * If the i_offset is beyond the current extent, look for the + * next extent. + */ + udf_long_ad_t *p_icb; + do { + i_offset -= icblen; + ad_offset = sizeof(udf_long_ad_t) * ad_num; + if (ad_offset > uint32_from_le(p_udf_fe->i_alloc_descs)) { + printf("File offset out of bounds\n"); + return CDIO_INVALID_LBA; + } + p_icb = (udf_long_ad_t *) + GETICB( uint32_from_le(p_udf_fe->i_extended_attr) + + ad_offset ); + icblen = p_icb->len; + ad_num++; + } while(i_offset >= icblen); + + lsector = (i_offset / UDF_BLOCKSIZE) + + uint32_from_le(((udf_long_ad_t *)(p_icb))->loc.lba); + + *pi_max_size = p_icb->len; + } + break; + case ICBTAG_FLAG_AD_IN_ICB: + /* + * This type means that the file *data* is stored in the + * allocation descriptor field of the file entry. + */ + *pi_max_size = 0; + printf("Don't know how to data in ICB handle yet\n"); + + case ICBTAG_FLAG_AD_EXTENDED: + printf("Don't know how to handle extended addresses yet\n"); + default: + printf("Unsupported allocation descriptor %d\n", addr_ilk); + return CDIO_INVALID_LBA; + } + + *pi_lba = lsector + p_udf->i_part_start; + return *pi_lba; + } + default: + printf("Unknown strategy type %d\n", strat_type); + return DRIVER_OP_ERROR; + } +} + +/** + Attempts to read up to count bytes from UDF directory entry + p_udf_dirent into the buffer starting at buf. buf should be a + multiple of UDF_BLOCKSIZE bytes. Reading continues after the point + at which we last read or from the beginning the first time. + + If count is zero, read() returns zero and has no other results. If + count is greater than SSIZE_MAX, the result is unspecified. + + If there is an error, cast the result to driver_return_code_t for + the specific error code. +*/ +ssize_t +udf_read_block(const udf_dirent_t *p_udf_dirent, void * buf, size_t count) +{ + if (count == 0) return 0; + else { + driver_return_code_t ret; + uint32_t i_max_size=0; + udf_t *p_udf = p_udf_dirent->p_udf; + lba_t i_lba = offset_to_lba(p_udf_dirent, p_udf->i_position, &i_lba, + &i_max_size); + if (i_lba != CDIO_INVALID_LBA) { + uint32_t i_max_blocks = CEILING(i_max_size, UDF_BLOCKSIZE); + if ( i_max_blocks < count ) { + printf("Warning: don't know how to handle yet\n" ); + count = i_max_blocks; + } + ret = udf_read_sectors(p_udf, buf, i_lba, count); + if (DRIVER_OP_SUCCESS == ret) { + ssize_t i_read_len = MIN(i_max_size, count * UDF_BLOCKSIZE); + p_udf->i_position += i_read_len; + return i_read_len; + } + return ret; + } else { + return DRIVER_OP_ERROR; + } + } +} diff --git a/lib/udf/udf_fs.c b/lib/udf/udf_fs.c new file mode 100644 index 00000000..48770fd9 --- /dev/null +++ b/lib/udf/udf_fs.c @@ -0,0 +1,694 @@ +/* + $Id: udf_fs.c,v 1.22 2008/04/18 16:02:10 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* + * Portions copyright (c) 2001, 2002 Scott Long + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif + +#ifdef HAVE_STDLIB_H +# include +#endif + +/* These definitions are also to make debugging easy. Note that they + have to come *before* #include which sets + #defines for these. +*/ +const char VSD_STD_ID_BEA01[] = {'B', 'E', 'A', '0', '1'}; +const char VSD_STD_ID_BOOT2[] = {'B', 'O', 'O', 'T', '2'}; +const char VSD_STD_ID_CD001[] = {'C', 'D', '0', '0', '1'}; +const char VSD_STD_ID_CDW01[] = {'C', 'D', 'W', '0', '2'}; +const char VSD_STD_ID_NSR03[] = {'N', 'S', 'R', '0', '3'}; +const char VSD_STD_ID_TEA01[] = {'T', 'E', 'A', '0', '1'}; + +#include +#include "udf_private.h" +#include "udf_fs.h" + +/* + * The UDF specs are pretty clear on how each data structure is made + * up, but not very clear on how they relate to each other. Here is + * the skinny... This demostrates a filesystem with one file in the + * root directory. Subdirectories are treated just as normal files, + * but they have File Id Descriptors of their children as their file + * data. As for the Anchor Volume Descriptor Pointer, it can exist in + * two of the following three places: sector 256, sector n (the max + * sector of the disk), or sector n - 256. It's a pretty good bet + * that one will exist at sector 256 though. One caveat is unclosed + * CD media. For that, sector 256 cannot be written, so the Anchor + * Volume Descriptor Pointer can exist at sector 512 until the media + * is closed. + * + * Sector: + * 256: + * n: Anchor Volume Descriptor Pointer + * n - 256: | + * | + * |-->Main Volume Descriptor Sequence + * | | + * | | + * | |-->Logical Volume Descriptor + * | | + * |-->Partition Descriptor | + * | | + * | | + * |-->Fileset Descriptor + * | + * | + * |-->Root Dir File Entry + * | + * | + * |-->File data: + * File Id Descriptor + * | + * | + * |-->File Entry + * | + * | + * |-->File data + */ + +static udf_dirent_t * +udf_new_dirent(udf_file_entry_t *p_udf_fe, udf_t *p_udf, + const char *psz_name, bool b_dir, bool b_parent); + +/** + * Check the descriptor tag for both the correct id and correct checksum. + * Return zero if all is good, -1 if not. + */ +int +udf_checktag(const udf_tag_t *p_tag, udf_Uint16_t tag_id) +{ + uint8_t *itag; + uint8_t i; + uint8_t cksum = 0; + + itag = (uint8_t *)p_tag; + + if (p_tag->id != tag_id) + return -1; + + for (i = 0; i < 15; i++) + cksum = cksum + itag[i]; + cksum = cksum - itag[4]; + + if (cksum == p_tag->cksum) + return 0; + + return -1; +} + +bool +udf_get_lba(const udf_file_entry_t *p_udf_fe, + /*out*/ uint32_t *start, /*out*/ uint32_t *end) +{ + if (! p_udf_fe->i_alloc_descs) + return false; + + switch (p_udf_fe->icb_tag.flags & ICBTAG_FLAG_AD_MASK) { + case ICBTAG_FLAG_AD_SHORT: + { + /* The allocation descriptor field is filled with short_ad's. */ + udf_short_ad_t *p_ad = (udf_short_ad_t *) + (p_udf_fe->ext_attr + p_udf_fe->i_extended_attr); + + *start = uint32_from_le(p_ad->pos); + *end = *start + + ((uint32_from_le(p_ad->len) & UDF_LENGTH_MASK) - 1) / UDF_BLOCKSIZE; + return true; + } + break; + case ICBTAG_FLAG_AD_LONG: + { + /* The allocation descriptor field is filled with long_ad's */ + udf_long_ad_t *p_ad = (udf_long_ad_t *) + (p_udf_fe->ext_attr + p_udf_fe->i_extended_attr); + + *start = uint32_from_le(p_ad->loc.lba); /* ignore partition number */ + *end = *start + + ((uint32_from_le(p_ad->len) & UDF_LENGTH_MASK) - 1) / UDF_BLOCKSIZE; + return true; + } + break; + case ICBTAG_FLAG_AD_EXTENDED: + { + udf_ext_ad_t *p_ad = (udf_ext_ad_t *) + (p_udf_fe->ext_attr + p_udf_fe->i_extended_attr); + + *start = uint32_from_le(p_ad->ext_loc.lba); /* ignore partition number */ + *end = *start + + ((uint32_from_le(p_ad->len) & UDF_LENGTH_MASK) - 1) / UDF_BLOCKSIZE; + return true; + } + break; + default: + return false; + } + return false; +} + +#define udf_PATH_DELIMITERS "/\\" + +/* Searches p_udf_dirent a directory entry called psz_token. + Note p_udf_dirent is continuously updated. If the entry is + not found p_udf_dirent is useless and thus the caller should + not use it afterwards. +*/ +static +udf_dirent_t * +udf_ff_traverse(udf_dirent_t *p_udf_dirent, char *psz_token) +{ + while (udf_readdir(p_udf_dirent)) { + if (strcmp(psz_token, p_udf_dirent->psz_name) == 0) { + char *next_tok = strtok(NULL, udf_PATH_DELIMITERS); + + if (!next_tok) + return p_udf_dirent; /* found */ + else if (p_udf_dirent->b_dir) { + udf_dirent_t * p_udf_dirent2 = udf_opendir(p_udf_dirent); + + if (p_udf_dirent2) { + udf_dirent_t * p_udf_dirent3 = + udf_ff_traverse(p_udf_dirent2, next_tok); + + /* if p_udf_dirent3 is null p_udf_dirent2 is free'd. */ + return p_udf_dirent3; + } + } + } + } + free(p_udf_dirent->psz_name); + return NULL; +} + +/* FIXME! */ +#define udf_MAX_PATHLEN 2048 + +udf_dirent_t * +udf_fopen(udf_dirent_t *p_udf_root, const char *psz_name) +{ + udf_dirent_t *p_udf_file = NULL; + + if (p_udf_root) { + char tokenline[udf_MAX_PATHLEN]; + char *psz_token; + + strncpy(tokenline, psz_name, udf_MAX_PATHLEN); + psz_token = strtok(tokenline, udf_PATH_DELIMITERS); + if (psz_token) { + /*** FIXME??? udf_dirent can be variable size due to the + extended attributes and descriptors. Given that, is this + correct? + */ + udf_dirent_t *p_udf_dirent = + udf_new_dirent(&p_udf_root->fe, p_udf_root->p_udf, + p_udf_root->psz_name, p_udf_root->b_dir, + p_udf_root->b_parent); + p_udf_file = udf_ff_traverse(p_udf_dirent, psz_token); + udf_dirent_free(p_udf_dirent); + } + else if ( 0 == strncmp("/", psz_name, sizeof("/")) ) { + return udf_new_dirent(&p_udf_root->fe, p_udf_root->p_udf, + p_udf_root->psz_name, p_udf_root->b_dir, + p_udf_root->b_parent); + } + } + return p_udf_file; +} + +/* Convert unicode16 to 8-bit char by dripping MSB. + Wonder if iconv can be used here +*/ +static int +unicode16_decode( const uint8_t *data, int i_len, char *target ) +{ + int p = 1, i = 0; + + if( ( data[ 0 ] == 8 ) || ( data[ 0 ] == 16 ) ) do { + if( data[ 0 ] == 16 ) p++; /* Ignore MSB of unicode16 */ + if( p < i_len ) { + target[ i++ ] = data[ p++ ]; + } + } while( p < i_len ); + + target[ i ] = '\0'; + return 0; +} + + +static udf_dirent_t * +udf_new_dirent(udf_file_entry_t *p_udf_fe, udf_t *p_udf, + const char *psz_name, bool b_dir, bool b_parent) +{ + const unsigned int i_alloc_size = p_udf_fe->i_alloc_descs + + p_udf_fe->i_extended_attr; + + udf_dirent_t *p_udf_dirent = (udf_dirent_t *) + calloc(1, sizeof(udf_dirent_t) + i_alloc_size); + if (!p_udf_dirent) return NULL; + + p_udf_dirent->psz_name = strdup(psz_name); + p_udf_dirent->b_dir = b_dir; + p_udf_dirent->b_parent = b_parent; + p_udf_dirent->p_udf = p_udf; + p_udf_dirent->i_part_start = p_udf->i_part_start; + p_udf_dirent->dir_left = uint64_from_le(p_udf_fe->info_len); + + memcpy(&(p_udf_dirent->fe), p_udf_fe, + sizeof(udf_file_entry_t) + i_alloc_size); + udf_get_lba( p_udf_fe, &(p_udf_dirent->i_loc), + &(p_udf_dirent->i_loc_end) ); + return p_udf_dirent; +} + +/*! + Seek to a position i_start and then read i_blocks. Number of blocks read is + returned. One normally expects the return to be equal to i_blocks. +*/ +driver_return_code_t +udf_read_sectors (const udf_t *p_udf, void *ptr, lsn_t i_start, + long int i_blocks) +{ + driver_return_code_t ret; + long int i_read; + long int i_byte_offset; + + if (!p_udf) return 0; + i_byte_offset = (i_start * UDF_BLOCKSIZE); + + if (p_udf->b_stream) { + ret = cdio_stream_seek (p_udf->stream, i_byte_offset, SEEK_SET); + if (DRIVER_OP_SUCCESS != ret) return ret; + i_read = cdio_stream_read (p_udf->stream, ptr, UDF_BLOCKSIZE, i_blocks); + if (i_read) return DRIVER_OP_SUCCESS; + return DRIVER_OP_ERROR; + } else { + return cdio_read_data_sectors(p_udf->cdio, ptr, i_start, UDF_BLOCKSIZE, + i_blocks); + } +} + +/*! + Open an UDF for reading. Maybe in the future we will have + a mode. NULL is returned on error. + + Caller must free result - use udf_close for that. +*/ +udf_t * +udf_open (const char *psz_path) +{ + udf_t *p_udf = (udf_t *) calloc(1, sizeof(udf_t)) ; + uint8_t data[UDF_BLOCKSIZE]; + + if (!p_udf) return NULL; + + p_udf->cdio = cdio_open(psz_path, DRIVER_UNKNOWN); + if (!p_udf->cdio) { + /* Not a CD-ROM drive or CD Image. Maybe it's a UDF file not + encapsulated as a CD-ROM Image (e.g. often .UDF or (sic) .ISO) + */ + p_udf->stream = cdio_stdio_new( psz_path ); + if (!p_udf->stream) + goto error; + p_udf->b_stream = true; + } + + /* + * Look for an Anchor Volume Descriptor Pointer at sector 256. + */ + if (DRIVER_OP_SUCCESS != udf_read_sectors (p_udf, &data, 256, 1) ) + goto error; + + memcpy(&(p_udf->anchor_vol_desc_ptr), &data, sizeof(anchor_vol_desc_ptr_t)); + + if (udf_checktag((udf_tag_t *)&(p_udf->anchor_vol_desc_ptr), TAGID_ANCHOR)) + goto error; + + /* + * Then try to find a reference to a Primary Volume Descriptor. + */ + { + const anchor_vol_desc_ptr_t *p_avdp = &p_udf->anchor_vol_desc_ptr; + const uint32_t mvds_start = + uint32_from_le(p_avdp->main_vol_desc_seq_ext.loc); + const uint32_t mvds_end = mvds_start + + (uint32_from_le(p_avdp->main_vol_desc_seq_ext.len) - 1) / UDF_BLOCKSIZE; + + uint32_t i_lba; + + for (i_lba = mvds_start; i_lba < mvds_end; i_lba++) { + + udf_pvd_t *p_pvd = (udf_pvd_t *) &data; + + if (DRIVER_OP_SUCCESS != udf_read_sectors (p_udf, p_pvd, i_lba, 1) ) + goto error; + + if (!udf_checktag(&p_pvd->tag, TAGID_PRI_VOL)) { + p_udf->pvd_lba = i_lba; + break; + } + + } + + /* + * If we couldn't find a reference, bail out. + */ + if (i_lba == mvds_end) + goto error; + } + + return p_udf; + + error: + free(p_udf); + return NULL; +} + +/** + * Gets the Volume Identifier string, in 8bit unicode (latin-1) + * psz_volid, place to put the string + * i_volid_size, size of the buffer volid points to + * returns the size of buffer needed for all data + */ +int +udf_get_volume_id(udf_t *p_udf, /*out*/ char *psz_volid, unsigned int i_volid) +{ + uint8_t data[UDF_BLOCKSIZE]; + const udf_pvd_t *p_pvd = (udf_pvd_t *) &data; + unsigned int volid_len; + + /* get primary volume descriptor */ + if ( DRIVER_OP_SUCCESS != udf_read_sectors(p_udf, &data, p_udf->pvd_lba, 1) ) + return 0; + + volid_len = p_pvd->vol_ident[UDF_VOLID_SIZE-1]; + if(volid_len > UDF_VOLID_SIZE-1) { + /* this field is only UDF_VOLID_SIZE bytes something is wrong */ + volid_len = UDF_VOLID_SIZE-1; + } + if(i_volid > volid_len) { + i_volid = volid_len; + } + unicode16_decode((uint8_t *) p_pvd->vol_ident, i_volid, psz_volid); + + return volid_len; +} + +/** + * Gets the Volume Set Identifier, as a 128-byte dstring (not decoded) + * WARNING This is not a null terminated string + * volsetid, place to put the data + * volsetid_size, size of the buffer volsetid points to + * the buffer should be >=128 bytes to store the whole volumesetidentifier + * returns the size of the available volsetid information (128) + * or 0 on error + */ +int +udf_get_volumeset_id(udf_t *p_udf, /*out*/ uint8_t *volsetid, + unsigned int i_volsetid) +{ + uint8_t data[UDF_BLOCKSIZE]; + const udf_pvd_t *p_pvd = (udf_pvd_t *) &data; + + /* get primary volume descriptor */ + if ( DRIVER_OP_SUCCESS != udf_read_sectors(p_udf, &data, p_udf->pvd_lba, 1) ) + return 0; + + if (i_volsetid > UDF_VOLSET_ID_SIZE) { + i_volsetid = UDF_VOLSET_ID_SIZE; + } + + memcpy(volsetid, p_pvd->volset_id, i_volsetid); + + return UDF_VOLSET_ID_SIZE; +} + +/*! + Get the root in p_udf. If b_any_partition is false then + the root must be in the given partition. + NULL is returned if the partition is not found or a root is not found or + there is on error. + + Caller must free result - use udf_file_free for that. +*/ +udf_dirent_t * +udf_get_root (udf_t *p_udf, bool b_any_partition, partition_num_t i_partition) +{ + const anchor_vol_desc_ptr_t *p_avdp = &p_udf->anchor_vol_desc_ptr; + const uint32_t mvds_start = + uint32_from_le(p_avdp->main_vol_desc_seq_ext.loc); + const uint32_t mvds_end = mvds_start + + (uint32_from_le(p_avdp->main_vol_desc_seq_ext.len) - 1) / UDF_BLOCKSIZE; + uint32_t i_lba; + uint8_t data[UDF_BLOCKSIZE]; + + /* + Now we have the joy of finding the Partition Descriptor and the + Logical Volume Descriptor for the Main Volume Descriptor + Sequence. Once we've got that, we use the Logical Volume + Descriptor to get a Fileset Descriptor and that has the Root + Directory File Entry. + */ + for (i_lba = mvds_start; i_lba < mvds_end; i_lba++) { + uint8_t data[UDF_BLOCKSIZE]; + + partition_desc_t *p_partition = (partition_desc_t *) &data; + + if (DRIVER_OP_SUCCESS != udf_read_sectors (p_udf, p_partition, i_lba, 1) ) + return NULL; + + if (!udf_checktag(&p_partition->tag, TAGID_PARTITION)) { + const partition_num_t i_partition_check + = uint16_from_le(p_partition->number); + if (b_any_partition || i_partition_check == i_partition) { + /* Squirrel away some data regarding partition */ + p_udf->i_partition = uint16_from_le(p_partition->number); + p_udf->i_part_start = uint32_from_le(p_partition->start_loc); + if (p_udf->lvd_lba) break; + } + } else if (!udf_checktag(&p_partition->tag, TAGID_LOGVOL)) { + /* Get fileset descriptor */ + logical_vol_desc_t *p_logvol = (logical_vol_desc_t *) &data; + bool b_valid = + UDF_BLOCKSIZE == uint32_from_le(p_logvol->logical_blocksize); + + if (b_valid) { + p_udf->lvd_lba = i_lba; + p_udf->fsd_offset = + uint32_from_le(p_logvol->lvd_use.fsd_loc.loc.lba); + if (p_udf->i_part_start) break; + } + } + } + if (p_udf->lvd_lba && p_udf->i_part_start) { + udf_fsd_t *p_fsd = (udf_fsd_t *) &data; + + driver_return_code_t ret = + udf_read_sectors(p_udf, p_fsd, p_udf->i_part_start + p_udf->fsd_offset, + 1); + + if (DRIVER_OP_SUCCESS == ret && !udf_checktag(&p_fsd->tag, TAGID_FSD)) { + udf_file_entry_t *p_udf_fe = (udf_file_entry_t *) &data; + const uint32_t parent_icb = uint32_from_le(p_fsd->root_icb.loc.lba); + + /* Check partition numbers match of last-read block? */ + + ret = udf_read_sectors(p_udf, p_udf_fe, + p_udf->i_part_start + parent_icb, 1); + if (ret == DRIVER_OP_SUCCESS && + !udf_checktag(&p_udf_fe->tag, TAGID_FILE_ENTRY)) { + + /* Check partition numbers match of last-read block? */ + + /* We win! - Save root directory information. */ + return udf_new_dirent(p_udf_fe, p_udf, "/", true, false ); + } + } + } + + return NULL; +} + +#define free_and_null(x) \ + free(x); \ + x=NULL + +/*! + Close UDF and free resources associated with p_udf. +*/ +bool +udf_close (udf_t *p_udf) +{ + if (!p_udf) return true; + if (p_udf->b_stream) { + cdio_stdio_destroy(p_udf->stream); + } else { + cdio_destroy(p_udf->cdio); + } + + /* Get rid of root directory if allocated. */ + + free_and_null(p_udf); + return true; +} + +udf_dirent_t * +udf_opendir(const udf_dirent_t *p_udf_dirent) +{ + if (p_udf_dirent->b_dir && !p_udf_dirent->b_parent && p_udf_dirent->fid) { + udf_t *p_udf = p_udf_dirent->p_udf; + uint8_t data[UDF_BLOCKSIZE]; + udf_file_entry_t *p_udf_fe = (udf_file_entry_t *) &data; + + driver_return_code_t i_ret = + udf_read_sectors(p_udf, p_udf_fe, p_udf->i_part_start + + p_udf_dirent->fid->icb.loc.lba, 1); + + if (DRIVER_OP_SUCCESS == i_ret + && !udf_checktag(&p_udf_fe->tag, TAGID_FILE_ENTRY)) { + + if (ICBTAG_FILE_TYPE_DIRECTORY == p_udf_fe->icb_tag.file_type) { + udf_dirent_t *p_udf_dirent_new = + udf_new_dirent(p_udf_fe, p_udf, p_udf_dirent->psz_name, true, true); + return p_udf_dirent_new; + } + } + } + return NULL; +} + +udf_dirent_t * +udf_readdir(udf_dirent_t *p_udf_dirent) +{ + udf_t *p_udf; + + if (p_udf_dirent->dir_left <= 0) { + udf_dirent_free(p_udf_dirent); + return NULL; + } + + p_udf = p_udf_dirent->p_udf; + if (p_udf_dirent->fid) { + /* advance to next File Identifier Descriptor */ + /* FIXME: need to advance file entry (fe) as well. */ + uint32_t ofs = 4 * + ((sizeof(*(p_udf_dirent->fid)) + p_udf_dirent->fid->i_imp_use + + p_udf_dirent->fid->i_file_id + 3) / 4); + + p_udf_dirent->fid = + (udf_fileid_desc_t *)((uint8_t *)p_udf_dirent->fid + ofs); + } + + if (!p_udf_dirent->fid) { + uint32_t i_sectors = + (p_udf_dirent->i_loc_end - p_udf_dirent->i_loc + 1); + uint32_t size = UDF_BLOCKSIZE * i_sectors; + driver_return_code_t i_ret; + + if (!p_udf_dirent->sector) + p_udf_dirent->sector = (uint8_t*) malloc(size); + i_ret = udf_read_sectors(p_udf, p_udf_dirent->sector, + p_udf_dirent->i_part_start+p_udf_dirent->i_loc, + i_sectors); + if (DRIVER_OP_SUCCESS == i_ret) + p_udf_dirent->fid = (udf_fileid_desc_t *) p_udf_dirent->sector; + else + p_udf_dirent->fid = NULL; + } + + if (p_udf_dirent->fid && !udf_checktag(&(p_udf_dirent->fid->tag), TAGID_FID)) + { + uint32_t ofs = + 4 * ((sizeof(*p_udf_dirent->fid) + p_udf_dirent->fid->i_imp_use + + p_udf_dirent->fid->i_file_id + 3) / 4); + + p_udf_dirent->dir_left -= ofs; + p_udf_dirent->b_dir = + (p_udf_dirent->fid->file_characteristics & UDF_FILE_DIRECTORY) != 0; + p_udf_dirent->b_parent = + (p_udf_dirent->fid->file_characteristics & UDF_FILE_PARENT) != 0; + + { + const unsigned int i_len = p_udf_dirent->fid->i_file_id; + uint8_t data[UDF_BLOCKSIZE] = {0}; + udf_file_entry_t *p_udf_fe = (udf_file_entry_t *) &data; + + udf_read_sectors(p_udf, p_udf_fe, p_udf->i_part_start + + p_udf_dirent->fid->icb.loc.lba, 1); + + memcpy(&(p_udf_dirent->fe), p_udf_fe, + sizeof(udf_file_entry_t) + p_udf_fe->i_alloc_descs + + p_udf_fe->i_extended_attr ); + + if (strlen(p_udf_dirent->psz_name) < i_len) + p_udf_dirent->psz_name = (char *) + realloc(p_udf_dirent->psz_name, sizeof(char)*i_len+1); + + unicode16_decode(p_udf_dirent->fid->imp_use + + p_udf_dirent->fid->i_imp_use, + i_len, p_udf_dirent->psz_name); + } + return p_udf_dirent; + } + return NULL; +} + +/*! + free free resources associated with p_udf_dirent. +*/ +bool +udf_dirent_free(udf_dirent_t *p_udf_dirent) +{ + if (p_udf_dirent) { + p_udf_dirent->fid = NULL; + free_and_null(p_udf_dirent->psz_name); + free_and_null(p_udf_dirent->sector); + free_and_null(p_udf_dirent); + } + return true; +} diff --git a/lib/udf/udf_fs.h b/lib/udf/udf_fs.h new file mode 100644 index 00000000..80e99554 --- /dev/null +++ b/lib/udf/udf_fs.h @@ -0,0 +1,39 @@ +/* + $Id: udf_fs.h,v 1.3 2008/04/18 16:02:10 karl Exp $ + + Copyright (C) 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __CDIO_UDF_FS_H__ +#define __CDIO_UDF_FS_H__ + +#include +/** + * Check the descriptor tag for both the correct id and correct checksum. + * Return zero if all is good, -1 if not. + */ +int udf_checktag(const udf_tag_t *p_tag, udf_Uint16_t tag_id); + +#endif /* __CDIO_UDF_FS_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/udf/udf_private.h b/lib/udf/udf_private.h new file mode 100644 index 00000000..dadca00a --- /dev/null +++ b/lib/udf/udf_private.h @@ -0,0 +1,78 @@ +/* + $Id: udf_private.h,v 1.12 2008/04/18 16:02:10 karl Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __CDIO_UDF_PRIVATE_H__ +#define __CDIO_UDF_PRIVATE_H__ + +#if defined(HAVE_CONFIG_H) && !defined(LIBCDIO_CONFIG_H) +#include "config.h" +#endif + +#include +#include +#include +#include "_cdio_stdio.h" + +/* Implementation of opaque types */ + +struct udf_s { + bool b_stream; /* Use stream pointer, else use + p_cdio. */ + ssize_t i_position; /* Position in file if positive. */ + CdioDataSource_t *stream; /* Stream pointer if stream */ + CdIo_t *cdio; /* Cdio pointer if read device */ + anchor_vol_desc_ptr_t anchor_vol_desc_ptr; + uint32_t pvd_lba; /* sector of Primary Volume Descriptor */ + partition_num_t i_partition; /* partition number */ + uint32_t i_part_start; /* start of Partition Descriptor */ + uint32_t lvd_lba; /* sector of Logical Volume Descriptor */ + uint32_t fsd_offset; /* lba of fileset descriptor */ +}; + +struct udf_dirent_s +{ + char *psz_name; + bool b_dir; /* true if this entry is a directory. */ + bool b_parent; /* True if has parent directory (e.g. not root + directory). If not set b_dir will probably + be true. */ + udf_t *p_udf; + uint32_t i_part_start; + uint32_t i_loc, i_loc_end; + uint64_t dir_left; + uint8_t *sector; + udf_fileid_desc_t *fid; + + /* This field has to come last because it is variable in length. */ + udf_file_entry_t fe; +}; + +bool udf_get_lba(const udf_file_entry_t *p_udf_fe, + /*out*/ uint32_t *start, /*out*/ uint32_t *end); + +#endif /* __CDIO_UDF_PRIVATE_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/lib/udf/udf_time.c b/lib/udf/udf_time.c new file mode 100644 index 00000000..0a7dbc3a --- /dev/null +++ b/lib/udf/udf_time.c @@ -0,0 +1,255 @@ +/* + $Id: udf_time.c,v 1.10 2008/04/24 07:28:00 rocky Exp $ + + Copyright (C) 2005, 2008 Rocky Bernstein + Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc. + + Modified From part of the GNU C Library. + Contributed by Paul Eggert. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Some history from the GNU/Linux kernel from which this is also taken... + dgb 10/02/98: ripped this from glibc source to help convert + timestamps to unix time + + 10/04/98: added new table-based lookup after seeing how ugly the + gnu code is + + blf 09/27/99: ripped out all the old code and inserted new table from + John Brockmeyer (without leap second corrections) + rewrote udf_stamp_to_time and fixed timezone + accounting in udf_timespec_to_stamp. +*/ + +/* + * We don't take into account leap seconds. This may be correct or incorrect. + * For more NIST information (especially dealing with leap seconds), see: + * http://www.boulder.nist.gov/timefreq/pubs/bulletin/leapsecond.htm + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef NEED_TIMEZONEVAR +#define timezonevar 1 +#endif + +#include "udf_private.h" +#include + +/** + Imagine the below enum values as #define'd or constant values + rather than distinct values of an enum. +*/ +enum { + HOURS_PER_DAY = 24, + SECS_PER_MINUTE = 60, + MAX_YEAR_SECONDS = 69, + DAYS_PER_YEAR = 365, /* That is, in most of the years. */ + EPOCH_YEAR = 1970, + SECS_PER_HOUR = (60 * SECS_PER_MINUTE), + SECS_PER_DAY = SECS_PER_HOUR * HOURS_PER_DAY +} debug_udf_time_enum; + +#ifndef __isleap +/* Nonzero if YEAR is a leap year (every 4 years, + except every 100th isn't, and every 400th is). */ +#define __isleap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) +#endif + +/* How many days come before each month (0-12). */ +static const unsigned short int __mon_yday[2][13] = + { + /* Normal years. */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, DAYS_PER_YEAR }, + /* Leap years. */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, DAYS_PER_YEAR+1 } + }; + +#define SPY(y,l,s) (SECS_PER_DAY * (DAYS_PER_YEAR*y+l)+s) /* Seconds per year */ + +static time_t year_seconds[MAX_YEAR_SECONDS]= { + /*1970*/ SPY( 0, 0,0), SPY( 1, 0,0), SPY( 2, 0,0), SPY( 3, 1,0), + /*1974*/ SPY( 4, 1,0), SPY( 5, 1,0), SPY( 6, 1,0), SPY( 7, 2,0), + /*1978*/ SPY( 8, 2,0), SPY( 9, 2,0), SPY(10, 2,0), SPY(11, 3,0), + /*1982*/ SPY(12, 3,0), SPY(13, 3,0), SPY(14, 3,0), SPY(15, 4,0), + /*1986*/ SPY(16, 4,0), SPY(17, 4,0), SPY(18, 4,0), SPY(19, 5,0), + /*1990*/ SPY(20, 5,0), SPY(21, 5,0), SPY(22, 5,0), SPY(23, 6,0), + /*1994*/ SPY(24, 6,0), SPY(25, 6,0), SPY(26, 6,0), SPY(27, 7,0), + /*1998*/ SPY(28, 7,0), SPY(29, 7,0), SPY(30, 7,0), SPY(31, 8,0), + /*2002*/ SPY(32, 8,0), SPY(33, 8,0), SPY(34, 8,0), SPY(35, 9,0), + /*2006*/ SPY(36, 9,0), SPY(37, 9,0), SPY(38, 9,0), SPY(39,10,0), + /*2010*/ SPY(40,10,0), SPY(41,10,0), SPY(42,10,0), SPY(43,11,0), + /*2014*/ SPY(44,11,0), SPY(45,11,0), SPY(46,11,0), SPY(47,12,0), + /*2018*/ SPY(48,12,0), SPY(49,12,0), SPY(50,12,0), SPY(51,13,0), + /*2022*/ SPY(52,13,0), SPY(53,13,0), SPY(54,13,0), SPY(55,14,0), + /*2026*/ SPY(56,14,0), SPY(57,14,0), SPY(58,14,0), SPY(59,15,0), + /*2030*/ SPY(60,15,0), SPY(61,15,0), SPY(62,15,0), SPY(63,16,0), + /*2034*/ SPY(64,16,0), SPY(65,16,0), SPY(66,16,0), SPY(67,17,0), + /*2038*/ SPY(68,17,0) +}; + +#ifdef HAVE_TIMEZONE_VAR +extern long timezone; +#endif + +time_t * +udf_stamp_to_time(time_t *dest, long int *dest_usec, + const udf_timestamp_t src) +{ + int yday; + uint8_t type = src.type_tz >> 12; + int16_t offset; + + if (type == 1) { + offset = src.type_tz << 4; + /* sign extent offset */ + offset = (offset >> 4); + if (offset == -2047) /* unspecified offset */ + offset = 0; + } + else + offset = 0; + + if ((src.year < EPOCH_YEAR) || + (src.year >= EPOCH_YEAR+MAX_YEAR_SECONDS)) + { + *dest = -1; + *dest_usec = -1; + return NULL; + } + *dest = year_seconds[src.year - EPOCH_YEAR]; + *dest -= offset * SECS_PER_MINUTE; + + yday = ((__mon_yday[__isleap (src.year)] + [src.month-1]) + (src.day-1)); + *dest += src.second + + ( SECS_PER_MINUTE * + ( ( (yday* HOURS_PER_DAY) + src.hour ) * 60 + src.minute ) ); + + *dest_usec = src.microseconds + + (src.centiseconds * 10000) + + (src.hundreds_of_microseconds * 100); + return dest; +} + +#ifdef HAVE_STRUCT_TIMESPEC +/*! + Convert a UDF timestamp to a time_t. If microseconds are desired, + use dest_usec. The return value is the same as dest. */ +udf_timestamp_t * +udf_timespec_to_stamp(const struct timespec ts, udf_timestamp_t *dest) +{ + long int days, rem, y; + const unsigned short int *ip; + int16_t offset = 0; + int16_t tv_sec; + +#ifdef HAVE_TIMEZONE_VAR + offset = -timezone; +#endif + + if (!dest) + return dest; + + dest->type_tz = 0x1000 | (offset & 0x0FFF); + + tv_sec = ts.tv_sec + (offset * SECS_PER_MINUTE); + days = tv_sec / SECS_PER_DAY; + rem = tv_sec % SECS_PER_DAY; + dest->hour = rem / SECS_PER_HOUR; + rem %= SECS_PER_HOUR; + dest->minute = rem / SECS_PER_MINUTE; + dest->second = rem % SECS_PER_MINUTE; + y = EPOCH_YEAR; + +#define DIV(a,b) ((a) / (b) - ((a) % (b) < 0)) +#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) + + while (days < 0 || days >= (__isleap(y) ? DAYS_PER_YEAR+1 : DAYS_PER_YEAR)) { + long int yg = y + days / DAYS_PER_YEAR - (days % DAYS_PER_YEAR < 0); + + /* Adjust DAYS and Y to match the guessed year. */ + days -= ((yg - y) * DAYS_PER_YEAR + + LEAPS_THRU_END_OF (yg - 1) + - LEAPS_THRU_END_OF (y - 1)); + y = yg; + } + dest->year = y; + ip = __mon_yday[__isleap(y)]; + for (y = 11; days < (long int) ip[y]; --y) + continue; + days -= ip[y]; + dest->month = y + 1; + dest->day = days + 1; + + dest->centiseconds = ts.tv_nsec / 10000000; + dest->hundreds_of_microseconds = ( (ts.tv_nsec / 1000) + - (dest->centiseconds * 10000) ) / 100; + dest->microseconds = ( (ts.tv_nsec / 1000) + - (dest->centiseconds * 10000) + - (dest->hundreds_of_microseconds * 100) ); + return dest; +} +#endif + +/*! + Return the modification time of the file. + */ +time_t +udf_get_modification_time(const udf_dirent_t *p_udf_dirent) +{ + if (p_udf_dirent) { + time_t ret_time; + long int usec; + udf_stamp_to_time(&ret_time, &usec, p_udf_dirent->fe.modification_time); + return ret_time; + } + return 0; +} + +/*! + Return the access time of the file. + */ +time_t +udf_get_access_time(const udf_dirent_t *p_udf_dirent) +{ + if (p_udf_dirent) { + time_t ret_time; + long int usec; + udf_stamp_to_time(&ret_time, &usec, p_udf_dirent->fe.access_time); + return ret_time; + } + return 0; +} + +/*! + Return the attribute (most recent create or access) time of the file + */ +time_t +udf_get_attribute_time(const udf_dirent_t *p_udf_dirent) +{ + if (p_udf_dirent) { + time_t ret_time; + long int usec; + udf_stamp_to_time(&ret_time, &usec, p_udf_dirent->fe.attribute_time); + return ret_time; + } + return 0; +} + diff --git a/libcdio++.pc.in b/libcdio++.pc.in new file mode 100644 index 00000000..02bb6dee --- /dev/null +++ b/libcdio++.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: @PACKAGE_NAME@++ +Description: C++ OO Portable CD-ROM I/O library +Version: @PACKAGE_VERSION@ +#Requires: glib-2.0 +Libs: -L${libdir} -lcdio++ -lcdio @LIBS@ @DARWIN_PKG_LIB_HACK@ +Cflags: -I${includedir} diff --git a/libcdio.pc.in b/libcdio.pc.in new file mode 100644 index 00000000..90474065 --- /dev/null +++ b/libcdio.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: @PACKAGE_NAME@ +Description: Portable CD-ROM I/O library +Version: @PACKAGE_VERSION@ +#Requires: glib-2.0 +Libs: -L${libdir} -lcdio @LIBS@ @LIBICONV@ @DARWIN_PKG_LIB_HACK@ +Cflags: -I${includedir} diff --git a/libcdio_cdda.pc.in b/libcdio_cdda.pc.in new file mode 100644 index 00000000..314f5ecf --- /dev/null +++ b/libcdio_cdda.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libcdio_cdda +Description: CD paranoia CD-DA library from libcdio +Version: @PACKAGE_VERSION@ +#Requires: glib-2.0 +Libs: -L${libdir} -lcdio_cdda -lcdio @COS_LIB@ +Cflags: -I${includedir} diff --git a/libcdio_paranoia.pc.in b/libcdio_paranoia.pc.in new file mode 100644 index 00000000..d273ed5c --- /dev/null +++ b/libcdio_paranoia.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libcdio_paranoia +Description: CD paranoia library from libcdio +Version: @PACKAGE_VERSION@ +#Requires: glib-2.0 +Libs: -L${libdir} -lcdio_paranoia -lcdio_cdda -lcdio +Cflags: -I${includedir} diff --git a/libiso9660++.pc.in b/libiso9660++.pc.in new file mode 100644 index 00000000..5c2a3028 --- /dev/null +++ b/libiso9660++.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +libiconv=@LIBICONV@ + +Name: libiso9660++ +Description: C++ OO ISO-9660 library of libcdio +Version: @PACKAGE_VERSION@ +Requires: libcdio +Libs: -L${libdir} -liso9660++ -lcdio++ -liso9660 @LIBICONV@ -lcdio +Cflags: -I${includedir} diff --git a/libiso9660.pc.in b/libiso9660.pc.in new file mode 100644 index 00000000..983c07f0 --- /dev/null +++ b/libiso9660.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +libiconv=@LIBICONV@ + +Name: libiso9660 +Description: ISO-9660 library of libcdio +Version: @PACKAGE_VERSION@ +Requires: libcdio +Libs: -L${libdir} -liso9660 @LIBICONV@ -lcdio +Cflags: -I${includedir} diff --git a/libudf.pc.in b/libudf.pc.in new file mode 100644 index 00000000..0a2e95ca --- /dev/null +++ b/libudf.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libudf +Description: UDF library of libcdio +Version: @PACKAGE_VERSION@ +Requires: libcdio +Libs: -L${libdir} -ludf -lcdio +Cflags: -I${includedir} diff --git a/m4/codeset.m4 b/m4/codeset.m4 new file mode 100644 index 00000000..223955b4 --- /dev/null +++ b/m4/codeset.m4 @@ -0,0 +1,21 @@ +# codeset.m4 serial 2 (gettext-0.16) +dnl Copyright (C) 2000-2002, 2006 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([AM_LANGINFO_CODESET], +[ + AC_CACHE_CHECK([for nl_langinfo and CODESET], am_cv_langinfo_codeset, + [AC_TRY_LINK([#include ], + [char* cs = nl_langinfo(CODESET); return !cs;], + am_cv_langinfo_codeset=yes, + am_cv_langinfo_codeset=no) + ]) + if test $am_cv_langinfo_codeset = yes; then + AC_DEFINE(HAVE_LANGINFO_CODESET, 1, + [Define if you have and nl_langinfo(CODESET).]) + fi +]) diff --git a/m4/iconv.m4 b/m4/iconv.m4 new file mode 100644 index 00000000..66bc76f4 --- /dev/null +++ b/m4/iconv.m4 @@ -0,0 +1,180 @@ +# iconv.m4 serial AM6 (gettext-0.17) +dnl Copyright (C) 2000-2002, 2007 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([AM_ICONV_LINKFLAGS_BODY], +[ + dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + + dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV + dnl accordingly. + AC_LIB_LINKFLAGS_BODY([iconv]) +]) + +AC_DEFUN([AM_ICONV_LINK], +[ + dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and + dnl those with the standalone portable GNU libiconv installed). + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + + dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV + dnl accordingly. + AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY]) + + dnl Add $INCICONV to CPPFLAGS before performing the following checks, + dnl because if the user has installed libiconv and not disabled its use + dnl via --without-libiconv-prefix, he wants to use it. The first + dnl AC_TRY_LINK will then fail, the second AC_TRY_LINK will succeed. + am_save_CPPFLAGS="$CPPFLAGS" + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV]) + + AC_CACHE_CHECK([for iconv], am_cv_func_iconv, [ + am_cv_func_iconv="no, consider installing GNU libiconv" + am_cv_lib_iconv=no + AC_TRY_LINK([#include +#include ], + [iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd);], + am_cv_func_iconv=yes) + if test "$am_cv_func_iconv" != yes; then + am_save_LIBS="$LIBS" + LIBS="$LIBS $LIBICONV" + AC_TRY_LINK([#include +#include ], + [iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd);], + am_cv_lib_iconv=yes + am_cv_func_iconv=yes) + LIBS="$am_save_LIBS" + fi + ]) + if test "$am_cv_func_iconv" = yes; then + AC_CACHE_CHECK([for working iconv], am_cv_func_iconv_works, [ + dnl This tests against bugs in AIX 5.1 and HP-UX 11.11. + am_save_LIBS="$LIBS" + if test $am_cv_lib_iconv = yes; then + LIBS="$LIBS $LIBICONV" + fi + AC_TRY_RUN([ +#include +#include +int main () +{ + /* Test against AIX 5.1 bug: Failures are not distinguishable from successful + returns. */ + { + iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8"); + if (cd_utf8_to_88591 != (iconv_t)(-1)) + { + static const char input[] = "\342\202\254"; /* EURO SIGN */ + char buf[10]; + const char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_utf8_to_88591, + (char **) &inptr, &inbytesleft, + &outptr, &outbytesleft); + if (res == 0) + return 1; + } + } +#if 0 /* This bug could be worked around by the caller. */ + /* Test against HP-UX 11.11 bug: Positive return value instead of 0. */ + { + iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591"); + if (cd_88591_to_utf8 != (iconv_t)(-1)) + { + static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337"; + char buf[50]; + const char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_88591_to_utf8, + (char **) &inptr, &inbytesleft, + &outptr, &outbytesleft); + if ((int)res > 0) + return 1; + } + } +#endif + /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is + provided. */ + if (/* Try standardized names. */ + iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1) + /* Try IRIX, OSF/1 names. */ + && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1) + /* Try AIX names. */ + && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1) + /* Try HP-UX names. */ + && iconv_open ("utf8", "eucJP") == (iconv_t)(-1)) + return 1; + return 0; +}], [am_cv_func_iconv_works=yes], [am_cv_func_iconv_works=no], + [case "$host_os" in + aix* | hpux*) am_cv_func_iconv_works="guessing no" ;; + *) am_cv_func_iconv_works="guessing yes" ;; + esac]) + LIBS="$am_save_LIBS" + ]) + case "$am_cv_func_iconv_works" in + *no) am_func_iconv=no am_cv_lib_iconv=no ;; + *) am_func_iconv=yes ;; + esac + else + am_func_iconv=no am_cv_lib_iconv=no + fi + if test "$am_func_iconv" = yes; then + AC_DEFINE(HAVE_ICONV, 1, + [Define if you have the iconv() function and it works.]) + fi + if test "$am_cv_lib_iconv" = yes; then + AC_MSG_CHECKING([how to link with libiconv]) + AC_MSG_RESULT([$LIBICONV]) + else + dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV + dnl either. + CPPFLAGS="$am_save_CPPFLAGS" + LIBICONV= + LTLIBICONV= + fi + AC_SUBST(LIBICONV) + AC_SUBST(LTLIBICONV) +]) + +AC_DEFUN([AM_ICONV], +[ + AM_ICONV_LINK + if test "$am_cv_func_iconv" = yes; then + AC_MSG_CHECKING([for iconv declaration]) + AC_CACHE_VAL(am_cv_proto_iconv, [ + AC_TRY_COMPILE([ +#include +#include +extern +#ifdef __cplusplus +"C" +#endif +#if defined(__STDC__) || defined(__cplusplus) +size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); +#else +size_t iconv(); +#endif +], [], am_cv_proto_iconv_arg1="", am_cv_proto_iconv_arg1="const") + am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"]) + am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` + AC_MSG_RESULT([$]{ac_t:- + }[$]am_cv_proto_iconv) + AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1, + [Define as const if the declaration of iconv() needs const.]) + fi +]) diff --git a/m4/lib-ld.m4 b/m4/lib-ld.m4 new file mode 100644 index 00000000..96c4e2c3 --- /dev/null +++ b/m4/lib-ld.m4 @@ -0,0 +1,110 @@ +# lib-ld.m4 serial 3 (gettext-0.13) +dnl Copyright (C) 1996-2003 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl Subroutines of libtool.m4, +dnl with replacements s/AC_/AC_LIB/ and s/lt_cv/acl_cv/ to avoid collision +dnl with libtool.m4. + +dnl From libtool-1.4. Sets the variable with_gnu_ld to yes or no. +AC_DEFUN([AC_LIB_PROG_LD_GNU], +[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], acl_cv_prog_gnu_ld, +[# I'd rather use --version here, but apparently some GNU ld's only accept -v. +case `$LD -v 2>&1 conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by GCC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]* | [A-Za-z]:[\\/]*)] + [re_direlt='/[^/][^/]*/\.\./'] + # Canonicalize the path of ld + ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'` + while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(acl_cv_path_LD, +[if test -z "$LD"; then + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + acl_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in + *GNU* | *'with BFD'*) + test "$with_gnu_ld" != no && break ;; + *) + test "$with_gnu_ld" != yes && break ;; + esac + fi + done + IFS="$ac_save_ifs" +else + acl_cv_path_LD="$LD" # Let the user override the test with a path. +fi]) +LD="$acl_cv_path_LD" +if test -n "$LD"; then + AC_MSG_RESULT($LD) +else + AC_MSG_RESULT(no) +fi +test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH]) +AC_LIB_PROG_LD_GNU +]) diff --git a/m4/lib-link.m4 b/m4/lib-link.m4 new file mode 100644 index 00000000..e3d26fc4 --- /dev/null +++ b/m4/lib-link.m4 @@ -0,0 +1,709 @@ +# lib-link.m4 serial 13 (gettext-0.17) +dnl Copyright (C) 2001-2007 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_PREREQ(2.54) + +dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and +dnl the libraries corresponding to explicit and implicit dependencies. +dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and +dnl augments the CPPFLAGS variable. +dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname +dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem. +AC_DEFUN([AC_LIB_LINKFLAGS], +[ + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + define([Name],[translit([$1],[./-], [___])]) + define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [ + AC_LIB_LINKFLAGS_BODY([$1], [$2]) + ac_cv_lib[]Name[]_libs="$LIB[]NAME" + ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME" + ac_cv_lib[]Name[]_cppflags="$INC[]NAME" + ac_cv_lib[]Name[]_prefix="$LIB[]NAME[]_PREFIX" + ]) + LIB[]NAME="$ac_cv_lib[]Name[]_libs" + LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs" + INC[]NAME="$ac_cv_lib[]Name[]_cppflags" + LIB[]NAME[]_PREFIX="$ac_cv_lib[]Name[]_prefix" + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) + AC_SUBST([LIB]NAME) + AC_SUBST([LTLIB]NAME) + AC_SUBST([LIB]NAME[_PREFIX]) + dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the + dnl results of this search when this library appears as a dependency. + HAVE_LIB[]NAME=yes + undefine([Name]) + undefine([NAME]) +]) + +dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode) +dnl searches for libname and the libraries corresponding to explicit and +dnl implicit dependencies, together with the specified include files and +dnl the ability to compile and link the specified testcode. If found, it +dnl sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} and +dnl LTLIB${NAME} variables and augments the CPPFLAGS variable, and +dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs +dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty. +dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname +dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem. +AC_DEFUN([AC_LIB_HAVE_LINKFLAGS], +[ + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + define([Name],[translit([$1],[./-], [___])]) + define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + + dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME + dnl accordingly. + AC_LIB_LINKFLAGS_BODY([$1], [$2]) + + dnl Add $INC[]NAME to CPPFLAGS before performing the following checks, + dnl because if the user has installed lib[]Name and not disabled its use + dnl via --without-lib[]Name-prefix, he wants to use it. + ac_save_CPPFLAGS="$CPPFLAGS" + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) + + AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [ + ac_save_LIBS="$LIBS" + LIBS="$LIBS $LIB[]NAME" + AC_TRY_LINK([$3], [$4], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name=no]) + LIBS="$ac_save_LIBS" + ]) + if test "$ac_cv_lib[]Name" = yes; then + HAVE_LIB[]NAME=yes + AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the $1 library.]) + AC_MSG_CHECKING([how to link with lib[]$1]) + AC_MSG_RESULT([$LIB[]NAME]) + else + HAVE_LIB[]NAME=no + dnl If $LIB[]NAME didn't lead to a usable library, we don't need + dnl $INC[]NAME either. + CPPFLAGS="$ac_save_CPPFLAGS" + LIB[]NAME= + LTLIB[]NAME= + LIB[]NAME[]_PREFIX= + fi + AC_SUBST([HAVE_LIB]NAME) + AC_SUBST([LIB]NAME) + AC_SUBST([LTLIB]NAME) + AC_SUBST([LIB]NAME[_PREFIX]) + undefine([Name]) + undefine([NAME]) +]) + +dnl Determine the platform dependent parameters needed to use rpath: +dnl acl_libext, +dnl acl_shlibext, +dnl acl_hardcode_libdir_flag_spec, +dnl acl_hardcode_libdir_separator, +dnl acl_hardcode_direct, +dnl acl_hardcode_minus_L. +AC_DEFUN([AC_LIB_RPATH], +[ + dnl Tell automake >= 1.10 to complain if config.rpath is missing. + m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])]) + AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS + AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld + AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host + AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir + AC_CACHE_CHECK([for shared library run path origin], acl_cv_rpath, [ + CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ + ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh + . ./conftest.sh + rm -f ./conftest.sh + acl_cv_rpath=done + ]) + wl="$acl_cv_wl" + acl_libext="$acl_cv_libext" + acl_shlibext="$acl_cv_shlibext" + acl_libname_spec="$acl_cv_libname_spec" + acl_library_names_spec="$acl_cv_library_names_spec" + acl_hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" + acl_hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" + acl_hardcode_direct="$acl_cv_hardcode_direct" + acl_hardcode_minus_L="$acl_cv_hardcode_minus_L" + dnl Determine whether the user wants rpath handling at all. + AC_ARG_ENABLE(rpath, + [ --disable-rpath do not hardcode runtime library paths], + :, enable_rpath=yes) +]) + +dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and +dnl the libraries corresponding to explicit and implicit dependencies. +dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables. +dnl Also, sets the LIB${NAME}_PREFIX variable to nonempty if libname was found +dnl in ${LIB${NAME}_PREFIX}/$acl_libdirstem. +AC_DEFUN([AC_LIB_LINKFLAGS_BODY], +[ + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + dnl Autoconf >= 2.61 supports dots in --with options. + define([N_A_M_E],[m4_if(m4_version_compare(m4_defn([m4_PACKAGE_VERSION]),[2.61]),[-1],[translit([$1],[.],[_])],[$1])]) + dnl By default, look in $includedir and $libdir. + use_additional=yes + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + AC_LIB_ARG_WITH([lib]N_A_M_E[-prefix], +[ --with-lib]N_A_M_E[-prefix[=DIR] search for lib$1 in DIR/include and DIR/lib + --without-lib]N_A_M_E[-prefix don't search for lib$1 in includedir and libdir], +[ + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + else + additional_includedir="$withval/include" + additional_libdir="$withval/$acl_libdirstem" + fi + fi +]) + dnl Search the library and its dependencies in $additional_libdir and + dnl $LDFLAGS. Using breadth-first-seach. + LIB[]NAME= + LTLIB[]NAME= + INC[]NAME= + LIB[]NAME[]_PREFIX= + rpathdirs= + ltrpathdirs= + names_already_handled= + names_next_round='$1 $2' + while test -n "$names_next_round"; do + names_this_round="$names_next_round" + names_next_round= + for name in $names_this_round; do + already_handled= + for n in $names_already_handled; do + if test "$n" = "$name"; then + already_handled=yes + break + fi + done + if test -z "$already_handled"; then + names_already_handled="$names_already_handled $name" + dnl See if it was already located by an earlier AC_LIB_LINKFLAGS + dnl or AC_LIB_HAVE_LINKFLAGS call. + uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` + eval value=\"\$HAVE_LIB$uppername\" + if test -n "$value"; then + if test "$value" = yes; then + eval value=\"\$LIB$uppername\" + test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value" + eval value=\"\$LTLIB$uppername\" + test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value" + else + dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined + dnl that this library doesn't exist. So just drop it. + : + fi + else + dnl Search the library lib$name in $additional_libdir and $LDFLAGS + dnl and the already constructed $LIBNAME/$LTLIBNAME. + found_dir= + found_la= + found_so= + found_a= + eval libname=\"$acl_libname_spec\" # typically: libname=lib$name + if test -n "$acl_shlibext"; then + shrext=".$acl_shlibext" # typically: shrext=.so + else + shrext= + fi + if test $use_additional = yes; then + dir="$additional_libdir" + dnl The same code as in the loop below: + dnl First look for a shared library. + if test -n "$acl_shlibext"; then + if test -f "$dir/$libname$shrext"; then + found_dir="$dir" + found_so="$dir/$libname$shrext" + else + if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then + ver=`(cd "$dir" && \ + for f in "$libname$shrext".*; do echo "$f"; done \ + | sed -e "s,^$libname$shrext\\\\.,," \ + | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ + | sed 1q ) 2>/dev/null` + if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then + found_dir="$dir" + found_so="$dir/$libname$shrext.$ver" + fi + else + eval library_names=\"$acl_library_names_spec\" + for f in $library_names; do + if test -f "$dir/$f"; then + found_dir="$dir" + found_so="$dir/$f" + break + fi + done + fi + fi + fi + dnl Then look for a static library. + if test "X$found_dir" = "X"; then + if test -f "$dir/$libname.$acl_libext"; then + found_dir="$dir" + found_a="$dir/$libname.$acl_libext" + fi + fi + if test "X$found_dir" != "X"; then + if test -f "$dir/$libname.la"; then + found_la="$dir/$libname.la" + fi + fi + fi + if test "X$found_dir" = "X"; then + for x in $LDFLAGS $LTLIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + case "$x" in + -L*) + dir=`echo "X$x" | sed -e 's/^X-L//'` + dnl First look for a shared library. + if test -n "$acl_shlibext"; then + if test -f "$dir/$libname$shrext"; then + found_dir="$dir" + found_so="$dir/$libname$shrext" + else + if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then + ver=`(cd "$dir" && \ + for f in "$libname$shrext".*; do echo "$f"; done \ + | sed -e "s,^$libname$shrext\\\\.,," \ + | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ + | sed 1q ) 2>/dev/null` + if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then + found_dir="$dir" + found_so="$dir/$libname$shrext.$ver" + fi + else + eval library_names=\"$acl_library_names_spec\" + for f in $library_names; do + if test -f "$dir/$f"; then + found_dir="$dir" + found_so="$dir/$f" + break + fi + done + fi + fi + fi + dnl Then look for a static library. + if test "X$found_dir" = "X"; then + if test -f "$dir/$libname.$acl_libext"; then + found_dir="$dir" + found_a="$dir/$libname.$acl_libext" + fi + fi + if test "X$found_dir" != "X"; then + if test -f "$dir/$libname.la"; then + found_la="$dir/$libname.la" + fi + fi + ;; + esac + if test "X$found_dir" != "X"; then + break + fi + done + fi + if test "X$found_dir" != "X"; then + dnl Found the library. + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name" + if test "X$found_so" != "X"; then + dnl Linking with a shared library. We attempt to hardcode its + dnl directory into the executable's runpath, unless it's the + dnl standard /usr/lib. + if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then + dnl No hardcoding is needed. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + dnl Use an explicit option to hardcode DIR into the resulting + dnl binary. + dnl Potentially add DIR to ltrpathdirs. + dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $found_dir" + fi + dnl The hardcoding into $LIBNAME is system dependent. + if test "$acl_hardcode_direct" = yes; then + dnl Using DIR/libNAME.so during linking hardcodes DIR into the + dnl resulting binary. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then + dnl Use an explicit option to hardcode DIR into the resulting + dnl binary. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + dnl Potentially add DIR to rpathdirs. + dnl The rpathdirs will be appended to $LIBNAME at the end. + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $found_dir" + fi + else + dnl Rely on "-L$found_dir". + dnl But don't add it if it's already contained in the LDFLAGS + dnl or the already constructed $LIBNAME + haveit= + for x in $LDFLAGS $LIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir" + fi + if test "$acl_hardcode_minus_L" != no; then + dnl FIXME: Not sure whether we should use + dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" + dnl here. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + dnl We cannot use $acl_hardcode_runpath_var and LD_RUN_PATH + dnl here, because this doesn't fit in flags passed to the + dnl compiler. So give up. No hardcoding. This affects only + dnl very old systems. + dnl FIXME: Not sure whether we should use + dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" + dnl here. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" + fi + fi + fi + fi + else + if test "X$found_a" != "X"; then + dnl Linking with a static library. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a" + else + dnl We shouldn't come here, but anyway it's good to have a + dnl fallback. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name" + fi + fi + dnl Assume the include files are nearby. + additional_includedir= + case "$found_dir" in + */$acl_libdirstem | */$acl_libdirstem/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'` + LIB[]NAME[]_PREFIX="$basedir" + additional_includedir="$basedir/include" + ;; + esac + if test "X$additional_includedir" != "X"; then + dnl Potentially add $additional_includedir to $INCNAME. + dnl But don't add it + dnl 1. if it's the standard /usr/include, + dnl 2. if it's /usr/local/include and we are using GCC on Linux, + dnl 3. if it's already present in $CPPFLAGS or the already + dnl constructed $INCNAME, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + for x in $CPPFLAGS $INC[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + dnl Really add $additional_includedir to $INCNAME. + INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir" + fi + fi + fi + fi + fi + dnl Look for dependencies. + if test -n "$found_la"; then + dnl Read the .la file. It defines the variables + dnl dlname, library_names, old_library, dependency_libs, current, + dnl age, revision, installed, dlopen, dlpreopen, libdir. + save_libdir="$libdir" + case "$found_la" in + */* | *\\*) . "$found_la" ;; + *) . "./$found_la" ;; + esac + libdir="$save_libdir" + dnl We use only dependency_libs. + for dep in $dependency_libs; do + case "$dep" in + -L*) + additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` + dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME. + dnl But don't add it + dnl 1. if it's the standard /usr/lib, + dnl 2. if it's /usr/local/lib and we are using GCC on Linux, + dnl 3. if it's already present in $LDFLAGS or the already + dnl constructed $LIBNAME, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then + haveit= + if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + haveit= + for x in $LDFLAGS $LIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LIBNAME. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir" + fi + fi + haveit= + for x in $LDFLAGS $LTLIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LTLIBNAME. + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir" + fi + fi + fi + fi + ;; + -R*) + dir=`echo "X$dep" | sed -e 's/^X-R//'` + if test "$enable_rpath" != no; then + dnl Potentially add DIR to rpathdirs. + dnl The rpathdirs will be appended to $LIBNAME at the end. + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $dir" + fi + dnl Potentially add DIR to ltrpathdirs. + dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $dir" + fi + fi + ;; + -l*) + dnl Handle this in the next round. + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` + ;; + *.la) + dnl Handle this in the next round. Throw away the .la's + dnl directory; it is already contained in a preceding -L + dnl option. + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` + ;; + *) + dnl Most likely an immediate library name. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep" + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep" + ;; + esac + done + fi + else + dnl Didn't find the library; assume it is in the system directories + dnl known to the linker and runtime loader. (All the system + dnl directories known to the linker should also be known to the + dnl runtime loader, otherwise the system is severely misconfigured.) + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name" + fi + fi + fi + done + done + if test "X$rpathdirs" != "X"; then + if test -n "$acl_hardcode_libdir_separator"; then + dnl Weird platform: only the last -rpath option counts, the user must + dnl pass all path elements in one option. We can arrange that for a + dnl single library, but not when more than one $LIBNAMEs are used. + alldirs= + for found_dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir" + done + dnl Note: acl_hardcode_libdir_flag_spec uses $libdir and $wl. + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" + else + dnl The -rpath options are cumulative. + for found_dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$found_dir" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" + done + fi + fi + if test "X$ltrpathdirs" != "X"; then + dnl When using libtool, the option that works for both libraries and + dnl executables is -R. The -R options are cumulative. + for found_dir in $ltrpathdirs; do + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir" + done + fi +]) + +dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR, +dnl unless already present in VAR. +dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes +dnl contains two or three consecutive elements that belong together. +AC_DEFUN([AC_LIB_APPENDTOVAR], +[ + for element in [$2]; do + haveit= + for x in $[$1]; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X$element"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + [$1]="${[$1]}${[$1]:+ }$element" + fi + done +]) + +dnl For those cases where a variable contains several -L and -l options +dnl referring to unknown libraries and directories, this macro determines the +dnl necessary additional linker options for the runtime path. +dnl AC_LIB_LINKFLAGS_FROM_LIBS([LDADDVAR], [LIBSVALUE], [USE-LIBTOOL]) +dnl sets LDADDVAR to linker options needed together with LIBSVALUE. +dnl If USE-LIBTOOL evaluates to non-empty, linking with libtool is assumed, +dnl otherwise linking without libtool is assumed. +AC_DEFUN([AC_LIB_LINKFLAGS_FROM_LIBS], +[ + AC_REQUIRE([AC_LIB_RPATH]) + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + $1= + if test "$enable_rpath" != no; then + if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then + dnl Use an explicit option to hardcode directories into the resulting + dnl binary. + rpathdirs= + next= + for opt in $2; do + if test -n "$next"; then + dir="$next" + dnl No need to hardcode the standard /usr/lib. + if test "X$dir" != "X/usr/$acl_libdirstem"; then + rpathdirs="$rpathdirs $dir" + fi + next= + else + case $opt in + -L) next=yes ;; + -L*) dir=`echo "X$opt" | sed -e 's,^X-L,,'` + dnl No need to hardcode the standard /usr/lib. + if test "X$dir" != "X/usr/$acl_libdirstem"; then + rpathdirs="$rpathdirs $dir" + fi + next= ;; + *) next= ;; + esac + fi + done + if test "X$rpathdirs" != "X"; then + if test -n ""$3""; then + dnl libtool is used for linking. Use -R options. + for dir in $rpathdirs; do + $1="${$1}${$1:+ }-R$dir" + done + else + dnl The linker is used for linking directly. + if test -n "$acl_hardcode_libdir_separator"; then + dnl Weird platform: only the last -rpath option counts, the user + dnl must pass all path elements in one option. + alldirs= + for dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$dir" + done + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + $1="$flag" + else + dnl The -rpath options are cumulative. + for dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$dir" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + $1="${$1}${$1:+ }$flag" + done + fi + fi + fi + fi + fi + AC_SUBST([$1]) +]) diff --git a/m4/lib-prefix.m4 b/m4/lib-prefix.m4 new file mode 100644 index 00000000..a8684e17 --- /dev/null +++ b/m4/lib-prefix.m4 @@ -0,0 +1,185 @@ +# lib-prefix.m4 serial 5 (gettext-0.15) +dnl Copyright (C) 2001-2005 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and +dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't +dnl require excessive bracketing. +ifdef([AC_HELP_STRING], +[AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])], +[AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])]) + +dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed +dnl to access previously installed libraries. The basic assumption is that +dnl a user will want packages to use other packages he previously installed +dnl with the same --prefix option. +dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate +dnl libraries, but is otherwise very convenient. +AC_DEFUN([AC_LIB_PREFIX], +[ + AC_BEFORE([$0], [AC_LIB_LINKFLAGS]) + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + dnl By default, look in $includedir and $libdir. + use_additional=yes + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + AC_LIB_ARG_WITH([lib-prefix], +[ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib + --without-lib-prefix don't search for libraries in includedir and libdir], +[ + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + else + additional_includedir="$withval/include" + additional_libdir="$withval/$acl_libdirstem" + fi + fi +]) + if test $use_additional = yes; then + dnl Potentially add $additional_includedir to $CPPFLAGS. + dnl But don't add it + dnl 1. if it's the standard /usr/include, + dnl 2. if it's already present in $CPPFLAGS, + dnl 3. if it's /usr/local/include and we are using GCC on Linux, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + for x in $CPPFLAGS; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + dnl Really add $additional_includedir to $CPPFLAGS. + CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir" + fi + fi + fi + fi + dnl Potentially add $additional_libdir to $LDFLAGS. + dnl But don't add it + dnl 1. if it's the standard /usr/lib, + dnl 2. if it's already present in $LDFLAGS, + dnl 3. if it's /usr/local/lib and we are using GCC on Linux, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then + haveit= + for x in $LDFLAGS; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LDFLAGS. + LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir" + fi + fi + fi + fi + fi +]) + +dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix, +dnl acl_final_exec_prefix, containing the values to which $prefix and +dnl $exec_prefix will expand at the end of the configure script. +AC_DEFUN([AC_LIB_PREPARE_PREFIX], +[ + dnl Unfortunately, prefix and exec_prefix get only finally determined + dnl at the end of configure. + if test "X$prefix" = "XNONE"; then + acl_final_prefix="$ac_default_prefix" + else + acl_final_prefix="$prefix" + fi + if test "X$exec_prefix" = "XNONE"; then + acl_final_exec_prefix='${prefix}' + else + acl_final_exec_prefix="$exec_prefix" + fi + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" + prefix="$acl_save_prefix" +]) + +dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the +dnl variables prefix and exec_prefix bound to the values they will have +dnl at the end of the configure script. +AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], +[ + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + $1 + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" +]) + +dnl AC_LIB_PREPARE_MULTILIB creates a variable acl_libdirstem, containing +dnl the basename of the libdir, either "lib" or "lib64". +AC_DEFUN([AC_LIB_PREPARE_MULTILIB], +[ + dnl There is no formal standard regarding lib and lib64. The current + dnl practice is that on a system supporting 32-bit and 64-bit instruction + dnl sets or ABIs, 64-bit libraries go under $prefix/lib64 and 32-bit + dnl libraries go under $prefix/lib. We determine the compiler's default + dnl mode by looking at the compiler's library search path. If at least + dnl of its elements ends in /lib64 or points to a directory whose absolute + dnl pathname ends in /lib64, we assume a 64-bit ABI. Otherwise we use the + dnl default, namely "lib". + acl_libdirstem=lib + searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'` + if test -n "$searchpath"; then + acl_save_IFS="${IFS= }"; IFS=":" + for searchdir in $searchpath; do + if test -d "$searchdir"; then + case "$searchdir" in + */lib64/ | */lib64 ) acl_libdirstem=lib64 ;; + *) searchdir=`cd "$searchdir" && pwd` + case "$searchdir" in + */lib64 ) acl_libdirstem=lib64 ;; + esac ;; + esac + fi + done + IFS="$acl_save_IFS" + fi +]) diff --git a/m4/pkg.m4 b/m4/pkg.m4 new file mode 100644 index 00000000..a0b9cd45 --- /dev/null +++ b/m4/pkg.m4 @@ -0,0 +1,155 @@ +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# +# Copyright © 2004 Scott James Remnant . +# +# 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. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi + +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# +# Similar to PKG_CHECK_MODULES, make sure that the first instance of +# this or PKG_CHECK_MODULES is called, or make sure to call +# PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_ifval([$2], [$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + ifelse([$4], , [AC_MSG_ERROR(dnl +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT +])], + [AC_MSG_RESULT([no]) + $4]) +elif test $pkg_failed = untried; then + ifelse([$4], , [AC_MSG_FAILURE(dnl +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])], + [$4]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + ifelse([$3], , :, [$3]) +fi[]dnl +])# PKG_CHECK_MODULES diff --git a/misc/libcdio.ebuild b/misc/libcdio.ebuild new file mode 100644 index 00000000..3c2c67d3 --- /dev/null +++ b/misc/libcdio.ebuild @@ -0,0 +1,24 @@ +# Copyright 1999-2003 Gentoo Technologies, Inc. +# Distributed under the terms of the GNU General Public License v2 +# $Header: /sources/libcdio/libcdio/misc/libcdio.ebuild,v 1.1 2003/04/29 08:17:59 rocky Exp $ + +IUSE="" + +DESCRIPTION="A library to encapsulate CD-ROM reading and control." +HOMEPAGE="http://savannah.nongnu.org/projects/libcdio/" +SRC_URI="http://savannah.nongnu.org/download/${PN}/${PN}.pkg/${PV}/${P}.tar.gz" +LICENSE="GPL" + +SLOT="0" +KEYWORDS="x86" + + +src_compile() { + econf || die + emake || die +} + +src_install() { + make DESTDIR=${D} install + dodoc AUTHORS COPYING Changelog INSTALL NEWS README THANKS +} diff --git a/package/.gitignore b/package/.gitignore new file mode 100644 index 00000000..e8c20f97 --- /dev/null +++ b/package/.gitignore @@ -0,0 +1 @@ +/libcdio.spec diff --git a/package/libcdio.spec.in b/package/libcdio.spec.in new file mode 100644 index 00000000..e6aa2783 --- /dev/null +++ b/package/libcdio.spec.in @@ -0,0 +1,137 @@ +### $Id: libcdio.spec.in,v 1.2 2006/03/28 14:11:56 rocky Exp $ +### autogenerated---edit *.spec.in + +%define name @PACKAGE@ +%define ver @VERSION@ +%define rel 0 + +Name: %{name} +Summary: CD-ROM access library +Summary(de): CD-ROM Zugriffsbibliothek +Version: %{ver} +Release: %{rel} +Copyright: GPL +Group: Development/Libraries +URL: http://www.gnu.org/software/libcdio/ + +Source0: %{name}-%{version}.tar.gz + +Packager: Manfred Tremmel +Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root + +%description +The libcdio package contains a library for CD-ROM and CD image +access. Applications wishing to be oblivious of the OS- and +device-dependent properties of a CD-ROM or of the specific details of +various CD-image formats may benefit from using this library. + +A library for working with ISO-9660 filesystems libiso9660 is +included. A generic interface for issuing MMC (multimedia commands) is +also part of the libcdio library. + +%description -l de +Diese Bibliothek dient zur Kapselung von CD-ROM Zugriffen und +dessen Kontrolle. Anwendungen brauchen sich nicht um die +Betriebssystemspezifischen oder Device-Abhängigen Eigenschaften +des CD-ROM zu kümmern, dies erledigt die Bibliothek. + +%package devel +Summary: libcdio development package +Summary(de): libcdio Entwicklerpaket +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} + +%description devel +libcdio development package + +%description devel -l de +libcdio Entwicklerpaket + +%package -n cdinfo +Summary: cd-info +Summary(de): cd-info +Group: Applications/Multimedia +Requires: %{name} = %{version}-%{release} +#Requires: libvcd >= 0.7.20 +Requires: libcddb +BuildRequires: libcddb-devel +#BuildRequires: libvcd-devel >= 0.7.20 + +%description -n cdinfo + +cd-info prints various information about a CD or CD image, analyzes +and gives information about each of the tracks, and tries to detect +the type of CD (e.g. VCD, Audio CD, PhotoCD, a bootable CD, etc.). For +audio CD's more information is given if libcddb is installed. For +Video CD's more information is given if the libvcdinfo library is +installed. + + +%description -n cdinfo -l de + +cd-info gibt verschiedene informationen über eine CD oder ein CD-Image +aus, analysiert und informiert über jeden der Tracks und versucht, den +Typ der CD zu ermitteln (z.B. VCD, Audio-CD, PhotoCD, eine bootbare CD +usw.). +Zu Audio-CDs werden weitere Informationen ausgegeben, wenn die libcddb +installiert ist. Zu Video-CDs werden mehr Informationen ausgegeben, wenn +die libvcdinfo installiert ist. + +%prep +%setup + +%build +%configure +%__make + +%install +[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT +%makeinstall +rm -rf ${RPM_BUILD_ROOT}/%{_mandir} + +%clean +[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT + +%post +/sbin/ldconfig + +%postun +/sbin/ldconfig + +%files +%defattr(-,root,root) +%doc AUTHORS INSTALL NEWS THANKS TODO +%{_libdir}/lib*.so.* + +%files devel +%defattr(-, root, root) +%doc ChangeLog COPYING README +%{_libdir}/lib*.so +%{_libdir}/*.*a +%{_includedir}/cdio/* +%{_infodir}/* +%{_libdir}/pkgconfig/*.pc + +%files -n cdinfo +%defattr(-,root,root) +%{_bindir}/* + +%changelog +* Sun Feb 15 2004 Manfred Tremmel +- some little changes + +* Sat Feb 14 2004 Rocky Bernstein +- require vcdimager 0.7.20, small rpm fixes + +* Tue Sep 9 2003 Rocky Bernstein +- small fixes really from Frantisek Dvorak + +* Sun Aug 30 2003 Frantisek Dvorak +- two files added + +* Wed Aug 06 2003 Rocky Bernstein +- fixes really Manfred Tremmel + at http://www.iiv.de/schwinde/buerger/tremmel/ + +* Fri Apr 25 2003 Manfred Tremmel +- first spec file diff --git a/parse/Makefile b/parse/Makefile new file mode 100644 index 00000000..d58c18bf --- /dev/null +++ b/parse/Makefile @@ -0,0 +1,64 @@ +# $Id: Makefile,v 1.5 2008/03/23 17:21:34 karl Exp $ +# +# Copyright (C) 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +INCLUDES = -I . +all: cueparser cuelexer tocparser + +lex.cue.c: cue.L cue.tab.h + flex -Pcue cue.L + +lex.cuelex.c: cue.L cue.tab.h + flex -Pcuelex cue.L + +lex.cue.o: lex.cue.c + gcc -g -Wall -c lex.cue.c $(INCLUDES) + +lex.cuelex.o: lex.cuelex.c + gcc -g -DSTANDALONE -Wall -c lex.cuelex.c + +cue.tab.c, cue.tab.h: cue.y + bison -p cue -d cue.y + +cue.tab.o: cue.tab.c cue.tab.h + gcc -g -Wall -DSTANDALONE -c cue.tab.c $(INCLUDES) + +cueparser: lex.cue.o cue.tab.o + gcc -g lex.cue.o cue.tab.o -lfl -o cueparser + +cuelexer: lex.cuelex.o + gcc -g lex.cuelex.o -lfl -o cuelexer + +toc.tab.h: toc.tab.c + +toc.tab.c: toc.y + bison -p toc -d toc.y + +toclexer.o: toclexer.c + gcc -g -Wall -c toclexer.c $(INCLUDES) + +toc.tab.o: toc.tab.c toc.tab.h + gcc -g -Wall -DSTANDALONE -c toc.tab.c $(INCLUDES) + +tocparser: toc.tab.o toclexer.o + gcc -g toclexer.o toc.tab.o -o tocparser + +clean: + rm -f lex.cue.c lex.cuelex.c lex.cue.o lex.cuelex.o cue.tab.c \ + cue.tab.o cueparser cuelexer + +check: + (cd test && ./runall) diff --git a/parse/cue.L b/parse/cue.L new file mode 100644 index 00000000..814c56e4 --- /dev/null +++ b/parse/cue.L @@ -0,0 +1,239 @@ +/* $Id: cue.L,v 1.2 2008/03/23 17:21:34 karl Exp $ -*- C -*- */ +/* CUE-sheet scanner + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +%{ +#undef yywrap +#ifdef STANDALONE +#include +#endif + +#include "cue.tab.h" + + static int debug_lex=0; +%} +%x filename + +TrackDef "TRACK" +Audio "AUDIO" +Mode1_2048 "MODE1/2048" +Mode1_2352 "MODE1/2352" +Mode2_2336 "MODE2/2336" +Mode2_2352 "MODE2/2352" +Index "INDEX" +File "FILE" +Pregap "PREGAP" +Postgap "POSTGAP" +Binary "BINARY" +Motorola "MOTOROLA" +Flags "FLAGS" +Catalog "CATALOG" +Isrc "ISRC" +Four_Channel "4CH" +Dont_Copy "DCP" +Pre_Emphasis "PRE" +Colon ":" +Integer [[:digit:]]+ +Spaces [[:blank:]\n\r]+ +String \".+\" +Filename [^[:blank:]\n\r]+ +%% +{TrackDef} { + /*"*/ + return TRACK_TOKEN; +} +{Audio} { + return AUDIO_TOKEN; +} + +{Mode1_2048} { + return MODE1_2048_TOKEN; +} + +{Mode1_2352} { + return MODE1_2352_TOKEN; +} + +{Mode2_2336} { + return MODE2_2336_TOKEN; +} + +{Mode2_2352} { + return MODE2_2352_TOKEN; +} + +{Index} { + return INDEX_TOKEN; +} + +{File} { + BEGIN(filename); + return FILE_TOKEN; +} + +{Pregap} { + return PREGAP_TOKEN; +} + +{Postgap} { + return POSTGAP_TOKEN; +} + +{Binary} { + return BINARY_TOKEN; +} + +{Motorola} { + return MOTOROLA_TOKEN; +} + +{Flags} { + return FLAGS_TOKEN; +} + +{Catalog} { + return CATALOG_TOKEN; +} + +{Isrc} { + if (debug_lex) printf("Isrc token\n"); +} + +{Four_Channel} { + return FOURCH_TOKEN; +} + +{Dont_Copy} { + return DCP_TOKEN; +} + +{Pre_Emphasis} { + if (debug_lex) printf("Pre_Emphasis token\n"); +} + +{Colon} { + return COLON_TOKEN; +} + +{Integer} { + return INTEGER_TOKEN; +} + +{Filename} { + BEGIN(INITIAL); + return FILENAME_TOKEN; +} + +{String} { + return STRING_TOKEN; +} + +{Spaces} { + return SPACES_TOKEN; +} + +<> { + return EOF; +} + + +%% + +#if STANDALONE +int +main( int argc, const char **argv ) +{ + int token; + + ++argv, --argc; /* skip over program name */ + debug_lex = 1; + if ( argc > 0 ) + yyin = fopen( argv[0], "r" ); + else + yyin = stdin; + + while ((token=yylex()) != EOF) { + switch (token) { + case TRACK_TOKEN: + printf("TRACK\n"); + break; + case AUDIO_TOKEN: + printf("AUDIO token\n"); + break; + case MODE1_2048_TOKEN: + printf("MODE1/2048\n"); + break; + case MODE1_2352_TOKEN: + printf("MODE1/2352\n"); + break; + case MODE2_2336_TOKEN: + printf("MODE1/2336\n"); + break; + case MODE2_2352_TOKEN: + printf("MODE2/2352\n"); + break; + case INDEX_TOKEN: + printf("INDEX\n"); + break; + case FILE_TOKEN: + printf("FILE\n"); + break; + case PREGAP_TOKEN: + printf("PREGAP\n"); + break; + case POSTGAP_TOKEN: + printf("POSTGAP\n"); + break; + case BINARY_TOKEN: + printf("BINARY\n"); + break; + case SPACES_TOKEN: + printf("spaces\n"); + break; + case STRING_TOKEN: + printf("string: %s\n", yytext); + break; + case INTEGER_TOKEN: + printf("integer: %d (%s)\n", atoi(yytext), yytext); + break; + case COLON_TOKEN: + printf(":\n"); + break; + case FLAGS_TOKEN: + printf("FLAGS\n"); + break; + case CATALOG_TOKEN: + printf("CATALOG\n"); + break; + case DCP_TOKEN: + printf("DCP\n"); + break; + case MOTOROLA_TOKEN: + printf("MOTOROLA\n"); + break; + case FOURCH_TOKEN: + printf("4CH\n"); + break; + case FILENAME_TOKEN: + printf("filename %s\n", yytext); + break; + default: ; + } + } + return 0; +} +#endif diff --git a/parse/cue.y b/parse/cue.y new file mode 100644 index 00000000..76c11c5e --- /dev/null +++ b/parse/cue.y @@ -0,0 +1,183 @@ +%{ +/* $Id: cue.y,v 1.2 2008/03/23 17:21:34 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +extern FILE *cuein; +int cuelex(void); +int cueerror(char *s); + +#ifdef STANDALONE +#include +#define YYDEBUG 1 +#endif + +%} + +/* BISON Declarations */ +%token TRACK_TOKEN +%token AUDIO_TOKEN +%token MODE1_2048_TOKEN +%token MODE1_2352_TOKEN +%token MODE2_2336_TOKEN +%token MODE2_2352_TOKEN +%token INDEX_TOKEN +%token FILE_TOKEN +%token PREGAP_TOKEN +%token POSTGAP_TOKEN +%token BINARY_TOKEN +%token MOTOROLA_TOKEN +%token FLAGS_TOKEN +%token CATALOG_TOKEN +%token ISRC_TOKEN +%token FOURCH_TOKEN +%token DCP_TOKEN +%token PRE_TOKEN +%token COLON_TOKEN +%token INTEGER_TOKEN +%token SPACES_TOKEN +%token STRING_TOKEN +%token FILENAME_TOKEN + + /* Grammar follows */ +%% + + /* We optionally allow spaces at the end of the file. + */ +cue: cue_stmts opt_spaces ; + +/* one or more cuesheet statements. We allow spaces before the beginning + of the cuesheet statement */ +cue_stmts: cue_stmts SPACES_TOKEN cue_stmt | opt_spaces cue_stmt ; + +cue_stmt: catalog_stmt + | file_stmt + | track_stmt + | flags_stmt + | pregap_stmt + | postgap_stmt + | index_stmt + ; + + +/* FILE *filename* {BINARY|MOTOROLA|AUDIO} */ +file_stmt: FILE_TOKEN SPACES_TOKEN file_id SPACES_TOKEN file_flags +; + +file_id: FILENAME_TOKEN | STRING_TOKEN +; + + +file_flags: BINARY_TOKEN | MOTOROLA_TOKEN | AUDIO_TOKEN +; + +/* CATALOG *n* */ +catalog_stmt: CATALOG_TOKEN INTEGER_TOKEN +; + +/* TRACK *n* {MODE1/2048 | MODE1/2352 | MODE2/2336 | MODE2/2352} */ +track_stmt: TRACK_TOKEN SPACES_TOKEN INTEGER_TOKEN SPACES_TOKEN track_mode_exp +; + +track_mode_exp: MODE1_2048_TOKEN + | MODE1_2352_TOKEN + | MODE2_2336_TOKEN + | MODE2_2352_TOKEN +; + +/* FLAGS {DCP | 4CH | PRE} */ +flags_stmt: FLAGS_TOKEN SPACES_TOKEN flags_exp +; + +flags_exp: DCP_TOKEN | FOURCH_TOKEN | PRE_TOKEN +; + +/* PREGAP dd:dd:ddx */ +pregap_stmt: PREGAP_TOKEN SPACES_TOKEN msf_exp +; + +/* PROSTGAP dd:dd:dd */ +postgap_stmt: POSTGAP_TOKEN SPACES_TOKEN msf_exp +; + +/* INDEX *n* dd:dd:dd */ +index_stmt: INDEX_TOKEN SPACES_TOKEN INTEGER_TOKEN SPACES_TOKEN msf_exp +; + +/* a MSF e.g. 00:00:00 or 02:00 */ +msf_exp: INTEGER_TOKEN COLON_TOKEN INTEGER_TOKEN COLON_TOKEN INTEGER_TOKEN + | INTEGER_TOKEN COLON_TOKEN INTEGER_TOKEN +; + + +opt_spaces: SPACES_TOKEN | ; + +%% + +#ifdef STANDALONE +/* The controlling function */ + +int +cueerror(char *s) /* called by cueparse on error */ +{ + printf("%s\n",s); + return(0); +} + +int +main( int argc, char **argv ) +{ + int c; + + cuedebug = 0; + + while (1) { + int option_index = 0; + static struct option long_options[] = { + {"debug", 0, 0, 'd'}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "d", long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'd': + cuedebug = 1; + break; + default: + printf ("?? getopt returned character code 0%o ??\n", c); + exit(1); + } + } + + if ( optind < argc ) + cuein = fopen( argv[optind], "r" ); + else + cuein = stdin; + + if (cueparse()==0) { + printf("Is a CUE file\n"); + } else { + printf("Isn't a CUE file\n"); + } + exit(0); +} +#endif /* STANDALONE*/ diff --git a/parse/test/runall b/parse/test/runall new file mode 100755 index 00000000..c2e0492f --- /dev/null +++ b/parse/test/runall @@ -0,0 +1,13 @@ +#!/usr/bin/perl -w +use strict; +my @tests; + +if (@ARGV) { + @tests = @ARGV; +} else { + @tests=qw(t1.cue t2.cue t3.cue); +} + +foreach my $cue_file (@tests) { + system("../cueparser $cue_file"); +} diff --git a/parse/test/t1.cue b/parse/test/t1.cue new file mode 100644 index 00000000..7d63589a --- /dev/null +++ b/parse/test/t1.cue @@ -0,0 +1,9 @@ +FILE "t1.bin" BINARY + TRACK 01 MODE2/2352 + FLAGS DCP + INDEX 01 00:00:00 + TRACK 02 MODE2/2352 + FLAGS DCP + INDEX 00 00:04:00 + INDEX 01 00:06:00 + INDEX 02 00:06:32 diff --git a/parse/test/t2.cue b/parse/test/t2.cue new file mode 100644 index 00000000..2c605579 --- /dev/null +++ b/parse/test/t2.cue @@ -0,0 +1,10 @@ + + +FILE t2.bin AUDIO +TRACK 01 MODE1/2352 + FLAGS DCP INDEX 01 00:00:00 + TRACK 02 MODE1/2048 + FLAGS DCP + INDEX 00 00:04:00 + INDEX 01 00:06:00 + INDEX 02 00:06:32 diff --git a/parse/test/t3.cue b/parse/test/t3.cue new file mode 100644 index 00000000..4c332144 --- /dev/null +++ b/parse/test/t3.cue @@ -0,0 +1,7 @@ +FILE BINARY BINARY + TRACK 01 MODE2/2352 + FLAGS DCP + INDEX 01 00:00:00 + TRACK 02 MODE2/2352 + FLAGS DCP +INDEX 00 00:04:00 INDEX 01 00:06:93 INDEX 02 00:61:32 diff --git a/parse/toc.L b/parse/toc.L new file mode 100644 index 00000000..945d6b27 --- /dev/null +++ b/parse/toc.L @@ -0,0 +1,91 @@ +/* $Id: toc.L,v 1.2 2008/03/23 17:21:34 karl Exp $ -*- C -*- */ +/* cdrdao TOC scanner + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +%{ +#undef yywrap +#ifdef STANDALONE +#include +#endif + +#include "toc.tab.h" + + static int debug_lex=0; +%} +%union { + unsigned long int val; /* For returning numbers. */ + char const * str; /* For returning stringss. */ +} + + +integer [[:digit:]]+ + +/* The below isn't octal. I'm just going by the name and pattern + in the cdrdao pacct grammar. */ +stringoctal [[:digit:]]{3} + +spaces [[:blank:]\n\r]+ + +# Need to expand this to include "octal" \000 and embedded quotes. +string \".+\" +%% +{integer} { + /*"*/ + return Integer; +} + +{stringoctal} { + /*"*/ + return StringOctal; +} + +{spaces} { + return Spaces; +} + +%% + +#if STANDALONE +int +main( int argc, const char **argv ) +{ + int token; + + ++argv, --argc; /* skip over program name */ + debug_lex = 1; + if ( argc > 0 ) + yyin = fopen( argv[0], "r" ); + else + yyin = stdin; + + while ((token=yylex()) != EOF) { + switch (token) { + case StringOctal: + printf("StringOctal\n"); + break; + case Spaces: + printf("Spaces\n"); + break; + case Integer: + printf("Integer\n"); + break; + default: ; + } + } + return 0; +} +#endif diff --git a/parse/toc.y b/parse/toc.y new file mode 100644 index 00000000..967ed089 --- /dev/null +++ b/parse/toc.y @@ -0,0 +1,296 @@ +/* + $Id: toc.y,v 1.3 2008/03/23 17:21:34 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* Yacc grammer for cdrdao TOC file */ +%{ +#include "toclexer.h" +#include "errno.h" +FILE *toc_in; +int tocerror (char const *s); + +#ifdef STANDALONE +#include +#define YYDEBUG 1 +#endif + +%} + +/* BISON Declarations */ + +%token ARRANGER +%token AUDIO +%token AUDIOFILE +%token CATALOG +%token CD_DA +%token CD_I +%token CD_ROM +%token CD_ROM_XA +%token CD_TEXT +%token COMPOSER +%token COPY +%token DATAFILE +%token DISC_ID +%token EN +%token END +%token FIFO +%token FILE_TOKEN +%token FOUR_CHANNEL_AUDIO +%token GENRE +%token INDEX +%token ISRC +%token LANGUAGE +%token LANGUAGE_MAP +%token MESSAGE +%token MODE0 +%token MODE1 +%token MODE1_RAW +%token MODE2 +%token MODE2_FORM1 +%token MODE2_FORM2 +%token MODE2_FORM_MIX +%token MODE2_RAW +%token NO +%token PERFORMER +%token PREGAP +%token PRE_EMPHASIS +%token RESERVED1 +%token RESERVED2 +%token RESERVED3 +%token RESERVED4 +%token RW +%token RW_RAW +%token SILENCE +%token SIZE_INFO +%token SONGWRITER +%token START +%token SWAP +%token TITLE +%token TOC_INFO1 +%token TOC_INFO2 +%token TRACK +%token TWO_CHANNEL_AUDIO +%token UPC_EAN +%token ZERO + +%token LeftBrace /* "{" */ +%token RightBrace /* "}" */ +%token Colon /* ":" */ +%token Error /* Error token return */ +%token Id /* Id but not one of the above keywords */ +%token Integer +%token String + +%union { + long unsigned int val; /* For returning numbers. */ + symrec *tptr; /* For returning symbol-table pointers. */ +} + +/* Grammar follows */ +%% + +/* We optionally allow spaces at the end of the TOC file. + */ +toc: catalog_or_tocType cdTextGlobal tracks ; + +catalog_or_tocType: catalog_or_tocType CATALOG String + | catalog_or_tocType tocType + | /* empty */ ; + +tracks: tracks track | track ; + +track: TRACK trackMode opt_subChannelMode opt_track_flags + cdTextTrack opt_pregap_msf subTracks_or_starts_or_ends opt_index_msfs + ; + +opt_track_flags: opt_track_flags track_flag + | /* empty */; + +track_flag: ISRC String + | opt_no COPY + | opt_no PRE_EMPHASIS + | TWO_CHANNEL_AUDIO + | FOUR_CHANNEL_AUDIO ; + +opt_no: NO + | /* empty */; + +opt_pregap_msf: PREGAP msf + | /* empty */; + +opt_index_msfs: opt_index_msfs INDEX msf + | /* empty */ ; + +subTrack_or_start_or_end: subTrack + | START opt_msf + | END opt_msf ; + +subTracks_or_starts_or_ends: subTracks_or_starts_or_ends + subTrack_or_start_or_end + | subTrack_or_start_or_end ; + +subTrack: + AudioFile String opt_swap opt_start_offset samples + | DATAFILE String opt_start_length + | FIFO String dataLength + | SILENCE samples + | ZERO opt_dataMode opt_subChannelMode dataLength + ; + +AudioFile: AUDIOFILE | FILE_TOKEN ; + +opt_swap: SWAP + | /* empty */; + +opt_start_offset: "#" sLong + | /* empty */; + +opt_start_length: "#" sLong + | '#' sLong dataLength + | /* empty */; + +opt_dataMode: dataMode + | /* empty */ ; + +opt_string: String + | /* empty */ ; + +uLong: Integer ; + +sLong: Integer ; + +msf: Integer Colon Integer Colon Integer ; + +opt_msf: msf | /* empty */ ; + +samples: msf | uLong ; + +dataLength: msf | uLong ; + +dataMode: AUDIO | MODE0 | MODE1 | MODE1_RAW | MODE2 + | MODE2_RAW | MODE2_FORM1 | MODE2_FORM2 | MODE2_FORM_MIX + ; + + +trackMode: AUDIO | MODE1 | MODE1_RAW | MODE2 + | MODE2_RAW | MODE2_FORM1 | MODE2_FORM2 | MODE2_FORM_MIX + ; + + +opt_subChannelMode: RW | RW_RAW + | /* empty */; + +tocType: CD_DA | CD_ROM | CD_ROM_XA | CD_I ; + +packType: TITLE | PERFORMER | SONGWRITER | COMPOSER | ARRANGER + | MESSAGE | DISC_ID | GENRE | TOC_INFO1 | TOC_INFO2 + | RESERVED1 | RESERVED2 | RESERVED3 | RESERVED4 | UPC_EAN + | ISRC | SIZE_INFO ; + + +binaryData: LeftBrace Integers RightBrace ; + +Integers: Integers "," Integer | Integer ; + + +cdTextItem: packType opt_string_or_binaryData | ; + +opt_string_or_binaryData: opt_string | binaryData ; + +cdTextBlock: LANGUAGE Integer LeftBrace cdTextItem RightBrace ; + +opt_cdTextBlock: cdTextBlock + | /* empty */; + +opt_cdTextBlocks: opt_cdTextBlocks cdTextBlock + | /* empty */ ; + +opt_cdTextLanguageMap : LANGUAGE_MAP LeftBrace Language_mappings RightBrace + | /* empty */; + +Language_mappings: Language_mappings Language_mapping | Language_mapping ; + +Language_mapping: Integer ":" Language_id ; + +Language_id: Integer | EN; + +cdTextTrack: CD_TEXT LeftBrace opt_cdTextBlocks RightBrace | ; + +cdTextGlobal: CD_TEXT LeftBrace opt_cdTextLanguageMap opt_cdTextBlock + RightBrace + | /* empty */; + + +%% + +#ifdef STANDALONE +/* The controlling function */ + +int +tocerror(char const *s) /* called by tocparse on error */ +{ + printf("%s\n",s); + return(0); +} + +int +main( int argc, char **argv ) +{ + int c; + + tocdebug = 0; + + while (1) { + int option_index = 0; + static struct option long_options[] = { + {"debug", 0, 0, 'd'}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "d", long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'd': + tocdebug = 1; + break; + default: + printf ("?? getopt returned character code 0%o ??\n", c); + exit(1); + } + } + + if ( optind < argc ) { + toc_in = fopen( argv[optind], "r" ); + if (!toc_in) { + printf("unable to open %s for reading: %s\n", argv[optind], + strerror(errno)); + exit(1); + } + } else + toc_in = stdin; + + + if (tocparse()==0) { + printf("Is a TOC file\n"); + } else { + printf("Isn't a TOC file\n"); + } + exit(0); +} +#endif /* STANDALONE*/ diff --git a/parse/toclexer.c b/parse/toclexer.c new file mode 100644 index 00000000..c49994b9 --- /dev/null +++ b/parse/toclexer.c @@ -0,0 +1,197 @@ +/* + $Id: toclexer.c,v 1.2 2008/03/23 17:21:34 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Lexical scanner for cdrdao's TOC. */ +#include "toclexer.h" +#include "toc.tab.h" +#include + +#define YYEOF 0 + +/* A structure for associating a word with a token. */ +typedef struct keyword_s +{ + char const *psz_keyword; + token_t i_token; +} keyword_t; + +/* These are all of the words that might appear in a TOC file and + the token association that the parser will use. +*/ +const keyword_t keywords[] = + { + {"ARRANGER", ARRANGER}, + {"AUDIO", AUDIO}, + {"AUDIOFILE", AUDIOFILE}, + {"CATALOG", CATALOG}, + {"CD_DA", CD_DA}, + {"CD_I", CD_I}, + {"CD_ROM", CD_ROM}, + {"CD_ROM_XA", CD_ROM_XA}, + {"CD_TEXT", CD_TEXT}, + {"COMPOSER", COMPOSER}, + {"COPY", COPY}, + {"DATAFILE", DATAFILE}, + {"DISC_ID", DISC_ID}, + {"EN", EN}, + {"END", END}, + {"FIFO", FIFO}, + {"FILE", FILE_TOKEN}, + {"FOUR_CHANNEL_AUDIO", FOUR_CHANNEL_AUDIO}, + {"GENRE", GENRE}, + {"INDEX", INDEX}, + {"ISRC", ISRC}, + {"LANGUAGE", LANGUAGE}, + {"LANGUAGE_MAP", LANGUAGE_MAP}, + {"MESSAGE", MESSAGE}, + {"MODE0", MODE0}, + {"MODE1", MODE1}, + {"MODE1_RAW", MODE1_RAW}, + {"MODE2", MODE2}, + {"MODE2_FORM1", MODE2_FORM1}, + {"MODE2_FORM2", MODE2_FORM2}, + {"MODE2_FORM_MIX", MODE2_FORM_MIX}, + {"MODE2_RAW", MODE2_RAW}, + {"NO", NO}, + {"PERFORMER", PERFORMER}, + {"PREGAP", PREGAP}, + {"PRE_EMPHASIS", PRE_EMPHASIS}, + {"RESERVED1", RESERVED1}, + {"RESERVED2", RESERVED2}, + {"RESERVED3", RESERVED3}, + {"RESERVED4", RESERVED4}, + {"RW", RW}, + {"RW_RAW", RW_RAW}, + {"SILENCE", SILENCE}, + {"SIZE_INFO", SIZE_INFO}, + {"SONGWRITER", SONGWRITER}, + {"START", START}, + {"SWAP", SWAP}, + {"TITLE", TITLE}, + {"TOC_INFO1", TOC_INFO1}, + {"TOC_INFO2", TOC_INFO2}, + {"TRACK", TRACK}, + {"TWO_CHANNEL_AUDIO", TWO_CHANNEL_AUDIO}, + {"UPC_EAN", UPC_EAN}, + {"ZERO", ZERO}, + {0, 0} + }; + +static int +compare_keyword(const void *p_id, const void *p_keyword) { + char *psz_id = (char *) p_id; + char const *psz_keyword = ((keyword_t *) p_keyword)->psz_keyword; + return strcmp(psz_id, psz_keyword); +} + +token_t +toclex (void) +{ + int c; + + start: + /* Skip white space. */ + while ( isspace(c = fgetc (toc_in)) ) + ; + + /* Process a number. */ + if (isdigit (c)) + { + ungetc (c, toc_in); + fscanf (toc_in, "%lu", &(toclval.val)); + return Integer; + } + + /* Process a comment. */ + if ( '/' == c ) { + if ('/' == (c = fgetc (toc_in)) ) { + while ((c = fgetc (toc_in)) != EOF && c != '\n') + ; + + /* Return end-of-input. */ + if (EOF == c) return YYEOF; + goto start; + } + /* Not a comment. So put back the character after the '/' and + return '/' */ + ungetc (c, toc_in); + return '/'; + } + + /* Char starts an identifier => read the name. */ + if (isalpha (c)) + { + static char symbol[50] = ""; + unsigned int i; + + i = 0; + do + { + /* Add this character to the buffer. */ + symbol[i++] = c; + /* Get another character. */ + c = fgetc (toc_in); + } + while (isgraph (c)); + + ungetc (c, toc_in); + symbol[i] = '\0'; + + toclval.psz_str = symbol; + + { + keyword_t *p_keyword; + p_keyword = bsearch(symbol, keywords, + (sizeof(keywords) / sizeof(keyword_t)) - 1, + sizeof(keyword_t), compare_keyword); + if (!p_keyword) return Id; + return p_keyword->i_token; + } + + } + + /* Process a string. + To do: save the value of the string and process octal numbers. + */ + if ( '"' == c ) { + int b_backslash = 0; + while ( EOF != (c = fgetc (toc_in)) + && (b_backslash || '"' != c ) ) { + b_backslash = ('\\' == c ); + } + + /* Return end-of-input. */ + if (EOF == c) return YYEOF; + + return String; + } + + /* Return end-of-input. */ + if (EOF == c) return YYEOF; + + switch (c) { + case ':': return Colon; + case '{': return LeftBrace; + case '}': return RightBrace; + default: + /* Return a single char. */ + return c; + } + +} diff --git a/parse/toclexer.h b/parse/toclexer.h new file mode 100644 index 00000000..26150558 --- /dev/null +++ b/parse/toclexer.h @@ -0,0 +1,43 @@ +/* + $Id: toclexer.h,v 1.2 2008/03/23 17:21:34 karl Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Common header between TOC lexer and parser. */ +#include +#include +#include "string.h" + +typedef int token_t; + +extern FILE *toc_in; + +typedef union { + long unsigned int val; /* For returning numbers. */ + char const *psz_str; /* For strings. */ +} tocval_t; + +#define YYSTYPE tocval_t + +YYSTYPE toclval; + +/* Call to the TOC scanner */ +token_t toclex (void); + + + + diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 00000000..f53478a0 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,3 @@ +/.deps +/Makefile +/Makefile.in diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..f9f7befd --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,95 @@ +# $Id: Makefile.am,v 1.48 2008/08/31 13:38:22 flameeyes Exp $ +# +# Copyright (C) 2003, 2004, 2006, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +GETOPT_C = getopt.c getopt1.c + +noinst_HEADERS = cddb.h getopt.h util.h + +#################################################### +# Things to make the utility/diagnostic programs +#################################################### + +if BUILD_CD_PARANOIA +SUBDIRS = cd-paranoia +endif + +CDDB_LIBS=@CDDB_LIBS@ +CDDA_PLAYER_LIBS=@CDDA_PLAYER_LIBS@ + +if BUILD_CDDA_PLAYER +cdda_player_SOURCES = cdda-player.c cddb.c cddb.h $(GETOPT_C) +cdda_player_LDADD = $(LIBCDIO_LIBS) $(CDDB_LIBS) $(CDDA_PLAYER_LIBS) +bin_cdda_player = cdda-player +endif + +if BUILD_CD_DRIVE +cd_drive_SOURCES = cd-drive.c util.c util.h $(GETOPT_C) +cd_drive_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +bin_cd_drive = cd-drive +man_cd_drive = cd-drive.1 +endif + +if BUILD_CDINFO +cd_info_SOURCES = cd-info.c cddb.c cddb.h util.c util.h $(GETOPT_C) +cd_info_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(CDDB_LIBS) $(VCDINFO_LIBS) $(LTLIBICONV) +bin_cd_info = cd-info +man_cd_info = cd-info.1 +endif + +if BUILD_CDINFO_LINUX +cdinfo_linux_SOURCES = cdinfo-linux.c +cdinfo_linux_LDADD = $(LIBCDIO_LIBS) +bin_cdinfo_linux = cdinfo-linux +endif + +if BUILD_CD_READ +cd_read_SOURCES = cd-read.c util.c util.h $(GETOPT_C) +cd_read_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +bin_cd_read = cd-read +man_cd_read = cd-read.1 +endif + +if BUILD_ISO_INFO +iso_info_SOURCES = iso-info.c util.c util.h $(GETOPT_C) +iso_info_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +bin_iso_info = iso-info +man_iso_info = iso-info.1 +endif + +if BUILD_ISO_READ +iso_read_SOURCES = iso-read.c util.c util.h $(GETOPT_C) +iso_read_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +bin_iso_read = iso-read +man_iso_read = iso-read.1 +endif + +mmc_tool_SOURCES = mmc-tool.c util.c util.h $(GETOPT_C) +mmc_tool_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +bin_mmc_tool = mmc-tool + +bin_PROGRAMS = $(bin_cd_drive) $(bin_cd_info) $(bin_cdinfo_linux) $(bin_cd_read) $(bin_iso_info) $(bin_iso_read) $(bin_cdda_player) $(bin_mmc_tool) + +INCLUDES = -I$(top_srcdir) $(LIBCDIO_CFLAGS) $(VCDINFO_CFLAGS) $(CDDB_CFLAGS) + +man_MANS = $(man_cd_drive) $(man_cd_info) $(man_cd_read) $(man_iso_read) $(man_iso_info) +EXTRA_DIST = cd-drive.help2man cd-info.help2man cd-read.help2man \ + iso-info.help2man iso-read.help2man $(GETOPT_C) getopt.h \ + $(man_MANS) + +$(man_MANS): %.1: % %.help2man + -$(HELP2MAN) --opt-include=$<.help2man --no-info --output=$@ ./$< +MOSTLYCLEANFILES = $(man_MANS) diff --git a/src/cd-drive.c b/src/cd-drive.c new file mode 100644 index 00000000..3f57cfe3 --- /dev/null +++ b/src/cd-drive.c @@ -0,0 +1,352 @@ +/* + $Id: cd-drive.c,v 1.29 2008/06/19 15:44:10 flameeyes Exp $ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Program to show drivers installed and capibilites of CD drives. */ + +#include "util.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include "getopt.h" +#include +#include + +/* Used by `main' to communicate with `parse_opt'. And global options + */ +static struct arguments +{ + uint32_t debug_level; + int version_only; + int silent; + source_image_t source_image; +} opts; + +/* Configuration option codes */ +enum { + OP_HANDLED, + + OP_SOURCE_DEVICE, + + OP_USAGE, + + /* These are the remaining configuration options */ + OP_VERSION, + +}; + +/* Parse all options. */ +static bool +parse_options (int argc, char *argv[]) +{ + int opt; + + static const char helpText[] = + "Usage: %s [OPTION...]\n" + " -d, --debug=INT Set debugging to LEVEL\n" + " -i, --cdrom-device[=DEVICE] show only info about CD-ROM device\n" + " -q, --quiet Don't produce warning output\n" + " -V, --version display version and copyright information\n" + " and exit\n" + "\n" + "Help options:\n" + " -?, --help Show this help message\n" + " --usage Display brief usage message\n"; + + static const char usageText[] = + "Usage: %s [-d|--debug INT] [-i|--cdrom-device DEVICE] [-q|--quiet]\n" + " [-V|--version] [-?|--help] [--usage]\n"; + + static const char optionsString[] = "d:i::qV?"; + static const struct option optionsTable[] = { + {"debug", required_argument, NULL, 'd' }, + {"cdrom-device", optional_argument, NULL, 'i' }, + {"quiet", no_argument, NULL, 'q' }, + {"version", no_argument, NULL, 'V' }, + {"help", no_argument, NULL, '?' }, + {"usage", no_argument, NULL, OP_USAGE }, + {NULL, 0, NULL, 0 } + }; + + program_name = strrchr(argv[0],'/'); + program_name = program_name ? strdup(program_name+1) : strdup(argv[0]); + + while ((opt = getopt_long(argc, argv, optionsString, optionsTable, NULL)) != -1) { + switch (opt) { + case 'd': + opts.debug_level = atoi(optarg); + break; + + case 'i': + if (opts.source_image != DRIVER_UNKNOWN) { + /* NOTE: The libpopt version already set source_name by this time. + To restore this behavior, fall through to the else{} block. + */ + report( stderr, "%s: another source type option given before.\n", + program_name ); + report( stderr, "%s: give only one source type option.\n", + program_name ); + break; + } else { + opts.source_image = DRIVER_DEVICE; + if (optarg != NULL) { + source_name = fillout_device_name(optarg); + } + break; + } + break; + + case 'q': + opts.silent = 1; + break; + + case 'V': + opts.version_only = 1; + break; + + case '?': + fprintf(stdout, helpText, program_name); + free(program_name); + exit(EXIT_INFO); + break; + + case OP_USAGE: + fprintf(stderr, usageText, program_name); + free(program_name); + exit(EXIT_FAILURE); + break; + + case OP_HANDLED: + break; + + default: + return false; + } + } + if (optind < argc) { + const char *remaining_arg = argv[optind++]; + + /* NOTE: A bug in the libpopt version checked source_image, which + rendered the subsequent source_image test useless. + */ + if (source_name != NULL) { + report( stderr, "%s: Source specified in option %s and as %s\n", + program_name, source_name, remaining_arg); + free(program_name); + exit (EXIT_FAILURE); + } + + if (opts.source_image == DRIVER_DEVICE) + source_name = fillout_device_name(remaining_arg); + else + source_name = strdup(remaining_arg); + + if (optind < argc) { + report( stderr, "%s: Source specified in previously %s and %s\n", + program_name, source_name, remaining_arg); + free(program_name); + exit (EXIT_FAILURE); + } + } + + return true; +} + +/* CDIO logging routines */ + +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_cdio_log_handler (level, message); +} + +/*! Prints out SCSI-MMC drive features */ +static void +print_mmc_drive_level(CdIo_t *p_cdio) +{ + cdio_mmc_level_t mmc_level = mmc_get_drive_mmc_cap(p_cdio); + + printf( "CD-ROM drive supports " ); + + switch(mmc_level) { + case CDIO_MMC_LEVEL_WEIRD: + printf("some nonstandard or degenerate set of MMC\n"); + break; + case CDIO_MMC_LEVEL_1: + printf("MMC 1\n"); + break; + case CDIO_MMC_LEVEL_2: + printf("MMC 2\n"); + break; + case CDIO_MMC_LEVEL_3: + printf("MMC 3\n"); + break; + case CDIO_MMC_LEVEL_NONE: + printf("no MMC\n"); + break; + } + printf("\n"); +} + +/* Initialize global variables. */ +static void +init(void) +{ + gl_default_cdio_log_handler = cdio_log_set_handler (_log_handler); + + /* Default option values. */ + opts.silent = false; + opts.debug_level = 0; + opts.source_image = DRIVER_UNKNOWN; +} + +int +main(int argc, char *argv[]) +{ + CdIo_t *p_cdio=NULL; + + init(); + + /* Parse our arguments; every option seen by `parse_opt' will + be reflected in `arguments'. */ + parse_options(argc, argv); + + print_version(program_name, CDIO_VERSION, false, opts.version_only); + + if (opts.debug_level == 3) { + cdio_loglevel_default = CDIO_LOG_INFO; + } else if (opts.debug_level >= 4) { + cdio_loglevel_default = CDIO_LOG_DEBUG; + } + + if (NULL == source_name) { + char *default_device; + + p_cdio = cdio_open (NULL, DRIVER_DEVICE); + + if (NULL == p_cdio) { + printf("No loaded CD-ROM device accessible.\n"); + } else { + default_device = cdio_get_default_device(p_cdio); + + printf("The driver selected is %s\n", cdio_get_driver_name(p_cdio)); + + if (default_device) { + printf("The default device for this driver is %s\n", default_device); + } + + free(default_device); + cdio_destroy(p_cdio); + p_cdio=NULL; + printf("\n"); + } + } + + /* Print out a drivers available */ + { + driver_id_t driver_id; + + printf("Drivers available...\n"); + for (driver_id=CDIO_MIN_DRIVER; driver_id<=CDIO_MAX_DRIVER; driver_id++) + if (cdio_have_driver(driver_id)) { + printf(" %-35s\n", cdio_driver_describe(driver_id)); + } + printf("\n"); + } + + + if (NULL == source_name) { + /* Print out a list of CD-drives */ + + char **ppsz_cdrives=NULL, **ppsz_cd; + driver_id_t driver_id = DRIVER_DEVICE; + + ppsz_cdrives = cdio_get_devices_ret(&driver_id); + if (NULL != ppsz_cdrives) + for( ppsz_cd = ppsz_cdrives; *ppsz_cd != NULL; ppsz_cd++ ) { + cdio_drive_read_cap_t i_read_cap; + cdio_drive_write_cap_t i_write_cap; + cdio_drive_misc_cap_t i_misc_cap; + cdio_hwinfo_t hwinfo; + CdIo_t *p_cdio = cdio_open(*ppsz_cd, driver_id); + + print_mmc_drive_level(p_cdio); + + printf("%28s: %s\n", "Drive", *ppsz_cd); + + if (p_cdio) { + if (cdio_get_hwinfo(p_cdio, &hwinfo)) { + printf("%-28s: %s\n%-28s: %s\n%-28s: %s\n", + "Vendor" , hwinfo.psz_vendor, + "Model" , hwinfo.psz_model, + "Revision", hwinfo.psz_revision); + } + print_mmc_drive_features(p_cdio); + cdio_get_drive_cap(p_cdio, &i_read_cap, &i_write_cap, + &i_misc_cap); + print_drive_capabilities(i_read_cap, i_write_cap, i_misc_cap); + } + printf("\n"); + if (p_cdio) cdio_destroy(p_cdio); + } + + cdio_free_device_list(ppsz_cdrives); + ppsz_cdrives = NULL; + } else { + /* Print CD-drive info for given source */ + cdio_drive_read_cap_t i_read_cap; + cdio_drive_write_cap_t i_write_cap; + cdio_drive_misc_cap_t i_misc_cap; + cdio_hwinfo_t hwinfo; + + printf("Drive %s\n", source_name); + p_cdio = cdio_open (source_name, DRIVER_UNKNOWN); + + if (p_cdio) { + + print_mmc_drive_level(p_cdio); + + if (cdio_get_hwinfo(p_cdio, &hwinfo)) { + printf("%-28s: %s\n%-28s: %s\n%-28s: %s\n", + "Vendor" , hwinfo.psz_vendor, + "Model" , hwinfo.psz_model, + "Revision", hwinfo.psz_revision); + } + print_mmc_drive_features(p_cdio); + } + cdio_get_drive_cap_dev(source_name, &i_read_cap, &i_write_cap, + &i_misc_cap); + print_drive_capabilities(i_read_cap, i_write_cap, i_misc_cap); + printf("\n"); + } + + myexit(p_cdio, EXIT_SUCCESS); + /* Not reached:*/ + return(EXIT_SUCCESS); +} diff --git a/src/cd-drive.help2man b/src/cd-drive.help2man new file mode 100644 index 00000000..63eb61ac --- /dev/null +++ b/src/cd-drive.help2man @@ -0,0 +1,10 @@ +[SYNOPSIS] +.B cd-drive +\fIOPTION\fR... +.TP +Shows CD-ROM drive characteristics. +[SEE ALSO] +\&\f(CWcd-info(1)\fR for information about the CD inside a CD-ROM. +[AUTHOR] +Rocky Bernstein rocky@panix.com + diff --git a/src/cd-info.c b/src/cd-info.c new file mode 100644 index 00000000..32ff77a5 --- /dev/null +++ b/src/cd-info.c @@ -0,0 +1,1275 @@ +/* + $Id: cd-info.c,v 1.156 2008/09/09 14:44:25 rocky Exp $ + + Copyright (C) 2003, 2004, 2005, 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* + CD Info - prints various information about a CD, and detects the type of + the CD. +*/ + +#include "util.h" +#include "getopt.h" +#include + +#ifdef HAVE_CDDB +#include +#include "cddb.h" +#endif + +#ifdef HAVE_VCDINFO +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdio_assert.h" + +#include +#ifdef __linux__ +# include +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,50) +# include +# endif +#endif + +#include + +#define STRONG "__________________________________\n" +#define NORMAL "" + +#if CDIO_IOCTL_FINISHED +static struct cdrom_multisession ms; +static struct cdrom_subchnl sub; +#endif + +/* Used by `main' to communicate with `parse_opt'. And global options + */ +static struct opts_s +{ + int no_tracks; + int no_ioctl; + int no_analysis; + char *access_mode; /* Access method driver should use for control */ + int no_cddb; /* If set the below are meaningless. */ + int no_vcd; + int show_dvd; + int no_device; + int no_disc_mode; + uint32_t debug_level; + int version_only; + int silent; + int no_header; + int no_joliet; + int no_xa; + int no_rock_ridge; + int print_iso9660; + int list_drives; + source_image_t source_image; +} opts; + +/* Configuration option codes */ +enum { + OP_HANDLED = 0, + + OP_SOURCE_UNDEF, + OP_SOURCE_AUTO, + OP_SOURCE_BIN, + OP_SOURCE_CUE, + OP_SOURCE_CDRDAO, + OP_SOURCE_NRG , + OP_SOURCE_DEVICE, + + OP_CDDB_SERVER, + OP_CDDB_CACHE, + OP_CDDB_EMAIL, + OP_CDDB_NOCACHE, + OP_CDDB_TIMEOUT, + + OP_USAGE, + + /* These are the remaining configuration options */ + OP_VERSION, + +}; + +/* Parse source options. */ +static void +parse_source(int opt) +{ + /* NOTE: The libpopt version made use of an extra temporary + variable (psz_my_source) for all sources _except_ devices. + This distinction seemed to serve no purpose. + */ + + if (opts.source_image != INPUT_UNKNOWN) { + report(stderr, "%s: another source type option given before.\n", + program_name); + report(stderr, "%s: give only one source type option.\n", + program_name); + return; + } + + /* For all input sources which are not a DEVICE, we need to make + a copy of the string; for a DEVICE the fill-out routine makes + the copy. + */ + if (OP_SOURCE_DEVICE != opt) + if (optarg != NULL) source_name = strdup(optarg); + + switch (opt) { + case OP_SOURCE_BIN: + opts.source_image = INPUT_BIN; + break; + case OP_SOURCE_CUE: + opts.source_image = INPUT_CUE; + break; + case OP_SOURCE_CDRDAO: + opts.source_image = INPUT_CDRDAO; + break; + case OP_SOURCE_NRG: + opts.source_image = INPUT_NRG; + break; + case OP_SOURCE_AUTO: + opts.source_image = INPUT_AUTO; + break; + + case OP_SOURCE_DEVICE: + opts.source_image = INPUT_DEVICE; + if (optarg != NULL) source_name = fillout_device_name(optarg); + break; + } +} + + +/* Parse all options. */ +static bool +parse_options (int argc, char *argv[]) +{ + int opt; /* used for argument parsing */ + + static const char helpText[] = + "Usage: %s [OPTION...]\n" + " -a, --access-mode=STRING Set CD access method\n" + " -d, --debug=INT Set debugging to LEVEL\n" + " -T, --no-tracks Don't show track information\n" + " -A, --no-analyze Don't filesystem analysis\n" +#ifdef HAVE_CDDB + " --no-cddb Don't look up audio CDDB information\n" + " or print it\n" + " -P, --cddb-port=INT CDDB port number to use (default 8880)\n" + " -H, --cddb-http Lookup CDDB via HTTP proxy (default no\n" + " proxy)\n" + " --cddb-server=STRING CDDB server to contact for information\n" + " (default: freedb.freedb.org)\n" + " --cddb-cache=STRING Location of CDDB cache directory\n" + " (default ~/.cddbclient)\n" + " --cddb-email=STRING Email address to give CDDB server\n" + " (default me@home)\n" + " --no-cddb-cache Disable caching of CDDB entries\n" + " locally (default caches)\n" + " --cddb-timeout=INT CDDB timeout value in seconds\n" + " (default 10 seconds)\n" +#else + " --no-cddb Does nothing since this program is not\n" + " -P, --cddb-port=INT CDDB-enabled\n" + " -H, --cddb-http\n" + " --cddb-server=STRING\n" + " --cddb-cache=STRING\n" + " --cddb-email=STRING\n" + " --no-cddb-cache\n" + " --cddb-timeout=INT\n" +#endif + " --no-device-info Don't show device info, just CD info\n" + " --no-disc-mode Don't show disc-mode info\n" + " --dvd Attempt to give DVD information if a DVD is\n" + " found.\n" +#ifdef HAVE_VCDINFO + " -v, --no-vcd Don't look up Video CD information\n" +#else + " -v, --no-vcd Don't look up Video CD information - for\n" + " this build, this is always set\n" +#endif + " -I, --no-ioctl Don't show ioctl() information\n" + " -b, --bin-file[=FILE] set \"bin\" CD-ROM disk image file as source\n" + " -c, --cue-file[=FILE] set \"cue\" CD-ROM disk image file as source\n" + " -N, --nrg-file[=FILE] set Nero CD-ROM disk image file as source\n" + " -t, --toc-file[=FILE] set cdrdao CD-ROM disk image file as source\n" + " -i, --input[=FILE] set source and determine if \"bin\" image or\n" + " device\n" + " --iso9660 print directory contents of any ISO-9660\n" + " filesystems\n" + " -C, --cdrom-device[=DEVICE] set CD-ROM device as source\n" + " -l, --list-drives Give a list of CD-drives\n" + " --no-header Don't display header and copyright (for\n" + " regression testing)\n" +#ifdef HAVE_JOLIET + " --no-joliet Don't use Joliet extensions\n" +#endif + " --no-rock-ridge Don't use Rock-Ridge-extension information\n" + " --no-xa Don't use XA-extension information\n" + " -q, --quiet Don't produce warning output\n" + " -V, --version display version and copyright information\n" + " and exit\n" + "\n" + "Help options:\n" + " -?, --help Show this help message\n" + " --usage Display brief usage message\n"; + + static const char usageText[] = + "Usage: %s [-a|--access-mode STRING] [-d|--debug INT] [-T|--no-tracks]\n" + " [-A|--no-analyze] [--no-cddb] [-P|--cddb-port INT] [-H|--cddb-http]\n" + " [--cddb-server=STRING] [--cddb-cache=STRING] [--cddb-email=STRING]\n" + " [--no-cddb-cache] [--cddb-timeout=INT] [--no-device-info]\n" + " [--no-disc-mode] [--dvd] [-v|--no-vcd] [-I|--no-ioctl]\n" + " [-b|--bin-file FILE] [-c|--cue-file FILE] [-N|--nrg-file FILE]\n" + " [-t|--toc-file FILE] [-i|--input FILE] [--iso9660]\n" + " [-C|--cdrom-device DEVICE] [-l|--list-drives] [--no-header]\n" + " [--no-joliet] [--no-rock-ridge] [--no-xa] [-q|--quiet] [-V|--version]\n" + " [-?|--help] [--usage]\n"; + + static const char optionsString[] = "a:d:TAP:HvIb::c::N::t::i::C::lqV?"; + static const struct option optionsTable[] = { + {"access-mode", required_argument, NULL, 'a'}, + {"debug", required_argument, NULL, 'd' }, + {"no-tracks", no_argument, NULL, 'T' }, + {"no-analyze", no_argument, NULL, 'A' }, + {"no-cddb", no_argument, &opts.no_cddb, 1 }, + {"cddb-port", required_argument, NULL, 'P' }, + {"cddb-http", no_argument, NULL, 'H' }, + {"cddb-server", required_argument, NULL, OP_CDDB_SERVER }, + {"cddb-cache", required_argument, NULL, OP_CDDB_CACHE }, + {"cddb-email", required_argument, NULL, OP_CDDB_EMAIL }, + {"no-cddb-cache", no_argument, NULL, OP_CDDB_NOCACHE }, + {"cddb-timeout", required_argument, NULL, OP_CDDB_TIMEOUT }, + {"no-device-info", no_argument, &opts.no_device, 1 }, + {"no-disc-mode", no_argument, &opts.no_disc_mode, 1 }, + {"dvd", no_argument, &opts.show_dvd, 1 }, + {"no-vcd", no_argument, NULL, 'v' }, + {"no-ioctl", no_argument, NULL, 'I' }, + {"bin-file", optional_argument, NULL, 'b' }, + {"cue-file", optional_argument, NULL, 'c' }, + {"nrg-file", optional_argument, NULL, 'N' }, + {"toc-file", optional_argument, NULL, 't' }, + {"input", optional_argument, NULL, 'i' }, + {"iso9660", no_argument, &opts.print_iso9660, 1 }, + {"cdrom-device", optional_argument, NULL, 'C' }, + {"list-drives", no_argument, NULL, 'l' }, + {"no-header", no_argument, &opts.no_header, 1 }, +#ifdef HAVE_JOLIET + {"no-joliet", no_argument, &opts.no_joliet, 1 }, +#endif /*HAVE_JOLIET*/ + {"no-rock-ridge", no_argument, &opts.no_rock_ridge, 1 }, + {"no-xa", no_argument, &opts.no_xa, 1 }, + {"quiet", no_argument, NULL, 'q' }, + {"version", no_argument, NULL, 'V' }, + + {"help", no_argument, NULL, '?' }, + {"usage", no_argument, NULL, OP_USAGE }, + { NULL, 0, NULL, 0 } + }; + + program_name = strrchr(argv[0],'/'); + program_name = program_name ? strdup(program_name+1) : strdup(argv[0]); + + while ((opt = getopt_long(argc, argv, optionsString, optionsTable, NULL)) >= 0) { + switch (opt) { + case 'a': opts.access_mode = strdup(optarg); break; + case 'd': opts.debug_level = atoi(optarg); break; + case 'T': opts.no_tracks = 1; break; + case 'A': opts.no_analysis = 1; break; +#ifdef HAVE_CDDB + case 'P': cddb_opts.port = atoi(optarg); break; + case 'H': cddb_opts.http = 1; break; + case OP_CDDB_SERVER: cddb_opts.server = strdup(optarg); break; + case OP_CDDB_CACHE: cddb_opts.cachedir = strdup(optarg); break; + case OP_CDDB_EMAIL: cddb_opts.email = strdup(optarg); break; + case OP_CDDB_NOCACHE: cddb_opts.disable_cache = 1; break; + case OP_CDDB_TIMEOUT: cddb_opts.timeout = atoi(optarg); break; +#endif + case 'v': opts.no_vcd = 1; break; + case 'I': opts.no_ioctl = 1; break; + case 'b': parse_source(OP_SOURCE_BIN); break; + case 'c': parse_source(OP_SOURCE_CUE); break; + case 'N': parse_source(OP_SOURCE_NRG); break; + case 't': parse_source(OP_SOURCE_CDRDAO); break; + case 'i': parse_source(OP_SOURCE_AUTO); break; + case 'C': parse_source(OP_SOURCE_DEVICE); break; + case 'l': opts.list_drives = 1; break; + case 'q': opts.silent = 1; break; + case 'V': opts.version_only = 1; break; + + case '?': + fprintf(stdout, helpText, program_name); + free(program_name); + exit(EXIT_INFO); + break; + + case OP_USAGE: + fprintf(stderr, usageText, program_name); + free(program_name); + exit(EXIT_FAILURE); + break; + + case OP_HANDLED: + break; + } + } + + if (optind < argc) { + const char *remaining_arg = argv[optind++]; + + if (source_name != NULL) { + report(stderr, "%s: Source '%s' given as an argument of an option and as " + "unnamed option '%s'\n", + program_name, source_name, remaining_arg); + free(program_name); + exit (EXIT_FAILURE); + } + + if (opts.source_image == INPUT_DEVICE) + source_name = fillout_device_name(remaining_arg); + else + source_name = strdup(remaining_arg); + + if (optind < argc) { + report(stderr, "%s: Source specified in previously %s and %s\n", + program_name, source_name, remaining_arg); + free(program_name); + exit (EXIT_FAILURE); + } + } + + return true; +} + + +/* CDIO logging routines */ + +#ifdef HAVE_CDDB +static cddb_log_handler_t gl_default_cddb_log_handler = NULL; +#endif +#ifdef HAVE_VCDINFO +static vcd_log_handler_t gl_default_vcd_log_handler = NULL; +#endif + +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_cdio_log_handler (level, message); +} + +#ifdef HAVE_CDDB +static void +_cddb_log_handler (cddb_log_level_t level, const char message[]) +{ + /* CDDB errors should not be considered fatal. */ + if (level == CDDB_LOG_ERROR) + level = CDIO_LOG_WARN; + + /* Might consider doing some sort of CDDB to cdio to log level conversion, + but right now it's a no op. */ + + _log_handler(level, message); +} +#endif + +static void +print_cdtext_track_info(CdIo_t *p_cdio, track_t i_track, const char *psz_msg) { + cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio, i_track); + + if (NULL != p_cdtext) { + cdtext_field_t i; + + printf("%s\n", psz_msg); + + for (i=0; i < MAX_CDTEXT_FIELDS; i++) { + if (p_cdtext->field[i]) { + printf("\t%s: %s\n", cdtext_field2str(i), p_cdtext->field[i]); + } + } + } + cdtext_destroy(p_cdtext); +} + +static void +print_cdtext_info(CdIo_t *p_cdio, track_t i_tracks, track_t i_first_track) { + track_t i_last_track = i_first_track+i_tracks; + + print_cdtext_track_info(p_cdio, 0, "\nCD-TEXT for Disc:"); + for ( ; i_first_track < i_last_track; i_first_track++ ) { + char msg[50]; + sprintf(msg, "CD-TEXT for Track %d:", i_first_track); + print_cdtext_track_info(p_cdio, i_first_track, msg); + } +} + +#ifdef HAVE_CDDB + +static void +cddb_errmsg(const char *msg) +{ + report(stderr, "%s: %s\n", program_name, msg); +} + + +static void +print_cddb_info(CdIo_t *p_cdio, track_t i_tracks, track_t i_first_track) +{ + int i, i_cddb_matches = 0; + + cddb_conn_t *p_conn = NULL; + cddb_disc_t *p_cddb_disc = NULL; + + if ( init_cddb(p_cdio, &p_conn, &p_cddb_disc, cddb_errmsg, i_first_track, + i_tracks, &i_cddb_matches) ) { + + if (-1 == i_cddb_matches) + printf("%s: %s\n", program_name, cddb_error_str(cddb_errno(p_conn))); + else { + printf("%s: Found %d matches in CDDB\n", program_name, i_cddb_matches); + for (i=1; i<=i_cddb_matches; i++) { + cddb_disc_print(p_cddb_disc); + cddb_query_next(p_conn, p_cddb_disc); + if (i != i_cddb_matches) cddb_read(p_conn, p_cddb_disc); + } + } + + cddb_disc_destroy(p_cddb_disc); + cddb_destroy(p_conn); + libcddb_shutdown(); + } +} +#endif + +#ifdef HAVE_VCDINFO +static void +print_vcd_info(driver_id_t driver) { + vcdinfo_open_return_t open_rc; + vcdinfo_obj_t *p_vcd = NULL; + open_rc = vcdinfo_open(&p_vcd, &source_name, driver, NULL); + switch (open_rc) { + case VCDINFO_OPEN_VCD: + if (vcdinfo_get_format_version (p_vcd) == VCD_TYPE_INVALID) { + report(stderr, "VCD format detection failed"); + vcdinfo_close(p_vcd); + return; + } + report (stdout, "Format : %s\n", + vcdinfo_get_format_version_str(p_vcd)); + report (stdout, "Album : `%.16s'\n", vcdinfo_get_album_id(p_vcd)); + report (stdout, "Volume count : %d\n", vcdinfo_get_volume_count(p_vcd)); + report (stdout, "volume number: %d\n", vcdinfo_get_volume_num(p_vcd)); + + break; + case VCDINFO_OPEN_ERROR: + report( stderr, "Error in Video CD opening of %s\n", source_name ); + break; + case VCDINFO_OPEN_OTHER: + report( stderr, "Even though we thought this was a Video CD, " + " further inspection says it is not.\n" ); + break; + } + if (p_vcd) vcdinfo_close(p_vcd); + +} +#endif + +static void +print_iso9660_recurse (CdIo_t *p_cdio, const char pathname[], + cdio_fs_anal_t fs) +{ + CdioList_t *p_entlist; + CdioList_t *p_dirlist = _cdio_list_new (); + CdioListNode_t *entnode; + uint8_t i_joliet_level; + char *translated_name = (char *) malloc(4096); + size_t translated_name_size = 4096; + + i_joliet_level = (opts.no_joliet) + ? 0 + : cdio_get_joliet_level(p_cdio); + + p_entlist = iso9660_fs_readdir (p_cdio, pathname, false); + + printf ("%s:\n", pathname); + + if (NULL == p_entlist) { + report( stderr, "Error getting above directory information\n" ); + return; + } + + /* Iterate over files in this directory */ + + _CDIO_LIST_FOREACH (entnode, p_entlist) + { + iso9660_stat_t *p_statbuf = _cdio_list_node_data (entnode); + char *psz_iso_name = p_statbuf->filename; + char _fullname[4096] = { 0, }; + if (strlen(psz_iso_name) >= translated_name_size) { + translated_name_size = strlen(psz_iso_name)+1; + free(translated_name); + translated_name = (char *) malloc(translated_name_size); + if (!translated_name) { + report( stderr, "Error allocating memory\n" ); + return; + } + } + + if (yep != p_statbuf->rr.b3_rock || 1 == opts.no_rock_ridge) { + iso9660_name_translate_ext(psz_iso_name, translated_name, + i_joliet_level); + } + + + snprintf (_fullname, sizeof (_fullname), "%s%s", pathname, + psz_iso_name); + + strncat (_fullname, "/", sizeof (_fullname)); + + if (p_statbuf->type == _STAT_DIR + && strcmp (psz_iso_name, ".") + && strcmp (psz_iso_name, "..")) + _cdio_list_append (p_dirlist, strdup (_fullname)); + + print_fs_attrs(p_statbuf, 0 == opts.no_rock_ridge, fs & CDIO_FS_ANAL_XA, + psz_iso_name, translated_name); + if (p_statbuf->rr.i_symlink) { + free(p_statbuf->rr.psz_symlink); + p_statbuf->rr.i_symlink = 0; + } + } + free (translated_name); + + _cdio_list_free (p_entlist, true); + + printf ("\n"); + + /* Now recurse over the directories. */ + + _CDIO_LIST_FOREACH (entnode, p_dirlist) + { + char *_fullname = _cdio_list_node_data (entnode); + + print_iso9660_recurse (p_cdio, _fullname, fs); + } + + _cdio_list_free (p_dirlist, true); +} + +static void +print_iso9660_fs (CdIo_t *p_cdio, cdio_fs_anal_t fs, + track_format_t track_format) +{ + iso_extension_mask_t iso_extension_mask = ISO_EXTENSION_ALL; + + if (fs & CDIO_FS_ANAL_XA) track_format = TRACK_FORMAT_XA; + + if (opts.no_joliet) { + iso_extension_mask &= ~ISO_EXTENSION_JOLIET; + } + + if ( !iso9660_fs_read_superblock(p_cdio, iso_extension_mask) ) + return; + + printf ("ISO9660 filesystem\n"); + print_iso9660_recurse (p_cdio, "/", fs); +} + +#define print_vd_info(title, fn) \ + psz_str = fn(&pvd); \ + if (psz_str) { \ + report(stdout, title ": %s\n", psz_str); \ + free(psz_str); \ + psz_str = NULL; \ + } + +static void +print_analysis(int ms_offset, cdio_iso_analysis_t cdio_iso_analysis, + cdio_fs_anal_t fs, int first_data, unsigned int num_audio, + track_t i_tracks, track_t i_first_track, + track_format_t track_format, CdIo_t *p_cdio) +{ + int need_lf; + + switch(CDIO_FSTYPE(fs)) { + case CDIO_FS_AUDIO: + if (num_audio > 0) { +#ifdef HAVE_CDDB + if (!opts.no_cddb) { + printf("Audio CD, CDDB disc ID is %08x\n", + cddb_discid(p_cdio, i_tracks)); + print_cddb_info(p_cdio, i_tracks, i_first_track); + } +#endif + print_cdtext_info(p_cdio, i_tracks, i_first_track); + } + break; + case CDIO_FS_ISO_9660: + printf("CD-ROM with ISO 9660 filesystem"); + if (fs & CDIO_FS_ANAL_JOLIET) { + printf(" and joliet extension level %d", cdio_iso_analysis.joliet_level); + } + if (fs & CDIO_FS_ANAL_ROCKRIDGE) + printf(" and rockridge extensions"); + printf("\n"); + break; + case CDIO_FS_ISO_9660_INTERACTIVE: + printf("CD-ROM with CD-RTOS and ISO 9660 filesystem\n"); + break; + case CDIO_FS_HIGH_SIERRA: + printf("CD-ROM with High Sierra filesystem\n"); + break; + case CDIO_FS_INTERACTIVE: + printf("CD-Interactive%s\n", num_audio > 0 ? "/Ready" : ""); + break; + case CDIO_FS_HFS: + printf("CD-ROM with Macintosh HFS\n"); + break; + case CDIO_FS_ISO_HFS: + printf("CD-ROM with both Macintosh HFS and ISO 9660 filesystem\n"); + break; + case CDIO_FS_UFS: + printf("CD-ROM with Unix UFS\n"); + break; + case CDIO_FS_EXT2: + printf("CD-ROM with GNU/Linux EXT2 (native) filesystem\n"); + break; + case CDIO_FS_3DO: + printf("CD-ROM with Panasonic 3DO filesystem\n"); + break; + case CDIO_FS_UDFX: + printf("CD-ROM with UDFX filesystem\n"); + break; + case CDIO_FS_UNKNOWN: + printf("CD-ROM with unknown filesystem\n"); + break; + case CDIO_FS_XISO: + printf("CD-ROM with Microsoft X-BOX XISO filesystem\n"); + break; + } + switch(CDIO_FSTYPE(fs)) { + case CDIO_FS_ISO_9660: + case CDIO_FS_ISO_9660_INTERACTIVE: + case CDIO_FS_ISO_HFS: + case CDIO_FS_ISO_UDF: + printf("ISO 9660: %i blocks, label `%.32s'\n", + cdio_iso_analysis.isofs_size, cdio_iso_analysis.iso_label); + + { + iso9660_pvd_t pvd; + + if ( iso9660_fs_read_pvd(p_cdio, &pvd) ) { + char *psz_str; + print_vd_info("Application", iso9660_get_application_id); + print_vd_info("Preparer ", iso9660_get_preparer_id); + print_vd_info("Publisher ", iso9660_get_publisher_id); + print_vd_info("System ", iso9660_get_system_id); + print_vd_info("Volume ", iso9660_get_volume_id); + print_vd_info("Volume Set ", iso9660_get_volumeset_id); + } + } + + if (opts.print_iso9660) + print_iso9660_fs(p_cdio, fs, track_format); + + break; + } + + switch(CDIO_FSTYPE(fs)) { + case CDIO_FS_UDF: + case CDIO_FS_ISO_UDF: + report(stdout, "UDF: version %x.%2.2x\n", + cdio_iso_analysis.UDFVerMajor, cdio_iso_analysis.UDFVerMinor); + break; + default: ; + } + + need_lf = 0; + if (first_data == 1 && num_audio > 0) + need_lf += printf("mixed mode CD "); + if (fs & CDIO_FS_ANAL_XA) + need_lf += printf("XA sectors "); + if (fs & CDIO_FS_ANAL_MULTISESSION) + need_lf += printf("Multisession, offset = %i ", ms_offset); + if (fs & CDIO_FS_ANAL_HIDDEN_TRACK) + need_lf += printf("Hidden Track "); + if (fs & CDIO_FS_ANAL_PHOTO_CD) + need_lf += printf("%sPhoto CD ", + num_audio > 0 ? " Portfolio " : ""); + if (fs & CDIO_FS_ANAL_CDTV) + need_lf += printf("Commodore CDTV "); + if (first_data > 1) + need_lf += printf("CD-Plus/Extra "); + if (fs & CDIO_FS_ANAL_BOOTABLE) + need_lf += printf("bootable CD "); + if (fs & CDIO_FS_ANAL_VIDEOCD && num_audio == 0) { + need_lf += printf("Video CD "); + } + if (fs & CDIO_FS_ANAL_SVCD) + need_lf += printf("Super Video CD (SVCD) or Chaoji Video CD (CVD)"); + if (fs & CDIO_FS_ANAL_CVD) + need_lf += printf("Chaoji Video CD (CVD)"); + if (need_lf) printf("\n"); +#ifdef HAVE_VCDINFO + if (fs & (CDIO_FS_ANAL_VIDEOCD|CDIO_FS_ANAL_CVD|CDIO_FS_ANAL_SVCD)) + if (!opts.no_vcd) { + printf("\n"); + print_vcd_info(cdio_get_driver_id(p_cdio)); + } +#endif + +} + +/* Initialize global variables. */ +static void +init(void) +{ + gl_default_cdio_log_handler = cdio_log_set_handler (_log_handler); +#ifdef HAVE_CDDB + gl_default_cddb_log_handler = + cddb_log_set_handler ((cddb_log_handler_t) _cddb_log_handler); +#endif + +#ifdef HAVE_VCDINFO + gl_default_vcd_log_handler = + vcd_log_set_handler ((vcd_log_handler_t) _log_handler); +#endif + + /* Default option values. */ + opts.silent = false; + opts.list_drives = false; + opts.no_header = false; + opts.no_joliet = 0; + opts.no_rock_ridge = 0; + opts.no_xa = 0; + opts.no_device = 0; + opts.no_disc_mode = 0; + opts.debug_level = 0; + opts.no_tracks = 0; + opts.print_iso9660 = 0; +#ifdef HAVE_CDDB + opts.no_cddb = 0; + cddb_opts.port = 8880; + cddb_opts.http = 0; + cddb_opts.cachedir = NULL; + cddb_opts.server = NULL; + cddb_opts.timeout = -1; + cddb_opts.disable_cache = false; +#endif +#ifdef HAVE_VCDINFO + opts.no_vcd = 0; +#else + opts.no_vcd = 1; +#endif + opts.no_ioctl = 0; + opts.no_analysis = 0; + opts.source_image = INPUT_UNKNOWN; + opts.access_mode = NULL; +} + +/* ------------------------------------------------------------------------ */ + +int +main(int argc, char *argv[]) +{ + + CdIo_t *p_cdio=NULL; + cdio_fs_anal_t fs = CDIO_FS_AUDIO; + int i; + lsn_t start_track_lsn; /* lsn of first track */ + lsn_t data_start = 0; /* start of data area */ + int ms_offset = 0; + track_t i_tracks = 0; + track_t i_first_track = 0; + unsigned int num_audio = 0; /* # of audio tracks */ + unsigned int num_data = 0; /* # of data tracks */ + int first_data = -1; /* # of first data track */ + int first_audio = -1; /* # of first audio track */ + bool b_playing_audio = false; /* currently playing a + CD-DA */ + cdio_iso_analysis_t cdio_iso_analysis; + char *media_catalog_number; + discmode_t discmode = CDIO_DISC_MODE_NO_INFO; + cdio_drive_read_cap_t i_read_cap = 0; + cdio_drive_write_cap_t i_write_cap; + cdio_drive_misc_cap_t i_misc_cap; + + memset(&cdio_iso_analysis, 0, sizeof(cdio_iso_analysis)); + init(); + + /* Parse our arguments; every option seen by `parse_opt' will + be reflected in `arguments'. */ + parse_options(argc, argv); + + print_version(program_name, CDIO_VERSION, opts.no_header, opts.version_only); + + if (opts.debug_level == 3) { + cdio_loglevel_default = CDIO_LOG_INFO; +#ifdef HAVE_VCDINFO + vcd_loglevel_default = VCD_LOG_INFO; +#endif + } else if (opts.debug_level >= 4) { + cdio_loglevel_default = CDIO_LOG_DEBUG; +#ifdef HAVE_VCDINFO + vcd_loglevel_default = VCD_LOG_DEBUG; +#endif + } + + p_cdio = open_input(source_name, opts.source_image, opts.access_mode); + + if (p_cdio==NULL) { + if (source_name) { + err_exit("%s: Error in opening device driver for input %s.\n", + program_name, source_name); + } else { + err_exit("%s: Error in opening device driver for unspecified input.\n", + program_name); + } + + } + + if (source_name==NULL) { + source_name=strdup(cdio_get_arg(p_cdio, "source")); + if (NULL == source_name) { + err_exit("%s: No input device given/found\n", program_name); + } + } + + if (0 == opts.silent) { + printf("CD location : %s\n", source_name); + printf("CD driver name: %s\n", cdio_get_driver_name(p_cdio)); + printf(" access mode: %s\n\n", cdio_get_arg(p_cdio, "access-mode")); + } + + cdio_get_drive_cap(p_cdio, &i_read_cap, &i_write_cap, &i_misc_cap); + if (0 == opts.no_device) { + cdio_hwinfo_t hwinfo; + if (cdio_get_hwinfo(p_cdio, &hwinfo)) { + printf("%-28s: %s\n%-28s: %s\n%-28s: %s\n", + "Vendor" , hwinfo.psz_vendor, + "Model" , hwinfo.psz_model, + "Revision", hwinfo.psz_revision); + } + print_drive_capabilities(i_read_cap, i_write_cap, i_misc_cap); + } + + if (opts.list_drives) { + driver_id_t driver_id = DRIVER_DEVICE; + char ** device_list = cdio_get_devices_ret(&driver_id); + char ** d = device_list; + + printf("list of devices found:\n"); + if (NULL != d) { + for ( ; *d != NULL ; d++ ) { + CdIo_t *p_cdio = cdio_open(source_name, driver_id); + cdio_hwinfo_t hwinfo; + printf("Drive %s\n", *d); + if (mmc_get_hwinfo(p_cdio, &hwinfo)) { + printf("%-8s: %s\n%-8s: %s\n%-8s: %s\n", + "Vendor" , hwinfo.psz_vendor, + "Model" , hwinfo.psz_model, + "Revision", hwinfo.psz_revision); + } + if (p_cdio) cdio_destroy(p_cdio); + } + } + cdio_free_device_list(device_list); + } + + report(stdout, STRONG "\n"); + + + discmode = cdio_get_discmode(p_cdio); + if ( 0 == opts.no_disc_mode ) { + printf("Disc mode is listed as: %s\n", + discmode2str[discmode]); + } + + if (cdio_is_discmode_dvd(discmode) && !opts.show_dvd) { + printf("No further information currently given for DVDs.\n"); + printf("Use --dvd to override.\n"); + myexit(p_cdio, EXIT_SUCCESS); + } + + i_first_track = cdio_get_first_track_num(p_cdio); + + if (CDIO_INVALID_TRACK == i_first_track) { + err_exit("Can't get first track number. I give up%s.\n", ""); + } + + i_tracks = cdio_get_num_tracks(p_cdio); + + if (CDIO_INVALID_TRACK == i_tracks) { + err_exit("Can't get number of tracks. I give up.%s\n", ""); + } + + if (!opts.no_tracks) { + printf("CD-ROM Track List (%i - %i)\n" NORMAL, + i_first_track, i_tracks); + + printf(" #: MSF LSN Type Green? Copy?"); + if ( CDIO_DISC_MODE_CD_DA == discmode + || CDIO_DISC_MODE_CD_MIXED == discmode ) + printf(" Channels Premphasis?"); + printf("\n"); + } + + start_track_lsn = cdio_get_track_lsn(p_cdio, i_first_track); + + /* Read and possibly print track information. */ + for (i = i_first_track; i <= CDIO_CDROM_LEADOUT_TRACK; i++) { + msf_t msf; + char *psz_msf; + track_format_t track_format; + + if (!cdio_get_track_msf(p_cdio, i, &msf)) { + err_exit("cdio_track_msf for track %i failed, I give up.\n", i); + } + + track_format = cdio_get_track_format(p_cdio, i); + psz_msf = cdio_msf_to_str(&msf); + if (i == CDIO_CDROM_LEADOUT_TRACK) { + if (!opts.no_tracks) { + lsn_t lsn= cdio_msf_to_lsn(&msf); + long unsigned int i_bytes_raw = lsn * CDIO_CD_FRAMESIZE_RAW; + long unsigned int i_bytes_formatted = lsn - start_track_lsn; + + printf( "%3d: %8s %06lu leadout ", (int) i, psz_msf, + (long unsigned int) lsn ); + + switch (discmode) { + case CDIO_DISC_MODE_DVD_ROM: + case CDIO_DISC_MODE_DVD_RAM: + case CDIO_DISC_MODE_DVD_R: + case CDIO_DISC_MODE_DVD_RW: + case CDIO_DISC_MODE_DVD_PR: + case CDIO_DISC_MODE_DVD_PRW: + case CDIO_DISC_MODE_DVD_OTHER: + case CDIO_DISC_MODE_CD_DATA: + i_bytes_formatted *= CDIO_CD_FRAMESIZE; + break; + case CDIO_DISC_MODE_CD_DA: + i_bytes_formatted *= CDIO_CD_FRAMESIZE_RAW; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + i_bytes_formatted *= CDIO_CD_FRAMESIZE_RAW0; + break; + default: + i_bytes_formatted *= CDIO_CD_FRAMESIZE_RAW; + } + + if (i_bytes_raw < 1024) + printf( "(%lu bytes", i_bytes_raw ); + if (i_bytes_raw < 1024 * 1024) + printf( "(%lu KB", i_bytes_raw / 1024 ); + else + printf( "(%lu MB", i_bytes_raw / (1024 * 1024) ); + + printf(" raw, "); + if (i_bytes_formatted < 1024) + printf( "%lu bytes", i_bytes_formatted ); + if (i_bytes_formatted < 1024 * 1024) + printf( "%lu KB", i_bytes_formatted / 1024 ); + else + printf( "%lu MB", i_bytes_formatted / (1024 * 1024) ); + + printf(" formatted)\n"); + + } + free(psz_msf); + break; + } else if (!opts.no_tracks) { + const char *psz; + printf( "%3d: %8s %06lu %-6s %-5s ", (int) i, psz_msf, + (long unsigned int) cdio_msf_to_lsn(&msf), + track_format2str[track_format], + cdio_get_track_green(p_cdio, i)? "true " : "false"); + + switch (cdio_get_track_copy_permit(p_cdio, i)) { + case CDIO_TRACK_FLAG_FALSE: + psz="no"; + break; + case CDIO_TRACK_FLAG_TRUE: + psz="yes"; + break; + case CDIO_TRACK_FLAG_UNKNOWN: + psz="?"; + break; + case CDIO_TRACK_FLAG_ERROR: + default: + psz="error"; + break; + } + printf("%-5s", psz); + + if (TRACK_FORMAT_AUDIO == track_format) { + const int i_channels = cdio_get_track_channels(p_cdio, i); + switch (cdio_get_track_preemphasis(p_cdio, i)) { + case CDIO_TRACK_FLAG_FALSE: + psz="no"; + break; + case CDIO_TRACK_FLAG_TRUE: + psz="yes"; + break; + case CDIO_TRACK_FLAG_UNKNOWN: + psz="?"; + break; + case CDIO_TRACK_FLAG_ERROR: + default: + psz="error"; + break; + } + if (i_channels == -2) + printf(" %-8s", "unknown"); + else if (i_channels == -1) + printf(" %-8s", "error"); + else + printf(" %-8d", i_channels); + printf( " %s", psz); + } + + printf( "\n" ); + + } + free(psz_msf); + + if (TRACK_FORMAT_AUDIO == track_format) { + num_audio++; + if (-1 == first_audio) first_audio = i; + } else { + num_data++; + if (-1 == first_data) first_data = i; + } + /* skip to leadout? */ + if (i == i_tracks) i = CDIO_CDROM_LEADOUT_TRACK-1; + } + + if (cdio_is_discmode_cdrom(discmode)) { + /* get and print MCN */ + + report(stdout, "Media Catalog Number (MCN): "); fflush(stdout); + + media_catalog_number = cdio_get_mcn(p_cdio); + + if (NULL == media_catalog_number) { + if (i_read_cap & CDIO_DRIVE_CAP_READ_MCN) + report(stdout, "not available\n"); + else + report(stdout, "not supported by drive/driver\n"); + } else { + report(stdout, "%s\n", media_catalog_number); + free(media_catalog_number); + } + + /* List number of sessions */ + { + lsn_t i_last_session; + report(stdout, "Last CD Session LSN: "); fflush(stdout); + if (DRIVER_OP_SUCCESS == cdio_get_last_session(p_cdio, &i_last_session)) + { + report(stdout, "%d\n", i_last_session); + } else { + if (i_misc_cap & CDIO_DRIVE_CAP_MISC_MULTI_SESSION) + report(stdout, "failed\n"); + else + report(stdout, "not supported by drive/driver\n"); + } + } + + /* get audio status from subchannel */ + if ( CDIO_DISC_MODE_CD_DA == discmode || + CDIO_DISC_MODE_CD_MIXED == discmode ) { + cdio_subchannel_t subchannel; + driver_return_code_t rc; + + memset(&subchannel, 0, sizeof(subchannel)); + report( stdout, "audio status: "); fflush(stdout); + + rc = cdio_audio_read_subchannel(p_cdio, &subchannel); + + if (DRIVER_OP_SUCCESS == rc) { + bool b_volume = false; + bool b_position = false; + + report ( stdout, "%s\n", + mmc_audio_state2str(subchannel.audio_status) ); + + switch (subchannel.audio_status) { + case CDIO_MMC_READ_SUB_ST_PLAY: + b_playing_audio = true; + /* Fall through to next case. */ + case CDIO_MMC_READ_SUB_ST_PAUSED: + b_position = true; + /* Fall through to next case. */ + case CDIO_MMC_READ_SUB_ST_NO_STATUS: + b_volume = true; + break; + default: + ; + } + + if (b_position) + report( stdout, " at: %02d:%02d abs / %02d:%02d track %d\n", + subchannel.abs_addr.m, + subchannel.abs_addr.s, + subchannel.rel_addr.m, + subchannel.rel_addr.s, + subchannel.track ); + + if (b_volume) { + cdio_audio_volume_t volume; + + if (DRIVER_OP_SUCCESS == cdio_audio_get_volume (p_cdio, &volume)) { + uint8_t i=0; + for (i=0; i<4; i++) { + uint8_t i_level = volume.level[i]; + report( stdout, + "volume level port %d: %3d (0..255) %3d (0..100)\n", + i, i_level, (i_level*100+128) / 256 ); + } + + } else + report( stdout, " can't get volume levels\n" ); + } + } else if (DRIVER_OP_UNSUPPORTED == rc) { + report( stdout, "not implemented\n" ); + } else { + report( stdout, "FAILED\n" ); + } + } + } + + if (!opts.no_analysis) { + if (b_playing_audio) { + /* Running a CD Analysis would mess up audio playback.*/ + report(stdout, + "CD Analysis Report omitted because audio is currently " + "playing.\n"); + myexit(p_cdio, EXIT_SUCCESS); + } + + report(stdout, STRONG "CD Analysis Report\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(p_cdio, i_first_track, &msf); + + /* CD-I/Ready says start_track_lsn <= 30*75 then CDDA */ + if (start_track_lsn > 100 /* 100 is just a guess */) { + fs = cdio_guess_cd_type(p_cdio, 0, 1, &cdio_iso_analysis); + if ((CDIO_FSTYPE(fs)) != CDIO_FS_UNKNOWN) + fs |= CDIO_FS_ANAL_HIDDEN_TRACK; + else { + fs &= ~CDIO_FS_MASK; /* del filesystem info */ + report( stdout, "Oops: %lu unused sectors at start, " + "but hidden track check failed.\n", + (long unsigned int) start_track_lsn); + } + } + print_analysis(ms_offset, cdio_iso_analysis, fs, first_data, num_audio, + i_tracks, i_first_track, + cdio_get_track_format(p_cdio, 1), p_cdio); + } else { + /* we have data track(s) */ + int j; + + for (j = 2, i = first_data; i <= i_tracks; i++) { + msf_t msf; + track_format_t track_format = cdio_get_track_format(p_cdio, i); + + cdio_get_track_msf(p_cdio, 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: + case TRACK_FORMAT_PSX: + ; + } + + start_track_lsn = (i == 1) ? 0 : cdio_msf_to_lsn(&msf); + + /* save the start of the data area */ + if (i == first_data) + data_start = start_track_lsn; + + /* skip tracks which belong to the current walked session */ + if (start_track_lsn < data_start + cdio_iso_analysis.isofs_size) + continue; + + fs = cdio_guess_cd_type(p_cdio, start_track_lsn, i, + &cdio_iso_analysis); + + if (i > 1) { + /* track is beyond last session -> new session found */ + ms_offset = start_track_lsn; + report( stdout, "session #%d starts at track %2i, LSN: %lu," + " ISO 9660 blocks: %6i\n", + j++, i, (unsigned long int) start_track_lsn, + cdio_iso_analysis.isofs_size); + report( stdout, "ISO 9660: %i blocks, label `%.32s'\n", + cdio_iso_analysis.isofs_size, cdio_iso_analysis.iso_label); + fs |= CDIO_FS_ANAL_MULTISESSION; + } else { + print_analysis(ms_offset, cdio_iso_analysis, fs, first_data, + num_audio, i_tracks, i_first_track, + track_format, p_cdio); + } + + if ( !(CDIO_FSTYPE(fs) == CDIO_FS_ISO_9660 || + CDIO_FSTYPE(fs) == CDIO_FS_ISO_HFS || + /* CDIO_FSTYPE(fs) == CDIO_FS_ISO_9660_INTERACTIVE) + && (fs & XA))) */ + CDIO_FSTYPE(fs) == CDIO_FS_ISO_9660_INTERACTIVE) ) + /* no method for non-ISO9660 multisessions */ + break; + } + } + } + + myexit(p_cdio, EXIT_SUCCESS); + /* Not reached:*/ + return(EXIT_SUCCESS); +} diff --git a/src/cd-info.help2man b/src/cd-info.help2man new file mode 100644 index 00000000..efdab10a --- /dev/null +++ b/src/cd-info.help2man @@ -0,0 +1,11 @@ +[SYNOPSIS] +.B cd-info +\fIOPTION\fR... +.TP +Shows Information about a CD or CD-image. +[SEE ALSO] +\&\f(CWcd-drive(1)\fR for CD-ROM characteristics; +\&\f(CWiso-info(1)\fR for information about an ISO-9660 image. +[AUTHOR] +Rocky Bernstein rocky@panix.com, based on the cdinfo program by +Gerd Knorr and Heiko Eissfeldt diff --git a/src/cd-paranoia/.cvsignore b/src/cd-paranoia/.cvsignore new file mode 100644 index 00000000..f0f4549f --- /dev/null +++ b/src/cd-paranoia/.cvsignore @@ -0,0 +1,8 @@ +.deps +.libs +Makefile +Makefile.in +*.o +cd-paranoia +usage.h +usage.txt diff --git a/src/cd-paranoia/.gitignore b/src/cd-paranoia/.gitignore new file mode 100644 index 00000000..f53478a0 --- /dev/null +++ b/src/cd-paranoia/.gitignore @@ -0,0 +1,3 @@ +/.deps +/Makefile +/Makefile.in diff --git a/src/cd-paranoia/Makefile.am b/src/cd-paranoia/Makefile.am new file mode 100644 index 00000000..c0ec4224 --- /dev/null +++ b/src/cd-paranoia/Makefile.am @@ -0,0 +1,59 @@ +# $Id: Makefile.am,v 1.27 2008/08/31 13:38:22 flameeyes Exp $ +# +# Copyright (C) 2004, 2005, 2006, 2007, 2008 Rocky Bernstein +# Copyright (C) 1998 Monty xiphmont@mit.edu +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +transform = s,cd-paranoia,@CDPARANOIA_NAME@, + +if BUILD_CD_PARANOIA + +SUBDIRS = doc + +GETOPT_C = getopt1.c getopt.c + +EXTRA_DIST = usage.txt.in usage-copy.h pod2c.pl \ + doc/FAQ.txt doc/overlapdef.txt $(GETOPT_C) getopt.h + +noinst_HEADERS = header.h report.h $(GETOPT_H) + + +cd_paranoia_SOURCES = cd-paranoia.c \ + buffering_write.c buffering_write.h \ + header.c report.c usage.h utils.h version.h $(GETOPT_C) + +cd_paranoia_LDADD = $(LIBCDIO_LIBS) $(LIBCDIO_CDDA_LIBS) $(LIBCDIO_PARANOIA_LIBS) $(LTLIBICONV) + +cd_paranoia_DEPENDENCIES = $(LIBCDIO_DEPS) $(LIBCDIO_CDDA_LIBS) $(LIBCDIO_PARANOIA_LIBS) + + +bin_PROGRAMS = cd-paranoia + +INCLUDES = -I$(top_srcdir) $(LIBCDIO_CFLAGS) + +cd-paranoia.$(OBJEXT): usage.h + +if HAVE_PERL +usage.h: usage.txt $(srcdir)/pod2c.pl + $(PERL) $(srcdir)/pod2c.pl $< >$@ +else +usage.h: usage-copy.h + cp $< $@ +endif + +endif + +MOSTLYCLEANFILES = usage.h usage.txt + diff --git a/src/cd-paranoia/buffering_write.c b/src/cd-paranoia/buffering_write.c new file mode 100644 index 00000000..d2017708 --- /dev/null +++ b/src/cd-paranoia/buffering_write.c @@ -0,0 +1,112 @@ +/* + $Id: buffering_write.c,v 1.4 2008/06/19 15:44:28 flameeyes Exp $ + + Copyright (C) 2004, 2008 Rocky Bernstein + Copyright (C) 1998, 1999 Monty + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Eliminate teeny little writes. patch submitted by + Rob Ross --Monty 19991008 */ + +#include +#include +#include +#include + +#define OUTBUFSZ 32*1024 + +#include "utils.h" +#include "buffering_write.h" + + +/* GLOBALS FOR BUFFERING CALLS */ +static int bw_fd = -1; +static long bw_pos = 0; +static char bw_outbuf[OUTBUFSZ]; + + + +static long int +blocking_write(int outf, char *buffer, long num){ + long int words=0,temp; + + while(words= 0 && bw_pos > 0) { + if (blocking_write(bw_fd, bw_outbuf, bw_pos)) { + perror("write (in buffering_write, flushing)"); + } + } + bw_fd = fd; + bw_pos = 0; + } + + if (bw_pos + num > OUTBUFSZ) { + /* fill our buffer first, then write, then modify buffer and num */ + memcpy(&bw_outbuf[bw_pos], buffer, OUTBUFSZ - bw_pos); + if (blocking_write(fd, bw_outbuf, OUTBUFSZ)) { + perror("write (in buffering_write, full buffer)"); + return(-1); + } + num -= (OUTBUFSZ - bw_pos); + buffer += (OUTBUFSZ - bw_pos); + bw_pos = 0; + } + /* save data */ + memcpy(&bw_outbuf[bw_pos], buffer, num); + bw_pos += num; + + return(0); +} + +/** buffering_close() - writes out remaining buffered data before + * closing file. + * + */ +int +buffering_close(int fd) +{ + if (fd == bw_fd && bw_pos > 0) { + /* write out remaining data and clean up */ + if (blocking_write(fd, bw_outbuf, bw_pos)) { + perror("write (in buffering_close)"); + } + bw_fd = -1; + bw_pos = 0; + } + return(close(fd)); +} diff --git a/src/cd-paranoia/buffering_write.h b/src/cd-paranoia/buffering_write.h new file mode 100644 index 00000000..0fb9e6fc --- /dev/null +++ b/src/cd-paranoia/buffering_write.h @@ -0,0 +1,35 @@ +/* + $Id: buffering_write.h,v 1.4 2008/06/19 15:44:30 flameeyes Exp $ + + Copyright (C) 2004, 2008 Rocky Bernstein + Copyright (C) 1998 Monty + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** buffering_write() - buffers data to a specified size before writing. + * + * Restrictions: + * - MUST CALL BUFFERING_CLOSE() WHEN FINISHED!!! + * + */ +extern long buffering_write(int outf, char *buffer, long num); + +/** buffering_close() - writes out remaining buffered data before + * closing file. + * + */ +extern int buffering_close(int fd); + + diff --git a/src/cd-paranoia/cd-paranoia.c b/src/cd-paranoia/cd-paranoia.c new file mode 100644 index 00000000..5c78a98d --- /dev/null +++ b/src/cd-paranoia/cd-paranoia.c @@ -0,0 +1,1373 @@ +/* + $Id: cd-paranoia.c,v 1.37 2008/06/19 15:44:30 flameeyes Exp $ + + Copyright (C) 2004, 2005, 2006, 2007, 2008 Rocky Bernstein + (C) 1998 Monty + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + See ChangeLog for recent changes. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STDIO_H +# include +#endif + +#ifdef HAVE_STDARG_H +# include +#endif + +#ifdef HAVE_LIMITS_H +#include +#endif + +#ifdef HAVE_STDLIB_H +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef HAVE_STRING_H +# include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#include "getopt.h" + +#ifdef HAVE_ERRNO_H +#include +#endif + +#include +#include + +#ifdef HAVE_SYS_STAT_H +# include +#endif + +#if !defined(HAVE_GETTIMEOFDAY) +/* MinGW uses sys/time.h and sys/timeb.h to roll its own gettimeofday() */ +# if defined(HAVE_SYS_TIME_H) && defined(HAVE_SYS_TIMEB_H) +# include +# include +static void gettimeofday(struct timeval* tv, void* timezone); +# endif +#endif /* !defined(HAVE_GETTIMEOFDAY) */ + +#include +#include +#include +#include +#include +#include +#include "utils.h" +#include "report.h" +#include "version.h" +#include "header.h" +#include "buffering_write.h" + +extern int verbose; +extern int quiet; + +/* I wonder how many alignment issues this is gonna trip in the + future... it shouldn't trip any... I guess we'll find out :) */ + +static int +bigendianp(void) +{ + int test=1; + char *hack=(char *)(&test); + if(hack[0])return(0); + return(1); +} + +static long +parse_offset(cdrom_drive_t *d, char *offset, int begin) +{ + track_t i_track= CDIO_INVALID_TRACK; + long hours = -1; + long minutes = -1; + long seconds = -1; + long sectors = -1; + char *time = NULL; + char *temp = NULL; + long ret; + + if (!offset) return -1; + + /* seperate track from time offset */ + temp=strchr(offset,']'); + if(temp){ + *temp='\0'; + temp=strchr(offset,'['); + if(temp==NULL){ + report("Error parsing span argument"); + exit(1); + } + *temp='\0'; + time=temp+1; + } + + /* parse track */ + { + int chars=strspn(offset,"0123456789"); + if(chars>0){ + offset[chars]='\0'; + i_track=atoi(offset); + if ( i_track > d->tracks ) { + /*take track i_first_track-1 as pre-gap of 1st track*/ + char buffer[256]; + snprintf(buffer, sizeof(buffer), + "Track #%d does not exist.", i_track); + report(buffer); + exit(1); + } + } + } + + while(time){ + long val,chars; + char *sec=strrchr(time,'.'); + if(!sec)sec=strrchr(time,':'); + if(!sec)sec=time-1; + + chars=strspn(sec+1,"0123456789"); + if(chars) + val=atoi(sec+1); + else + val=0; + + switch(*sec){ + case '.': + if(sectors!=-1){ + report("Error parsing span argument"); + exit(1); + } + sectors=val; + break; + default: + if(seconds==-1) + seconds=val; + else + if(minutes==-1) + minutes=val; + else + if(hours==-1) + hours=val; + else{ + report("Error parsing span argument"); + exit(1); + } + break; + } + + if (sec<=time) break; + *sec='\0'; + } + + if (i_track == CDIO_INVALID_TRACK) { + if (seconds==-1 && sectors==-1) return -1; + if (begin==-1) { + ret=cdda_disc_firstsector(d); + } else + ret = begin; + } else { + if ( seconds==-1 && sectors==-1 ) { + if (begin==-1){ + /* first half of a span */ + return(cdda_track_firstsector(d, i_track)); + }else{ + return(cdda_track_lastsector(d, i_track)); + } + } else { + /* relative offset into a track */ + ret=cdda_track_firstsector(d, i_track); + } + } + + /* OK, we had some sort of offset into a track */ + + if (sectors != -1) ret += sectors; + if (seconds != -1) ret += seconds*CDIO_CD_FRAMES_PER_SEC; + if (minutes != -1) ret += minutes*CDIO_CD_FRAMES_PER_MIN; + if (hours != -1) ret += hours *60*CDIO_CD_FRAMES_PER_MIN; + + /* We don't want to outside of the track; if it's relative, that's OK... */ + if( i_track != CDIO_INVALID_TRACK ){ + if (cdda_sector_gettrack(d,ret) != i_track) { + report("Time/sector offset goes beyond end of specified track."); + exit(1); + } + } + + /* Don't pass up end of session */ + + if( ret>cdda_disc_lastsector(d) ) { + report("Time/sector offset goes beyond end of disc."); + exit(1); + } + + return(ret); +} + +static void +display_toc(cdrom_drive_t *d) +{ + long audiolen=0; + track_t i; + + report("\nTable of contents (audio tracks only):\n" + "track length begin copy pre ch\n" + "==========================================================="); + + for( i=1; i<=d->tracks; i++) + if ( cdda_track_audiop(d,i) ) { + char buffer[256]; + + lsn_t sec=cdda_track_firstsector(d,i); + lsn_t off=cdda_track_lastsector(d,i)-sec+1; + + sprintf(buffer, + "%3d. %7ld [%02d:%02d.%02d] %7ld [%02d:%02d.%02d] %s %s %s", + i, + (long int) off, + (int) (off/(CDIO_CD_FRAMES_PER_MIN)), + (int) ((off/CDIO_CD_FRAMES_PER_SEC) % CDIO_CD_SECS_PER_MIN), + (int) (off % CDIO_CD_FRAMES_PER_SEC), + (long int) sec, + (int) (sec/(CDIO_CD_FRAMES_PER_MIN)), + (int) ((sec/CDIO_CD_FRAMES_PER_SEC) % CDIO_CD_SECS_PER_MIN), + (int) (sec % CDIO_CD_FRAMES_PER_SEC), + cdda_track_copyp(d,i)?" OK":" no", + cdda_track_preemp(d,i)?" yes":" no", + cdda_track_channels(d,i)==2?" 2":" 4"); + report(buffer); + audiolen+=off; + } + { + char buffer[256]; + sprintf(buffer, "TOTAL %7ld [%02d:%02d.%02d] (audio only)", + audiolen, + (int) (audiolen/(CDIO_CD_FRAMES_PER_MIN)), + (int) ((audiolen/CDIO_CD_FRAMES_PER_SEC) % CDIO_CD_SECS_PER_MIN), + (int) (audiolen % CDIO_CD_FRAMES_PER_SEC)); + report(buffer); + } + report(""); +} + +#include "usage.h" +static void usage(FILE *f) +{ + fprintf( f, usage_help); +} + +static long callbegin; +static long callend; +static long callscript=0; + +static int skipped_flag=0; +static int abort_on_skip=0; +static FILE *logfile = NULL; + +#if TRACE_PARANOIA +static void +callback(long int inpos, paranoia_cb_mode_t function) +{ +} +#else +static const char *callback_strings[15]={ + "wrote", + "finished", + "read", + "verify", + "jitter", + "correction", + "scratch", + "scratch repair", + "skip", + "drift", + "backoff", + "overlap", + "dropped", + "duped", + "transport error"}; + +static void +callback(long int inpos, paranoia_cb_mode_t function) +{ + /* + + (== PROGRESS == [--+!---x--------------> | 007218 01 ] == :-) . ==) + + */ + + int graph=30; + char buffer[256]; + static long c_sector=0, v_sector=0; + static char dispcache[]=" "; + static int last=0; + static long lasttime=0; + long int sector, osector=0; + struct timeval thistime; + static char heartbeat=' '; + int position=0,aheadposition=0; + static int overlap=0; + static int printit=-1; + + static int slevel=0; + static int slast=0; + static int stimeout=0; + const char *smilie="= :-)"; + + if (callscript) + fprintf(stderr, "##: %d [%s] @ %ld\n", + function, ((int) function >= -2 && (int) function <= 13 ? + callback_strings[function+2] : ""), + inpos); + + if(!quiet){ + long test; + osector=inpos; + sector=inpos/CD_FRAMEWORDS; + + if(printit==-1){ + if(isatty(STDERR_FILENO)){ + printit=1; + }else{ + printit=0; + } + } + + if(printit==1){ /* else don't bother; it's probably being + redirected */ + position=((float)(sector-callbegin)/ + (callend-callbegin))*graph; + + aheadposition=((float)(c_sector-callbegin)/ + (callend-callbegin))*graph; + + if(function==-2){ + v_sector=sector; + return; + } + if(function==-1){ + last=8; + heartbeat='*'; + slevel=0; + v_sector=sector; + }else + if(position=0) + switch(function){ + case PARANOIA_CB_VERIFY: + if(stimeout>=30){ + if(overlap>CD_FRAMEWORDS) + slevel=2; + else + slevel=1; + } + break; + case PARANOIA_CB_READ: + if(sector>c_sector)c_sector=sector; + break; + + case PARANOIA_CB_FIXUP_EDGE: + if(stimeout>=5){ + if(overlap>CD_FRAMEWORDS) + slevel=2; + else + slevel=1; + } + if(dispcache[position]==' ') + dispcache[position]='-'; + break; + case PARANOIA_CB_FIXUP_ATOM: + if(slevel<3 || stimeout>5)slevel=3; + if(dispcache[position]==' ' || + dispcache[position]=='-') + dispcache[position]='+'; + break; + case PARANOIA_CB_READERR: + slevel=6; + if(dispcache[position]!='V') + dispcache[position]='e'; + break; + case PARANOIA_CB_SKIP: + slevel=8; + dispcache[position]='V'; + break; + case PARANOIA_CB_OVERLAP: + overlap=osector; + break; + case PARANOIA_CB_SCRATCH: + slevel=7; + break; + case PARANOIA_CB_DRIFT: + if(slevel<4 || stimeout>5)slevel=4; + break; + case PARANOIA_CB_FIXUP_DROPPED: + case PARANOIA_CB_FIXUP_DUPED: + slevel=5; + if(dispcache[position]==' ' || + dispcache[position]=='-' || + dispcache[position]=='+') + dispcache[position]='!'; + break; + case PARANOIA_CB_REPAIR: + case PARANOIA_CB_BACKOFF: + break; + } + + switch(slevel){ + case 0: /* finished, or no jitter */ + if(skipped_flag) + smilie=" 8-X"; + else + smilie=" :^D"; + break; + case 1: /* normal. no atom, low jitter */ + smilie=" :-)"; + break; + case 2: /* normal, overlap > 1 */ + smilie=" :-|"; + break; + case 4: /* drift */ + smilie=" :-/"; + break; + case 3: /* unreported loss of streaming */ + smilie=" :-P"; + break; + case 5: /* dropped/duped bytes */ + smilie=" 8-|"; + break; + case 6: /* scsi error */ + smilie=" :-0"; + break; + case 7: /* scratch */ + smilie=" :-("; + break; + case 8: /* skip */ + smilie=" ;-("; + skipped_flag=1; + break; + + } + + gettimeofday(&thistime,NULL); + test=thistime.tv_sec*10+thistime.tv_usec/100000; + + if(lasttime!=test || function==-1 || slast!=slevel){ + if(lasttime!=test || function==-1){ + last++; + lasttime=test; + if(last>7)last=0; + stimeout++; + switch(last){ + case 0: + heartbeat=' '; + break; + case 1:case 7: + heartbeat='.'; + break; + case 2:case 6: + heartbeat='o'; + break; + case 3:case 5: + heartbeat='0'; + break; + case 4: + heartbeat='O'; + break; + } + if(function==-1) + heartbeat='*'; + + } + if(slast!=slevel){ + stimeout=0; + } + slast=slevel; + + if(abort_on_skip && skipped_flag && function !=-1){ + sprintf(buffer, + "\r (== PROGRESS == [%s| %06ld %02d ] ==%s %c ==) ", + " ...aborting; please wait... ", + v_sector,overlap/CD_FRAMEWORDS,smilie,heartbeat); + }else{ + if(v_sector==0) + sprintf(buffer, + "\r (== PROGRESS == [%s| ...... %02d ] ==%s %c ==) ", + dispcache,overlap/CD_FRAMEWORDS,smilie,heartbeat); + + else + sprintf(buffer, + "\r (== PROGRESS == [%s| %06ld %02d ] ==%s %c ==) ", + dispcache,v_sector,overlap/CD_FRAMEWORDS,smilie,heartbeat); + + if(aheadposition>=0 && aheadpositiontv_sec=timebuffer.time; + tv->tv_usec=1000*timebuffer.millitm; +} +#endif + +/* This is run automatically before leaving the program. + Free allocated resources. +*/ +static void +cleanup (void) +{ + if (p) paranoia_free(p); + if (d) cdda_close(d); + free_and_null(force_cdrom_device); + free_and_null(span); + if(logfile && logfile != stdout) { + fclose(logfile); + logfile = NULL; + } +} + +/* Returns true if we have an integer argument. + If so, pi_arg is set. + If no argument or integer argument found, we give an error + message and return false. +*/ +static bool +get_int_arg(char c, long int *pi_arg) +{ + long int i_arg; + char *p_end; + if (!optarg) { + /* This shouldn't happen, but we'll check anyway. */ + fprintf(stderr, + "An (integer) argument for option -%c was expected " + " but not found. Option ignored\n", c); + return false; + } + errno = 0; + i_arg = strtol(optarg, &p_end, 10); + if ( (LONG_MIN == i_arg || LONG_MAX == i_arg) && (0 != errno) ) { + fprintf(stderr, + "Value '%s' for option -%c out of range. Value %ld " + "used instead.\n", optarg, c, i_arg); + *pi_arg = i_arg; + return false; + } else if (*p_end) { + fprintf(stderr, + "Can't convert '%s' for option -%c completely into an integer. " + "Option ignored.\n", optarg, c); + return false; + } else { + *pi_arg = i_arg; + return true; + } +} + +int +main(int argc,char *argv[]) +{ + int toc_bias = 0; + int force_cdrom_endian = -1; + int output_type = 1; /* 0=raw, 1=wav, 2=aifc */ + int output_endian = 0; /* -1=host, 0=little, 1=big */ + int query_only = 0; + int batch = 0; + long int force_cdrom_overlap = -1; + long int force_cdrom_sectors = -1; + long int force_cdrom_speed = -1; + long int sample_offset = 0; + long int test_flags = 0; + long int toc_offset = 0; + long int max_retries = 20; + + /* full paranoia, but allow skipping */ + int paranoia_mode=PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP; + + int out; + + int search=0; + int c,long_option_index; + + atexit(cleanup); + + while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){ + switch(c){ + case 'a': + output_type=2; + output_endian=1; + break; + case 'B': + batch=1; + break; + case 'c': + force_cdrom_endian=0; + break; + case 'C': + force_cdrom_endian=1; + break; + case 'e': + callscript=1; + fprintf(stderr, + "Sending all callback output to stderr for wrapper script\n"); + break; + case 'f': + output_type=3; + output_endian=1; + break; + case 'F': + paranoia_mode&=~(PARANOIA_MODE_FRAGMENT); + break; + case 'g': + case 'd': + if (force_cdrom_device) { + fprintf(stderr, + "Multiple cdrom devices given. Previous device %s ignored\n", + force_cdrom_device); + free(force_cdrom_device); + } + force_cdrom_device=strdup(optarg); + break; + case 'h': + usage(stdout); + exit(0); + case 'l': + if(logfile && logfile != stdout)fclose(logfile); + if(!strcmp(optarg,"-")) + logfile=stdout; + else{ + logfile=fopen(optarg,"w"); + if(logfile==NULL){ + report3("Cannot open log summary file %s: %s",(char*)optarg, + strerror(errno)); + exit(1); + } + } + break; + case 'm': + { + long int mmc_timeout_sec; + if (get_int_arg(c, &mmc_timeout_sec)) { + mmc_timeout_ms = 1000*mmc_timeout_sec; + } + } + break; + case 'n': + get_int_arg(c, &force_cdrom_sectors); + break; + case 'o': + get_int_arg(c, &force_cdrom_overlap); + break; + case 'O': + get_int_arg(c, &sample_offset); + break; + case 'p': + output_type=0; + output_endian=-1; + break; + case 'r': + output_type=0; + output_endian=0; + break; + case 'q': + verbose=CDDA_MESSAGE_FORGETIT; + quiet=1; + break; + case 'Q': + query_only=1; + break; + case 'R': + output_type=0; + output_endian=1; + break; + case 's': + search=1; + break; + case 'S': + get_int_arg(c, &force_cdrom_speed); + break; + case 't': + get_int_arg(c, &toc_offset); + break; + case 'T': + toc_bias=-1; + break; + case 'v': + verbose=CDDA_MESSAGE_PRINTIT; + quiet=0; + break; + case 'V': + fprintf(stderr,PARANOIA_VERSION); + fprintf(stderr,"\n"); + exit(0); + break; + case 'w': + output_type=1; + output_endian=0; + break; + case 'W': + paranoia_mode&=~PARANOIA_MODE_REPAIR; + break; + case 'x': + get_int_arg(c, &test_flags); + break; + case 'X': + /*paranoia_mode&=~(PARANOIA_MODE_SCRATCH|PARANOIA_MODE_REPAIR);*/ + abort_on_skip=1; + break; + case 'Y': + paranoia_mode|=PARANOIA_MODE_OVERLAP; /* cdda2wav style overlap + check only */ + paranoia_mode&=~PARANOIA_MODE_VERIFY; + break; + case 'Z': + paranoia_mode=PARANOIA_MODE_DISABLE; + break; + case 'z': + if (optarg) { + get_int_arg(c, &max_retries); + paranoia_mode&=~PARANOIA_MODE_NEVERSKIP; + } else { + paranoia_mode|=PARANOIA_MODE_NEVERSKIP; + } + break; + default: + usage(stderr); + exit(1); + } + } + + if(logfile){ + /* log command line and version */ + int i; + for (i = 0; i < argc; i++) + fprintf(logfile,"%s ",argv[i]); + fprintf(logfile,"\n"); + + fprintf(logfile,VERSION); + fprintf(logfile,"\n"); + fflush(logfile); + } + + if(optind>=argc && !query_only){ + if(batch) + span=NULL; + else{ + /* D'oh. No span. Fetch me a brain, Igor. */ + usage(stderr); + exit(1); + } + }else + if (argv[optind]) span=strdup(argv[optind]); + + report(PARANOIA_VERSION); + + /* Query the cdrom/disc; we may need to override some settings */ + + if(force_cdrom_device) + d=cdda_identify(force_cdrom_device,verbose,NULL); + else { + driver_id_t driver_id; + char **ppsz_cd_drives = cdio_get_devices_with_cap_ret(NULL, + CDIO_FS_AUDIO, + false, + &driver_id); + if (ppsz_cd_drives && *ppsz_cd_drives) { + d=cdda_identify(*ppsz_cd_drives,verbose, NULL); + } else { + report("\nUnable find or access a CD-ROM drive with an audio CD" + " in it."); + report("\nYou might try specifying the drive, especially if it has" + " mixed-mode (and non-audio) format tracks"); + exit(1); + } + + cdio_free_device_list(ppsz_cd_drives); + } + + if(!d){ + if(!verbose) + report("\nUnable to open cdrom drive; -v might give more information."); + exit(1); + } + + if(verbose) + cdda_verbose_set(d,CDDA_MESSAGE_PRINTIT,CDDA_MESSAGE_PRINTIT); + else + cdda_verbose_set(d,CDDA_MESSAGE_PRINTIT,CDDA_MESSAGE_FORGETIT); + + /* possibly force hand on endianness of drive, sector request size */ + if(force_cdrom_endian!=-1){ + d->bigendianp=force_cdrom_endian; + switch(force_cdrom_endian){ + case 0: + report("Forcing CDROM sense to little-endian; ignoring preset and autosense"); + break; + case 1: + report("Forcing CDROM sense to big-endian; ignoring preset and autosense"); + break; + } + } + if (force_cdrom_sectors!=-1) { + if(force_cdrom_sectors<0 || force_cdrom_sectors>100){ + report("Default sector read size must be 1<= n <= 100\n"); + cdda_close(d); + d=NULL; + exit(1); + } + { + char buffer[256]; + sprintf(buffer,"Forcing default to read %ld sectors; " + "ignoring preset and autosense", force_cdrom_sectors); + report(buffer); + d->nsectors=force_cdrom_sectors; + } + } + if (force_cdrom_overlap!=-1) { + if (force_cdrom_overlap<0 || force_cdrom_overlap>CDIO_CD_FRAMES_PER_SEC) { + report("Search overlap sectors must be 0<= n <=75\n"); + cdda_close(d); + d=NULL; + if(logfile && logfile != stdout) + fclose(logfile); + exit(1); + } + { + char buffer[256]; + sprintf(buffer,"Forcing search overlap to %ld sectors; " + "ignoring autosense", force_cdrom_overlap); + report(buffer); + } + } + + switch( cdda_open(d) ) { + case -2:case -3:case -4:case -5: + report("\nUnable to open disc. Is there an audio CD in the drive?"); + exit(1); + case -6: + report("\nCdparanoia could not find a way to read audio from this drive."); + exit(1); + case 0: + break; + default: + report("\nUnable to open disc."); + exit(1); + } + + d->i_test_flags = test_flags; + + /* Dump the TOC */ + if (query_only || verbose ) display_toc(d); + + if (query_only) exit(0); + + /* bias the disc. A hack. Of course. this is never the default. */ + /* + Some CD-ROM/CD-R drives will add an offset to the position on + reading audio data. This is usually around 500-700 audio samples + (ca. 1/75 second) on reading. So when this program queries a + specific sector, it might not receive exactly that sector, but + shifted by some amount. + + Note that if ripping includes the end of the CD, this will this + cause this program to attempt to read partial sectors before or + past the known user data area of the disc, probably causing read + errors on most drives and possibly even hard lockups on some + buggy hardware. + + [Note to libcdio driver hackers: make sure all CD-drivers don't + try to read outside of the stated disc boundaries.] + */ + if(sample_offset){ + toc_offset+=sample_offset/588; + sample_offset%=588; + if(sample_offset<0){ + sample_offset+=588; + toc_offset--; + } + } + + if (toc_bias) { + toc_offset = -cdda_track_firstsector(d,1); + } + + { + int i; + for( i=0; i < d->tracks+1; i++ ) + d->disc_toc[i].dwStartSector+=toc_offset; + } + + if (force_cdrom_speed != -1) { + cdda_speed_set(d,force_cdrom_speed); + } + + if (d->nsectors==1) { + report("WARNING: The autosensed/selected sectors per read value is\n" + " one sector, making it very unlikely Paranoia can \n" + " work.\n\n" + " Attempting to continue...\n\n"); + } + + /* parse the span, set up begin and end sectors */ + + { + long i_first_lsn; + long i_last_lsn; + long batch_first; + long batch_last; + int batch_track; + + if (span) { + /* look for the hyphen */ + char *span2=strchr(span,'-'); + if(strrchr(span,'-')!=span2){ + report("Error parsing span argument"); + exit(1); + } + + if (span2!=NULL) { + *span2='\0'; + span2++; + } + + i_first_lsn=parse_offset(d, span, -1); + + if(i_first_lsn==-1) + i_last_lsn=parse_offset(d, span2, cdda_disc_firstsector(d)); + + else + i_last_lsn=parse_offset(d, span2, i_first_lsn); + + if (i_first_lsn == -1) { + if (i_last_lsn == -1) { + report("Error parsing span argument"); + exit(1); + } else { + i_first_lsn=cdda_disc_firstsector(d); + } + } else { + if (i_last_lsn==-1) { + if (span2) { /* There was a hyphen */ + i_last_lsn=cdda_disc_lastsector(d); + } else { + i_last_lsn= + cdda_track_lastsector(d,cdda_sector_gettrack(d, i_first_lsn)); + } + } + } + } else { + i_first_lsn = cdda_disc_firstsector(d); + i_last_lsn = cdda_disc_lastsector(d); + } + + { + char buffer[250]; + int track1 = cdda_sector_gettrack(d, i_first_lsn); + int track2 = cdda_sector_gettrack(d, i_last_lsn); + long off1 = i_first_lsn - cdda_track_firstsector(d, track1); + long off2 = i_last_lsn - cdda_track_firstsector(d, track2); + int i; + + for( i=track1; i<=track2; i++ ) + if(i != 0 && !cdda_track_audiop(d,i)){ + report("Selected span contains non audio tracks. Aborting.\n\n"); + exit(1); + } + + sprintf(buffer, "Ripping from sector %7ld (track %2d [%d:%02d.%02d])\n" + "\t to sector %7ld (track %2d [%d:%02d.%02d])\n", + i_first_lsn, + track1, + (int) (off1/(CDIO_CD_FRAMES_PER_MIN)), + (int) ((off1/CDIO_CD_FRAMES_PER_SEC) % CDIO_CD_SECS_PER_MIN), + (int) (off1 % CDIO_CD_FRAMES_PER_SEC), + i_last_lsn, + track2, + (int) (off2/(CDIO_CD_FRAMES_PER_MIN)), + (int) ((off2/CDIO_CD_FRAMES_PER_SEC) % CDIO_CD_SECS_PER_MIN), + (int) (off2 % CDIO_CD_FRAMES_PER_SEC)); + report(buffer); + + } + + { + long cursor; + int16_t offset_buffer[1176]; + int offset_buffer_used=0; + int offset_skip=sample_offset*4; + + p=paranoia_init(d); + paranoia_modeset(p,paranoia_mode); + if(force_cdrom_overlap!=-1)paranoia_overlapset(p,force_cdrom_overlap); + + if(verbose) { + cdda_verbose_set(d,CDDA_MESSAGE_LOGIT,CDDA_MESSAGE_LOGIT); + cdio_loglevel_default = CDIO_LOG_INFO; + } else + cdda_verbose_set(d,CDDA_MESSAGE_FORGETIT,CDDA_MESSAGE_FORGETIT); + + paranoia_seek(p,cursor=i_first_lsn,SEEK_SET); + + /* this is probably a good idea in general */ +#if defined(HAVE_GETUID) && defined(HAVE_SETEUID) + seteuid(getuid()); +#endif +#if defined(HAVE_GETGID) && defined(HAVE_SETEGID) + setegid(getgid()); +#endif + + /* we'll need to be able to read one sector past user data if we + have a sample offset in order to pick up the last bytes. We + need to set the disc length forward here so that the libs are + willing to read past, assuming that works on the hardware, of + course */ + if(sample_offset) + d->disc_toc[d->tracks].dwStartSector++; + + while(cursor<=i_last_lsn){ + char outfile_name[256]; + if ( batch ){ + batch_first = cursor; + batch_track = cdda_sector_gettrack(d,cursor); + batch_last = cdda_track_lastsector(d, batch_track); + if (batch_last>i_last_lsn) batch_last=i_last_lsn; + } else { + batch_first = i_first_lsn; + batch_last = i_last_lsn; + batch_track = -1; + } + + callbegin=batch_first; + callend=batch_last; + + /* argv[optind] is the span, argv[optind+1] (if exists) is outfile */ + + if (optind+1256?256:pos); + + if(batch) + snprintf(outfile_name, 246, " %strack%02d.%s", path, + batch_track, file); + else + snprintf(outfile_name, 246, "%s%s", path, file); + + if(file[0]=='\0'){ + switch (output_type) { + case 0: /* raw */ + strncat(outfile_name, "cdda.raw", sizeof("cdda.raw")); + break; + case 1: + strncat(outfile_name, "cdda.wav", sizeof("cdda.wav")); + break; + case 2: + strncat(outfile_name, "cdda.aifc", sizeof("cdda.aifc")); + break; + case 3: + strncat(outfile_name, "cdda.aiff", sizeof("cdda.aiff")); + break; + } + } + + out=open(outfile_name,O_RDWR|O_CREAT|O_TRUNC,0666); + if(out==-1){ + report3("Cannot open specified output file %s: %s", + outfile_name, strerror(errno)); + exit(1); + } + report2("outputting to %s\n", outfile_name); + if(logfile){ + fprintf(logfile,"outputting to %s\n",outfile_name); + fflush(logfile); + } + } + } else { + /* default */ + if (batch) + sprintf(outfile_name,"track%02d.", batch_track); + else + outfile_name[0]='\0'; + + switch(output_type){ + case 0: /* raw */ + strncat(outfile_name, "cdda.raw", sizeof("cdda.raw")); + break; + case 1: + strncat(outfile_name, "cdda.wav", sizeof("cdda.wav")); + break; + case 2: + strncat(outfile_name, "cdda.aifc", sizeof("cdda.aifc")); + break; + case 3: + strncat(outfile_name, "cdda.aiff", sizeof("cdda.aiff")); + break; + } + + out = open(outfile_name, O_RDWR|O_CREAT|O_TRUNC, 0666); + if(out==-1){ + report3("Cannot open default output file %s: %s", outfile_name, + strerror(errno)); + exit(1); + } + report2("outputting to %s\n", outfile_name); + if(logfile){ + fprintf(logfile,"outputting to %s\n",outfile_name); + fflush(logfile); + } + + } + + switch(output_type) { + case 0: /* raw */ + break; + case 1: /* wav */ + WriteWav(out, (batch_last-batch_first+1)*CDIO_CD_FRAMESIZE_RAW); + break; + case 2: /* aifc */ + WriteAifc(out, (batch_last-batch_first+1)*CDIO_CD_FRAMESIZE_RAW); + break; + case 3: /* aiff */ + WriteAiff(out, (batch_last-batch_first+1)*CDIO_CD_FRAMESIZE_RAW); + break; + } + + /* Off we go! */ + + if(offset_buffer_used){ + /* partial sector from previous batch read */ + cursor++; + if (buffering_write(out, + ((char *)offset_buffer)+offset_buffer_used, + CDIO_CD_FRAMESIZE_RAW-offset_buffer_used)){ + report2("Error writing output: %s", strerror(errno)); + exit(1); + } + } + + skipped_flag=0; + while(cursor<=batch_last){ + /* read a sector */ + int16_t *readbuf=paranoia_read_limited(p, callback, max_retries); + char *err=cdda_errors(d); + char *mes=cdda_messages(d); + + if(mes || err) + fprintf(stderr,"\r " + " \r%s%s\n", + mes?mes:"",err?err:""); + + if (err) free(err); + if (mes) free(mes); + if( readbuf==NULL) { + skipped_flag=1; + report("\nparanoia_read: Unrecoverable error, bailing.\n"); + break; + } + if(skipped_flag && abort_on_skip){ + cursor=batch_last+1; + break; + } + + skipped_flag=0; + cursor++; + + if (output_endian!=bigendianp()) { + int i; + for (i=0; ibatch_last){ + int i; + /* read a sector and output the partial offset. Save the + rest for the next batch iteration */ + readbuf=paranoia_read_limited(p,callback,max_retries); + err=cdda_errors(d);mes=cdda_messages(d); + + if(mes || err) + fprintf(stderr,"\r " + " \r%s%s\n", + mes?mes:"",err?err:""); + + if(err)free(err);if(mes)free(mes); + if(readbuf==NULL){ + skipped_flag=1; + report("\nparanoia_read: Unrecoverable error reading through " + "sample_offset shift\n\tat end of track, bailing.\n"); + break; + } + if (skipped_flag && abort_on_skip) break; + skipped_flag=0; + /* do not move the cursor */ + + if(output_endian!=bigendianp()) + for(i=0;i +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +SUBDIRS = en ja +EXTRA_DIST = FAQ.txt overlapdef.txt + +mostlyclean-generic: + -rm -f *~ \#* .*~ .\#* + +maintainer-clean-generic: + -@echo "This command is intended for maintainers to use;" + -@echo "it deletes files that may require special tools to rebuild." + -rm -f Makefile.in diff --git a/src/cd-paranoia/doc/en/.cvsignore b/src/cd-paranoia/doc/en/.cvsignore new file mode 100644 index 00000000..1d681d1e --- /dev/null +++ b/src/cd-paranoia/doc/en/.cvsignore @@ -0,0 +1,4 @@ +Makefile +Makefile.in +cd-paranoia.1 + diff --git a/src/cd-paranoia/doc/en/.gitignore b/src/cd-paranoia/doc/en/.gitignore new file mode 100644 index 00000000..6304f6b1 --- /dev/null +++ b/src/cd-paranoia/doc/en/.gitignore @@ -0,0 +1,3 @@ +/Makefile +/Makefile.in +/cd-paranoia.1 diff --git a/src/cd-paranoia/doc/en/Makefile.am b/src/cd-paranoia/doc/en/Makefile.am new file mode 100644 index 00000000..b70fbe8b --- /dev/null +++ b/src/cd-paranoia/doc/en/Makefile.am @@ -0,0 +1,30 @@ +# $Id: Makefile.am,v 1.3 2008/04/17 17:39:48 karl Exp $ +# +# Copyright (C) 2005, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +manfiles = cd-paranoia.1 +man_MANS = $(manfiles) +transform = s,cd-paranoia,@CDPARANOIA_NAME@, + +EXTRA_DIST = $(manfiles) cd-paranoia.1.in + +mostlyclean-generic: + -rm -f *~ \#* .*~ .\#* + +maintainer-clean-generic: + -@echo "This command is intended for maintainers to use;" + -@echo "it deletes files that may require special tools to rebuild." + -rm -f Makefile.in $(manfiles) diff --git a/src/cd-paranoia/doc/en/cd-paranoia.1.in b/src/cd-paranoia/doc/en/cd-paranoia.1.in new file mode 100644 index 00000000..0d6fa4af --- /dev/null +++ b/src/cd-paranoia/doc/en/cd-paranoia.1.in @@ -0,0 +1,381 @@ +.TH @CDPARANOIA_NAME@ 1 "version III release alpha 9.8 libcdio" +.SH NAME +@CDPARANOIA_NAME@ 9.8 (Paranoia release III via libcdio) \- an audio CD reading utility which includes extra data verification features +.SH SYNOPSIS +.B @CDPARANOIA_NAME@ +.RB [ options ] +.B span +.RB [ outfile ] +.SH DESCRIPTION +.B @CDPARANOIA_NAME@ +retrieves audio tracks from CDDA capable CD-ROM drives. The data can +be saved to a file or directed to standard output in WAV, AIFF, AIFF-C +or raw format. Most ATAPI, SCSI and several proprietary CD-ROM drive +makes are supported; +.B @CDPARANOIA_NAME@ +can determine if the target drive is CDDA capable. +.P +In addition to simple reading, +.B @CDPARANOIA_NAME@ +adds extra-robust data verification, synchronization, error handling +and scratch reconstruction capability. +.P +This version uses the libcdio library for interaction with a CD-ROM +drive. The jitter and error correction however are the same as used in +Xiph's cdparanoia. +.SH OPTIONS + +.TP +.B \-v --verbose +Be absurdly verbose about the autosensing and reading process. Good +for setup and debugging. + +.TP +.B \-q --quiet +Do not print any progress or error information during the reading process. + +.TP +.B \-e --stderr-progress +Force output of progress information to stderr (for wrapper scripts). + +.TP +.B \-V --version +Print the program version and quit. + +.TP +.B \-Q --query +Perform CD-ROM drive autosense, query and print the CD-ROM table of +contents, then quit. + +.TP +.B \-s --search-for-drive +Forces a complete search for a cdrom drive, even if the /dev/cdrom link exists. + +.TP +.B \-h --help +Print a brief synopsis of +.B @CDPARANOIA_NAME@ +usage and options. + +.TP +.BI "\-l --log-summary " file +Save result summary to file. + +.TP +.B \-p --output-raw +Output headerless data as raw 16 bit PCM data with interleaved samples in host byte order. To force little or big endian byte order, use +.B \-r +or +.B \-R +as described below. + +.TP +.B \-r --output-raw-little-endian +Output headerless data as raw 16 bit PCM data with interleaved samples in LSB first byte order. + +.TP +.B \-R --output-raw-big-endian +Output headerless data as raw 16 bit PCM data with interleaved samples in MSB first byte order. + +.TP +.B \-w --output-wav +Output data in Micro$oft RIFF WAV format (note that WAV data is always +LSB first byte order). + +.TP +.B \-f --output-aiff +Output data in Apple AIFF format (note that AIFC data is +always in MSB first byte order). + +.TP +.B \-a --output-aifc +Output data in uncompressed Apple AIFF-C format (note that AIFF-C data is +always in MSB first byte order). + +.TP +.BI "\-B --batch " + +Cdda2wav-style batch output flag; @CDPARANOIA_NAME@ will split the output +into multiple files at track boundaries. Output file names are +prepended with 'track#.' + +.TP +.B \-c --force-cdrom-little-endian +Some CD-ROM drives misreport their endianness (or do not report it at +all); it's possible that @CDPARANOIA_NAME@ will guess wrong. Use +.B \-c +to force @CDPARANOIA_NAME@ to treat the drive as a little endian device. + +.TP +.B \-C --force-cdrom-big-endian +As above but force @CDPARANOIA_NAME@ to treat the drive as a big endian device. + +.TP +.BI "\-n --force-default-sectors " n +Force the interface backend to do atomic reads of +.B n +sectors per read. This number can be misleading; the kernel will often +split read requests into multiple atomic reads (the automated Paranoia +code is aware of this) or allow reads only wihin a restricted size +range. +.B This option should generally not be used. + +.TP +.BI "\-d --force-cdrom-device " device +Force the interface backend to read from +.B device +rather than the first readable CD-ROM drive it finds containing a +CD-DA disc. This can be used to specify devices of any valid +interface type (ATAPI, SCSI or proprietary). + +.TP +.BI "\-g --force-generic-device " device +This option is an alias for +.B \-d +and is retained for compatibility. + +.TP +.BI "\-S --force-read-speed " number +Use this option explicitly to set the read rate of the CD drive (where +supported). This can reduce underruns on machines with slow disks, or +which are low on memory. + +.TP +.BI "\-t --toc-offset " number +Use this option to force the entire disc LBA addressing to shift by +the given amount; the value is added to the beginning offsets in the +TOC. This can be used to shift track boundaries for the whole disc +manually on sector granularity. The next option does something +similar... + +.TP +.BI "\-T --toc-bias " +Some drives (usually random Toshibas) report the actual track +beginning offset values in the TOC, but then treat the beginning of +track 1 index 1 as sector 0 for all read operations. This results in +every track seeming to start too late (losing a bit of the beginning +and catching a bit of the next track). +\-T accounts for this behavior. Note that this option will cause +@CDPARANOIA_NAME@ to attempt to read sectors before or past the known user +data area of the disc, resulting in read errors at disc edges on most +drives and possibly even hard lockups on some buggy hardware. + +.TP +.BI "\-O --sample-offset " number +Some CD-ROM/CD-R drives will add an offset to the position on reading +audio data. This is usually around 500-700 audio samples (ca. 1/75 +second) on reading. So when @CDPARANOIA_NAME@ queries a specific +sector, it might not receive exactly that sector, but shifted by some +amount. +.P +Use this option to force the entire disc to shift sample position +output by the given amount; This can be used to shift track boundaries +for the whole disc manually on sample granularity. Note that if you +are ripping something including the ending of the CD (e.g. the entire +disk), this option will cause @CDPARANOIA_NAME@ to attempt to read +partial sectors before or past the known user data area, probably +causing read errors on most drives and possibly even hard lockups on +some buggy hardware. + +.TP +.B \-Z --disable-paranoia +Disable +.B all +data verification and correction features. When using -Z, @CDPARANOIA_NAME@ +reads data exactly as would cdda2wav with an overlap setting of zero. +This option implies that +.B \-Y +is active. + +.TP +.B \-z --never-skip[=max_retries] +Do not accept any skips; retry forever if needed. An optional maximum +number of retries can be specified; for comparison, default without -z is +currently 20. + +.TP +.B \-Y --disable-extra-paranoia +Disables intra-read data verification; only overlap checking at read +boundaries is performed. It can wedge if errors occur in the attempted overlap area. Not recommended. + +.TP +.B \-X --abort-on-skip +If the read skips due to imperfect data, a scratch, whatever, abort reading this track. If output is to a file, delete the partially completed file. + +.TP +.B \-x --test-flags mask +Simulate CD-reading errors. This is used in regression testing, but +other uses might be to see how well a CD-ROM performs under +(simulated) CD degradation. mask specifies the artificial kinds of +errors to introduced; "or"-ing values from the selection below will +simulate the kind of specified failure. +.P + 0x10 - Simulate under-run reading +.TP + + +.SH OUTPUT SMILIES +.TP +.B + :-) +Normal operation, low/no jitter +.TP +.B + :-| +Normal operation, considerable jitter +.TP +.B + :-/ +Read drift +.TP +.B + :-P +Unreported loss of streaming in atomic read operation +.TP +.B + 8-| +Finding read problems at same point during reread; hard to correct +.TP +.B + :-0 +SCSI/ATAPI transport error +.TP +.B + :-( +Scratch detected +.TP +.B + ;-( +Gave up trying to perform a correction +.TP +.B + 8-X +Aborted read due to known, uncorrectable error +.TP +.B + :^D +Finished extracting + +.SH PROGRESS BAR SYMBOLS +.TP +.B + +No corrections needed +.TP +.B + - +Jitter correction required +.TP +.B + + +Unreported loss of streaming/other error in read +.TP +.B + ! +Errors found after stage 1 correction; the drive is making the +same error through multiple re-reads, and @CDPARANOIA_NAME@ is having trouble +detecting them. +.TP +.B + e +SCSI/ATAPI transport error (corrected) +.TP +.B + V +Uncorrected error/skip + +.SH SPAN ARGUMENT + +The span argument specifies which track, tracks or subsections of +tracks to read. This argument is required. +.B NOTE: +Unless the span is a simple number, it's generally a good idea to +quote the span argument to protect it from the shell. +.P +The span argument may be a simple track number or an offset/span +specification. The syntax of an offset/span takes the rough form: +.P +1[ww:xx:yy.zz]-2[aa:bb:cc.dd] +.P +Here, 1 and 2 are track numbers; the numbers in brackets provide a +finer grained offset within a particular track. [aa:bb:cc.dd] is in +hours/minutes/seconds/sectors format. Zero fields need not be +specified: [::20], [:20], [20], [20.], etc, would be interpreted as +twenty seconds, [10:] would be ten minutes, [.30] would be thirty +sectors (75 sectors per second). +.P +When only a single offset is supplied, it is interpreted as a starting +offset and ripping will continue to the end of the track. If a single +offset is preceeded or followed by a hyphen, the implicit missing +offset is taken to be the start or end of the disc, respectively. Thus: + +.TP +.B 1:[20.35] +Specifies ripping from track 1, second 20, sector 35 to the end of +track 1. +.TP +.B 1:[20.35]- +Specifies ripping from 1[20.35] to the end of the disc +.TP +.B \-2 +Specifies ripping from the beginning of the disc up to (and including) track 2 +.TP +.B \-2:[30.35] +Specifies ripping from the beginning of the disc up to 2:[30.35] +.TP +.B 2-4 +Specifies ripping from the beginning of track 2 to the end of track 4. +.P +Again, don't forget to protect square brackets and preceeding hyphens from +the shell. + +.SH EXAMPLES + +A few examples, protected from the shell: +.TP +Query only with exhaustive search for a drive and full reporting of autosense: +.P + @CDPARANOIA_NAME@ -vsQ +.TP +Extract an entire disc, putting each track in a seperate file: +.P + @CDPARANOIA_NAME@ -B +.TP +Extract from track 1, time 0:30.12 to 1:10.00: +.P + @CDPARANOIA_NAME@ "1[:30.12]-1[1:10]" +.TP +Extract from the beginning of the disc up to track 3: +.P + @CDPARANOIA_NAME@ -- "-3" +.TP +The "--" above is to distinguish "-3" from an option flag. +.SH OUTPUT + +The output file argument is optional; if it is not specified, +@CDPARANOIA_NAME@ will output samples to one of +.BR cdda.wav ", " cdda.aifc ", or " cdda.raw +depending on whether +.BR \-w ", " \-a ", " \-r " or " \-R " is used (" \-w +is the implicit default). The output file argument of +.B \- +specifies standard output; all data formats may be piped. + +.SH ACKNOWLEDGEMENTS +@CDPARANOIA_NAME@ sprang from and once drew heavily from the interface of +Heiko Eissfeldt's (heiko@colossus.escape.de) 'cdda2wav' +package. @CDPARANOIA_NAME@ would not have happened without it. +.P +Joerg Schilling has also contributed SCSI expertise through his +generic SCSI transport library. +.P +.SH AUTHOR +Monty +.P +Cdparanoia's homepage may be found at: +http://www.xiph.org/paranoia/ +.P +Revised for use with libcdio by Rocky +.P +The libcdio homepage may be found at: +http://www.gnu.org/software/libcdio diff --git a/src/cd-paranoia/doc/ja/.cvsignore b/src/cd-paranoia/doc/ja/.cvsignore new file mode 100644 index 00000000..899f03aa --- /dev/null +++ b/src/cd-paranoia/doc/ja/.cvsignore @@ -0,0 +1,3 @@ +Makefile +Makefile.in +cd-paranoia.1 diff --git a/src/cd-paranoia/doc/ja/.gitignore b/src/cd-paranoia/doc/ja/.gitignore new file mode 100644 index 00000000..6304f6b1 --- /dev/null +++ b/src/cd-paranoia/doc/ja/.gitignore @@ -0,0 +1,3 @@ +/Makefile +/Makefile.in +/cd-paranoia.1 diff --git a/src/cd-paranoia/doc/ja/Makefile.am b/src/cd-paranoia/doc/ja/Makefile.am new file mode 100644 index 00000000..bf4f7170 --- /dev/null +++ b/src/cd-paranoia/doc/ja/Makefile.am @@ -0,0 +1,72 @@ +# $Id: Makefile.am,v 1.2 2008/04/17 17:39:48 karl Exp $ +# +# Copyright (C) 2005, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +mansubdir=/jp/man1 +manfiles = cd-paranoia.1 +man_MANS = $(manfiles) cd-paranoia.1 +transform = s,cd-paranoia,@CDPARANOIA_NAME@, + +EXTRA_DIST = $(manfiles) cd-paranoia.1.in + +install-man1: $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man1dir)" || $(mkdir_p) "$(DESTDIR)$(mandir)$(mansubdir)" + @list='$(man1_MANS)'; \ + l2='$(man_MANS)'; for i in $$l2; do \ + case "$$i" in \ + *.1*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 1*) ;; \ + *) ext='1' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)$(mansubdir)/$$inst"; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)$(mansubdir)/$$inst; \ + done + +uninstall-man1: + @$(NORMAL_UNINSTALL) + @list='$(man1_MANS)'; \ + l2='$(man_MANS)'; for i in $$l2; do \ + case "$$i" in \ + *.1*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f $(DESTDIR)$(mandir)$(mansubdir)/$$inst"; \ + rm -f $(DESTDIR)$(mandir)$(mansubdir)/$$inst; \ + done + +mostlyclean-generic: + -rm -f *~ \#* .*~ .\#* + +maintainer-clean-generic: + -@echo "This command is intended for maintainers to use;" + -@echo "it deletes files that may require special tools to rebuild." + -rm -f Makefile.in $(manfiles) diff --git a/src/cd-paranoia/doc/ja/cd-paranoia.1.in b/src/cd-paranoia/doc/ja/cd-paranoia.1.in new file mode 100644 index 00000000..5143e06e --- /dev/null +++ b/src/cd-paranoia/doc/ja/cd-paranoia.1.in @@ -0,0 +1,359 @@ +.TH @CDPARANOIA_NAME@ 1 +.\" Translated Sun Aug 22 18:02:41 JST 1999 +.\" by FUJIWARA Teruyoshi +.SH ̾Á° +@CDPARANOIA_NAME@ (Paranoia release III libcdio) \- ¥ª¡¼¥Ç¥£¥ª CD ÆÉ¤ß¼è¤ê¥æ¡¼¥Æ¥£¥ê¥Æ¥£¡£ÆÃÊ̤ʥǡ¼¥¿¾È¹çµ¡Ç½¤ò»ý¤Ä¡£ +.SH ÆüÉÕ +¥Ð¡¼¥¸¥ç¥óIII ¥ê¥ê¡¼¥¹¦Á9.6 (17 Aug 1999) +.SH ½ñ¼° +.B @CDPARANOIA_NAME@ +.RB [ options ] +.B span +.RB [ outfile ] +.SH ÀâÌÀ +.B @CDPARANOIA_NAME@ +¤Ï CD-DA µ¡Ç½¤ò»ý¤Ä CD-ROM ¥É¥é¥¤¥Ö¤«¤é¥ª¡¼¥Ç¥£¥ª¥È¥é¥Ã¥¯¤ò¼è¤ê½Ð¤·¤Þ +¤¹¡£¤³¤Î¥Ç¡¼¥¿¤Ï WAV, AIFF, AIFF-C, raw ·Á¼°¤Ç¥Õ¥¡¥¤¥ë¤Ë¥»¡¼¥Ö¤¹¤ë¤³¤È +¤ä¡¢É¸½à½ÐÎϤËÁ÷¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤Û¤È¤ó¤É¤Î ATAPI, SCSI, ¥á¡¼¥«¡¼ÆÈ¼« +¤Î CD-ROM ¥É¥é¥¤¥Ö¤¬¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Þ¤¹¡£ +.B @CDPARANOIA_NAME@ +¤ÏÂоݤΥɥ饤¥Ö¤¬ CD-DA µ¡Ç½¤ò»ý¤Ã¤Æ¤¤¤ë¤«¤É¤¦¤«¤òȽÊ̤Ǥ­¤Þ¤¹¡£ +.P +ñ½ã¤ÊÆÉ¤ß¼è¤ê¤À¤±¤Ç¤Ê¤¯¡¢ +.B @CDPARANOIA_NAME@ +¤ÏÆÃÊ̤˴è·ò¤Ê¥Ç¡¼¥¿¾È¹çµ¡Ç½¡¢Æ±´üµ¡Ç½¡¢¥¨¥é¡¼½èÍýµ¡Ç½¡¢ÇË»¥Ç¡¼¥¿¤ÎºÆ +¹½À®µ¡Ç½¤ò»ý¤Ã¤Æ¤¤¤Þ¤¹¡£ +.SH ¥ª¥×¥·¥ç¥ó + +.TP +.B \-v --verbose +¼«Æ°¸¡½Ð¤ÈÆÉ¤ß¼è¤ê¤Î½èÍý¤Ë¤Ä¤¤¤Æ¡¢¤Ð¤«¤Ð¤«¤·¤¤¤Û¤É¾éŤÊɽ¼¨¤ò¹Ô¤¤¤Þ¤¹¡£ +ÀßÄê¤ä¥Ç¥Ð¥Ã¥°¤ÎºÝ¤ËÊØÍø¤Ç¤¹¡£ + +.TP +.B \-q --quiet +ÆÉ¤ß¼è¤ê½èÍý¤ÎÅÓÃæ¤Ë¡¢¿Ê¹Ô¾õ¶·¤ä¥¨¥é¡¼¾ðÊó¤òÁ´¤¯É½¼¨¤·¤Þ¤»¤ó¡£ + +.TP +.B \-e --stderr-progress +¿Ê¹Ô¾õ¶·¤ò(¥é¥Ã¥Ñ¥¹¥¯¥ê¥×¥È¤Î¤¿¤á¤Ë)ɸ½à¥¨¥é¡¼½ÐÎϤ˽ÐÎϤ·¤Þ¤¹¡£ + +.TP +.B \-V --version +¥×¥í¥°¥é¥à¤Î¥Ð¡¼¥¸¥ç¥ó¤òɽ¼¨¤·¤Æ½ªÎ»¤·¤Þ¤¹¡£ + +.TP +.B \-Q --query +CD-ROM ¥É¥é¥¤¥Ö¤Î¼«Æ°¸¡½Ð¤ò¹Ô¤¤¡¢CD-ROM ¤Î TOC ¤ÎÌ䤤¹ç¤ï¤»¤Èɽ¼¨¤ò¹Ô +¤¤¡¢½ªÎ»¤·¤Þ¤¹¡£ + +.TP +.B \-s --search-for-drive +¤¿¤È¤¨ /dev/cdrom ¤Î¥ê¥ó¥¯¤¬Â¸ºß¤·¤Æ¤¤¤Æ¤â¡¢CD-ROM ¥É¥é¥¤¥Ö¤Î´°Á´¤Ê +¸¡º÷¤ò¹Ô¤¤¤Þ¤¹¡£ + +.TP +.B \-h --help +.B @CDPARANOIA_NAME@ +¤Î»È¤¤Êý¤È¥ª¥×¥·¥ç¥ó¤ò´Êñ¤ÊÀâÌÀ¤ò½ÐÎϤ·¤Þ¤¹¡£ + +.TP +.B \-p --output-raw +¥Ø¥Ã¥À̵¤·¤Î¥Ç¡¼¥¿¤ò¥Û¥¹¥È¤Î¥Ð¥¤¥È½ç¤Ç¡¢¥¤¥ó¥¿¥ê¡¼¥Ö½èÍý¤ò»Ü¤·¤¿ +¥µ¥ó¥×¥ë²»À¼¤ò´Þ¤à raw ·Á¼°¤Î 16 ¥Ó¥Ã¥È PCM ¥Ç¡¼¥¿¤È¤·¤Æ½ÐÎϤ·¤Þ¤¹¡£ +¥Ð¥¤¥È½ç¤È¤·¤Æ¥ê¥È¥ë¥¨¥ó¥Ç¥£¥¢¥ó¤¢¤ë¤¤¤Ï¥Ó¥Ã¥°¥¨¥ó¥Ç¥£¥¢¥ó¤ò»ØÄꤹ¤ë¤Ë +¤Ï¡¢¸å½Ò¤Î +.B \-r +¤Þ¤¿¤Ï +.B \-R +¥ª¥×¥·¥ç¥ó¤ò»È¤Ã¤Æ¤¯¤À¤µ¤¤¡£ + +.TP +.B \-r --output-raw-little-endian +¥Ø¥Ã¥À̵¤·¤Î¥Ç¡¼¥¿¤ò LSB first ¤Î¥Ð¥¤¥È½ç¤Ç¡¢¥¤¥ó¥¿¥ê¡¼¥Ö½èÍý¤ò»Ü¤·¤¿ +¥µ¥ó¥×¥ë²»À¼¤ò´Þ¤à raw ·Á¼°¤Î 16 ¥Ó¥Ã¥È PCM ¥Ç¡¼¥¿¤È¤·¤Æ½ÐÎϤ·¤Þ¤¹¡£ + +.TP +.B \-R --output-raw-big-endian +¥Ø¥Ã¥À̵¤·¤Î¥Ç¡¼¥¿¤ò MSB first ¤Î¥Ð¥¤¥È½ç¤Ç¡¢¥¤¥ó¥¿¥ê¡¼¥Ö½èÍý¤ò»Ü¤·¤¿ +¥µ¥ó¥×¥ë²»À¼¤ò´Þ¤à raw ·Á¼°¤Î 16 ¥Ó¥Ã¥È PCM ¥Ç¡¼¥¿¤È¤·¤Æ½ÐÎϤ·¤Þ¤¹¡£ + +.TP +.B \-w --output-wav +¥Ç¡¼¥¿¤ò Micro$oft ¤Î RIFF WAV ·Á¼°¤Ç½ÐÎϤ·¤Þ¤¹(WAV ¥Ç¡¼¥¿¤Î¥Ð¥¤¥È½ç¤Ï +ɬ¤º LSB first ¤Ç¤¢¤ëÅÀ¤ËÃí°Õ)¡£ + +.TP +.B \-f --output-aiff +¥Ç¡¼¥¿¤ò Apple ¤Î AIFF ·Á¼°¤Ç½ÐÎϤ·¤Þ¤¹(AIFC ¥Ç¡¼¥¿¤Î¥Ð¥¤¥È½ç¤Ïɬ¤º +MSB first ¤Ç¤¢¤ëÅÀ¤ËÃí°Õ)¡£ + +.TP +.B \-a --output-aifc +¥Ç¡¼¥¿¤ò̵°µ½Ì ¤Î Apple AIFF-C ·Á¼°¤Ç½ÐÎϤ·¤Þ¤¹(AIFF-C ¥Ç¡¼¥¿¤Î¥Ð¥¤¥È +½ç¤Ïɬ¤º MSB first ¤Ç¤¢¤ëÅÀ¤ËÃí°Õ)¡£ + +.TP +.BI "\-B --batch " +cdda2wav ·Á¼°¤Î¥Ð¥Ã¥Á½ÐÎϤò¹Ô¤¤¤Þ¤¹¡£@CDPARANOIA_NAME@ ¤Ï½ÐÎϤò¥È¥é¥Ã¥¯¶­³¦¤Ç +Ê£¿ô¥Õ¥¡¥¤¥ë¤Ëʬ³ä¤·¤Þ¤¹¡£½ÐÎÏ¥Õ¥¡¥¤¥ë¤Î¥Õ¥¡¥¤¥ë̾¤ÎÀèÆ¬Éôʬ¤Ï¡¢'track(ÈÖ¹æ)' +¤È¤Ê¤ê¤Þ¤¹¡£ + +.TP +.B \-c --force-cdrom-little-endian +°ìÉô¤Î CD-ROM ¤Ï´Ö°ã¤Ã¤¿¥¨¥ó¥Ç¥£¥¢¥ó¤òÊó¹ð¤·¤Þ¤¹(¤¢¤ë¤¤¤Ï¥¨¥ó¥Ç¥£¥¢¥ó +¤Ë´Ø¤¹¤ë¾ðÊó¤òÁ´¤¯Êó¹ð¤·¤Þ¤»¤ó)¡£¤½¤Î¤¿¤á¡¢@CDPARANOIA_NAME@ ¤¬¥¨¥ó¥Ç¥£¥¢¥ó¤ò +´Ö°ã¤¨¤ë¤³¤È¤¬¤¢¤ê¤Þ¤¹¡£¥É¥é¥¤¥Ö¤ò¥ê¥È¥ë¥¨¥ó¥Ç¥£¥¢¥ó¤Î¥Ç¥Ð¥¤¥¹¤È¤·¤Æ +@CDPARANOIA_NAME@ ¤Ë°·¤ï¤»¤ë¤Ë¤Ï¡¢ +.B \-c +¥ª¥×¥·¥ç¥ó¤ò»È¤¤¤Þ¤¹¡£ + +.TP +.B \-C --force-cdrom-big-endian +Á°¤Î¥ª¥×¥·¥ç¥ó¤ÎµÕ¤Ç¡¢¥Ç¥Ð¥¤¥¹¤ò¥Ó¥Ã¥°¥¨¥ó¥Ç¥£¥¢¥ó¤Î¥Ç¥Ð¥¤¥¹¤È¤·¤Æ +@CDPARANOIA_NAME@ ¤Ë°·¤ï¤»¤Þ¤¹¡£ + +.TP +.BI "\-n --force-default-sectors " n +¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î¥Ð¥Ã¥¯¥¨¥ó¥É¤¬¹Ô¤¦ºÇ¾®Ã±°Ì¤ÎÆÉ¤ß¼è¤ê¤ò¡¢ +1 ²ó¤ÎÆÉ¤ß¼è¤ê¤´¤È¤Ë +.B n +¥»¥¯¥¿¤È¤·¤Þ¤¹¡£¤³¤Î¿ô¤ÏÌäÂê¤òµ¯¤³¤¹¤ª¤½¤ì¤¬¤¢¤ê¤Þ¤¹¡£¥«¡¼¥Í¥ë¤Ï¿¤¯¤Î +¾ì¹ç¡¢ÆÉ¤ß¼è¤êÍ×µá¤òºÇ¾®Ã±°Ì¤ÎÆÉ¤ß¼è¤ê(@CDPARANOIA_NAME@ ¤Ë¤è¤ë¼«Æ°½èÍý¤Ï¤³¤ì +¤ËÂбþ¤·¤Æ¤¤¤Þ¤¹)Ê£¿ô¸Ä¤Ëʬ³ä¤¹¤ë¤«¡¢À©¸Â¤µ¤ì¤¿Â礭¤µ¤ÎÈϰϤǤ·¤« +ÆÉ¤ß¼è¤ê¤òµö²Ä¤·¤Þ¤»¤ó¡£ +.B ÉáÄ̤Ϥ³¤Î¥ª¥×¥·¥ç¥ó¤ò»È¤¦¤Ù¤­¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¡£ + +.TP +.BI "\-d --force-cdrom-device " device +¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î¥Ð¥Ã¥¯¥¨¥ó¥É¤Ë¤è¤ëÆÉ¤ß¼è¤ê¤ò¡¢ºÇ½é¤Ë¸«¤Ä¤±¤¿ÆÉ¤ß¼è¤ê²Ä +ǽ¤Ê CD-ROM ¥É¥é¥¤¥Ö¤Ç¤Ï¤Ê¤¯¡¢»ØÄꤷ¤¿ +.B device +¤«¤é¹Ô¤¦¤è¤¦¤Ë¤·¤Þ¤¹¡£¤³¤Î¥ª¥×¥·¥ç¥ó¤Ç¤Ï¡¢ÍøÍѲÄǽ¤Ç¤¢¤ëǤ°Õ¤Î +¥¤¥ó¥¿¥Õ¥§¡¼¥¹(ATAPI, SCSI, ¥á¡¼¥«¡¼ÆÈ¼«)¤ò»ý¤Ä¥Ç¥Ð¥¤¥¹¤ò»ØÄꤹ¤ë¤³¤È +¤¬¤Ç¤­¤Þ¤¹¡£ + +.TP +.BI "\-g --force-generic-device " device +¤³¤Î¥ª¥×¥·¥ç¥ó¤Ï¡¢SCSI CD-ROM ¤ÈÈÆÍѥǥХ¤¥¹¤ÎÀßÄê¤òÌÀ¼¨Åª¤ËÊÌ¡¹¤ËÀ©¸æ +¤·¤¿¤¤»þ¤Ë +.B \-d +¥ª¥×¥·¥ç¥ó¤ÈÁȤ߹ç¤ï¤»¤Æ»È¤¤¤Þ¤¹¡£¤³¤Î¥ª¥×¥·¥ç¥ó¤¬ÌòΩ¤Ä¤Î¤Ï¡¢SCSI ¤Î +ÀßÄ꤬ɸ½à¤È°Û¤Ê¤ë¾ì¹ç¤À¤±¤Ç¤¹¡£ + +.TP +.BI "\-S --force-read-speed " number +CD ¥É¥é¥¤¥Ö¤«¤é¤ÎÆÉ¤ß¹þ¤ß®ÅÙ¤òÀßÄꤹ¤ë¤Ë¤Ï¡¢¤³¤Î¥ª¥×¥·¥ç¥ó¤òÌÀ¼¨Åª¤Ë +»È¤Ã¤Æ¤¯¤À¤µ¤¤(¥É¥é¥¤¥Ö¤¬Âбþ¤·¤Æ¤¤¤ë¾ì¹ç)¡£¤³¤Î¥ª¥×¥·¥ç¥ó¤òÍѤ¤¤ë¤È¡¢ +¥Ç¥£¥¹¥¯¤¬ÃÙ¤¤¾ì¹ç¤ä¥á¥â¥ê¤¬¾¯¤Ê¤¤¾ì¹ç¤Ëµ¯¤³¤ë¥¢¥ó¥À¡¼¥é¥ó¤ò¸º¤é¤¹¤³¤È +¤¬¤Ç¤­¤Þ¤¹¡£ + +.TP +.B \-Z --disable-paranoia +¥Ç¡¼¥¿¾È¹ç¤ÈÄûÀµµ¡Ç½¤ò +.b Á´¤Æ +̵¸ú¤Ë¤·¤Þ¤¹¡£-Z ¥ª¥×¥·¥ç¥ó¤òÍѤ¤¤ë¤È¡¢@CDPARANOIA_NAME@ ¤Ï +¥ª¡¼¥Ð¡¼¥é¥Ã¥×¤ÎÀßÄ꤬ 0 ¤Ç¤¢¤ë cdda2wav ¤ÈÁ´¤¯Æ±¤¸¤è¤¦¤Ë¥Ç¡¼¥¿¤Î +ÆÉ¤ß¼è¤ê¤ò¹Ô¤¤¤Þ¤¹¡£ +¤³¤Î¥ª¥×¥·¥ç¥ó¤ò»ØÄꤹ¤ë¤È +.B \-W , +.B \-X , +.B \-Y +¥ª¥×¥·¥ç¥ó¤âÍ­¸ú¤Ë¤Ê¤ê¤Þ¤¹¤¬¡¢ +.B \-Z \-W \-X \-Y +¤ÈÁ´¤¯Æ±¤¸¤Ç¤Ï +.B ¤¢¤ê¤Þ¤»¤ó¡£ +¤Ê¤¼¤Ê¤é¡¢ +.B \-W +¤«¤é +.B \-Z +¤Þ¤Ç¤Î¥ª¥×¥·¥ç¥ó¤Ë¤è¤ê¾È¹ç¤Î¥ì¥Ù¥ë¤¬³¬ÁØÅª¤ËÊѤï¤ë¤«¤é¤Ç¤¹¡£¼ÂºÝ¤ËÍ­¸ú +¤Ë¤Ê¤ë¤Î¤ÏºÇ¸å¤Ë»ØÄꤷ¤¿¥ª¥×¥·¥ç¥ó¤À¤±¤Ç¤¹¡£ + +.TP +.B \-Y --disable-extra-paranoia +ÆÉ¤ß¼è¤Ã¤¿¥Ç¡¼¥¿¤ÎÃæ´Ö¤Ë¤ª¤±¤ë¥Ç¡¼¥¿¾È¹ç¤ò¹Ô¤¤¤Þ¤»¤ó¡£¤Ä¤Þ¤ê¡¢ +¥Ç¡¼¥¿¤ÎÆÉ¤ß¼è¤ê¶­³¦¤Ë¤ª¤±¤ë¥ª¡¼¥Ð¡¼¥é¥Ã¥×Éôʬ¤Î¥Á¥§¥Ã¥¯¤·¤«¹Ô¤¤¤Þ¤»¤ó¡£ + +.TP +.B \-X --disable-scratch-detection +¾È¹ç¤ÎÅÓÃæ¤Ç¤Ï½ý¤Îõºº¤â¹Ô¤ï¤º¡¢½ý¤ËÂФ·¤Æ´è·ò¤ÊƱ´ü½èÍý¤â¹Ô¤¤¤Þ¤»¤ó¡£ +.B \-X +¥ª¥×¥·¥ç¥ó¤ò»ØÄꤷ¤¿¾ì¹ç¡¢½ý¤Ä¤¤¤¿ CD ¤òÍ¿¤¨¤ë¤È @CDPARANOIA_NAME@ ¤ÏÆÉ¤ß¼è¤ê +¤Î¼ºÇÔ¤òµ¯¤³¤·¤Þ¤¹¡£ + +.TP +.B \-W --disable-scratch-repair +½ý¤ò¸¡½Ð¤·¡¢Æ±´ü¤òÊݤĽèÍý¤ò¹Ô¤¤¤Þ¤¹¡£¤¿¤À¤·²õ¤ì¤¿¥Ç¡¼¥¿¤Î½¤Éü¤Ï¹Ô¤¤¤Þ +¤»¤ó¡£¥í¥°¥Õ¥¡¥¤¥ë¤Î½ÐÎϤò¹Ô¤¦¤È( +.RB \-i +¥ª¥×¥·¥ç¥ó)¡¢Á´¤Æ¤Î½ý¤Î¥Õ¥ì¡¼¥à°ÌÃÖ¤¬¥í¥°¥Õ¥¡¥¤¥ë¤Ë½ÐÎϤµ¤ì¤Þ¤¹¡£ + +.SH ½ÐÎϤµ¤ì¤ë´éʸ»ú +.TP +.B + :-) +Àµ¾ïưºî¡£¥¸¥Ã¥¿¤Ï¾¯¤Ê¤¤¤«¡¢Á´¤¯¤Ê¤¤ +.TP +.B + :-| +Àµ¾ïưºî¡£¥¸¥Ã¥¿¤ÏµöÍÆÈÏ°Ï +.TP +.B + :-/ +ÆÉ¤ß¼è¤ê¤Ç¥É¥ê¥Õ¥È¤¬È¯À¸ +.TP +.B + :-P +ºÇ¾®Ã±°Ì¤ÎÆÉ¤ß¼è¤êÁàºî¤Ë¤ª¤¤¤Æ¡¢Êó¹ð¤µ¤ì¤Æ¤¤¤Ê¤¤Â»¼º¤¬¥¹¥È¥ê¡¼¥ß¥ó¥°¤Ë¤¢¤ë +.TP +.B + 8-| +·«¤êÊÖ¤·¤ÆÆÉ¤ß¼è¤ê¤ò¹Ô¤Ã¤¿¤¬¡¢Æ±¤¸°ÌÃÖ¤ÇÌäÂ꤬µ¯¤­¤¿¡£½¤Àµ¤Ïº¤Æñ¤Ç¤¢¤ë +.TP +.B + :-0 +SCSI/ATAPI ¤Î¥Ç¡¼¥¿Å¾Á÷¥¨¥é¡¼ +.TP +.B + :-( +½ý¤¬¸¡½Ð¤µ¤ì¤¿ +.TP +.B + ;-( +¥Ç¡¼¥¿¤ÎÄûÀµ¤ò¤¢¤­¤é¤á¤¿ +.TP +.B + :^D +ÆÉ¤ß¼è¤ê½ªÎ» + +.SH ¿Ê¹Ôɽ¼¨¤Î°ÕÌ£ +.TP +.B +<¥¹¥Ú¡¼¥¹> +ÄûÀµ¤ÏÉÔÍ× +.TP +.B + - +¥¸¥Ã¥¿¤ÎÄûÀµ¤¬É¬Í× +.TP +.B + + +Êó¹ð¤µ¤ì¤Æ¤¤¤Ê¤¤Â»¼º¤¬¥¹¥È¥ê¡¼¥ß¥ó¥°¤Ë¤¢¤ë¡£¤¢¤ë¤¤¤ÏÊ̤Υ¨¥é¡¼¤¬ÆÉ¤ß¼è¤ê +»þ¤ËȯÀ¸¤·¤¿ +.TP +.B + ! +¥¹¥Æ¡¼¥¸ 1 ÄûÀµ¤Î¸å¤Ë¥¨¥é¡¼¤¬¸«¤Ä¤«¤Ã¤¿¡£ÆÉ¤ß¼è¤ê¤òÊ£¿ô²ó·«¤êÊÖ¤·¤Æ¤â +Ʊ¤¸¥¨¥é¡¼¤¬È¯À¸¤·¡¢@CDPARANOIA_NAME@ ¤Ï¤½¤Î¥¨¥é¡¼¤ò¤¦¤Þ¤¯¸¡½Ð¤Ç¤­¤Ê¤¤¡£ +.TP +.B + e +SCSI/ATAPI ¤Î¥Ç¡¼¥¿Å¾Á÷¥¨¥é¡¼(ÄûÀµºÑ¤ß) +.TP +.B + V +ÄûÀµ¤Ç¤­¤Ê¤¤¥¨¥é¡¼/¥Ç¡¼¥¿¤Î¥¹¥­¥Ã¥× + +.SH °ú¤­¿ô 'span' + +°ú¤­¿ô span ¤Ï¡¢ÆÉ¤ß¼è¤ê¤ò¹Ô¤¦¥È¥é¥Ã¥¯¤Þ¤¿¤Ï¥È¥é¥Ã¥¯¤Î°ìÉô¤ò»ØÄꤷ¤Þ¤¹¡£ +¤³¤Î°ú¤­¿ô¤Ïɬ¤ºÉ¬ÍפǤ¹¡£ +.B Ãí°Õ: +span ¤¬Ã±¤Ê¤ë¿ô»ú¤Ç¤Ê¤±¤ì¤Ð¡¢¥·¥§¥ë¤¬°ú¤­¿ô span ¤òŸ³«¤·¤Æ¤·¤Þ¤ï¤Ê¤¤ +¤è¤¦¤Ë¥¯¥©¡¼¥È¤¹¤ë¤Î¤¬ÉáÄ̤Ǥ·¤ç¤¦¡£ +.P +°ú¤­¿ô span ¤Ï¡¢Ã±¤Ê¤ë¥È¥é¥Ã¥¯Èֹ椫¡¢¥ª¥Õ¥»¥Ã¥È¤È¥¹¥Ñ¥ó¤ÎÁȹ礻¤Î»ØÄê +¤È¤Ê¤ê¤Þ¤¹¡£¥ª¥Õ¥»¥Ã¥È¤È¥¹¥Ñ¥ó¤ÎÁȹ礻¤ò»ØÄꤹ¤ëÊýË¡¤Ï¡¢¤À¤¤¤¿¤¤°Ê²¼¤Î +¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹: +.P +1[ww:xx:yy.zz]-2[aa:bb:cc.dd] +.P +¤³¤³¤Ç 1 ¤È 2 ¤Ï¥È¥é¥Ã¥¯ÈÖ¹æ¤Ç¤¹¡£³Ñ³ç¸Ì¤ÎÃæ¤Î¿ôÃͤϡ¢»ØÄꤵ¤ì¤¿¥È¥é¥Ã¥¯ +¤Ë¤ª¤±¤ë¡¢¤è¤êºÙ¤«¤¤¥ª¥Õ¥»¥Ã¥È»ØÄê¤Ç¤¹¡£[aa:bb:cc.dd] ¤Ï +¡Ö»þ´Ö/ʬ/ÉÃ/¥»¥¯¥¿¡×¤Î·Á¼°¤Ç¤¹¡£Ãͤ¬ 0 ¤Ç¤¢¤ë¥Õ¥£¡¼¥ë¥É¤Ï»ØÄꤷ¤Ê¤¯¤Æ +¤â¹½¤¤¤Þ¤»¤ó¡£¤Ä¤Þ¤ê [::20], [:20], [20], [20.] Åù¤Ï 20 ÉäȲò¼á¤µ¤ì¡¢ +[10:] ¤Ï 10 ÉäȲò¼á¤µ¤ì¡¢[.30] ¤Ï 30 ¥»¥¯¥¿¤È²ò¼á¤µ¤ì¤Þ¤¹(75 ¥»¥¯¥¿¤Ç +1 ÉäǤ¹)¡£ +.P +¥ª¥Õ¥»¥Ã¥È¤ò 1 ¤Ä¤·¤«»ØÄꤷ¤Ê¤±¤ì¤Ð¡¢¤³¤ì¤Ï³«»Ï°ÌÃ֤Υª¥Õ¥»¥Ã¥È¤òɽ¤·¡¢ +µÛ¤¤½Ð¤·¤Ï¤½¤Î¥È¥é¥Ã¥¯¤Î½ª¤ï¤ê¤Þ¤Ç¹Ô¤ï¤ì¤Þ¤¹¡£¥ª¥Õ¥»¥Ã¥È¤¬ 1 ¤Ä¤À¤±¤¢ +¤ê¡¢¤½¤ÎÁ°¸å¤Ë¥Ï¥¤¥Õ¥ó(-)¤¬¤¢¤ë¾ì¹ç¤Ë¤Ï¡¢¾Êά¤µ¤ì¤Æ¤¤¤ë¥ª¥Õ¥»¥Ã¥È¤Ï +¥Ç¥£¥¹¥¯¤ÎÀèÆ¬¤¢¤ë¤¤¤ÏËöÈø¤È¤·¤Æ²ò¼á¤µ¤ì¤Þ¤¹¡£Îã¤ò°Ê²¼¤Ë¼¨¤·¤Þ¤¹: + +.TP +.B 1:[20.35] +¥È¥é¥Ã¥¯ 1 ¤Î 20 Éá¢35 ¥»¥¯¥¿¤Î°ÌÃÖ¤«¤é¡¢¥È¥é¥Ã¥¯ 1 ¤ÎËöÈø¤Þ¤Ç¤òµÛ¤¤ +½Ð¤·¤Þ¤¹¡£ +.TP +.B 1:[20.35]- +1[20.35] ¤Î°ÌÃÖ¤«¤é¥Ç¥£¥¹¥¯¤ÎËöÈø¤Þ¤Ç¤òµÛ¤¤½Ð¤·¤Þ¤¹¡£ +.TP +.B \-2 +¥Ç¥£¥¹¥¯¤ÎÀèÆ¬¤«¤é¥È¥é¥Ã¥¯ 2 ¤Þ¤Ç(¥È¥é¥Ã¥¯ 2 ¤â´Þ¤ß¤Þ¤¹)¤òµÛ¤¤½Ð¤·¤Þ¤¹¡£ +.TP +.B \-2:[30.35] +¥Ç¥£¥¹¥¯¤ÎÀèÆ¬¤«¤é 2:[30.35] ¤Î°ÌÃ֤ޤǵۤ¤½Ð¤·¤Þ¤¹¡£ +.TP +.B 2-4 +¥È¥é¥Ã¥¯ 2 ¤ÎÀèÆ¬¤«¤é¥È¥é¥Ã¥¯ 4 ¤ÎËöÈø¤Þ¤Ç¤òµÛ¤¤½Ð¤·¤Þ¤¹¡£ +.P +·«¤êÊÖ¤·¤Ë¤Ê¤ê¤Þ¤¹¤¬¡¢³Ñ³ç¸Ì¤ª¤è¤Óñ¸ì¤ÎÀèÆ¬¤Ë¤¢¤ë¥Ï¥¤¥Õ¥ó¤Ïɬ¤º¥¯¥©¡¼¥È +¤·¤Æ¡¢¥·¥§¥ë¤ËŸ³«¤µ¤ì¤Ê¤¤¤è¤¦¤Ë¤·¤Æ¤¯¤À¤µ¤¤¡£ + +.SH »ØÄêÎã + +¥¯¥©¡¼¥È¤â´Þ¤á¤¿»ØÄêÎã¤ò¤¤¤¯¤Ä¤«¼¨¤·¤Þ¤¹: +.TP +¥É¥é¥¤¥Ö¤ÎÄ´ºº¤À¤±¤òŰÄìŪ¤Ë¹Ô¤¤¡¢¼«Æ°¸¡½Ð¤Î·ë²Ì¤òÁ´¤ÆÊó¹ð¤·¤Þ¤¹: +.P + @CDPARANOIA_NAME@ -vsQ +.TP +¥Ç¥£¥¹¥¯Á´ÂΤòµÛ¤¤½Ð¤·¤Þ¤¹¡£¤½¤ì¤¾¤ì¤Î¥È¥é¥Ã¥¯¤ÏÊÌ¡¹¤Î¥Õ¥¡¥¤¥ë¤Ë¤·¤Þ¤¹: +.P + @CDPARANOIA_NAME@ -B "1-" +.TP +¥È¥é¥Ã¥¯ 1 ¤Î»þ¹ï 0:30.12 ¤«¤é»þ¹ï 1:10.00 ¤Þ¤Ç¤òµÛ¤¤½Ð¤·¤Þ¤¹: +.P + @CDPARANOIA_NAME@ "1[:30.12]-1[1:10]" +.TP +¥È¥é¥Ã¥¯ 1 ¤Î»þ¹ï 0:30.12 ¤«¤é 1 ʬ´Ö¤Î¥Ç¡¼¥¿¤òµÛ¤¤½Ð¤·¤Þ¤¹: +.P + @CDPARANOIA_NAME@ "1[:30.12]-[1:00]" + +.SH ½ÐÎÏ + +½ÐÎÏ¥Õ¥¡¥¤¥ë¤ò»ØÄꤹ¤ë°ú¤­¿ô¤Ï¾Êά²Äǽ¤Ç¤¹¡£»ØÄꤵ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¡¢ +@CDPARANOIA_NAME@ ¤Ï¥µ¥ó¥×¥ë²»À¼¤ò +.BR cdda.wav ", " cdda.aifc ", " cdda.raw +¤Î¤¤¤º¤ì¤«¤Ë½ÐÎϤ·¤Þ¤¹¡£¤É¤Î¥Õ¥¡¥¤¥ë¤Ë½ÐÎϤµ¤ì¤ë¤Î¤«¤Ï¡¢¥ª¥×¥·¥ç¥ó +.BR \-w ", " \-a ", " \-r "," \-R +¤Î¤¦¤Á¤¤¤º¤ì¤ò»È¤¦¤«¤Ë¤è¤Ã¤Æ·è¤Þ¤ê¤Þ¤¹(²¿¤â»ØÄꤷ¤Ê¤±¤ì¤Ð +.BR \-w +¤¬¥Ç¥Õ¥©¥ë¥ÈÃͤǤ¹)¡£½ÐÎÏ¥Õ¥¡¥¤¥ë¤ò»ØÄꤹ¤ë°ú¤­¿ô¤¬ +.B \- +¤Ê¤é¤Ð¡¢½ÐÎϤÏɸ½à½ÐÎϤËÂФ·¤Æ¹Ô¤ï¤ì¤Þ¤¹¡£¤É¤Î¥Ç¡¼¥¿·Á¼°¤Ç¤â¥Ñ¥¤¥×¤ËÁ÷ +¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ + +.SH ¼Õ¼­ +@CDPARANOIA_NAME@ ¤Î´ð¤È¤Ê¤Ã¤¿¤Î¤Ï Heiko Eissfeldt ¤µ¤ó +(heiko@colossus.escape.de)¤¬ºîÀ®¤·¤¿ 'cdda2wav' ¥Ñ¥Ã¥±¡¼¥¸¤Ç¤¢¤ê¡¢ +°ÊÁ°¤Ï @CDPARANOIA_NAME@ ¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ÎÂçÉôʬ¤Ï cdda2wav ¤«¤é¤â¤é¤Ã¤Æ¤­¤¿ +¤â¤Î¤Ç¤·¤¿¡£cdda2wav ¤¬¤Ê¤±¤ì¤Ð¡¢@CDPARANOIA_NAME@ ¤¬ºî¤é¤ì¤ë¤³¤È¤Ï¤Ê¤«¤Ã¤¿¤Ç +¤·¤ç¤¦¡£ +.P +Joerg Schilling ¤µ¤ó¤¬ºîÀ®¤·¤¿ÈÆÍÑ SCSI ¥Ç¡¼¥¿Å¾Á÷¥é¥¤¥Ö¥é¥ê¤«¤é¡¢SCSI +¤ÎÀìÌçÃ챤ò¿¤¯³Ø¤Ð¤»¤Æ¤¤¤¿¤À¤­¤Þ¤·¤¿¡£ +.P +.SH ºî¼Ô +Monty +.P +cdparanoia ¤Î¥Û¡¼¥à¥Ú¡¼¥¸¤Ï°Ê²¼¤Î¾ì½ê¤Ë¤¢¤ê¤Þ¤¹: +.P +.ce +http://www.xiph.org/paranoia/ +.P +libcdio ¤Î¥Û¡¼¥à¥Ú¡¼¥¸¤Ï°Ê²¼¤Î¾ì½ê¤Ë¤¢¤ê¤Þ¤¹: +.P +.ce +http://www.gnu.org/libcdio/ diff --git a/src/cd-paranoia/doc/overlapdef.txt b/src/cd-paranoia/doc/overlapdef.txt new file mode 100644 index 00000000..be51ffa0 --- /dev/null +++ b/src/cd-paranoia/doc/overlapdef.txt @@ -0,0 +1,18 @@ + 0 70 100 +A |----------|-----| +B |-----|---------| + 0 40 100 + +offset=-30 +begin=30 +end=100 + + 0 70 100 +A |----------|-----| +B |-----|---------| + 50 90 150 + +offset=20 +begin=30 +end=100 + diff --git a/src/cd-paranoia/getopt.c b/src/cd-paranoia/getopt.c new file mode 100644 index 00000000..c41531e6 --- /dev/null +++ b/src/cd-paranoia/getopt.c @@ -0,0 +1,1056 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98 + Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, 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 tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include +# include +#endif /* GNU C library. */ + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +# ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +# else +# define _(msgid) (msgid) +# endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +# include +# define my_index strchr +#else + +# if HAVE_STRING_H +# include +# else +# if HAVE_STRINGS_H +# include +# endif +# endif + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv (); +#endif + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((unused)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +# ifdef text_set_element +text_set_element (__libc_subinit, store_args_and_env); +# endif /* text_set_element */ + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/src/cd-paranoia/getopt.h b/src/cd-paranoia/getopt.h new file mode 100644 index 00000000..fb30719a --- /dev/null +++ b/src/cd-paranoia/getopt.h @@ -0,0 +1,133 @@ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, 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 _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if defined (__STDC__) && __STDC__ +#ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +extern int getopt (); +#endif /* __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* getopt.h */ diff --git a/src/cd-paranoia/getopt1.c b/src/cd-paranoia/getopt1.c new file mode 100644 index 00000000..ff257374 --- /dev/null +++ b/src/cd-paranoia/getopt1.c @@ -0,0 +1,190 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 + Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, 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 +#endif + +#include "getopt.h" + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/src/cd-paranoia/header.c b/src/cd-paranoia/header.c new file mode 100644 index 00000000..05eab01a --- /dev/null +++ b/src/cd-paranoia/header.c @@ -0,0 +1,134 @@ +/* + $Id: header.c,v 1.2 2008/04/11 15:44:00 karl Exp $ + + Copyright (C) 2004 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + and Heiko Eissfeldt heiko@escape.colossus.de + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file header.h + * \brief WAV, AIFF and AIFC header-writing routines. + */ + +#include "header.h" +#include +#include +#include +#include + +static void PutNum(long num,int f,int endianness,int bytes){ + int i; + unsigned char c; + + if(!endianness) + i=0; + else + i=bytes-1; + while(bytes--){ + c=(num>>(i<<3))&0xff; + if(write(f,&c,1)==-1){ + perror("Could not write to output."); + exit(1); + } + if(endianness) + i--; + else + i++; + } +} + +/** Writes WAV headers */ +void +WriteWav(int f, long int bytes) +{ + /* quick and dirty */ + + write(f,"RIFF",4); /* 0-3 */ + PutNum(bytes+44-8,f,0,4); /* 4-7 */ + write(f,"WAVEfmt ",8); /* 8-15 */ + PutNum(16,f,0,4); /* 16-19 */ + PutNum(1,f,0,2); /* 20-21 */ + PutNum(2,f,0,2); /* 22-23 */ + PutNum(44100,f,0,4); /* 24-27 */ + PutNum(44100*2*2,f,0,4); /* 28-31 */ + PutNum(4,f,0,2); /* 32-33 */ + PutNum(16,f,0,2); /* 34-35 */ + write(f,"data",4); /* 36-39 */ + PutNum(bytes,f,0,4); /* 40-43 */ +} + +/** Writes AIFF headers */ +void +WriteAiff(int f, long int bytes) +{ + long size=bytes+54; + long frames=bytes/4; + + /* Again, quick and dirty */ + + write(f,"FORM",4); /* 4 */ + PutNum(size-8,f,1,4); /* 8 */ + write(f,"AIFF",4); /* 12 */ + + write(f,"COMM",4); /* 16 */ + PutNum(18,f,1,4); /* 20 */ + PutNum(2,f,1,2); /* 22 */ + PutNum(frames,f,1,4); /* 26 */ + PutNum(16,f,1,2); /* 28 */ + write(f,"@\016\254D\0\0\0\0\0\0",10); /* 38 (44.100 as a float) */ + + write(f,"SSND",4); /* 42 */ + PutNum(bytes+8,f,1,4); /* 46 */ + PutNum(0,f,1,4); /* 50 */ + PutNum(0,f,1,4); /* 54 */ + +} + +/** Writes AIFC headers */ +void +WriteAifc(int f, long bytes) +{ + long size=bytes+86; + long frames=bytes/4; + + /* Again, quick and dirty */ + + write(f,"FORM",4); /* 4 */ + PutNum(size-8,f,1,4); /* 8 */ + write(f,"AIFC",4); /* 12 */ + write(f,"FVER",4); /* 16 */ + PutNum(4,f,1,4); /* 20 */ + PutNum(2726318400UL,f,1,4); /* 24 */ + + write(f,"COMM",4); /* 28 */ + PutNum(38,f,1,4); /* 32 */ + PutNum(2,f,1,2); /* 34 */ + PutNum(frames,f,1,4); /* 38 */ + PutNum(16,f,1,2); /* 40 */ + write(f,"@\016\254D\0\0\0\0\0\0",10); /* 50 (44.100 as a float) */ + + write(f,"NONE",4); /* 54 */ + PutNum(14,f,1,1); /* 55 */ + write(f,"not compressed",14); /* 69 */ + PutNum(0,f,1,1); /* 70 */ + + write(f,"SSND",4); /* 74 */ + PutNum(bytes+8,f,1,4); /* 78 */ + PutNum(0,f,1,4); /* 82 */ + PutNum(0,f,1,4); /* 86 */ + +} + diff --git a/src/cd-paranoia/header.h b/src/cd-paranoia/header.h new file mode 100644 index 00000000..99f94f9d --- /dev/null +++ b/src/cd-paranoia/header.h @@ -0,0 +1,32 @@ +/* + $Id: header.h,v 1.2 2008/04/11 15:44:00 karl Exp $ + + Copyright (C) 2008 Rocky Bernstein + Copyright (C) 1998 Monty + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** \file header.h + * \brief header for WAV, AIFF and AIFC header-writing routines. + */ + +/** Writes WAV headers */ +extern void WriteWav(int f,long int i_bytes); + +/** Writes AIFC headers */ +extern void WriteAifc(int f,long int i_bytes); + +/** Writes AIFF headers */ +extern void WriteAiff(int f,long int_bytes); diff --git a/src/cd-paranoia/pod2c.pl b/src/cd-paranoia/pod2c.pl new file mode 100755 index 00000000..a37d2a2c --- /dev/null +++ b/src/cd-paranoia/pod2c.pl @@ -0,0 +1,46 @@ +#!/usr/bin/perl +# $Id: pod2c.pl,v 1.4 2008/06/19 15:44:31 flameeyes Exp $ +# Utility to turn pieces of pod text to help text. +use File::Basename; + +die "Expecting exactly one argument, a filename" if @ARGV != 1; +$filename = shift; + +($name, $path, $suffix)=fileparse($filename,"\.txt"); +close(STDIN); +open(STDIN, "<$filename") + || die "Can't open $filename for reading:\n$!"; + +#$outfile="../${filename}.h"; +#open(STDOUT, ">$outfile") +# || die "Can't open $outfile for writing:\n$!"; + +print "/* + Copyright (C) 1999, 2005, 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +static const char ${name}_help[] =\n"; +while() { + s/["]/\\"/g; + # Change POD'ed items to quoted items, e.g. See L and L becomes + # See "y" and "z" (with \'s around the quotes) + s/[C,L,I,B,F]<(.+?)>/\\"$1\\"/g; + chomp; + if ( $^O eq "cygwin" ) { + s/\ // + } + print "\"$_\\n\"\n"; +} +print ";\n"; diff --git a/src/cd-paranoia/report.c b/src/cd-paranoia/report.c new file mode 100644 index 00000000..c1be3508 --- /dev/null +++ b/src/cd-paranoia/report.c @@ -0,0 +1,60 @@ +/* + $Id: report.c,v 1.3 2008/04/11 15:44:00 karl Exp $ + + Copyright (C) 2004, 2008 Rocky Bernstein + Copyright (C) 1998 Monty xiphmont@mit.edu + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/****************************************************************** + * + * reporting/logging routines + * + ******************************************************************/ + +#include +#include "config.h" +#include +#include "report.h" + +int quiet=0; +int verbose=CDDA_MESSAGE_FORGETIT; + +void +report(const char *s) +{ + if (!quiet) { + fprintf(stderr,s); + fputc('\n',stderr); + } +} + +void +report2(const char *s, char *s2) +{ + if (!quiet) { + fprintf(stderr,s,s2); + fputc('\n',stderr); + } +} + +void +report3(const char *s, char *s2, char *s3) +{ + if (!quiet) { + fprintf(stderr,s,s2,s3); + fputc('\n',stderr); + } +} diff --git a/src/cd-paranoia/report.h b/src/cd-paranoia/report.h new file mode 100644 index 00000000..d3031dfd --- /dev/null +++ b/src/cd-paranoia/report.h @@ -0,0 +1,20 @@ +/* + Copyright (C) 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +extern void report(const char *s); +extern void report2(const char *s, char *s2); +extern void report3(const char *s, char *s2, char *s3); diff --git a/src/cd-paranoia/usage-copy.h b/src/cd-paranoia/usage-copy.h new file mode 100644 index 00000000..4297672d --- /dev/null +++ b/src/cd-paranoia/usage-copy.h @@ -0,0 +1,143 @@ +/* + Copyright (C) 1999, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +const char usage_help[] = +"USAGE:\n" +" cd-paranoia [options] [outfile]\n" +"\n" +"OPTIONS:\n" +" -v --verbose : extra verbose operation\n" +" -q --quiet : quiet operation\n" +" -e --stderr-progress : force output of progress information to\n" +" -l --log-summary : save result summary to file\n" +" stderr (for wrapper scripts)\n" +" -V --version : print version info and quit\n" +" -Q --query : autosense drive, query disc and quit\n" +" -B --batch : 'batch' mode (saves each track to a\n" +" seperate file.\n" +" -s --search-for-drive : do an exhaustive search for drive\n" +" -h --help : print help\n" +"\n" +" -p --output-raw : output raw 16 bit PCM in host byte \n" +" order\n" +" -r --output-raw-little-endian : output raw 16 bit little-endian PCM\n" +" -R --output-raw-big-endian : output raw 16 bit big-endian PCM\n" +" -w --output-wav : output as WAV file (default)\n" +" -f --output-aiff : output as AIFF file\n" +" -a --output-aifc : output as AIFF-C file\n" +"\n" +" -c --force-cdrom-little-endian : force treating drive as little endian\n" +" -C --force-cdrom-big-endian : force treating drive as big endian\n" +" -n --force-default-sectors : force default number of sectors in read\n" +" to n sectors\n" +" -o --force-search-overlap : force minimum overlap search during\n" +" verification to n sectors\n" +" -d --force-cdrom-device : use specified device; disallow \n" +" autosense\n" +" -g --force-generic-device : really an alias for -d. Kept for \n" +" compatibility.\n" +" -S --force-read-speed : read from device at specified speed\n" +" -t --toc-offset : Add sectors to the values reported\n" +" when addressing tracks. May be negative\n" +" -T --toc-bias : Assume that the beginning offset of \n" +" track 1 as reported in the TOC will be\n" +" addressed as LBA 0. Necessary for some\n" +" Toshiba drives to get track boundaries\n" +" correct\n" +" -m --mmc-timeout : Set SCSI-MMC timeout to seconds.\n" +" -O --sample-offset : Add samples to the offset when\n" +" reading data. May be negative.\n" +" -z --never-skip[=n] : never accept any less than perfect\n" +" data reconstruction (don't allow 'V's)\n" +" but if [n] is given, skip after [n]\n" +" retries without progress.\n" +" -Z --disable-paranoia : disable all paranoia checking\n" +" -Y --disable-extra-paranoia : only do cdda2wav-style overlap checking\n" +" -X --abort-on-skip : abort on imperfect reads/skips\n" +" -x --test-flags=mask : simulate CD-reading errors of ilk-mask n\n" +" mask & 0x10 - simulate underrun errors\n" +"\n" +"OUTPUT SMILIES:\n" +" :-) Normal operation, low/no jitter\n" +" :-| Normal operation, considerable jitter\n" +" :-/ Read drift\n" +" :-P Unreported loss of streaming in atomic read operation\n" +" 8-| Finding read problems at same point during reread; hard to correct\n" +" :-0 SCSI/ATAPI transport error\n" +" :-( Scratch detected\n" +" ;-( Gave up trying to perform a correction\n" +" 8-X Aborted (as per -X) due to a scratch/skip\n" +" :^D Finished extracting\n" +"\n" +"PROGRESS BAR SYMBOLS:\n" +" No corrections needed\n" +" - Jitter correction required\n" +" + Unreported loss of streaming/other error in read\n" +" ! Errors are getting through stage 1 but corrected in stage2\n" +" e SCSI/ATAPI transport error (corrected)\n" +" V Uncorrected error/skip\n" +"\n" +"SPAN ARGUMENT:\n" +"The span argument may be a simple track number or a offset/span\n" +"specification. The syntax of an offset/span takes the rough form:\n" +" \n" +" 1[ww:xx:yy.zz]-2[aa:bb:cc.dd] \n" +"\n" +"Here, 1 and 2 are track numbers; the numbers in brackets provide a\n" +"finer grained offset within a particular track. [aa:bb:cc.dd] is in\n" +"hours/minutes/seconds/sectors format. Zero fields need not be\n" +"specified: [::20], [:20], [20], [20.], etc, would be interpreted as\n" +"twenty seconds, [10:] would be ten minutes, [.30] would be thirty\n" +"sectors (75 sectors per second).\n" +"\n" +"When only a single offset is supplied, it is interpreted as a starting\n" +"offset and ripping will continue to the end of he track. If a single\n" +"offset is preceeded or followed by a hyphen, the implicit missing\n" +"offset is taken to be the start or end of the disc, respectively. Thus:\n" +"\n" +" 1:[20.35] Specifies ripping from track 1, second 20, sector 35 to \n" +" the end of track 1.\n" +"\n" +" 1:[20.35]- Specifies ripping from 1[20.35] to the end of the disc\n" +"\n" +" -2 Specifies ripping from the beginning of the disc up to\n" +" (and including) track 2\n" +"\n" +" -2:[30.35] Specifies ripping from the beginning of the disc up to\n" +" 2:[30.35]\n" +"\n" +" 2-4 Specifies ripping from the beginning of track two to the\n" +" end of track 4.\n" +"\n" +"Don't forget to protect square brackets and preceeding hyphens from\n" +"the shell...\n" +"\n" +"A few examples, protected from the shell:\n" +" A) query only with exhaustive search for a drive and full reporting\n" +" of autosense:\n" +" cd-paranoia -vsQ\n" +"\n" +" B) extract up to and including track 3, putting each track in a seperate\n" +" file:\n" +" cd-paranoia -B -- \"-3\"\n" +"\n" +" C) extract from track 1, time 0:30.12 to 1:10.00:\n" +" cd-paranoia \"[:30.12]-1[1:10]\"\n" +"\n" +"Submit bug reports to bug-libcdio@gnu.org\n" +"\n" +; diff --git a/src/cd-paranoia/usage.txt.in b/src/cd-paranoia/usage.txt.in new file mode 100644 index 00000000..79de07b8 --- /dev/null +++ b/src/cd-paranoia/usage.txt.in @@ -0,0 +1,124 @@ +USAGE: + @CDPARANOIA_NAME@ [options] [outfile] + +OPTIONS: + -v --verbose : extra verbose operation + -q --quiet : quiet operation + -e --stderr-progress : force output of progress information to + stderr (for wrapper scripts) + -l --log-summary : save result summary to file + -V --version : print version info and quit + -Q --query : autosense drive, query disc and quit + -B --batch : 'batch' mode (saves each track to a + seperate file. + -s --search-for-drive : do an exhaustive search for drive + -h --help : print help + + -p --output-raw : output raw 16 bit PCM in host byte + order + -r --output-raw-little-endian : output raw 16 bit little-endian PCM + -R --output-raw-big-endian : output raw 16 bit big-endian PCM + -w --output-wav : output as WAV file (default) + -f --output-aiff : output as AIFF file + -a --output-aifc : output as AIFF-C file + + -c --force-cdrom-little-endian : force treating drive as little endian + -C --force-cdrom-big-endian : force treating drive as big endian + -n --force-default-sectors : force default number of sectors in read + to n sectors + -o --force-search-overlap : force minimum overlap search during + verification to n sectors + -d --force-cdrom-device : use specified device; disallow + autosense + -g --force-generic-device : really an alias for -d. Kept for + compatibility. + -S --force-read-speed : read from device at specified speed + -t --toc-offset : Add sectors to the values reported + when addressing tracks. May be negative + -T --toc-bias : Assume that the beginning offset of + track 1 as reported in the TOC will be + addressed as LBA 0. Necessary for some + Toshiba drives to get track boundaries + correct + -m --mmc-timeout : Set SCSI-MMC timeout to seconds. + -O --sample-offset : Add samples to the offset when + reading data. May be negative. + -z --never-skip[=n] : never accept any less than perfect + data reconstruction (don't allow 'V's) + but if [n] is given, skip after [n] + retries without progress. + -Z --disable-paranoia : disable all paranoia checking + -Y --disable-extra-paranoia : only do cdda2wav-style overlap checking + -X --abort-on-skip : abort on imperfect reads/skips + -x --test-flags=mask : simulate CD-reading errors of ilk-mask n + mask & 0x10 - simulate underrun errors + +OUTPUT SMILIES: + :-) Normal operation, low/no jitter + :-| Normal operation, considerable jitter + :-/ Read drift + :-P Unreported loss of streaming in atomic read operation + 8-| Finding read problems at same point during reread; hard to correct + :-0 SCSI/ATAPI transport error + :-( Scratch detected + ;-( Gave up trying to perform a correction + 8-X Aborted (as per -X) due to a scratch/skip + :^D Finished extracting + +PROGRESS BAR SYMBOLS: + No corrections needed + - Jitter correction required + + Unreported loss of streaming/other error in read + ! Errors are getting through stage 1 but corrected in stage2 + e SCSI/ATAPI transport error (corrected) + V Uncorrected error/skip + +SPAN ARGUMENT: +The span argument may be a simple track number or a offset/span +specification. The syntax of an offset/span takes the rough form: + + 1[ww:xx:yy.zz]-2[aa:bb:cc.dd] + +Here, 1 and 2 are track numbers; the numbers in brackets provide a +finer grained offset within a particular track. [aa:bb:cc.dd] is in +hours/minutes/seconds/sectors format. Zero fields need not be +specified: [::20], [:20], [20], [20.], etc, would be interpreted as +twenty seconds, [10:] would be ten minutes, [.30] would be thirty +sectors (75 sectors per second). + +When only a single offset is supplied, it is interpreted as a starting +offset and ripping will continue to the end of he track. If a single +offset is preceeded or followed by a hyphen, the implicit missing +offset is taken to be the start or end of the disc, respectively. Thus: + + 1:[20.35] Specifies ripping from track 1, second 20, sector 35 to + the end of track 1. + + 1:[20.35]- Specifies ripping from 1[20.35] to the end of the disc + + -2 Specifies ripping from the beginning of the disc up to + (and including) track 2 + + -2:[30.35] Specifies ripping from the beginning of the disc up to + 2:[30.35] + + 2-4 Specifies ripping from the beginning of track two to the + end of track 4. + +Don't forget to protect square brackets and preceeding hyphens from +the shell... + +A few examples, protected from the shell: + A) query only with exhaustive search for a drive and full reporting + of autosense: + @CDPARANOIA_NAME@ -vsQ + + B) extract up to and including track 3, putting each track in a seperate + file: + @CDPARANOIA_NAME@ -B -- "-3" + + C) extract from track 1, time 0:30.12 to 1:10.00: + @CDPARANOIA_NAME@ "[:30.12]-1[1:10]" + +Submit bug reports to bug-libcdio@gnu.org + diff --git a/src/cd-paranoia/utils.h b/src/cd-paranoia/utils.h new file mode 100644 index 00000000..895229ba --- /dev/null +++ b/src/cd-paranoia/utils.h @@ -0,0 +1,39 @@ +/* + $Id: utils.h,v 1.5 2008/04/11 15:44:00 karl Exp $ + + Copyright (C) 2004, 2005, 2008 Rocky Bernstein + Copyright (C) 1998 Monty + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include + +#define copystring(s) (s) ? s : NULL; + +static inline char * +catstring(char *buff, const char *s) +{ + if(s){ + if(buff) + buff=realloc(buff,strlen(buff)+strlen(s)+1); + else + buff=calloc(strlen(s)+1,1); + strcat(buff,s); + } + return(buff); +} + diff --git a/src/cd-paranoia/version.h b/src/cd-paranoia/version.h new file mode 100644 index 00000000..6a366c97 --- /dev/null +++ b/src/cd-paranoia/version.h @@ -0,0 +1,29 @@ +/* + $Id: version.h,v 1.6 2008/04/11 15:44:00 karl Exp $ + + Copyright (C) 2004, 2005, 2008 Rocky Bernstein + Copyright (C) 2001 Monty + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/****************************************************************** + * cdda_paranoia generation III release 9.8 libcdio + ******************************************************************/ +#include + +#define PARANOIA_VERSION \ + "cdparanoia III release 9.8 libcdio " CDIO_VERSION "\n" \ + "(C) 2001 Monty and Xiphophorus\n" \ + "(C) 2004, 2005, 2008 Rocky Bernstein \n\n" \ + "Report bugs to bug-libcdio@gnu.org\n" diff --git a/src/cd-read.c b/src/cd-read.c new file mode 100644 index 00000000..32028332 --- /dev/null +++ b/src/cd-read.c @@ -0,0 +1,604 @@ +/* + $Id: cd-read.c,v 1.33 2008/06/19 15:44:13 flameeyes Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Program to debug read routines audio, auto, mode1, mode2 forms 1 & 2. */ + +#include "util.h" +#include + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "getopt.h" + +/* Configuration option codes */ +enum { + OP_HANDLED = 0, + + /* NOTE: libpopt version associated these with drivers. That + appeared to be an unused historical artifact. + */ + OP_SOURCE_AUTO, + OP_SOURCE_BIN, + OP_SOURCE_CUE, + OP_SOURCE_NRG, + OP_SOURCE_CDRDAO, + OP_SOURCE_DEVICE, + + OP_USAGE, + + /* These are the remaining configuration options */ + OP_READ_MODE, + OP_VERSION, + +}; + +typedef enum +{ + READ_AUDIO = CDIO_READ_MODE_AUDIO, + READ_M1F1 = CDIO_READ_MODE_M1F1, + READ_M1F2 = CDIO_READ_MODE_M1F2, + READ_M2F1 = CDIO_READ_MODE_M2F1, + READ_M2F2 = CDIO_READ_MODE_M2F2, + READ_MODE_UNINIT, + READ_ANY +} read_mode_t; + +/* Structure used so we can binary sort and set the --mode switch. */ +typedef struct +{ + char name[30]; + read_mode_t read_mode; +} subopt_entry_t; + + +/* Sub-options for --mode. Note: entries must be sorted! */ +static const subopt_entry_t modes_sublist[] = { + {"any", READ_ANY}, + {"audio", READ_AUDIO}, + {"m1f1", READ_M1F1}, + {"m1f2", READ_M1F2}, + {"m2f1", READ_M2F1}, + {"m2f2", READ_M2F2}, + {"mode1form1", READ_M1F1}, + {"mode1form2", READ_M1F2}, + {"mode2form1", READ_M2F1}, + {"mode2form2", READ_M2F2}, + {"red", READ_AUDIO}, +}; + +/* Used by `main' to communicate with `parse_opt'. And global options + */ +static struct arguments +{ + char *access_mode; /* Access method driver should use for control */ + char *output_file; /* file to output blocks if not NULL. */ + int debug_level; + int hexdump; /* Show output as a hexdump */ + int nohexdump; /* Don't output as a hexdump. I don't know + how to get popt to combine these as + one variable. + */ + int just_hex; /* Don't try to print "printable" characters + in hex dump. */ + read_mode_t read_mode; + int version_only; + int no_header; + int print_iso9660; + source_image_t source_image; + lsn_t start_lsn; + lsn_t end_lsn; + int num_sectors; +} opts; + +static void +hexdump (FILE *stream, uint8_t * buffer, unsigned int len, + int just_hex) +{ + unsigned int i; + for (i = 0; i < len; i++, buffer++) + { + if (i % 16 == 0) + fprintf (stream, "0x%04x: ", i); + fprintf (stream, "%02x", *buffer); + if (i % 2 == 1) + fprintf (stream, " "); + if (i % 16 == 15) { + if (!just_hex) { + uint8_t *p; + fprintf (stream, " "); + for (p=buffer-15; p <= buffer; p++) { + fprintf(stream, "%c", isprint(*p) ? *p : '.'); + } + } + fprintf (stream, "\n"); + } + } + fprintf (stream, "\n"); + fflush (stream); +} + +/* Comparison function called by bearch() to find sub-option record. */ +static int +compare_subopts(const void *key1, const void *key2) +{ + subopt_entry_t *a = (subopt_entry_t *) key1; + subopt_entry_t *b = (subopt_entry_t *) key2; + return (strncmp(a->name, b->name, 30)); +} + +/* Do processing of a --mode sub option. + Basically we find the option in the array, set it's corresponding + flag variable to true as well as the "show.all" false. +*/ +static void +process_suboption(const char *subopt, const subopt_entry_t *sublist, const int num, + const char *subopt_name) +{ + subopt_entry_t *subopt_rec = + bsearch(subopt, sublist, num, sizeof(subopt_entry_t), + &compare_subopts); + if (subopt_rec != NULL) { + opts.read_mode = subopt_rec->read_mode; + return; + } else { + unsigned int i; + bool is_help=strcmp(subopt, "help")==0; + if (is_help) { + report( stderr, "The list of sub options for \"%s\" are:\n", + subopt_name ); + } else { + report( stderr, "Invalid option following \"%s\": %s.\n", + subopt_name, subopt ); + report( stderr, "Should be one of: " ); + } + for (i=0; i= 0) + switch (opt) + { + case 'a': opts.access_mode = strdup(optarg); break; + case 'd': opts.debug_level = atoi(optarg); break; + case 'x': opts.hexdump = 1; break; + case 's': opts.start_lsn = atoi(optarg); break; + case 'e': opts.end_lsn = atoi(optarg); break; + case 'n': opts.num_sectors = atoi(optarg); break; + case 'b': parse_source(OP_SOURCE_BIN); break; + case 'c': parse_source(OP_SOURCE_CUE); break; + case 'i': parse_source(OP_SOURCE_AUTO); break; + case 'C': parse_source(OP_SOURCE_DEVICE); break; + case 'N': parse_source(OP_SOURCE_NRG); break; + case 't': parse_source(OP_SOURCE_CDRDAO); break; + case 'o': opts.output_file = strdup(optarg); break; + + case 'm': + process_suboption(optarg, modes_sublist, + sizeof(modes_sublist) / sizeof(subopt_entry_t), + "--mode"); + break; + + case 'V': + print_version(program_name, VERSION, 0, true); + free(program_name); + exit (EXIT_SUCCESS); + break; + + case '?': + fprintf(stdout, helpText, program_name); + free(program_name); + exit(EXIT_INFO); + break; + + case OP_USAGE: + fprintf(stderr, usageText, program_name); + free(program_name); + exit(EXIT_FAILURE); + break; + + case OP_HANDLED: + break; + } + + if (optind < argc) { + const char *remaining_arg = argv[optind++]; + + /* NOTE: A bug in the libpopt version checked source_image, which + rendered the subsequent source_image test useless. + */ + if (source_name != NULL) { + report( stderr, "%s: Source specified in option %s and as %s\n", + program_name, source_name, remaining_arg ); + free(program_name); + exit (EXIT_FAILURE); + } + + if (opts.source_image == INPUT_DEVICE) + source_name = fillout_device_name(remaining_arg); + else + source_name = strdup(remaining_arg); + + if (optind < argc) { + report( stderr, "%s: Source specified in previously %s and %s\n", + program_name, source_name, remaining_arg ); + free(program_name); + exit (EXIT_FAILURE); + } + } + + if (opts.debug_level == 3) { + cdio_loglevel_default = CDIO_LOG_INFO; + } else if (opts.debug_level >= 4) { + cdio_loglevel_default = CDIO_LOG_DEBUG; + } + + if (opts.read_mode == READ_MODE_UNINIT) { + report( stderr, + "%s: Need to give a read mode " + "(audio, m1f1, m1f2, m2f1, m2f2, or auto)\n", + program_name ); + free(program_name); + exit(10); + } + + /* Check consistency between start_lsn, end_lsn and num_sectors. */ + + if (opts.nohexdump && opts.hexdump != 2) { + report( stderr, + "%s: don't give both --hexdump and --no-hexdump together\n", + program_name ); + exit(13); + } + + if (opts.nohexdump) opts.hexdump = 0; + + if (opts.start_lsn == CDIO_INVALID_LSN) { + /* Maybe we derive the start from the end and num sectors. */ + if (opts.end_lsn == CDIO_INVALID_LSN) { + /* No start or end LSN, so use 0 for the start */ + opts.start_lsn = 0; + if (opts.num_sectors == 0) opts.num_sectors = 1; + } else if (opts.num_sectors != 0) { + if (opts.end_lsn <= opts.num_sectors) { + report( stderr, "%s: end LSN (%lu) needs to be greater than " + " the sector to read (%lu)\n", + program_name, (unsigned long) opts.end_lsn, + (unsigned long) opts.num_sectors ); + exit(12); + } + opts.start_lsn = opts.end_lsn - opts.num_sectors + 1; + } + } + + /* opts.start_lsn has been set somehow or we've aborted. */ + + if (opts.end_lsn == CDIO_INVALID_LSN) { + if (0 == opts.num_sectors) opts.num_sectors = 1; + opts.end_lsn = opts.start_lsn + opts.num_sectors - 1; + } else { + /* We were given an end lsn. */ + if (opts.end_lsn < opts.start_lsn) { + report( stderr, + "%s: end LSN (%lu) needs to be less than start LSN (%lu)\n", + program_name, (unsigned long) opts.start_lsn, + (unsigned long) opts.end_lsn ); + free(program_name); + exit(13); + } + if (opts.num_sectors != opts.end_lsn - opts.start_lsn + 1) + if (opts.num_sectors != 0) { + report( stderr, + "%s: inconsistency between start LSN (%lu), end (%lu), " + "and count (%d)\n", + program_name, (unsigned long) opts.start_lsn, + (unsigned long) opts.end_lsn, opts.num_sectors ); + free(program_name); + exit(14); + } + opts.num_sectors = opts.end_lsn - opts.start_lsn + 1; + } + + return true; +} + +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.debug_level < 0) + return; + + gl_default_cdio_log_handler (level, message); +} + + +static void +init(void) +{ + opts.debug_level = 0; + opts.start_lsn = CDIO_INVALID_LSN; + opts.end_lsn = CDIO_INVALID_LSN; + opts.num_sectors = 0; + opts.read_mode = READ_MODE_UNINIT; + opts.source_image = INPUT_UNKNOWN; + opts.hexdump = 2; /* Not set. */ + + gl_default_cdio_log_handler = cdio_log_set_handler (log_handler); +} + +int +main(int argc, char *argv[]) +{ + uint8_t buffer[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + unsigned int blocklen=CDIO_CD_FRAMESIZE_RAW; + CdIo *p_cdio=NULL; + int output_fd=-1; + FILE *output_stream; + + init(); + + /* Parse our arguments; every option seen by `parse_opt' will + be reflected in `arguments'. */ + parse_options(argc, argv); + + print_version(program_name, VERSION, opts.no_header, opts.version_only); + + p_cdio = open_input(source_name, opts.source_image, opts.access_mode); + + if (opts.output_file!=NULL) { + + /* If hexdump not explicitly set, then don't produce hexdump + when writing to a file. + */ + if (opts.hexdump == 2) opts.hexdump = 0; + + output_fd = open(opts.output_file, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (-1 == output_fd) { + err_exit("Error opening output file %s: %s\n", + opts.output_file, strerror(errno)); + + } + } else + /* If we are writing to stdout, then the default is to produce + a hexdump. + */ + if (opts.hexdump == 2) opts.hexdump = 1; + + + for ( ; opts.start_lsn <= opts.end_lsn; opts.start_lsn++ ) { + switch (opts.read_mode) { + case READ_AUDIO: + case READ_M1F1: + case READ_M1F2: + case READ_M2F1: + case READ_M2F2: + if (DRIVER_OP_SUCCESS != + cdio_read_sector(p_cdio, &buffer, + opts.start_lsn, + (cdio_read_mode_t) opts.read_mode)) { + report( stderr, "error reading block %u\n", + (unsigned int) opts.start_lsn ); + blocklen = 0; + } else { + switch (opts.read_mode) { + case READ_M1F1: + blocklen=CDIO_CD_FRAMESIZE; + break; + case READ_M1F2: + blocklen=M2RAW_SECTOR_SIZE; + case READ_M2F1: + blocklen=CDIO_CD_FRAMESIZE; + case READ_M2F2: + blocklen=M2F2_SECTOR_SIZE; + default: ; + } + } + break; + + case READ_ANY: + { + driver_id_t driver_id = cdio_get_driver_id(p_cdio); + if (cdio_is_device(source_name, driver_id)) { + if (DRIVER_OP_SUCCESS != + mmc_read_sectors(p_cdio, &buffer, + opts.start_lsn, CDIO_MMC_READ_TYPE_ANY, 1)) { + report( stderr, "error reading block %u\n", + (unsigned int) opts.start_lsn ); + blocklen = 0; + } + } else { + err_exit( + "%s: mode 'any' must be used with a real CD-ROM, not an image file.\n", program_name); + } + } + break; + + case READ_MODE_UNINIT: + err_exit("%s: Reading mode not set\n", program_name); + break; + } + + if (!opts.output_file) { + output_stream = stdout; + } else { + output_stream = fdopen(output_fd, "w"); + } + + if (opts.hexdump) + hexdump(output_stream, buffer, blocklen, opts.just_hex); + else if (opts.output_file) + write(output_fd, buffer, blocklen); + else { + unsigned int i; + for (i=0; i diff --git a/src/cdda-player.c b/src/cdda-player.c new file mode 100644 index 00000000..6f3c7b42 --- /dev/null +++ b/src/cdda-player.c @@ -0,0 +1,1727 @@ +/* + $Id: cdda-player.c,v 1.50 2008/06/19 15:44:14 flameeyes Exp $ + + Copyright (C) 2005, 2006, 2008 Rocky Bernstein + + Adapted from Gerd Knorr's player.c program + Copyright (C) 1997, 1998 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#include + +#ifdef HAVE_CDDB +#include +#endif + +#include + +#ifdef HAVE_ERRNO_H +#include +#endif + +#ifdef HAVE_CURSES_H +#include +#else +#ifdef HAVE_NCURSES_H +#include +#else +# error "You need or +#include +#include +#include +#include + +#include "cddb.h" +#include "getopt.h" + +static void action(const char *psz_action); +static void display_cdinfo(CdIo_t *p_cdio, track_t i_tracks, + track_t i_first_track); +static void display_tracks(void); +static void get_cddb_track_info(track_t i_track); +static void get_cdtext_track_info(track_t i_track); +static void get_track_info(track_t i_track); +static bool play_track(track_t t1, track_t t2); + +static CdIo_t *p_cdio; /* libcdio handle */ +static driver_id_t driver_id = DRIVER_DEVICE; +static int b_sig = false; /* set on some signals */ + +/* cdrom data */ +static track_t i_first_track; +static track_t i_last_track; +static track_t i_first_audio_track; +static track_t i_last_audio_track; +static track_t i_last_display_track = CDIO_INVALID_TRACK; +static track_t i_tracks; +static msf_t toc[CDIO_CDROM_LEADOUT_TRACK+1]; +static cdio_subchannel_t sub; /* subchannel last time read */ +static int i_data; /* # of data tracks present ? */ +static int start_track = 0; +static int stop_track = 0; +static int one_track = 0; +static int i_vol_port = 5; /* If 5, retrieve volume port. + Otherwise the port number 0..3 + of a working volume port and + 4 for no working port. + */ + +/* settings which can be set from the command or interactively. */ +static bool b_cd = false; +static bool auto_mode = false; +static bool b_verbose = false; +static bool debug = false; +static bool b_interactive = true; +static bool b_prefer_cdtext = true; +static bool b_cddb = false; /* CDDB database present */ +static bool b_db = false; /* we have a database at all */ +static bool b_record = false; /* we have a record for +static the inserted CD */ +static bool b_all_tracks = false; /* True if we display all tracks*/ +static int8_t i_volume_level = -1; /* Valid range is 0..100 */ + + +static char *psz_device=NULL; +static char *psz_program; + +/* Info about songs and titles. The 0 entry will contain the disc info. + */ +typedef struct +{ + char title[80]; + char artist[80]; + char length[8]; + char ext_data[80]; + bool b_cdtext; /* true if from CD-Text, false if from CDDB */ +} cd_track_info_rec_t; + +static cd_track_info_rec_t cd_info[CDIO_CD_MAX_TRACKS+2]; + +static char title[80]; +static char artist[80]; +static char genre[40]; +static char category[40]; +static char year[5]; + +static bool b_cdtext_title; /* true if from CD-Text, false if from CDDB */ +static bool b_cdtext_artist; /* true if from CD-Text, false if from CDDB */ +static bool b_cdtext_genre; /* true if from CD-Text, false if from CDDB */ +static bool b_cdtext_category; /* true if from CD-Text, false if from CDDB */ +static bool b_cdtext_year; /* true if from CD-Text, false if from CDDB */ + +static cdio_audio_volume_t audio_volume; + +#ifdef HAVE_CDDB +static cddb_conn_t *p_conn = NULL; +static cddb_disc_t *p_cddb_disc = NULL; +static int i_cddb_matches = 0; +#endif + +#define MAX_KEY_STR 50 +static const char key_bindings[][MAX_KEY_STR] = { + " right play / next track", + " left previous track", + " up/down 10 sec forward / back", + " 1-9 jump to track 1-9", + " 0 jump to track 10", + " F1-F20 jump to track 11-30", + " ", + " k, h, ? show this key help", + " l, toggle listing all tracks", + " e eject", + " c close tray", + " p, space pause / resume", + " s stop", + " q, ^C quit", + " x quit and continue playing", + " a toggle auto-mode", + " - decrease volume level", + " + increase volume level", +}; + +static const unsigned int i_key_bindings = sizeof(key_bindings) / MAX_KEY_STR; + +/* ---------------------------------------------------------------------- */ +/* tty stuff */ + +typedef enum { + LINE_STATUS = 0, + LINE_CDINFO = 1, + + LINE_ARTIST = 3, + LINE_CDNAME = 4, + LINE_GENRE = 5, + LINE_YEAR = 6, + + LINE_TRACK_PREV = 8, + LINE_TRACK_TITLE = 9, + LINE_TRACK_ARTIST = 10, + LINE_TRACK_NEXT = 11, + +} track_line_t; + +static unsigned int LINE_ACTION = 25; +static unsigned int COLS_LAST; +static char psz_action_line[300] = ""; + +static int rounded_div(unsigned int i_a, unsigned int i_b) +{ + const unsigned int i_b_half=i_b/2; + return ((i_a)+i_b_half)/i_b; +} + +/** Curses window initialization. */ +static void +tty_raw() +{ + if (!b_interactive) return; + + initscr(); + cbreak(); + clear(); + noecho(); +#ifdef HAVE_KEYPAD + keypad(stdscr,1); +#endif + getmaxyx(stdscr, LINE_ACTION, COLS_LAST); + LINE_ACTION--; + refresh(); +} + +/** Curses window finalization. */ +static void +tty_restore() +{ + if (!b_interactive) return; + endwin(); +} + +/* Called when window is resized. */ +static void +sigwinch() +{ + tty_restore(); + tty_raw(); + action(NULL); +} + +/* Signal handler - Ctrl-C and others. */ +static void +ctrlc(int signal) +{ + b_sig = true; +} + +/* Timed wait on an event. */ +static int +select_wait(int sec) +{ + struct timeval tv; + fd_set se; + + FD_ZERO(&se); + FD_SET(0,&se); + tv.tv_sec = sec; + tv.tv_usec = 0; + return select(1,&se,NULL,NULL,&tv); +} + +/* ---------------------------------------------------------------------- */ + +/* Display the action line. */ +static void +action(const char *psz_action) +{ + if (!b_interactive) { + if (b_verbose && psz_action) + fprintf(stderr,"action: %s\n", psz_action); + return; + } + + if (!psz_action) + psz_action = psz_action_line; + else if (psz_action && strlen(psz_action)) + snprintf(psz_action_line, sizeof(psz_action_line), "action : %s", + psz_action); + else + snprintf(psz_action_line, sizeof(psz_action_line), "%s", "" ); + mvprintw(LINE_ACTION, 0, psz_action_line); + clrtoeol(); + refresh(); +} + + +/* Display an error message.. */ +static void +xperror(const char *psz_msg) +{ + char line[80]; + + if (!b_interactive) { + if (b_verbose) { + fprintf(stderr, "error: "); + perror(psz_msg); + } + return; + } + + if (b_verbose) { + sprintf(line,"%s: %s", psz_msg, strerror(errno)); + attron(A_STANDOUT); + mvprintw(LINE_ACTION, 0, (char *) "error : %s", line); + attroff(A_STANDOUT); + clrtoeol(); + refresh(); + select_wait(3); + action(""); + } +} + +static void +finish(const char *psz_msg, int rc) +{ + if (b_interactive) { + attron(A_STANDOUT); + mvprintw(LINE_ACTION, 0, (char *) "%s, exiting...\n", psz_msg); + attroff(A_STANDOUT); + clrtoeol(); + refresh(); + } + tty_restore(); +#ifdef HAVE_CDDB + if (p_conn) cddb_destroy(p_conn); + cddb_disc_destroy(p_cddb_disc); + libcddb_shutdown(); +#endif /*HAVE_CDDB*/ + cdio_destroy (p_cdio); + free (psz_device); + exit (rc); +} + +/* ---------------------------------------------------------------------- */ + +/* Set all audio channels to level. level is assumed to be in the range + 0..100. +*/ +static bool +set_volume_level(CdIo_t *p_cdio, uint8_t i_level) +{ + const unsigned int i_new_level= rounded_div(i_level*256, 100); + unsigned int i; + driver_return_code_t rc; + for (i=0; i<=3; i++) { + audio_volume.level[i] = i_new_level; + } + + rc = cdio_audio_set_volume(p_cdio, &audio_volume); + if ( DRIVER_OP_SUCCESS != rc ) { + /* If we can't do a get volume, audio_volume.level is used as + a second-best guess. But if this set failed restore it to + an invalid value so we don't get confused and think that this + was set. */ + for (i=0; i<=3; i++) { + audio_volume.level[i] = 0; + } + } else { + /* Set i_vol_port so volume levels set above will get used. */ + i_vol_port=0; + } + return rc; + +} + +/* Add 1 to volume level. If we are at the max or min value wrap + around. If volume level is undefined, then this means we + could not get the current value and we'll' set it to 50 the midway + point. + Return status of setting the volume level. +*/ +static bool +decrease_volume_level(CdIo_t *p_cdio) +{ + if (i_volume_level == -1) i_volume_level = 50; + if (i_volume_level == 0) i_volume_level = 100; + return set_volume_level(p_cdio, --i_volume_level); +} + +/* Subtract 1 to volume level. If we are at the max or min value wrap + around. If volume level is undefined, then this means we + could not get the current value and we'll' set it to 50 the midway + point. + Return status of setting the volume level. +*/ +static bool +increase_volume_level(CdIo_t *p_cdio) +{ + if (i_volume_level == -1) i_volume_level = 50; + if (i_volume_level == 100) i_volume_level = -1; + return set_volume_level(p_cdio, ++i_volume_level); +} + +/** Stop playing audio CD */ +static bool +cd_stop(CdIo_t *p_cdio) +{ + bool b_ok = true; + if (b_cd && p_cdio) { + action("stop..."); + i_last_audio_track = CDIO_INVALID_TRACK; + b_ok = DRIVER_OP_SUCCESS == cdio_audio_stop(p_cdio); + if ( !b_ok ) + xperror("stop"); + if (b_all_tracks) display_tracks(); + } + return b_ok; +} + +/** Eject CD */ +static bool +cd_eject(void) +{ + bool b_ok = true; + if (p_cdio) { + cd_stop(p_cdio); + action("eject..."); + b_ok = DRIVER_OP_SUCCESS == cdio_eject_media(&p_cdio); + if (!b_ok) + xperror("eject"); + b_cd = false; + cdio_destroy (p_cdio); + p_cdio = NULL; + } + return b_ok; +} + +/** Close CD tray */ +static bool +cd_close(const char *psz_device) +{ + bool b_ok = true; + if (!b_cd) { + action("close..."); + b_ok = DRIVER_OP_SUCCESS == cdio_close_tray(psz_device, &driver_id); + if (!b_ok) + xperror("close"); + } + return b_ok; +} + +/** Pause playing audio CD */ +static bool +cd_pause(CdIo_t *p_cdio) +{ + bool b_ok = true; + if (sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) { + b_ok = DRIVER_OP_SUCCESS == cdio_audio_pause(p_cdio); + if (!b_ok) + xperror("pause"); + } + return b_ok; +} + +/** Get status/track/position info of an audio CD */ +static bool +read_subchannel(CdIo_t *p_cdio) +{ + bool b_ok = true; + if (!p_cdio) return false; + + b_ok = DRIVER_OP_SUCCESS == cdio_audio_read_subchannel(p_cdio, &sub); + if (!b_ok) { + xperror("read subchannel"); + b_cd = 0; + } + if (auto_mode && sub.audio_status == CDIO_MMC_READ_SUB_ST_COMPLETED) + cd_eject(); + return b_ok; +} + +#ifdef HAVE_CDDB +/** This routine is called by vcd routines on error. + Setup is done by init_input_plugin. +*/ +static void +cddb_log_handler (cddb_log_level_t level, const char message[]) +{ + switch (level) { + case CDDB_LOG_DEBUG: + case CDDB_LOG_INFO: + if (!b_verbose) + return; + /* Fall through if to warn case */ + case CDDB_LOG_WARN: + case CDDB_LOG_ERROR: + case CDDB_LOG_CRITICAL: + default: + xperror(message); + break; + } + + /* gl_default_cdio_log_handler (level, message); */ +} +#endif /* HAVE_CDDB */ + +static void +get_cddb_disc_info(CdIo_t *p_cdio) +{ +#ifdef HAVE_CDDB + b_db = init_cddb(p_cdio, &p_conn, &p_cddb_disc, xperror, i_first_track, + i_tracks, &i_cddb_matches); + if (b_db) { + int i_year; + i_year = atoi(year); + cddb_disc_set_artist(p_cddb_disc, artist); + cddb_disc_set_title(p_cddb_disc, title); + cddb_disc_set_genre(p_cddb_disc, genre); + cddb_disc_set_year(p_cddb_disc, i_year); + } +#endif /* HAVE_CDDB */ + return; +} + +#define add_cdtext_disc_info(format_str, info_field, FIELD) \ + if (p_cdtext->field[FIELD] && !strlen(info_field)) { \ + snprintf(info_field, sizeof(info_field), format_str, \ + p_cdtext->field[FIELD]); \ + b_cdtext_ ## info_field = true; \ + } + +static void +get_cdtext_disc_info(CdIo_t *p_cdio) +{ + cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio, 0); + + if (p_cdtext) { + add_cdtext_disc_info("%s", title, CDTEXT_TITLE); + add_cdtext_disc_info("%s", artist, CDTEXT_PERFORMER); + add_cdtext_disc_info("%s", genre, CDTEXT_GENRE); + cdtext_destroy(p_cdtext); + } +} + +static void +get_disc_info(CdIo_t *p_cdio) +{ + b_db = false; + if (b_prefer_cdtext) { + get_cdtext_disc_info(p_cdio); + get_cddb_disc_info(p_cdio); + } else { + get_cddb_disc_info(p_cdio); + get_cdtext_disc_info(p_cdio); + } +} + +/** Read CD TOC and set CD information. */ +static void +read_toc(CdIo_t *p_cdio) +{ + track_t i; + + action("read toc..."); + + memset(cd_info, 0, sizeof(cd_info)); + title[0] = artist[0] = genre[0] = category[0] = year[0] = '\0'; + + i_first_track = cdio_get_first_track_num(p_cdio); + i_last_track = cdio_get_last_track_num(p_cdio); + i_tracks = cdio_get_num_tracks(p_cdio); + i_first_audio_track = i_first_track; + i_last_audio_track = i_last_track; + + + cdio_audio_get_volume(p_cdio, &audio_volume); + for (i_vol_port=0; i_vol_port<4; i_vol_port++) { + if (audio_volume.level[i_vol_port] > 0) break; + } + + if ( CDIO_INVALID_TRACK == i_first_track || + CDIO_INVALID_TRACK == i_last_track ) { + xperror("read toc header"); + b_cd = false; + b_record = false; + i_last_display_track = CDIO_INVALID_TRACK; + } else { + b_cd = true; + i_data = 0; + get_disc_info(p_cdio); + for (i = i_first_track; i <= i_last_track+1; i++) { + int s; + if ( !cdio_get_track_msf(p_cdio, i, &(toc[i])) ) + { + xperror("read toc entry"); + b_cd = false; + return; + } + if ( TRACK_FORMAT_AUDIO == cdio_get_track_format(p_cdio, i) ) { + + if (i != i_first_track) + { + s = cdio_audio_get_msf_seconds(&toc[i]) + - cdio_audio_get_msf_seconds(&toc[i-1]); + snprintf(cd_info[i-1].length, sizeof(cd_info[0].length), + "%02d:%02d", + s / CDIO_CD_SECS_PER_MIN, s % CDIO_CD_SECS_PER_MIN); + } + } else { + if ((i != i_last_track+1) ) { + i_data++; + if (i == i_first_track) { + if (i == i_last_track) + i_first_audio_track = CDIO_CDROM_LEADOUT_TRACK; + else + i_first_audio_track++; + } + } + } + get_track_info(i); + } + b_record = true; + read_subchannel(p_cdio); + if (auto_mode && sub.audio_status != CDIO_MMC_READ_SUB_ST_PLAY) + play_track(1, CDIO_CDROM_LEADOUT_TRACK); + } + action(""); + if (!b_all_tracks) + display_cdinfo(p_cdio, i_tracks, i_first_track); +} + +/** Play an audio track. */ +static bool +play_track(track_t i_start_track, track_t i_end_track) +{ + bool b_ok = true; + char line[80]; + + if (!b_cd) { + read_toc(p_cdio); + } + + read_subchannel(p_cdio); + if (!b_cd || i_first_track == CDIO_CDROM_LEADOUT_TRACK) + return false; + + if (debug) + fprintf(stderr,"play tracks: %d-%d => ", i_start_track, i_end_track-1); + if (i_start_track < i_first_track) i_start_track = i_first_track; + if (i_start_track > i_last_audio_track) i_start_track = i_last_audio_track; + if (i_end_track < i_first_track) i_end_track = i_first_track; + if (i_end_track > i_last_audio_track) i_end_track = i_last_audio_track; + i_end_track++; + if (debug) + fprintf(stderr,"%d-%d\n",i_start_track, i_end_track-1); + + cd_pause(p_cdio); + sprintf(line,"play track %d to track %d.", i_start_track, i_end_track-1); + action(line); + b_ok = (DRIVER_OP_SUCCESS == cdio_audio_play_msf(p_cdio, + &(toc[i_start_track]), + &(toc[i_end_track])) ); + if (!b_ok) xperror("play"); + return b_ok; +} + +static void +skip(int diff) +{ + msf_t start_msf; + int sec; + + read_subchannel(p_cdio); + if (!b_cd || i_first_track == CDIO_CDROM_LEADOUT_TRACK) + return; + + sec = cdio_audio_get_msf_seconds(&sub.abs_addr); + sec += diff; + if (sec < 0) sec = 0; + + start_msf.m = cdio_to_bcd8(sec / CDIO_CD_SECS_PER_MIN); + start_msf.s = cdio_to_bcd8(sec % CDIO_CD_SECS_PER_MIN); + start_msf.f = 0; + + cd_pause(p_cdio); + if ( DRIVER_OP_SUCCESS != cdio_audio_play_msf(p_cdio, &start_msf, + &(toc[i_last_audio_track])) ) + xperror("play"); +} + +static bool +toggle_pause() +{ + bool b_ok = true; + if (!b_cd) return false; + + if (CDIO_MMC_READ_SUB_ST_PAUSED == sub.audio_status) { + b_ok = DRIVER_OP_SUCCESS != cdio_audio_resume(p_cdio); + if (!b_ok) + xperror("resume"); + } else { + b_ok = DRIVER_OP_SUCCESS != cdio_audio_pause(p_cdio); + if (!b_ok) + xperror("pause"); + } + return b_ok; +} + +/** Update windows with status and possibly track info. This used in + interactive playing not batch mode. + */ +static void +display_status(bool b_status_only) +{ + char line[80]; + + if (!b_interactive) return; + + if (!b_cd) { + sprintf(line,"no CD in drive (%s)", psz_device); + + } else if (i_first_track == CDIO_CDROM_LEADOUT_TRACK) { + sprintf(line,"CD has only data tracks"); + + } else if (sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED || + sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) { + cdio_audio_get_volume(p_cdio, &audio_volume); + if (i_vol_port < 4) { + i_volume_level = rounded_div(audio_volume.level[i_vol_port]*100, 256); + sprintf(line, + "track %2d - %02x:%02x of %s (%02x:%02x abs) %s volume: %d", + sub.track, sub.rel_addr.m, sub.rel_addr.s, + cd_info[sub.track].length, + sub.abs_addr.m, sub.abs_addr.s, + mmc_audio_state2str(sub.audio_status), + i_volume_level); + } else + sprintf(line,"track %2d - %02x:%02x of %s (%02x:%02x abs) %s", + sub.track, sub.rel_addr.m, sub.rel_addr.s, + cd_info[sub.track].length, sub.abs_addr.m, sub.abs_addr.s, + mmc_audio_state2str(sub.audio_status)); + } else { + sprintf(line,"%s", mmc_audio_state2str(sub.audio_status)); + + } + + action(NULL); + mvprintw(LINE_STATUS, 0, (char *) "status%s: %s", + auto_mode ? "*" : " ", line); + clrtoeol(); + + if ( !b_status_only && b_db && i_last_display_track != sub.track && + (sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED || + sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) && + b_cd) { + + if (b_all_tracks) + display_tracks(); + else { + const cd_track_info_rec_t *p_cd_info = &cd_info[sub.track]; + i_last_display_track = sub.track; + if (i_first_audio_track != sub.track && + strlen(cd_info[sub.track-1].title)) { + const cd_track_info_rec_t *p_cd_info = &cd_info[sub.track-1]; + mvprintw(LINE_TRACK_PREV, 0, (char *) " track %2d title : %s [%s]", + sub.track-1, p_cd_info->title, + p_cd_info->b_cdtext ? "CD-Text" : "CDDB"); + clrtoeol(); + } else { + mvprintw(LINE_TRACK_PREV, 0, (char *) "%s",""); + clrtoeol(); + } + if (strlen(p_cd_info->title)) { + mvprintw(LINE_TRACK_TITLE, 0, (char *) ">track %2d title : %s [%s]", + sub.track, p_cd_info->title, + (char *) (p_cd_info->b_cdtext ? "CD-Text" : "CDDB")); + clrtoeol(); + } + if (strlen(p_cd_info->artist)) { + mvprintw(LINE_TRACK_ARTIST, 0, (char *) ">track %2d artist: %s [%s]", + sub.track, p_cd_info->artist, + p_cd_info->b_cdtext ? "CD-Text" : "CDDB"); + clrtoeol(); + } + if (i_last_audio_track != sub.track && + strlen(cd_info[sub.track+1].title)) { + const cd_track_info_rec_t *p_cd_info = &cd_info[sub.track+1]; + mvprintw(LINE_TRACK_NEXT, 0, (char *) " track %2d title : %s [%s]", + sub.track+1, p_cd_info->title, + p_cd_info->b_cdtext ? "CD-Text" : "CDDB"); + clrtoeol(); + } else { + mvprintw(LINE_TRACK_NEXT, 0, (char *) "%s",""); + clrtoeol(); + } + clrtobot(); + } + } + + action(NULL); /* Redisplay action line. */ + +} + +static void +get_cddb_track_info(track_t i_track) +{ +#ifdef HAVE_CDDB + cddb_track_t *t = cddb_disc_get_track(p_cddb_disc, + i_track - i_first_track); + if (t) { + cddb_track_set_title(t, title); + cddb_track_set_artist(t, artist); + } + +#else + ; +#endif +} + +#define add_cdtext_track_info(format_str, info_field, FIELD) \ + if (p_cdtext->field[FIELD]) { \ + snprintf(cd_info[i_track].info_field, \ + sizeof(cd_info[i_track].info_field), \ + format_str, p_cdtext->field[FIELD]); \ + cd_info[i_track].b_cdtext = true; \ + } + + +static void +get_cdtext_track_info(track_t i_track) +{ + + cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio, i_track); + + if (NULL != p_cdtext) { + add_cdtext_track_info("%s", title, CDTEXT_TITLE); + add_cdtext_track_info("%s", artist, CDTEXT_PERFORMER); + cdtext_destroy(p_cdtext); + } +} + +static void +get_track_info(track_t i_track) +{ + + if (b_prefer_cdtext) { + get_cdtext_track_info(i_track); + get_cddb_track_info(i_track); + } else { + get_cddb_track_info(i_track); + get_cdtext_track_info(i_track); + } +} + +#define display_line(LINE_NO, COL_NO, format_str, field) \ + if (field && field[0]) { \ + mvprintw(LINE_NO, COL_NO, (char *) format_str " [%s]", \ + field, \ + b_cdtext_ ## field ? "CD-Text": "CDDB"); \ + clrtoeol(); \ + } + +static void +display_cdinfo(CdIo_t *p_cdio, track_t i_tracks, track_t i_first_track) +{ + int len; + char line[80]; + + if (!b_interactive) return; + + if (!b_cd) sprintf(line, "-"); + else { + len = sprintf(line, "%2u tracks (%02x:%02x min)", + (unsigned int) i_last_track, + toc[i_last_track+1].m, toc[i_last_track+1].s); + if (i_data && i_first_track != CDIO_CDROM_LEADOUT_TRACK) + sprintf(line+len,", audio=%u-%u", (unsigned int) i_first_audio_track, + (unsigned int) i_last_audio_track); + + display_line(LINE_ARTIST, 0, "CD Artist : %s", artist); + display_line(LINE_CDNAME, 0, "CD Title : %s", title); + display_line(LINE_GENRE, 0, "CD Genre : %s", genre); + display_line(LINE_YEAR, 0, "CD Year : %s", year); + } + + mvprintw(LINE_CDINFO, 0, (char *) "CD info: %0s", line); + clrtoeol(); + action(NULL); +} + +/* ---------------------------------------------------------------------- */ + +static void +usage(char *prog) +{ + fprintf(stderr, + "%s is a simple curses CD player. It can pick up artist,\n" + "CD name and song title from CD-Text info on the CD or\n" + "via CDDB.\n" + "\n" + "usage: %s [options] [device]\n" + "\n" + "default for to search for a CD-ROM device with a CD-DA loaded\n" + "\n" + "These command line options available:\n" + " -h print this help\n" + " -k print key mapping\n" + " -a start up in auto-mode\n" + " -v verbose\n" + "\n" + "for non-interactive use (only one) of these:\n" + " -l list tracks\n" + " -c print cover (PostScript to stdout)\n" + " -C close CD-ROM tray. If you use this option,\n" + " a CD-ROM device name must be specified.\n" + " -p play the whole CD\n" + " -t n play track >n<\n" + " -t a-b play all tracks between a and b (inclusive)\n" + " -L set volume level\n" + " -s stop playing\n" + " -S list audio subchannel information\n" + " -e eject cdrom\n" + "\n" + "That's all. Oh, maybe a few words more about the auto-mode. This\n" + "is the 'dont-touch-any-key' feature. You load a CD, player starts\n" + "to play it, and when it is done it ejects the CD. Start it that\n" + "way on a spare console and forget about it...\n" + "\n" + "(c) 1997,98 Gerd Knorr \n" + "(c) 2005, 2006 Rocky Bernstein \n" + , prog, prog); +} + +static void +print_keys() +{ + unsigned int i; + for (i=0; i < i_key_bindings; i++) + fprintf(stderr, "%s\n", key_bindings[i]); +} + +static void +keypress_wait(CdIo_t *p_cdio) + { + int key; + action("press any key to continue"); + while (1 != select_wait(b_cd ? 1 : 5)) { + read_subchannel(p_cdio); + display_status(true); + } + key = getch(); + clrtobot(); + action(NULL); + if (!b_all_tracks) + display_cdinfo(p_cdio, i_tracks, i_first_track); + i_last_display_track = CDIO_INVALID_TRACK; + } + +static void +list_keys() +{ + unsigned int i; + for (i=0; i < i_key_bindings; i++) { + mvprintw(LINE_TRACK_PREV+i, 0, (char *) "%s", key_bindings[i]); + clrtoeol(); + } + keypress_wait(p_cdio); +} + +static void +display_tracks(void) +{ + track_t i; + int i_line=0; + int s; + + if (b_record) { + i_line=LINE_TRACK_PREV - 1; + for (i = i_first_track; i <= i_last_track; i++) { + char line[200]=""; + s = cdio_audio_get_msf_seconds(&toc[i+1]) + - cdio_audio_get_msf_seconds(&toc[i]); + read_subchannel(p_cdio); + sprintf(line, "%2d %02d:%02d %s ", i, + s / CDIO_CD_SECS_PER_MIN, s % CDIO_CD_SECS_PER_MIN, + ( ( sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY || + sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED ) && + sub.track == i ) ? "->" : " |"); + if (b_record) { + if ( strlen(cd_info[i].title) ) + strcat(line, cd_info[i].title); + if ( strlen(cd_info[i].artist) > 0 ) { + if (strlen(cd_info[i].title)) + strcat(line, " / "); + strcat(line, cd_info[i].artist); + } + } + if (sub.track == i) { + attron(A_STANDOUT); + mvprintw(i_line++, 0, line); + attroff(A_STANDOUT); + } else + mvprintw(i_line++, 0, line); + clrtoeol(); + } + } +} + +/* + * PostScript 8bit latin1 handling + * stolen from mpage output -- please don't ask me how this works... + */ +#define ENCODING_TRICKS \ + "/reencsmalldict 12 dict def\n" \ + "/ReEncodeSmall { reencsmalldict begin\n" \ + "/newcodesandnames exch def /newfontname exch def\n" \ + "/basefontname exch def\n" \ + "/basefontdict basefontname findfont def\n" \ + "/newfont basefontdict maxlength dict def\n" \ + "basefontdict { exch dup /FID ne { dup /Encoding eq\n" \ + "{ exch dup length array copy newfont 3 1 roll put }\n" \ + "{ exch newfont 3 1 roll put }\n" \ + "ifelse }\n" \ + "{ pop pop }\n" \ + "ifelse } forall\n" \ + "newfont /FontName newfontname put\n" \ + "newcodesandnames aload pop newcodesandnames length 2 idiv\n" \ + "{ newfont /Encoding get 3 1 roll put } repeat\n" \ + "newfontname newfont definefont pop end } def\n" \ + "/charvec [\n" \ + "026 /Scaron\n" \ + "027 /Ydieresis\n" \ + "028 /Zcaron\n" \ + "029 /scaron\n" \ + "030 /trademark\n" \ + "031 /zcaron\n" \ + "032 /space\n" \ + "033 /exclam\n" \ + "034 /quotedbl\n" \ + "035 /numbersign\n" \ + "036 /dollar\n" \ + "037 /percent\n" \ + "038 /ampersand\n" \ + "039 /quoteright\n" \ + "040 /parenleft\n" \ + "041 /parenright\n" \ + "042 /asterisk\n" \ + "043 /plus\n" \ + "044 /comma\n" \ + "045 /minus\n" \ + "046 /period\n" \ + "047 /slash\n" \ + "048 /zero\n" \ + "049 /one\n" \ + "050 /two\n" \ + "051 /three\n" \ + "052 /four\n" \ + "053 /five\n" \ + "054 /six\n" \ + "055 /seven\n" \ + "056 /eight\n" \ + "057 /nine\n" \ + "058 /colon\n" \ + "059 /semicolon\n" \ + "060 /less\n" \ + "061 /equal\n" \ + "062 /greater\n" \ + "063 /question\n" \ + "064 /at\n" \ + "065 /A\n" \ + "066 /B\n" \ + "067 /C\n" \ + "068 /D\n" \ + "069 /E\n" \ + "070 /F\n" \ + "071 /G\n" \ + "072 /H\n" \ + "073 /I\n" \ + "074 /J\n" \ + "075 /K\n" \ + "076 /L\n" \ + "077 /M\n" \ + "078 /N\n" \ + "079 /O\n" \ + "080 /P\n" \ + "081 /Q\n" \ + "082 /R\n" \ + "083 /S\n" \ + "084 /T\n" \ + "085 /U\n" \ + "086 /V\n" \ + "087 /W\n" \ + "088 /X\n" \ + "089 /Y\n" \ + "090 /Z\n" \ + "091 /bracketleft\n" \ + "092 /backslash\n" \ + "093 /bracketright\n" \ + "094 /asciicircum\n" \ + "095 /underscore\n" \ + "096 /quoteleft\n" \ + "097 /a\n" \ + "098 /b\n" \ + "099 /c\n" \ + "100 /d\n" \ + "101 /e\n" \ + "102 /f\n" \ + "103 /g\n" \ + "104 /h\n" \ + "105 /i\n" \ + "106 /j\n" \ + "107 /k\n" \ + "108 /l\n" \ + "109 /m\n" \ + "110 /n\n" \ + "111 /o\n" \ + "112 /p\n" \ + "113 /q\n" \ + "114 /r\n" \ + "115 /s\n" \ + "116 /t\n" \ + "117 /u\n" \ + "118 /v\n" \ + "119 /w\n" \ + "120 /x\n" \ + "121 /y\n" \ + "122 /z\n" \ + "123 /braceleft\n" \ + "124 /bar\n" \ + "125 /braceright\n" \ + "126 /asciitilde\n" \ + "127 /.notdef\n" \ + "128 /fraction\n" \ + "129 /florin\n" \ + "130 /quotesingle\n" \ + "131 /quotedblleft\n" \ + "132 /guilsinglleft\n" \ + "133 /guilsinglright\n" \ + "134 /fi\n" \ + "135 /fl\n" \ + "136 /endash\n" \ + "137 /dagger\n" \ + "138 /daggerdbl\n" \ + "139 /bullet\n" \ + "140 /quotesinglbase\n" \ + "141 /quotedblbase\n" \ + "142 /quotedblright\n" \ + "143 /ellipsis\n" \ + "144 /dotlessi\n" \ + "145 /grave\n" \ + "146 /acute\n" \ + "147 /circumflex\n" \ + "148 /tilde\n" \ + "149 /oe\n" \ + "150 /breve\n" \ + "151 /dotaccent\n" \ + "152 /perthousand\n" \ + "153 /emdash\n" \ + "154 /ring\n" \ + "155 /Lslash\n" \ + "156 /OE\n" \ + "157 /hungarumlaut\n" \ + "158 /ogonek\n" \ + "159 /caron\n" \ + "160 /lslash\n" \ + "161 /exclamdown\n" \ + "162 /cent\n" \ + "163 /sterling\n" \ + "164 /currency\n" \ + "165 /yen\n" \ + "166 /brokenbar\n" \ + "167 /section\n" \ + "168 /dieresis\n" \ + "169 /copyright\n" \ + "170 /ordfeminine\n" \ + "171 /guillemotleft\n" \ + "172 /logicalnot\n" \ + "173 /hyphen\n" \ + "174 /registered\n" \ + "175 /macron\n" \ + "176 /degree\n" \ + "177 /plusminus\n" \ + "178 /twosuperior\n" \ + "179 /threesuperior\n" \ + "180 /acute\n" \ + "181 /mu\n" \ + "182 /paragraph\n" \ + "183 /periodcentered\n" \ + "184 /cedilla\n" \ + "185 /onesuperior\n" \ + "186 /ordmasculine\n" \ + "187 /guillemotright\n" \ + "188 /onequarter\n" \ + "189 /onehalf\n" \ + "190 /threequarters\n" \ + "191 /questiondown\n" \ + "192 /Agrave\n" \ + "193 /Aacute\n" \ + "194 /Acircumflex\n" \ + "195 /Atilde\n" \ + "196 /Adieresis\n" \ + "197 /Aring\n" \ + "198 /AE\n" \ + "199 /Ccedilla\n" \ + "200 /Egrave\n" \ + "201 /Eacute\n" \ + "202 /Ecircumflex\n" \ + "203 /Edieresis\n" \ + "204 /Igrave\n" \ + "205 /Iacute\n" \ + "206 /Icircumflex\n" \ + "207 /Idieresis\n" \ + "208 /Eth\n" \ + "209 /Ntilde\n" \ + "210 /Ograve\n" \ + "211 /Oacute\n" \ + "212 /Ocircumflex\n" \ + "213 /Otilde\n" \ + "214 /Odieresis\n" \ + "215 /multiply\n" \ + "216 /Oslash\n" \ + "217 /Ugrave\n" \ + "218 /Uacute\n" \ + "219 /Ucircumflex\n" \ + "220 /Udieresis\n" \ + "221 /Yacute\n" \ + "222 /Thorn\n" \ + "223 /germandbls\n" \ + "224 /agrave\n" \ + "225 /aacute\n" \ + "226 /acircumflex\n" \ + "227 /atilde\n" \ + "228 /adieresis\n" \ + "229 /aring\n" \ + "230 /ae\n" \ + "231 /ccedilla\n" \ + "232 /egrave\n" \ + "233 /eacute\n" \ + "234 /ecircumflex\n" \ + "235 /edieresis\n" \ + "236 /igrave\n" \ + "237 /iacute\n" \ + "238 /icircumflex\n" \ + "239 /idieresis\n" \ + "240 /eth\n" \ + "241 /ntilde\n" \ + "242 /ograve\n" \ + "243 /oacute\n" \ + "244 /ocircumflex\n" \ + "245 /otilde\n" \ + "246 /odieresis\n" \ + "247 /divide\n" \ + "248 /oslash\n" \ + "249 /ugrave\n" \ + "250 /uacute\n" \ + "251 /ucircumflex\n" \ + "252 /udieresis\n" \ + "253 /yacute\n" \ + "254 /thorn\n" \ + "255 /ydieresis\n" \ + "] def" + + +static void +ps_list_tracks(void) +{ + int i,s,y,sy; + + if (!b_record) return; + + printf("%%!PS-Adobe-2.0\n"); + + /* encoding tricks */ + puts(ENCODING_TRICKS); + printf("/Times /TimesLatin1 charvec ReEncodeSmall\n"); + printf("/Helvetica /HelveticaLatin1 charvec ReEncodeSmall\n"); + + /* Spaces */ + printf("0 setlinewidth\n"); + printf(" 100 100 moveto\n"); + printf(" 390 0 rlineto\n"); + printf(" 0 330 rlineto\n"); + printf("-390 0 rlineto\n"); + printf("closepath stroke\n"); + + printf(" 100 100 moveto\n"); + printf("-16 0 rlineto\n"); + printf(" 0 330 rlineto\n"); + printf("422 0 rlineto\n"); + printf(" 0 -330 rlineto\n"); + printf("closepath stroke\n"); + + /* Title */ + printf("/TimesLatin1 findfont 24 scalefont setfont\n"); + printf("120 400 moveto (%s) show\n", title); + printf("/TimesLatin1 findfont 18 scalefont setfont\n"); + printf("120 375 moveto (%s) show\n", artist); + + /* List */ + sy = 250 / i_last_track; + if (sy > 14) sy = 14; + printf("/labelfont /TimesLatin1 findfont %d scalefont def\n",sy-2); + printf("/timefont /Courier findfont %d scalefont def\n",sy-2); + for (i = i_first_track, y = 350; i <= i_last_track; i++, y -= sy) { + s = cdio_audio_get_msf_seconds(&toc[i+1]) + - cdio_audio_get_msf_seconds(&toc[i]); + + printf("labelfont setfont\n"); + printf("120 %d moveto (%d) show\n", y, i); + { + char line[200]=""; + if ( strlen(cd_info[i].title) ) + strcat(line, cd_info[i].title); + if ( strlen(cd_info[i].artist) > 0 ) { + if (strlen(cd_info[i].title)) + strcat(line, " / "); + strcat(line, cd_info[i].artist); + } + printf("150 %d moveto (%s) show\n", y, line); + } + printf("timefont setfont\n"); + printf("420 %d moveto (%2d:%02d) show\n", y, + s / CDIO_CD_SECS_PER_MIN, s % CDIO_CD_SECS_PER_MIN); + } + + /* Seitenbanner */ + printf("/HelveticaLatin1 findfont 12 scalefont setfont\n"); + printf(" 97 105 moveto (%s: %s) 90 rotate show -90 rotate\n", + artist, title); + printf("493 425 moveto (%s: %s) -90 rotate show 90 rotate\n", + artist, title); + printf("showpage\n"); +} + +static void +list_tracks(void) +{ + int i,s; + + if (!b_record) return; + + printf("Title : %s\n", title); + printf("Artist: %s\n", artist); + + for (i = i_first_track; i <= i_last_track; i++) { + s = cdio_audio_get_msf_seconds(&toc[i+1]) + - cdio_audio_get_msf_seconds(&toc[i]); + printf("%2d: %s [%d seconds]\n", i, cd_info[i].title, s); + } +} + +typedef enum { + NO_OP=0, + PLAY_CD=1, + PLAY_TRACK=2, + STOP_PLAYING=3, + EJECT_CD=4, + CLOSE_CD=5, + SET_VOLUME=6, + LIST_SUBCHANNEL=7, + LIST_KEYS=8, + LIST_TRACKS=9, + PS_LIST_TRACKS=10, + TOGGLE_PAUSE=11, + EXIT_PROGRAM=12 +} cd_operation_t; + +int +main(int argc, char *argv[]) +{ + int c, nostop=0; + char *h; + int i_rc = 0; + cd_operation_t cd_op = NO_OP; /* operation to do in non-interactive mode */ + + + psz_program = strrchr(argv[0],'/'); + psz_program = psz_program ? psz_program+1 : argv[0]; + + memset(&cddb_opts, 0, sizeof(cddb_opts)); + + cdio_loglevel_default = CDIO_LOG_WARN; + /* parse options */ + while ( 1 ) { + if (-1 == (c = getopt(argc, argv, "acCdehkplL:sSt:vx"))) + break; + switch (c) { + case 'v': + b_verbose = true; + if (cdio_loglevel_default > CDIO_LOG_INFO) + cdio_loglevel_default = CDIO_LOG_INFO; + break; + case 'd': + debug = 1; + if (cdio_loglevel_default > CDIO_LOG_DEBUG) + cdio_loglevel_default = CDIO_LOG_DEBUG; + break; + case 'a': + auto_mode = 1; + break; + + case 'L': + i_volume_level = atoi(optarg); + cd_op = SET_VOLUME; + b_interactive = false; + break; + + case 't': + if (NULL != (h = strchr(optarg,'-'))) { + *h = 0; + start_track = atoi(optarg); + stop_track = atoi(h+1)+1; + if (0 == start_track) start_track = 1; + if (1 == stop_track) stop_track = CDIO_CDROM_LEADOUT_TRACK; + } else { + start_track = atoi(optarg); + stop_track = start_track+1; + one_track = 1; + } + b_interactive = false; + cd_op = PLAY_TRACK; + break; + case 'p': + b_interactive = false; + cd_op = PLAY_CD; + break; + case 'l': + b_interactive = false; + cd_op = LIST_TRACKS; + break; + case 'C': + b_interactive = false; + cd_op = CLOSE_CD; + break; + case 'c': + b_interactive = false; + cd_op = PS_LIST_TRACKS; + break; + case 's': + b_interactive = false; + cd_op = STOP_PLAYING; + break; + case 'S': + b_interactive = false; + cd_op = LIST_SUBCHANNEL; + break; + case 'e': + b_interactive = false; + cd_op = EJECT_CD; + break; + case 'k': + print_keys(); + exit(1); + case 'h': + usage(psz_program); + exit(1); + default: + usage(psz_program); + exit(1); + } + } + + if (argc > optind) { + psz_device = strdup(argv[optind]); + } else { + char **ppsz_cdda_drives=NULL; + char **ppsz_all_cd_drives = cdio_get_devices_ret(&driver_id); + + if (!ppsz_all_cd_drives) { + fprintf(stderr, "Can't find a CD-ROM drive\n"); + exit(2); + } + ppsz_cdda_drives = cdio_get_devices_with_cap(ppsz_all_cd_drives, + CDIO_FS_AUDIO, false); + if (!ppsz_cdda_drives || !ppsz_cdda_drives[0]) { + fprintf(stderr, "Can't find a CD-ROM drive with a CD-DA in it\n"); + exit(3); + } + psz_device = strdup(ppsz_cdda_drives[0]); + cdio_free_device_list(ppsz_all_cd_drives); + cdio_free_device_list(ppsz_cdda_drives); + } + + if (!b_interactive) { + b_sig = true; + nostop=1; + } + + tty_raw(); + signal(SIGINT,ctrlc); + signal(SIGQUIT,ctrlc); + signal(SIGTERM,ctrlc); + signal(SIGHUP,ctrlc); + signal(SIGWINCH, sigwinch); + + if (CLOSE_CD != cd_op) { + /* open device */ + if (b_verbose) + fprintf(stderr, "open %s... ", psz_device); + p_cdio = cdio_open (psz_device, driver_id); + if (!p_cdio && cd_op != EJECT_CD) { + cd_close(psz_device); + p_cdio = cdio_open (psz_device, driver_id); + } + + if (p_cdio && b_verbose) + fprintf(stderr,"ok\n"); + } + + if (b_interactive) { +#ifdef HAVE_CDDB + cddb_log_set_handler (cddb_log_handler); +#else + ; +#endif + } else { + b_sig = true; + nostop=1; + if (EJECT_CD == cd_op) { + i_rc = cd_eject() ? 0 : 1; + } else { + switch (cd_op) { + case PS_LIST_TRACKS: + case LIST_TRACKS: + case PLAY_TRACK: + read_toc(p_cdio); + default: + break; + } + if (p_cdio) + switch (cd_op) { + case STOP_PLAYING: + b_cd = true; + i_rc = cd_stop(p_cdio) ? 0 : 1; + break; + case EJECT_CD: + /* Should have been handled above. */ + cd_eject(); + break; + case LIST_TRACKS: + list_tracks(); + break; + case PS_LIST_TRACKS: + ps_list_tracks(); + break; + + case PLAY_TRACK: + /* play just this one track */ + if (b_record) { + printf("%s / %s\n", artist, title); + if (one_track) + printf("%s\n", cd_info[start_track].title); + } + i_rc = play_track(start_track, stop_track) ? 0 : 1; + break; + + case PLAY_CD: + if (b_record) + printf("%s / %s\n", artist, title); + play_track(1,CDIO_CDROM_LEADOUT_TRACK); + break; + + case SET_VOLUME: + i_rc = set_volume_level(p_cdio, i_volume_level); + break; + + case LIST_SUBCHANNEL: + if (read_subchannel(p_cdio)) { + if (sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED || + sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) { + { + printf("track %2d - %02x:%02x (%02x:%02x abs) ", + sub.track, sub.rel_addr.m, sub.rel_addr.s, + sub.abs_addr.m, sub.abs_addr.s); + } + } + printf("drive state: %s\n", + mmc_audio_state2str(sub.audio_status)); + } else { + i_rc = 1; + } + break; + case CLOSE_CD: /* Handled below */ + case LIST_KEYS: + case TOGGLE_PAUSE: + case EXIT_PROGRAM: + case NO_OP: + break; + } + else if (CLOSE_CD == cd_op) { + i_rc = (DRIVER_OP_SUCCESS == cdio_close_tray(psz_device, NULL)) + ? 0 : 1; + } else { + fprintf(stderr,"no CD in drive (%s)\n", psz_device); + } + } + } + + /* Play all tracks *unless* we have a play or paused status + already. */ + + read_subchannel(p_cdio); + if (sub.audio_status != CDIO_MMC_READ_SUB_ST_PAUSED && + sub.audio_status != CDIO_MMC_READ_SUB_ST_PLAY) + play_track(1, CDIO_CDROM_LEADOUT_TRACK); + + while ( !b_sig ) { + int key; + if (!b_cd) read_toc(p_cdio); + read_subchannel(p_cdio); + display_status(false); + + if (1 == select_wait(b_cd ? 1 : 5)) { + switch (key = getch()) { + case '-': + decrease_volume_level(p_cdio); + break; + case '+': + increase_volume_level(p_cdio); + break; + case 'A': + case 'a': + auto_mode = !auto_mode; + break; + case 'X': + case 'x': + nostop=1; + /* fall through */ + case 'Q': + case 'q': + b_sig = true; + break; + case 'E': + case 'e': + cd_eject(); + break; + case 's': + cd_stop(p_cdio); + break; + case 'C': + case 'c': + cd_close(psz_device); + break; + case 'L': + case 'l': + b_all_tracks = !b_all_tracks; + if (b_all_tracks) + display_tracks(); + else { + i_last_display_track = CDIO_INVALID_TRACK; + display_cdinfo(p_cdio, i_tracks, i_first_track); + } + + break; + case 'K': + case 'k': + case 'h': + case 'H': + case '?': + list_keys(); + break; + case ' ': + case 'P': + case 'p': + toggle_pause(); + break; + case KEY_RIGHT: + if (b_cd && + (sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED || + sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY)) + play_track(sub.track+1, CDIO_CDROM_LEADOUT_TRACK); + else + play_track(1,CDIO_CDROM_LEADOUT_TRACK); + break; + case KEY_LEFT: + if (b_cd && + (sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED || + sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY)) + play_track(sub.track-1,CDIO_CDROM_LEADOUT_TRACK); + break; + case KEY_UP: + if (b_cd && sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) + skip(10); + break; + case KEY_DOWN: + if (b_cd && sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) + skip(-10); + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + play_track(key - '0', CDIO_CDROM_LEADOUT_TRACK); + break; + case '0': + play_track(10, CDIO_CDROM_LEADOUT_TRACK); + break; + case KEY_F(1): + case KEY_F(2): + case KEY_F(3): + case KEY_F(4): + case KEY_F(5): + case KEY_F(6): + case KEY_F(7): + case KEY_F(8): + case KEY_F(9): + case KEY_F(10): + case KEY_F(11): + case KEY_F(12): + case KEY_F(13): + case KEY_F(14): + case KEY_F(15): + case KEY_F(16): + case KEY_F(17): + case KEY_F(18): + case KEY_F(19): + case KEY_F(20): + play_track(key - KEY_F(1) + 11, CDIO_CDROM_LEADOUT_TRACK); + break; + } + } + } + if (!nostop) cd_stop(p_cdio); + tty_restore(); + finish("bye", i_rc); + + return 0; /* keep compiler happy */ +} diff --git a/src/cddb.c b/src/cddb.c new file mode 100644 index 00000000..8e45f073 --- /dev/null +++ b/src/cddb.c @@ -0,0 +1,143 @@ +/* + $Id: cddb.c,v 1.8 2008/06/25 08:01:54 rocky Exp $ + + Copyright (C) 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include "cddb.h" + +/*! + 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; + } +} + +/*! + 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. +*/ +uint32_t +cddb_discid(CdIo_t *p_cdio, track_t i_tracks) +{ + int i,t,n=0; + msf_t start_msf; + msf_t msf; + + for (i = 1; i <= i_tracks; i++) { + cdio_get_track_msf(p_cdio, i, &msf); + n += cddb_dec_digit_sum(cdio_audio_get_msf_seconds(&msf)); + } + + cdio_get_track_msf(p_cdio, 1, &start_msf); + cdio_get_track_msf(p_cdio, CDIO_CDROM_LEADOUT_TRACK, &msf); + + t = cdio_audio_get_msf_seconds(&msf)-cdio_audio_get_msf_seconds(&start_msf); + + return ((n % 0xff) << 24 | t << 8 | i_tracks); +} + +#ifdef HAVE_CDDB +bool +init_cddb(CdIo_t *p_cdio, cddb_conn_t **pp_conn, cddb_disc_t **pp_cddb_disc, + error_fn_t errmsg, track_t i_first_track, track_t i_tracks, + int *i_cddb_matches) +{ + track_t i; + + *pp_conn = cddb_new(); + *pp_cddb_disc = NULL; + + if (!*pp_conn) { + errmsg("unable to initialize libcddb"); + return false; + } + + if (NULL == cddb_opts.email) + cddb_set_email_address(*pp_conn, "me@home"); + else + cddb_set_email_address(*pp_conn, cddb_opts.email); + + if (NULL == cddb_opts.server) + cddb_set_server_name(*pp_conn, "freedb.freedb.org"); + else + cddb_set_server_name(*pp_conn, cddb_opts.server); + + if (cddb_opts.timeout >= 0) + cddb_set_timeout(*pp_conn, cddb_opts.timeout); + + cddb_set_server_port(*pp_conn, cddb_opts.port); + + if (cddb_opts.http) + cddb_http_enable(*pp_conn); + else + cddb_http_disable(*pp_conn); + + if (NULL != cddb_opts.cachedir) + cddb_cache_set_dir(*pp_conn, cddb_opts.cachedir); + + if (cddb_opts.disable_cache) + cddb_cache_disable(*pp_conn); + + *pp_cddb_disc = cddb_disc_new(); + if (!*pp_cddb_disc) { + errmsg("unable to create CDDB disc structure"); + cddb_destroy(*pp_conn); + return false; + } + for(i = 0; i < i_tracks; i++) { + cddb_track_t *t = cddb_track_new(); + cddb_track_set_frame_offset(t, + cdio_get_track_lba(p_cdio, i+i_first_track)); + cddb_disc_add_track(*pp_cddb_disc, t); + } + + cddb_disc_set_length(*pp_cddb_disc, + cdio_get_track_lba(p_cdio, CDIO_CDROM_LEADOUT_TRACK) + / CDIO_CD_FRAMES_PER_SEC); + + if (!cddb_disc_calc_discid(*pp_cddb_disc)) { + errmsg("libcddb calc discid failed."); + cddb_destroy(*pp_conn); + return false; + } + + *i_cddb_matches = cddb_query(*pp_conn, *pp_cddb_disc); + + if (-1 == *i_cddb_matches) + errmsg(cddb_error_str(cddb_errno(*pp_conn))); + + cddb_read(*pp_conn, *pp_cddb_disc); + return true; +} +#endif /*HAVE_CDDB*/ diff --git a/src/cddb.h b/src/cddb.h new file mode 100644 index 00000000..c9aaae82 --- /dev/null +++ b/src/cddb.h @@ -0,0 +1,50 @@ +/* + $Id: cddb.h,v 1.6 2008/06/25 08:01:54 rocky Exp $ + + Copyright (C) 2005, 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +typedef struct cddb_opts_s +{ + char *email; /* email to report to CDDB server. */ + char *server; /* CDDB server to contact */ + int port; /* port number to contact CDDB server. */ + int http; /* 1 if use http proxy */ + int timeout; + bool disable_cache; /* If set the below is meaningless. */ + char *cachedir; +} cddb_opts_t; + +cddb_opts_t cddb_opts; + +/*! + 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. +*/ +uint32_t cddb_discid(CdIo_t *p_cdio, track_t i_tracks); + +#ifdef HAVE_CDDB +#include + +typedef void (*error_fn_t) (const char *msg); + +bool init_cddb(CdIo_t *p_cdio, cddb_conn_t **pp_conn, + cddb_disc_t **pp_cddb_disc, error_fn_t errmsg, + track_t i_first_track, track_t i_tracks, int *i_cddb_matches); +#endif diff --git a/src/cdinfo-linux.c b/src/cdinfo-linux.c new file mode 100644 index 00000000..9199213c --- /dev/null +++ b/src/cdinfo-linux.c @@ -0,0 +1,904 @@ +/* + $Id: cdinfo-linux.c,v 1.4 2008/04/14 17:30:27 karl Exp $ + + Copyright (C) 2003,2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* + 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 "config.h" +#include +#include +#include +#include +#include + +#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 + +#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][CDIO_CD_FRAMESIZE_RAW]; /* for CD-Data */ + +CdIo *img; +track_t num_tracks; +track_t first_track_num; + +struct cdrom_tocentry *toc[CDIO_CDROM_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, CDIO_CD_FRAMESIZE); + + 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, CDIO_CD_FRAMESIZE*(offset+superblock), SEEK_SET)) + return -1; + + memset(buffer[5],0,CDIO_CD_FRAMESIZE); + if (0 > read(filehandle,buffer[5], CDIO_CD_FRAMESIZE)) + return -1; + + if (memcmp(buffer[bufnum], buffer[5], CDIO_CD_FRAMESIZE) != 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 cdio_from_bcd8(msf->m)*60 + cdio_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_CDROM_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(""); +} + + +/* ------------------------------------------------------------------------ */ + +/* 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(); + + if (devname==NULL) { + devname=strdup(cdio_get_default_device(img)); + } + img = cdio_open (devname, DRIVER_UNKNOWN); + + /* 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_CDROM_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_CDROM_LEADOUT_TRACK == i ? " (leadout)" : ""); + } + + if (i == CDIO_CDROM_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_CDROM_LEADOUT_TRACK-1; + } + + if (opts.show_ioctl) { + printf(STRONG "What ioctl's report...\n" NORMAL); + + /* 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); + + /* 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"); + } + + /* 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":""); + + /* 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); + } + } + } + + 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: + case TRACK_FORMAT_PSX: + ; + } + + 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/getopt.c b/src/getopt.c new file mode 100644 index 00000000..c41531e6 --- /dev/null +++ b/src/getopt.c @@ -0,0 +1,1056 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98 + Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, 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 tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include +# include +#endif /* GNU C library. */ + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +# ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +# else +# define _(msgid) (msgid) +# endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +# include +# define my_index strchr +#else + +# if HAVE_STRING_H +# include +# else +# if HAVE_STRINGS_H +# include +# endif +# endif + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv (); +#endif + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((unused)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +# ifdef text_set_element +text_set_element (__libc_subinit, store_args_and_env); +# endif /* text_set_element */ + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/src/getopt.h b/src/getopt.h new file mode 100644 index 00000000..fb30719a --- /dev/null +++ b/src/getopt.h @@ -0,0 +1,133 @@ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, 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 _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if defined (__STDC__) && __STDC__ +#ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +extern int getopt (); +#endif /* __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* getopt.h */ diff --git a/src/getopt1.c b/src/getopt1.c new file mode 100644 index 00000000..ff257374 --- /dev/null +++ b/src/getopt1.c @@ -0,0 +1,190 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 + Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, 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 +#endif + +#include "getopt.h" + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/src/iso-info.c b/src/iso-info.c new file mode 100644 index 00000000..b185f52f --- /dev/null +++ b/src/iso-info.c @@ -0,0 +1,380 @@ +/* + $Id: iso-info.c,v 1.40 2008/06/19 15:44:16 flameeyes Exp $ + + Copyright (C) 2004, 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* + ISO Info - prints various information about a ISO 9660 image. +*/ +#include "getopt.h" +#include "util.h" +#undef err_exit + +#define err_exit(fmt, args...) \ + report (stderr, "%s: "fmt, program_name, ##args); \ + iso9660_close(p_iso); \ + free(program_name); \ + return(EXIT_FAILURE); + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +#include + +#if 0 +#define STRONG "\033[1m" +#define NORMAL "\033[0m" +#else +#define STRONG "__________________________________\n" +#define NORMAL "" +#endif + +/* Used by `main' to communicate with `parse_opt'. And global options + */ +static struct arguments +{ + uint32_t debug_level; + int version_only; + int silent; + int no_header; + int no_joliet; + int no_xa; + int no_rock_ridge; + int print_iso9660; + int print_iso9660_short; +} opts; + +/* Configuration option codes */ +enum { + OP_HANDLED = 0, + + OP_USAGE, + + /* These are the remaining configuration options */ + OP_VERSION, + +}; + +/* Parse a all options. */ +static bool +parse_options (int argc, char *argv[]) +{ + int opt; + + static const char helpText[] = + "Usage: %s [OPTION...]\n" + " -d, --debug=INT Set debugging to LEVEL\n" + " -i, --input[=FILE] Filename to read ISO-9960 image from\n" + " -f Generate output similar to 'find . -print'\n" + " -l, --iso9660 Generate output similar to 'ls -lR'\n" + " --no-header Don't display header and copyright (for regression\n" + " testing)\n" +#ifdef HAVE_JOLIET + " --no-joliet Don't use Joliet-extension information\n" +#endif /*HAVE_JOLIET*/ + " --no-rock-ridge Don't use Rock-Ridge-extension information\n" + " --no-xa Don't use XA-extension information\n" + " -q, --quiet Don't produce warning output\n" + " -V, --version display version and copyright information and exit\n" + "\n" + "Help options:\n" + " -?, --help Show this help message\n" + " --usage Display brief usage message\n"; + + static const char usageText[] = + "Usage: %s [-d|--debug INT] [-i|--input FILE] [-f] [-l|--iso9660]\n" + " [--no-header] [--no-joliet] [--no-rock-ridge] [--no-xa] [-q|--quiet]\n" + " [-V|--version] [-?|--help] [--usage]\n"; + + static const char optionsString[] = "d:i::flqV?"; + static const struct option optionsTable[] = { + {"debug", required_argument, NULL, 'd'}, + {"input", optional_argument, NULL, 'i'}, + {"iso9660", no_argument, NULL, 'l'}, + {"no-header", no_argument, &opts.no_header, 1 }, +#ifdef HAVE_JOLIET + {"no-joliet", no_argument, &opts.no_joliet, 1 }, +#endif /*HAVE_JOLIET*/ + {"no-rock-ridge", no_argument, &opts.no_rock_ridge, 1 }, + {"no-xa", no_argument, &opts.no_xa, 1 }, + {"quiet", no_argument, NULL, 'q'}, + {"version", no_argument, NULL, 'V'}, + + {"help", no_argument, NULL, '?' }, + {"usage", no_argument, NULL, OP_USAGE }, + { NULL, 0, NULL, 0 } + }; + + program_name = strrchr(argv[0],'/'); + program_name = program_name ? strdup(program_name+1) : strdup(argv[0]); + + while ((opt = getopt_long(argc, argv, optionsString, optionsTable, NULL)) >= 0) { + switch (opt) + { + case 'd': opts.debug_level = atoi(optarg); break; + case 'i': if (optarg != NULL) source_name = strdup(optarg); break; + case 'f': opts.print_iso9660_short = 1; break; + case 'l': opts.print_iso9660 = 1; break; + case 'q': opts.silent = 1; break; + case 'V': opts.version_only = 1; break; + + case '?': + fprintf(stdout, helpText, program_name); + free(program_name); + exit(EXIT_INFO); + break; + + case OP_USAGE: + fprintf(stderr, usageText, program_name); + free(program_name); + exit(EXIT_FAILURE); + break; + + case OP_HANDLED: + break; + } + } + + if (optind < argc) { + const char *remaining_arg = argv[optind++]; + if ( optind < argc ) { + report( stderr, "%s: Source specified in previously %s and %s\n", + program_name, source_name, remaining_arg ); + free(program_name); + exit (EXIT_FAILURE); + } + source_name = strdup(remaining_arg); + } + + return true; +} + +/* CDIO logging routines */ + +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_cdio_log_handler (level, message); +} + +static void +print_iso9660_recurse (iso9660_t *p_iso, const char psz_path[]) +{ + CdioList_t *entlist; + CdioList_t *dirlist = _cdio_list_new (); + CdioListNode_t *entnode; + uint8_t i_joliet_level = iso9660_ifs_get_joliet_level(p_iso); + char *translated_name = (char *) malloc(4096); + size_t translated_name_size = 4096; + entlist = iso9660_ifs_readdir (p_iso, psz_path); + + if (opts.print_iso9660) { + printf ("%s:\n", psz_path); + } + + if (NULL == entlist) { + report( stderr, "Error getting above directory information\n" ); + return; + } + + /* Iterate over files in this directory */ + + _CDIO_LIST_FOREACH (entnode, entlist) + { + iso9660_stat_t *p_statbuf = _cdio_list_node_data (entnode); + char *psz_iso_name = p_statbuf->filename; + char _fullname[4096] = { 0, }; + if (strlen(psz_iso_name) >= translated_name_size) { + translated_name_size = strlen(psz_iso_name)+1; + free(translated_name); + translated_name = (char *) malloc(translated_name_size); + if (!translated_name) { + report( stderr, "Error allocating memory\n" ); + return; + } + } + + if (yep != p_statbuf->rr.b3_rock || 1 == opts.no_rock_ridge) { + iso9660_name_translate_ext(psz_iso_name, translated_name, + i_joliet_level); + snprintf (_fullname, sizeof (_fullname), "%s%s", psz_path, + translated_name); + } else { + snprintf (_fullname, sizeof (_fullname), "%s%s", psz_path, + psz_iso_name); + } + + strncat (_fullname, "/", sizeof (_fullname)); + + if (p_statbuf->type == _STAT_DIR + && strcmp (psz_iso_name, ".") + && strcmp (psz_iso_name, "..")) + _cdio_list_append (dirlist, strdup (_fullname)); + + if (opts.print_iso9660) { + print_fs_attrs(p_statbuf, + 0 == opts.no_rock_ridge, + iso9660_ifs_is_xa(p_iso) && 0 == opts.no_xa, + psz_iso_name, translated_name); + } else + if ( strcmp (psz_iso_name, ".") && strcmp (psz_iso_name, "..")) + printf("%9u %s%s\n", (unsigned int) p_statbuf->size, psz_path, + yep == p_statbuf->rr.b3_rock + ? psz_iso_name : translated_name); + if (p_statbuf->rr.i_symlink) { + free(p_statbuf->rr.psz_symlink); + p_statbuf->rr.i_symlink = 0; + } + } + free (translated_name); + + _cdio_list_free (entlist, true); + + if (opts.print_iso9660) { + printf ("\n"); + } + + /* Now recurse over the directories. */ + + _CDIO_LIST_FOREACH (entnode, dirlist) + { + char *_fullname = _cdio_list_node_data (entnode); + + print_iso9660_recurse (p_iso, _fullname); + } + + _cdio_list_free (dirlist, true); +} + +static void +print_iso9660_fs (iso9660_t *iso) +{ + print_iso9660_recurse (iso, "/"); +} + + +/* Initialize global variables. */ +static void +init(void) +{ + gl_default_cdio_log_handler = cdio_log_set_handler (_log_handler); + + /* Default option values. */ + opts.silent = false; + opts.no_header = false; + opts.no_joliet = 0; + opts.no_rock_ridge = 0; + opts.no_xa = 0; + opts.debug_level = 0; + opts.print_iso9660 = 0; + opts.print_iso9660_short = 0; +} + +#define print_vd_info(title, fn) \ + if (fn(p_iso, &psz_str)) { \ + printf(title ": %s\n", psz_str); \ + } \ + free(psz_str); \ + psz_str = NULL; + +/* ------------------------------------------------------------------------ */ + +int +main(int argc, char *argv[]) +{ + + iso9660_t *p_iso=NULL; + iso_extension_mask_t iso_extension_mask = ISO_EXTENSION_ALL; + + init(); + + /* Parse our arguments; every option seen by `parse_opt' will + be reflected in `arguments'. */ + parse_options(argc, argv); + + print_version(program_name, CDIO_VERSION, opts.no_header, opts.version_only); + + if (opts.debug_level == 3) { + cdio_loglevel_default = CDIO_LOG_INFO; + } else if (opts.debug_level >= 4) { + cdio_loglevel_default = CDIO_LOG_DEBUG; + } + + if (source_name==NULL) { + err_exit("No input device given/found%s\n", ""); + } + + if (opts.no_joliet) { + iso_extension_mask &= ~ISO_EXTENSION_JOLIET; + } + + p_iso = iso9660_open_ext (source_name, iso_extension_mask); + + if (p_iso==NULL) { + free(source_name); + err_exit("Error in opening ISO-9660 image%s\n", ""); + } + + if (opts.silent == 0) { + char *psz_str = NULL; + + printf(STRONG "ISO 9660 image: %s\n", source_name); + print_vd_info("Application", iso9660_ifs_get_application_id); + print_vd_info("Preparer ", iso9660_ifs_get_preparer_id); + print_vd_info("Publisher ", iso9660_ifs_get_publisher_id); + print_vd_info("System ", iso9660_ifs_get_system_id); + print_vd_info("Volume ", iso9660_ifs_get_volume_id); + print_vd_info("Volume Set ", iso9660_ifs_get_volumeset_id); + } + + if (opts.print_iso9660 || opts.print_iso9660_short) { + printf(STRONG "ISO-9660 Information\n" NORMAL); + if (opts.print_iso9660 && opts.print_iso9660_short) { + printf("Note: both -f and -l options given -- " + "-l (long listing) takes precidence\n"); + } + print_iso9660_fs(p_iso); + } + + free(source_name); + iso9660_close(p_iso); + /* Not reached:*/ + free(program_name); + return(EXIT_SUCCESS); +} diff --git a/src/iso-info.help2man b/src/iso-info.help2man new file mode 100644 index 00000000..7f3273e4 --- /dev/null +++ b/src/iso-info.help2man @@ -0,0 +1,10 @@ +[SYNOPSIS] +.B iso-info +\fIOPTION\fR... +.TP +Shows Information about an ISO 9660 image. +[SEE ALSO] +\&\f(CWcd-info(1)\fR for information about an ISO-9660 image. +\&\f(CWcd-read(1)\fR to read portions of an ISO 9660 image. +[AUTHOR] +Rocky Bernstein diff --git a/src/iso-read.c b/src/iso-read.c new file mode 100644 index 00000000..b6140112 --- /dev/null +++ b/src/iso-read.c @@ -0,0 +1,291 @@ +/* + $Id: iso-read.c,v 1.16 2008/06/19 15:44:19 flameeyes Exp $ + + Copyright (C) 2004, 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Program to read ISO-9660 images. */ + +#include "util.h" +#include "portable.h" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include + +#include + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include "getopt.h" + +/* Used by `main' to communicate with `parse_opt'. And global options + */ +static struct arguments +{ + char *file_name; + char *output_file; + char *iso9660_image; + int debug_level; + int no_header; + int ignore; +} opts; + +/* Parse a options. */ +static bool +parse_options (int argc, char *argv[]) +{ + + int opt; + + /* Configuration option codes */ + enum { + OP_HANDLED = 0, + OP_VERSION=1, + OP_USAGE + }; + + static const char helpText[] = + "Usage: %s [OPTION...]\n" + " -d, --debug=INT Set debugging to LEVEL.\n" + " -i, --image=FILE Read from ISO-9660 image. This option is mandatory\n" + " -e, --extract=FILE Extract FILE from ISO-9660 image. This option is\n" + " mandatory.\n" + " -k, --ignore Ignore read error(s), i.e. keep going\n" + " --no-header Don't display header and copyright (for\n" + " regression testing)\n" + " -o, --output-file=FILE Output file. This option is mandatory.\n" + " -V, --version display version and copyright information and exit\n" + "\n" + "Help options:\n" + " -?, --help Show this help message\n" + " --usage Display brief usage message\n"; + + static const char usageText[] = + "Usage: %s [-d|--debug INT] [-i|--image FILE] [-e|--extract FILE]\n" + " [--no-header] [-o|--output-file FILE] [-V|--version] [-?|--help]\n" + " [--usage]\n"; + + /* Command-line options */ + static const char* optionsString = "d:i:e:o:Vk?"; + static const struct option optionsTable[] = { + {"debug", required_argument, NULL, 'd' }, + {"image", required_argument, NULL, 'i' }, + {"extract", required_argument, NULL, 'e' }, + {"no-header", no_argument, &opts.no_header, 1 }, + {"ignore", no_argument, &opts.ignore, 'k' }, + {"output-file", required_argument, NULL, 'o' }, + {"version", no_argument, NULL, 'V' }, + + {"help", no_argument, NULL, '?' }, + {"usage", no_argument, NULL, OP_USAGE }, + { NULL, 0, NULL, 0 } + }; + + program_name = strrchr(argv[0],'/'); + program_name = program_name ? strdup(program_name+1) : strdup(argv[0]); + + while ((opt = getopt_long(argc, argv, optionsString, optionsTable, NULL)) != -1) + switch (opt) + { + case 'd': opts.debug_level = atoi(optarg); break; + case 'i': opts.iso9660_image = strdup(optarg); break; + case 'k': opts.ignore = 1; break; + case 'e': opts.file_name = strdup(optarg); break; + case 'o': opts.output_file = strdup(optarg); break; + + case 'V': + print_version(program_name, CDIO_VERSION, 0, true); + free(program_name); + exit (EXIT_SUCCESS); + break; + + case '?': + fprintf(stdout, helpText, program_name); + free(program_name); + exit(EXIT_INFO); + break; + + case OP_USAGE: + fprintf(stderr, usageText, program_name); + free(program_name); + exit(EXIT_FAILURE); + break; + + case OP_HANDLED: + break; + } + + if (optind < argc) { + const char *remaining_arg = argv[optind++]; + if (opts.iso9660_image != NULL) { + report( stderr, "%s: Source specified as --image %s and as %s\n", + program_name, opts.iso9660_image, remaining_arg ); + free(program_name); + exit (EXIT_FAILURE); + } + + opts.iso9660_image = strdup(remaining_arg); + + if (optind < argc ) { + report( stderr, + "%s: use only one unnamed argument for the ISO 9660 " + "image name\n", + program_name ); + free(program_name); + exit (EXIT_FAILURE); + } + } + + if (NULL == opts.iso9660_image) { + report( stderr, "%s: you need to specify an ISO-9660 image name.\n", + program_name ); + report( stderr, "%s: Use option --image or try --help.\n", + program_name ); + exit (EXIT_FAILURE); + } + + if (NULL == opts.file_name) { + report( stderr, "%s: you need to specify a filename to extract.\n", + program_name ); + report( stderr, "%s: Use option --extract or try --help.\n", + program_name ); + exit (EXIT_FAILURE); + } + + if (NULL == opts.output_file) { + report( stderr, + "%s: you need to specify a place write filename extraction to.\n", + program_name ); + report( stderr, "%s: Use option --output-file or try --help.\n", + program_name ); + exit (EXIT_FAILURE); + } + + return true; +} + +static void +init(void) +{ + opts.debug_level = 0; + opts.ignore = 0; + opts.file_name = NULL; + opts.output_file = NULL; + opts.iso9660_image = NULL; +} + +int +main(int argc, char *argv[]) +{ + iso9660_stat_t *statbuf; + FILE *outfd; + int i; + iso9660_t *iso; + + init(); + + /* Parse our arguments; every option seen by `parse_opt' will + be reflected in `arguments'. */ + if (!parse_options(argc, argv)) { + report(stderr, + "error while parsing command line - try --help\n"); + return 2; + } + + iso = iso9660_open (opts.iso9660_image); + + if (NULL == iso) { + report(stderr, + "%s: Sorry, couldn't open ISO-9660 image file '%s'.\n", + program_name, opts.iso9660_image); + return 1; + } + + statbuf = iso9660_ifs_stat_translate (iso, opts.file_name); + + if (NULL == statbuf) + { + report(stderr, + "%s: Could not get ISO-9660 file information out of %s" + " for file %s.\n", + program_name, opts.iso9660_image, opts.file_name); + report(stderr, + "%s: iso-info may be able to show the contents of %s.\n", + program_name, opts.iso9660_image); + return 2; + } + + if (!(outfd = fopen (opts.output_file, "wb"))) + { + report(stderr, + "%s: Could not open %s for writing: %s\n", + program_name, opts.output_file, strerror(errno)); + return 3; + } + + /* Copy the blocks from the ISO-9660 filesystem to the local filesystem. */ + for (i = 0; i < statbuf->size; i += ISO_BLOCKSIZE) + { + char buf[ISO_BLOCKSIZE]; + + memset (buf, 0, ISO_BLOCKSIZE); + + if ( ISO_BLOCKSIZE != iso9660_iso_seek_read (iso, buf, statbuf->lsn + + (i / ISO_BLOCKSIZE), + 1) ) + { + report(stderr, "Error reading ISO 9660 file at lsn %lu\n", + (long unsigned int) statbuf->lsn + (i / ISO_BLOCKSIZE)); + if (!opts.ignore) return 4; + } + + + fwrite (buf, ISO_BLOCKSIZE, 1, outfd); + + if (ferror (outfd)) + { + perror ("fwrite()"); + return 5; + } + } + + fflush (outfd); + + /* Make sure the file size has the exact same byte size. Without the + truncate below, the file will a multiple of ISO_BLOCKSIZE. + */ + if (ftruncate (fileno (outfd), statbuf->size)) + perror ("ftruncate()"); + + fclose (outfd); + iso9660_close(iso); + return 0; +} diff --git a/src/iso-read.help2man b/src/iso-read.help2man new file mode 100644 index 00000000..9f95b1f9 --- /dev/null +++ b/src/iso-read.help2man @@ -0,0 +1,10 @@ +[SYNOPSIS] +.B iso-read +\fIOPTION\fR... +.TP +Reads portions of an ISO 9660 image. +[SEE ALSO] +\&\f(CWiso-info(1)\fR for information about an ISO-9660 image. +\&\f(CWcd-read(1)\fR to read portions of an ISO 9660 image. +[AUTHOR] +Rocky Bernstein diff --git a/src/mmc-tool.c b/src/mmc-tool.c new file mode 100644 index 00000000..7dda539d --- /dev/null +++ b/src/mmc-tool.c @@ -0,0 +1,529 @@ +/* + $Id: mmc-tool.c,v 1.12 2008/06/19 15:44:24 flameeyes Exp $ + + Copyright (C) 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* A program to using the MMC interface to list CD and drive features + from the MMC GET_CONFIGURATION command . */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#include +#include "util.h" +#include "getopt.h" + +static void +init(const char *argv0) +{ + program_name = strrchr(argv0,'/'); + program_name = program_name ? strdup(program_name+1) : strdup(argv0); +} + +/* Configuration option codes */ +typedef enum { + OPT_HANDLED = 0, + OPT_USAGE, + OPT_DRIVE_CAP, + OPT_VERSION, +} option_t; + +typedef enum { + /* These are the remaining configuration options */ + OP_FINISHED = 0, + OP_BLOCKSIZE, + OP_CLOSETRAY, + OP_EJECT, + OP_IDLE, + OP_INQUIRY, + OP_MODE_SENSE_2A, + OP_MCN, + OP_SPEED, +} operation_enum_t; + +typedef struct +{ + operation_enum_t op; + union + { + long int i_num; + char * psz; + } arg; +} operation_t; + + +enum { MAX_OPS = 10 }; + +static unsigned int last_op = 0; +static operation_t operation[MAX_OPS] = { {OP_FINISHED, {0}} }; + +static void +push_op(operation_t *p_op) +{ + if (last_op < MAX_OPS) { + memcpy(&operation[last_op], p_op, sizeof(operation_t)); + last_op++; + } +} + +/* Parse a options. */ +static bool +parse_options (int argc, char *argv[]) +{ + int opt; + operation_t op; + int i_blocksize = 0; + + static const char helpText[] = + "Usage: %s [OPTION...]\n" + " Issues libcdio Multimedia commands. Operations occur in the order\n" + " in which the options are given and a given operation may appear\n" + " more than once to have it run more than once.\n" + "options: \n" + " -b, --blocksize[=INT] set blocksize. If no block size or a \n" + " zero blocksize is given we return the\n" + " current setting.\n" + " -C, --drive-cap [6|10] print mode sense 2a data\n" + " using 6-byte or 10-byte form\n" + " -c, --close drive close drive via ALLOW_MEDIUM_REMOVAL\n" + " -e, --eject [drive] eject drive via ALLOW_MEDIUM_REMOVAL\n" + " and a MMC START/STOP command\n" + " -I, --idle set CD-ROM to idle or power down\n" + " via MMC START/STOP command\n" + " -i, --inquiry print HW info via INQUIRY\n" + " -m, --mcn get media catalog number (AKA UPC)\n" + " -s, --speed-KB=INT Set drive speed to SPEED K bytes/sec\n" + " Note: 1x = 176 KB/s \n" + " -S, --speed-X=INT Set drive speed to INT X\n" + " Note: 1x = 176 KB/s \n" + " -V, --version display version and copyright information\n" + " and exit\n" + "\n" + "Help options:\n" + " -?, --help Show this help message\n" + " --usage Display brief usage message\n"; + + static const char usageText[] = + "Usage: %s [-b|--blocksize[=INT]] [-m|--mcn]\n" + " [-I|--idle] [-I|inquiry] [-m[-s|--speed-KB INT]\n" + " [-V|--version] [-?|--help] [--usage]\n"; + + /* Command-line options */ + static const char optionsString[] = "b::c:C::e::Iis:V?"; + const struct option optionsTable[] = { + + {"blocksize", optional_argument, &i_blocksize, 'b' }, + {"close", required_argument, NULL, 'c'}, + {"drive-cap", optional_argument, NULL, 'C'}, + {"eject", optional_argument, NULL, 'e'}, + {"idle", no_argument, NULL, 'I'}, + {"inquiry", no_argument, NULL, 'i'}, + {"mcn", no_argument, NULL, 'm'}, + {"speed-KB", required_argument, NULL, 's'}, + {"speed-X", required_argument, NULL, 'S'}, + + {"version", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, '?' }, + {"usage", no_argument, NULL, OPT_USAGE }, + { NULL, 0, NULL, 0 } + }; + + while ((opt = getopt_long(argc, argv, optionsString, optionsTable, NULL)) >= 0) + switch (opt) + { + case 'b': + op.op = OP_BLOCKSIZE; + op.arg.i_num = i_blocksize; + push_op(&op); + break; + case 'C': + op.arg.i_num = optarg ? atoi(optarg) : 10; + switch (op.arg.i_num) { + case 10: + op.op = OP_MODE_SENSE_2A; + op.arg.i_num = 10; + push_op(&op); + break; + case 6: + op.op = OP_MODE_SENSE_2A; + op.arg.i_num = 6; + push_op(&op); + break; + default: + report( stderr, "%s: Expecting 6 or 10 or nothing\n", program_name ); + } + break; + case 'c': + op.op = OP_CLOSETRAY; + op.arg.psz = strdup(optarg); + push_op(&op); + break; + case 'e': + op.op = OP_EJECT; + op.arg.psz=NULL; + if (optarg) op.arg.psz = strdup(optarg); + push_op(&op); + break; + case 'i': + op.op = OP_INQUIRY; + op.arg.psz=NULL; + push_op(&op); + break; + case 'I': + op.op = OP_IDLE; + op.arg.psz=NULL; + push_op(&op); + break; + case 'm': + op.op = OP_MCN; + op.arg.psz=NULL; + push_op(&op); + break; + case 's': + op.op = OP_SPEED; + op.arg.i_num=atoi(optarg); + push_op(&op); + break; + case 'S': + op.op = OP_SPEED; + op.arg.i_num=176 * atoi(optarg); + push_op(&op); + break; + case 'V': + print_version(program_name, VERSION, 0, true); + free(program_name); + exit (EXIT_SUCCESS); + break; + + case '?': + fprintf(stdout, helpText, program_name); + free(program_name); + exit(EXIT_INFO); + break; + + case OPT_USAGE: + fprintf(stderr, usageText, program_name); + free(program_name); + exit(EXIT_FAILURE); + break; + + case OPT_HANDLED: + break; + i_blocksize = 0; + } + + if (optind < argc) { + const char *remaining_arg = argv[optind++]; + + if (source_name != NULL) { + report( stderr, "%s: Source specified in option %s and as %s\n", + program_name, source_name, remaining_arg ); + free(program_name); + exit (EXIT_FAILURE); + } + + source_name = strdup(remaining_arg); + + if (optind < argc) { + report( stderr, "%s: Source specified in previously %s and %s\n", + program_name, source_name, remaining_arg ); + free(program_name); + exit (EXIT_FAILURE); + } + } + + return true; +} + +static void +print_mode_sense (unsigned int i_mmc_size, const uint8_t buf[30]) +{ + printf("Mode sense %d information\n", i_mmc_size); + if (buf[2] & 0x01) { + printf("\tReads CD-R media.\n"); + } + if (buf[2] & 0x02) { + printf("\tReads CD-RW media.\n"); + } + if (buf[2] & 0x04) { + printf("\tReads fixed-packet tracks when Addressing type is method 2.\n"); + } + if (buf[2] & 0x08) { + printf("\tReads DVD ROM media.\n"); + } + if (buf[2] & 0x10) { + printf("\tReads DVD-R media.\n"); + } + if (buf[2] & 0x20) { + printf("\tReads DVD-RAM media.\n"); + } + if (buf[2] & 0x40) { + printf("\tReads DVD-RAM media.\n"); + } + if (buf[3] & 0x01) { + printf("\tWrites CD-R media.\n"); + } + if (buf[3] & 0x02) { + printf("\tWrites CD-RW media.\n"); + } + if (buf[3] & 0x04) { + printf("\tSupports emulation write.\n"); + } + if (buf[3] & 0x10) { + printf("\tWrites DVD-R media.\n"); + } + if (buf[3] & 0x20) { + printf("\tWrites DVD-RAM media.\n"); + } + if (buf[4] & 0x01) { + printf("\tCan play audio.\n"); + } + if (buf[4] & 0x02) { + printf("\tDelivers composition A/V stream.\n"); + } + if (buf[4] & 0x04) { + printf("\tSupports digital output on port 2.\n"); + } + if (buf[4] & 0x08) { + printf("\tSupports digital output on port 1.\n"); + } + if (buf[4] & 0x10) { + printf("\tReads Mode-2 form 1 (e.g. XA) media.\n"); + } + if (buf[4] & 0x20) { + printf("\tReads Mode-2 form 2 media.\n"); + } + if (buf[4] & 0x40) { + printf("\tReads multi-session CD media.\n"); + } + if (buf[4] & 0x80) { + printf("\tSupports Buffer under-run free recording on CD-R/RW media.\n"); + } + if (buf[4] & 0x01) { + printf("\tCan read audio data with READ CD.\n"); + } + if (buf[4] & 0x02) { + printf("\tREAD CD data stream is accurate.\n"); + } + if (buf[5] & 0x04) { + printf("\tReads R-W subchannel information.\n"); + } + if (buf[5] & 0x08) { + printf("\tReads de-interleaved R-W subchannel.\n"); + } + if (buf[5] & 0x10) { + printf("\tSupports C2 error pointers.\n"); + } + if (buf[5] & 0x20) { + printf("\tReads ISRC information.\n"); + } + if (buf[5] & 0x40) { + printf("\tReads ISRC informaton.\n"); + } + if (buf[5] & 0x40) { + printf("\tReads media catalog number (MCN also known as UPC).\n"); + } + if (buf[5] & 0x80) { + printf("\tReads bar codes.\n"); + } + if (buf[6] & 0x01) { + printf("\tPREVENT/ALLOW may lock media.\n"); + } + printf("\tLock state is %slocked.\n", (buf[6] & 0x02) ? "" : "un"); + printf("\tPREVENT/ALLOW jumper is %spresent.\n", (buf[6] & 0x04) ? "": "not "); + if (buf[6] & 0x08) { + printf("\tEjects media with START STOP UNIT.\n"); + } + { + const unsigned int i_load_type = (buf[6]>>5 & 0x07); + printf("\tLoading mechanism type is %d: ", i_load_type); + switch (buf[6]>>5 & 0x07) { + case 0: + printf("caddy type loading mechanism.\n"); + break; + case 1: + printf("tray type loading mechanism.\n"); + break; + case 2: + printf("popup type loading mechanism.\n"); + break; + case 3: + printf("reserved\n"); + break; + case 4: + printf("changer with individually changeable discs.\n"); + break; + case 5: + printf("changer using Magazine mechanism.\n"); + break; + case 6: + printf("changer using Magazine mechanism.\n"); + break; + default: + printf("Invalid.\n"); + break; + } + } + + if (buf[7] & 0x01) { + printf("\tVolume controls each channel separately.\n"); + } + if (buf[7] & 0x02) { + printf("\tHas a changer that supports disc present reporting.\n"); + } + if (buf[7] & 0x04) { + printf("\tCan load empty slot in changer.\n"); + } + if (buf[7] & 0x08) { + printf("\tSide change capable.\n"); + } + if (buf[7] & 0x10) { + printf("\tReads raw R-W subchannel information from lead in.\n"); + } + { + const unsigned int i_speed_Kbs = CDIO_MMC_GETPOS_LEN16(buf, 8); + printf("\tMaximum read speed is %d K bytes/sec (about %dX)\n", + i_speed_Kbs, i_speed_Kbs / 176) ; + } + printf("\tNumber of Volume levels is %d\n", CDIO_MMC_GETPOS_LEN16(buf, 10)); + printf("\tBuffers size for data is %d KB\n", CDIO_MMC_GETPOS_LEN16(buf, 12)); + printf("\tCurrent read speed is %d KB\n", CDIO_MMC_GETPOS_LEN16(buf, 14)); + printf("\tMaximum write speed is %d KB\n", CDIO_MMC_GETPOS_LEN16(buf, 18)); + printf("\tCurrent write speed is %d KB\n", CDIO_MMC_GETPOS_LEN16(buf, 28)); +} + +int +main(int argc, char *argv[]) +{ + CdIo_t *p_cdio; + + driver_return_code_t rc = DRIVER_OP_SUCCESS; + unsigned int i; + + init(argv[0]); + + parse_options(argc, argv); + p_cdio = cdio_open (source_name, DRIVER_DEVICE); + + if (NULL == p_cdio) { + printf("Couldn't find CD\n"); + return 1; + } + + for (i=0; i < last_op; i++) { + const operation_t *p_op = &operation[i]; + switch (p_op->op) { + case OP_SPEED: + rc = mmc_set_speed(p_cdio, p_op->arg.i_num); + report(stdout, "%s (mmc_set_speed): %s\n", program_name, + cdio_driver_errmsg(rc)); + break; + case OP_BLOCKSIZE: + if (p_op->arg.i_num) { + driver_return_code_t rc = mmc_set_blocksize(p_cdio, p_op->arg.i_num); + report(stdout, "%s (mmc_set_blocksize): %s\n", program_name, + cdio_driver_errmsg(rc)); + } else { + int i_blocksize = mmc_get_blocksize(p_cdio); + if (i_blocksize > 0) { + report(stdout, "%s (mmc_get_blocksize): %d\n", program_name, + i_blocksize); + } else { + report(stdout, "%s (mmc_get_blocksize): can't retrieve.\n", + program_name); + } + } + break; + case OP_MODE_SENSE_2A: + { + uint8_t buf[30] = { 0, }; /* Place to hold returned data */ + if (p_op->arg.i_num == 10) { + rc = mmc_mode_sense_10(p_cdio, buf, sizeof(buf), + CDIO_MMC_CAPABILITIES_PAGE); + } else { + rc = mmc_mode_sense_6(p_cdio, buf, sizeof(buf), + CDIO_MMC_CAPABILITIES_PAGE); + } + if (DRIVER_OP_SUCCESS == rc) { + print_mode_sense(p_op->arg.i_num, buf); + } else { + report(stdout, "%s (mmc_mode_sense 2a - drive_cap %d): %s\n", + program_name, p_op->arg.i_num, cdio_driver_errmsg(rc)); + } + } + break; + case OP_CLOSETRAY: + rc = mmc_close_tray(p_cdio); + report(stdout, "%s (mmc_close_tray): %s\n", program_name, + cdio_driver_errmsg(rc)); + free(p_op->arg.psz); + break; + case OP_EJECT: + rc = mmc_eject_media(p_cdio); + report(stdout, "%s (mmc_eject_media): %s\n", program_name, + cdio_driver_errmsg(rc)); + if (p_op->arg.psz) free(p_op->arg.psz); + break; + case OP_IDLE: + rc = mmc_start_stop_media(p_cdio, false, false, true); + report(stdout, "%s (mmc_start_stop_media - powerdown): %s\n", + program_name, cdio_driver_errmsg(rc)); + break; + case OP_INQUIRY: + { + cdio_hwinfo_t hw_info = { "", "", ""}; + if (mmc_get_hwinfo(p_cdio, &hw_info)) { + printf("%-8s: %s\n%-8s: %s\n%-8s: %s\n", + "Vendor" , hw_info.psz_vendor, + "Model" , hw_info.psz_model, + "Revision", hw_info.psz_revision); + } else { + report(stdout, "%s (mmc_gpcmd_inquiry error)\n", program_name); + } + } + break; + case OP_MCN: + { + char *psz_mcn = mmc_get_mcn(p_cdio); + if (psz_mcn) { + report(stdout, "%s (mmc_get_mcn): %s\n", program_name, psz_mcn); + free(psz_mcn); + } else + report(stdout, "%s (mmc_get_mcn): can't retrieve\n", program_name); + } + break; + default: + ; + } + } + + + free(source_name); + cdio_destroy(p_cdio); + + return rc; +} diff --git a/src/util.c b/src/util.c new file mode 100644 index 00000000..4667d0b3 --- /dev/null +++ b/src/util.c @@ -0,0 +1,534 @@ +/* + $Id: util.c,v 1.54 2008/04/14 17:30:27 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Miscellaneous things common to standalone programs. */ + +#include "util.h" +#include +#include + +#ifdef HAVE_SYS_STAT_H +# include +#endif + +cdio_log_handler_t gl_default_cdio_log_handler = NULL; +char *source_name = NULL; +char *program_name; + +void +myexit(CdIo_t *cdio, int rc) +{ + if (NULL != cdio) cdio_destroy(cdio); + if (NULL != program_name) free(program_name); + if (NULL != source_name) free(source_name); + exit(rc); +} + +void +print_version (char *program_name, const char *version, + int no_header, bool version_only) +{ + + driver_id_t driver_id; + + if (no_header == 0) + report( stdout, + "%s version %s\nCopyright (c) 2003, 2004, 2005, 2007, 2008 R. Bernstein\n", + program_name, version); + report( stdout, + _("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\ +")); + + if (version_only) { + char *default_device; + for (driver_id=DRIVER_UNKNOWN+1; driver_id<=CDIO_MAX_DRIVER; driver_id++) { + if (cdio_have_driver(driver_id)) { + report( stdout, "Have driver: %s\n", cdio_driver_describe(driver_id)); + } + } + default_device=cdio_get_default_device(NULL); + if (default_device) + report( stdout, "Default CD-ROM device: %s\n", default_device); + else + report( stdout, "No CD-ROM device found.\n"); + free(program_name); + exit(EXIT_INFO); + } + +} + +/*! Device input routine. If successful we return an open CdIo_t + pointer. On error the program exits. + */ +CdIo_t * +open_input(const char *psz_source, source_image_t source_image, + const char *psz_access_mode) +{ + CdIo_t *p_cdio = NULL; + switch (source_image) { + case INPUT_UNKNOWN: + case INPUT_AUTO: + p_cdio = cdio_open_am (psz_source, DRIVER_UNKNOWN, psz_access_mode); + if (!p_cdio) { + if (psz_source) { + err_exit("Error in automatically selecting driver for input %s.\n", + psz_source); + } else { + err_exit("%s", "Error in automatically selecting driver.\n"); + } + } + break; + case INPUT_DEVICE: + p_cdio = cdio_open_am (psz_source, DRIVER_DEVICE, psz_access_mode); + if (!p_cdio) { + if (psz_source) { + err_exit("Cannot use CD-ROM device %s. Is a CD loaded?\n", + psz_source); + } else { + err_exit("%s", "Cannot find a CD-ROM with a CD loaded.\n"); + } + } + break; + case INPUT_BIN: + p_cdio = cdio_open_am (psz_source, DRIVER_BINCUE, psz_access_mode); + if (!p_cdio) { + if (psz_source) { + err_exit("%s: Error in opening CDRWin BIN/CUE image for BIN" + " input %s\n", psz_source); + } else { + err_exit("%s", "Cannot find CDRWin BIN/CUE image.\n"); + } + } + break; + case INPUT_CUE: + p_cdio = cdio_open_cue(psz_source); + if (p_cdio==NULL) { + if (psz_source) { + err_exit("%s: Error in opening CDRWin BIN/CUE image for CUE" + " input %s\n", psz_source); + } else { + err_exit("%s", "Cannot find CDRWin BIN/CUE image.\n"); + } + } + break; + case INPUT_NRG: + p_cdio = cdio_open_am (psz_source, DRIVER_NRG, psz_access_mode); + if (p_cdio==NULL) { + if (psz_source) { + err_exit("Error in opening Nero NRG image for input %s\n", + psz_source); + } else { + err_exit("%s", "Cannot find Nero NRG image.\n"); + } + } + break; + + case INPUT_CDRDAO: + p_cdio = cdio_open_am (psz_source, DRIVER_CDRDAO, psz_access_mode); + if (p_cdio==NULL) { + if (psz_source) { + err_exit("Error in opening cdrdao TOC with input %s.\n", psz_source); + } else { + err_exit("%s", "Cannot find cdrdao TOC image.\n"); + } + } + break; + } + return p_cdio; +} + + +#define DEV_PREFIX "/dev/" +char * +fillout_device_name(const char *device_name) +{ +#if defined(HAVE_WIN32_CDROM) + return strdup(device_name); +#else + unsigned int prefix_len = strlen(DEV_PREFIX); + if (!device_name) return NULL; + if (0 == strncmp(device_name, DEV_PREFIX, prefix_len)) + return strdup(device_name); + else { + char *full_device_name = (char*) calloc(1, strlen(device_name)+prefix_len); + report( stdout, full_device_name, DEV_PREFIX "%s", device_name); + return full_device_name; + } +#endif +} + +/*! Prints out SCSI-MMC drive features */ +void +print_mmc_drive_features(CdIo_t *p_cdio) +{ + + int i_status; /* Result of SCSI MMC command */ + uint8_t buf[500] = { 0, }; /* Place to hold returned data */ + mmc_cdb_t cdb = {{0, }}; /* Command Descriptor Block */ + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_GET_CONFIGURATION); + CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(buf)); + cdb.field[1] = CDIO_MMC_GET_CONF_ALL_FEATURES; + cdb.field[3] = 0x0; + + i_status = mmc_run_cmd(p_cdio, 0, &cdb, SCSI_MMC_DATA_READ, sizeof(buf), + &buf); + if (i_status == 0) { + uint8_t *p; + uint32_t i_data; + uint8_t *p_max = buf + 65530; + + i_data = (unsigned int) CDIO_MMC_GET_LEN32(buf); + + /* set to first sense feature code, and then walk through the masks */ + p = buf + 8; + while( (p < &(buf[i_data])) && (p < p_max) ) { + uint16_t i_feature; + uint8_t i_feature_additional = p[3]; + + i_feature = CDIO_MMC_GET_LEN16(p); + { + uint8_t *q; + const char *feature_str = mmc_feature2str(i_feature); + report( stdout, "%s Feature\n", feature_str); + switch( i_feature ) + { + case CDIO_MMC_FEATURE_PROFILE_LIST: + for ( q = p+4 ; q < p + i_feature_additional ; q += 4 ) { + int i_profile=CDIO_MMC_GET_LEN16(q); + const char *feature_profile_str = + mmc_feature_profile2str(i_profile); + report( stdout, "\t%s", feature_profile_str ); + if (q[2] & 1) { + report( stdout, " - on"); + } + report( stdout, "\n"); + } + report( stdout, "\n"); + + break; + case CDIO_MMC_FEATURE_CORE: + { + uint8_t *q = p+4; + uint32_t i_interface_standard = CDIO_MMC_GET_LEN32(q); + switch(i_interface_standard) { + case CDIO_MMC_FEATURE_INTERFACE_UNSPECIFIED: + report( stdout, "\tunspecified interface\n"); + break; + case CDIO_MMC_FEATURE_INTERFACE_SCSI: + report( stdout, "\tSCSI interface\n"); + break; + case CDIO_MMC_FEATURE_INTERFACE_ATAPI: + report( stdout, "\tATAPI interface\n"); + break; + case CDIO_MMC_FEATURE_INTERFACE_IEEE_1394: + report( stdout, "\tIEEE 1394 interface\n"); + break; + case CDIO_MMC_FEATURE_INTERFACE_IEEE_1394A: + report( stdout, "\tIEEE 1394A interface\n"); + break; + case CDIO_MMC_FEATURE_INTERFACE_FIBRE_CH: + report( stdout, "\tFibre Channel interface\n"); + } + report( stdout, "\n"); + break; + } + case CDIO_MMC_FEATURE_MORPHING: + report( stdout, + "\tOperational Change Request/Notification %ssupported\n", + (p[4] & 2) ? "": "not " ); + report( stdout, "\t%synchronous GET EVENT/STATUS NOTIFICATION " + "supported\n", + (p[4] & 1) ? "As": "S" ); + report( stdout, "\n"); + break; + ; + + case CDIO_MMC_FEATURE_REMOVABLE_MEDIUM: + switch(p[4] >> 5) { + case 0: + report( stdout, + "\tCaddy/Slot type loading mechanism\n" ); + break; + case 1: + report( stdout, + "\tTray type loading mechanism\n" ); + break; + case 2: + report( stdout, "\tPop-up type loading mechanism\n"); + break; + case 4: + report( stdout, + "\tEmbedded changer with individually changeable discs\n"); + break; + case 5: + report( stdout, + "\tEmbedded changer using a magazine mechanism\n" ); + break; + default: + report( stdout, + "\tUnknown changer mechanism\n" ); + } + + report( stdout, + "\tcan%s eject the medium or magazine via the normal " + "START/STOP command\n", + (p[4] & 8) ? "": "not" ); + report( stdout, "\tcan%s be locked into the Logical Unit\n", + (p[4] & 1) ? "": "not" ); + report( stdout, "\n" ); + break; + case CDIO_MMC_FEATURE_CD_READ: + report( stdout, "\tC2 Error pointers are %ssupported\n", + (p[4] & 2) ? "": "not " ); + report( stdout, "\tCD-Text is %ssupported\n", + (p[4] & 1) ? "": "not " ); + report( stdout, "\n" ); + break; + case CDIO_MMC_FEATURE_ENHANCED_DEFECT: + report( stdout, "\t%s-DRM mode is supported\n", + (p[4] & 1) ? "DRT": "Persistent" ); + report( stdout, "\n" ); + break; + case CDIO_MMC_FEATURE_CDDA_EXT_PLAY: + report( stdout, "\tSCAN command is %ssupported\n", + (p[4] & 4) ? "": "not "); + report( stdout, + "\taudio channels can %sbe muted separately\n", + (p[4] & 2) ? "": "not "); + report( stdout, + "\taudio channels can %shave separate volume levels\n", + (p[4] & 1) ? "": "not "); + { + uint8_t *q = p+6; + uint16_t i_vol_levels = CDIO_MMC_GET_LEN16(q); + report( stdout, "\t%d volume levels can be set\n", i_vol_levels ); + } + report( stdout, "\n"); + break; + case CDIO_MMC_FEATURE_DVD_CSS: +#if 0 + report( stdout, "\tMedium does%s have Content Scrambling (CSS/CPPM)\n", + (p[2] & 1) ? "": "not " ); +#endif + report( stdout, "\tCSS version %d\n", p[7] ); + report( stdout, "\t\n"); + break; + case CDIO_MMC_FEATURE_LU_SN: { + uint8_t i_serial = *(p+3); + char serial[257] = { '\0', }; + + memcpy(serial, p+4, i_serial ); + report( stdout, "\t%s\n\n", serial ); + + break; + } + default: + report( stdout, "\n"); + break; + } + } + p += i_feature_additional + 4; + } + } else { + report( stdout, "Didn't get all feature codes\n"); + } +} + + +/* Prints out drive capabilities */ +void +print_drive_capabilities(cdio_drive_read_cap_t i_read_cap, + cdio_drive_write_cap_t i_write_cap, + cdio_drive_misc_cap_t i_misc_cap) +{ + if (CDIO_DRIVE_CAP_ERROR == i_misc_cap) { + report( stdout, "Error in getting drive hardware properties\n"); + } else if (CDIO_DRIVE_CAP_UNKNOWN == i_misc_cap) { + report( stdout, "Uknown drive hardware properties\n"); + } else { + report( stdout, _("Hardware : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_FILE + ? "Disk Image" : "CD-ROM or DVD"); + report( stdout, _("Can eject : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_EJECT ? "Yes" : "No" ); + report( stdout, _("Can close tray : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_CLOSE_TRAY ? "Yes" : "No" ); + report( stdout, _("Can disable manual eject : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_LOCK ? "Yes" : "No" ); + report( stdout, _("Can select juke-box disc : %s\n\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_SELECT_DISC ? "Yes" : "No" ); + + report( stdout, _("Can set drive speed : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_SELECT_SPEED ? "Yes" : "No" ); +#if FIXED + report( stdout, _("Can detect if CD changed : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED ? "Yes" : "No" ); +#endif + report( stdout, _("Can read multiple sessions (e.g. PhotoCD) : %s\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_MULTI_SESSION ? "Yes" : "No" ); + report( stdout, _("Can hard reset device : %s\n\n"), + i_misc_cap & CDIO_DRIVE_CAP_MISC_RESET ? "Yes" : "No" ); + } + + + if (CDIO_DRIVE_CAP_ERROR == i_read_cap) { + report( stdout, "Error in getting drive reading properties\n" ); + } else if (CDIO_DRIVE_CAP_UNKNOWN == i_misc_cap) { + report( stdout, "Uknown drive reading properties\n" ); + } else { + report( stdout, "Reading....\n"); + report( stdout, _(" Can read Mode 2 Form 1 : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_MODE2_FORM1 ? "Yes" : "No" ); + report( stdout, _(" Can read Mode 2 Form 2 : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_MODE2_FORM2 ? "Yes" : "No" ); + report( stdout, _(" Can read (S)VCD (i.e. Mode 2 Form 1/2) : %s\n"), + i_read_cap & + (CDIO_DRIVE_CAP_READ_MODE2_FORM1|CDIO_DRIVE_CAP_READ_MODE2_FORM2) + ? "Yes" : "No" ); + report( stdout, _(" Can read C2 Errors : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_C2_ERRS ? "Yes" : "No" ); + report( stdout, _(" Can read IRSC : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_ISRC ? "Yes" : "No" ); + report( stdout, _(" Can read Media Channel Number (or UPC) : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_MCN ? "Yes" : "No" ); + report( stdout, _(" Can play audio : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_AUDIO ? "Yes" : "No" ); + report( stdout, _(" Can read CD-DA : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_CD_DA ? "Yes" : "No" ); + report( stdout, _(" Can read CD-R : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_CD_R ? "Yes" : "No" ); + report( stdout, _(" Can read CD-RW : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_CD_RW ? "Yes" : "No" ); + report( stdout, _(" Can read DVD-ROM : %s\n"), + i_read_cap & CDIO_DRIVE_CAP_READ_DVD_ROM ? "Yes" : "No" ); + } + + + if (CDIO_DRIVE_CAP_ERROR == i_write_cap) { + report( stdout, "Error in getting drive writing properties\n" ); + } else if (CDIO_DRIVE_CAP_UNKNOWN == i_misc_cap) { + report( stdout, "Uknown drive writing properties\n" ); + } else { + report( stdout, "\nWriting....\n"); +#if FIXED + report( stdout, _(" Can write using Burn Proof : %s\n"), + i_write_cap & CDIO_DRIVE_CAP_WRITE_BURN_PROOF ? "Yes" : "No" ); +#endif + report( stdout, _(" Can write CD-RW : %s\n"), + i_write_cap & CDIO_DRIVE_CAP_WRITE_CD_RW ? "Yes" : "No" ); + report( stdout, _(" Can write DVD-R : %s\n"), + i_write_cap & CDIO_DRIVE_CAP_WRITE_DVD_R ? "Yes" : "No" ); + report( stdout, _(" Can write DVD-RAM : %s\n"), + i_write_cap & CDIO_DRIVE_CAP_WRITE_DVD_RAM ? "Yes" : "No" ); + report( stdout, _(" Can write DVD-RW : %s\n"), + i_write_cap & CDIO_DRIVE_CAP_WRITE_DVD_RW ? "Yes" : "No" ); + report( stdout, _(" Can write DVD+RW : %s\n"), + i_write_cap & CDIO_DRIVE_CAP_WRITE_DVD_RPW ? "Yes" : "No" ); + } +} + +/*! Common place for output routine. In some environments, like XBOX, + it may not be desireable to send output to stdout and stderr. */ +void +report (FILE *stream, const char *psz_format, ...) +{ + va_list args; + va_start (args, psz_format); +#ifdef _XBOX + OutputDebugString(psz_format, args); +#else + vfprintf (stream, psz_format, args); +#endif + va_end(args); +} + +/* Prints "ls"-like file attributes */ +void +print_fs_attrs(iso9660_stat_t *p_statbuf, bool b_rock, bool b_xa, + const char *psz_name_untranslated, + const char *psz_name_translated) +{ + char date_str[30]; + +#ifdef HAVE_ROCK + if (yep == p_statbuf->rr.b3_rock && b_rock) { + report ( stdout, " %s %3d %d %d [LSN %6lu] %9u", + iso9660_get_rock_attr_str (p_statbuf->rr.st_mode), + p_statbuf->rr.st_nlinks, + p_statbuf->rr.st_uid, + p_statbuf->rr.st_gid, + (long unsigned int) p_statbuf->lsn, + S_ISLNK(p_statbuf->rr.st_mode) + ? strlen(p_statbuf->rr.psz_symlink) + : (unsigned int) p_statbuf->size ); + + } else +#endif + if (b_xa) { + report ( stdout, " %s %d %d [fn %.2d] [LSN %6lu] ", + iso9660_get_xa_attr_str (p_statbuf->xa.attributes), + uint16_from_be (p_statbuf->xa.user_id), + uint16_from_be (p_statbuf->xa.group_id), + p_statbuf->xa.filenum, + (long unsigned int) p_statbuf->lsn ); + + if (uint16_from_be(p_statbuf->xa.attributes) & XA_ATTR_MODE2FORM2) { + report ( stdout, "%9u (%9u)", + (unsigned int) p_statbuf->secsize * M2F2_SECTOR_SIZE, + (unsigned int) p_statbuf->size ); + } else + report (stdout, "%9u", (unsigned int) p_statbuf->size); + } else { + report ( stdout," %c [LSN %6lu] %9u", + (p_statbuf->type == _STAT_DIR) ? 'd' : '-', + (long unsigned int) p_statbuf->lsn, + (unsigned int) p_statbuf->size ); + } + + if (yep == p_statbuf->rr.b3_rock && b_rock) { + struct tm tm; + + strftime(date_str, sizeof(date_str), "%b %d %Y %H:%M:%S ", &p_statbuf->tm); + + /* Now try the proper field for mtime: attributes */ + if (p_statbuf->rr.modify.b_used) { + if (p_statbuf->rr.modify.b_longdate) { + iso9660_get_ltime(&p_statbuf->rr.modify.t.ltime, &tm); + } else { + iso9660_get_dtime(&p_statbuf->rr.modify.t.dtime, true, &tm); + } + strftime(date_str, sizeof(date_str), "%b %d %Y %H:%M:%S ", &tm); + } + + report (stdout," %s %s", date_str, psz_name_untranslated ); + + if (S_ISLNK(p_statbuf->rr.st_mode)) { + report(stdout, " -> %s", p_statbuf->rr.psz_symlink); + } + + } else { + strftime(date_str, sizeof(date_str), "%b %d %Y %H:%M:%S ", &p_statbuf->tm); + report (stdout," %s %s", date_str, psz_name_translated); + } + + report(stdout, "\n"); +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 00000000..7ef6fc1e --- /dev/null +++ b/src/util.h @@ -0,0 +1,146 @@ +/* + $Id: util.h,v 1.16 2008/04/14 17:30:27 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Miscellaneous things common to standalone programs. */ + +#ifndef UTIL_H +#define UTIL_H +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include + +#ifdef HAVE_STDARG_H +/* Get a definition for va_list. */ +#include +#endif + +/* FreeBSD 4 has getopt in unistd.h. So we include that before + getopt.h */ +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_GETOPT_H +#include +#endif + +#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 + +#ifndef EXIT_INFO +# define EXIT_INFO 100 +#endif + +#define DEBUG 1 +#if DEBUG +#define dbg_print(level, s, args...) \ + if (opts.debug_level >= level) \ + report(stderr, "%s: "s, __func__ , ##args) +#else +#define dbg_print(level, s, args...) +#endif + +#define err_exit(fmt, args...) \ + report(stderr, "%s: "fmt, program_name, ##args); \ + myexit(p_cdio, EXIT_FAILURE) + +typedef enum +{ + INPUT_AUTO, + INPUT_DEVICE, + INPUT_BIN, + INPUT_CUE, + INPUT_NRG, + INPUT_CDRDAO, + INPUT_UNKNOWN +} source_image_t; + +extern char *source_name; +extern char *program_name; +extern cdio_log_handler_t gl_default_cdio_log_handler; + +/*! Common error exit routine which frees p_cdio. rc is the + return code to pass to exit. +*/ +void myexit(CdIo_t *p_cdio, int rc); + +/*! Print our version string */ +void print_version (char *psz_program, const char *psz_version, + int no_header, bool version_only); + +/*! Device input routine. If successful we return an open CdIo_t + pointer. On error the program exits. + */ +CdIo_t * +open_input(const char *psz_source, source_image_t source_image, + const char *psz_access_mode); + +/*! On Unixish OS's we fill out the device name, from a short name. + For example cdrom might become /dev/cdrom. +*/ +char *fillout_device_name(const char *device_name); + +/*! Prints out SCSI-MMC drive features */ +void print_mmc_drive_features(CdIo *p_cdio); + +/*! Prints out drive capabilities */ +void print_drive_capabilities(cdio_drive_read_cap_t p_read_cap, + cdio_drive_write_cap_t p_write_cap, + cdio_drive_misc_cap_t p_misc_cap); + +/*! Common place for output routine. In some environments, like XBOX, + it may not be desireable to send output to stdout and stderr. */ +void report (FILE *stream, const char *psz_format, ...); + +/* Prints "ls"-like file attributes */ +void print_fs_attrs(iso9660_stat_t *p_statbuf, bool b_rock, bool b_xa, + const char *psz_name_untranslated, + const char *psz_name_translated); + +#endif /* UTIL_H */ diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 00000000..28969641 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,12 @@ +/.deps +/Makefile +/Makefile.in +/check_common_fn +/check_cue.sh +/check_iso.sh +/check_nrg.sh +/check_paranoia.sh +/testbincue.c +/testisocd2.c +/testnrg.c +/testpregap.c diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 00000000..8d79e2ed --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,113 @@ +# $Id: Makefile.am,v 1.69 2008/08/31 13:38:22 flameeyes Exp $ +# +# Copyright (C) 2003, 2004, 2006, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +#################################################### +# Things for regression testing +#################################################### +# +# +# There's a problem with doing make distcheck for testdefault. +# A reminder of why I hate automake. + +if BUILD_CD_PARANOIA +testparanoia=testparanoia +testparanoia_LDADD = $(LIBCDIO_PARANOIA_LIBS) $(LIBCDIO_CDDA_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +endif + +hack = check_sizeof testassert testbincue testischar \ + testisocd testisocd2 testiso9660 \ + testnrg $(testparanoia) testtoc testpregap + +EXTRA_PROGRAMS = testdefault + +INCLUDES = -I$(top_srcdir) $(LIBCDIO_CFLAGS) $(LIBISO9660_CFLAGS) + +check_sizeof_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +testassert_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) +testdefault_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) +testischar_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +testiso9660_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) + +testisocd_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) +testisocd2_LDADD = $(LIBISO9660_LIBS) $(LIBCDIO_LIBS) $(LTLIBICONV) + +testtoc_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) +testtoc_CFLAGS = -DTEST_DIR=\"$(srcdir)\" + +testpregap_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) +testpregap_CFLAGS = -DTEST_DIR=\"$(srcdir)\" + +testbincue_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) +testbincue_CFLAGS = -DTEST_DIR=\"$(srcdir)\" + +testnrg_LDADD = $(LIBCDIO_LIBS) $(LTLIBICONV) +testnrg_CFLAGS = -DTEST_DIR=\"$(srcdir)\" + +check_SCRIPTS = check_nrg.sh check_cue.sh check_cd_read.sh \ + check_iso.sh check_fuzzyiso.sh check_paranoia.sh check_opts.sh +# If we beefed this up so it checked to see if a CD-DA was loaded +# it could be an automatic test. But for now, not so. +# check_paranoia.sh + +check_PROGRAMS = $(hack) + +check_DATA = vcd_demo.right vcd_demo_vcdinfo.right \ + videocd.right videocd.nrg \ + cdda.right cdda.toc cdda.cue cdda.bin \ + isofs-m1.right isofs-m1-no-rr.right \ + isofs-m1.toc isofs-m1.cue isofs-m1.bin \ + cd-paranoia-log.right \ + check_opts0.right check_opts1.right check_opts2.right \ + check_opts3.right check_opts4.right check_opts5.right \ + check_opts6.right check_opts7.right \ + isofs-m1-read.right cdda-read.right \ + t1.toc t2.toc t3.toc t4.toc t5.toc t6.toc t7.toc t8.toc t9.toc \ + data1.toc data2.toc data5.toc data6.toc data7.toc \ + vcd2.toc cdtext.toc \ + bad-mode1.cue bad-msf-1.cue bad-msf-2.cue bad-msf-3.cue \ + bad-mode1.toc bad-msf-1.toc bad-msf-2.toc bad-msf-3.toc \ + bad-cat1.cue bad-cat2.cue bad-cat3.cue \ + bad-cat1.toc bad-cat2.toc bad-cat3.toc bad-file.toc \ + copying.iso copying.right copying-rr.iso copying-rr.right \ + joliet.iso joliet.right joliet-nojoliet.right \ + udf102.iso copying.gpl copying-rr.gpl + +EXTRA_DIST = $(check_SCRIPTS) $(check_DATA) \ + check_common_fn check_cue.sh.in check_nrg.sh.in \ + testbincue.c.in testpregap.c.in testnrg.c.in \ + check_iso.sh.in p1.bin p1.cue p1.nrg + +TESTS = $(check_PROGRAMS) $(check_SCRIPTS) +XFAIL_TESTS = testassert + +MOSTLYCLEANFILES = core core.* *.dump cdda-orig.wav cdda-try.wav *.raw + +test: check-am + +# This is a really bad hack to make sure check_nrg and check_cue.sh +# are executable. Automake will remake check_nrg.sh and check_cue.sh +# but not run the configure default commands for them to make sure +# they are executable. You know it would be nice one could just set +# permissions and mode when it makes the files. I'm sure there's some +# cleaner a way to do this, but frankly I've wasted far too much of my +# life the crappy automess system that I've really lost interest in +# learning any more of this awful system than I need to. +check-am: make-executable +make-executable: check_nrg.sh check_cue.sh check_paranoia.sh + chmod +x *.sh + if test ! -f cdda.bin ; then $(LN_S) $(srcdir)/cdda.bin cdda.bin ; fi + if test ! -f isofs-m1.bin ; then $(LN_S) $(srcdir)/isofs-m1.bin isofs-m1.bin ; fi diff --git a/test/bad-cat1.cue b/test/bad-cat1.cue new file mode 100644 index 00000000..d48fda63 --- /dev/null +++ b/test/bad-cat1.cue @@ -0,0 +1,9 @@ +REM $Id: bad-cat1.cue,v 1.1 2004/07/12 03:59:59 rocky Exp $ +REM test catalog number - no catalog after word CATALOG + +CATALOG + +FILE "cdda.bin" BINARY + +TRACK 01 AUDIO + INDEX 01 00:00:00 diff --git a/test/bad-cat1.toc b/test/bad-cat1.toc new file mode 100644 index 00000000..864c9b4f --- /dev/null +++ b/test/bad-cat1.toc @@ -0,0 +1,8 @@ +// $Id: bad-cat1.toc,v 1.1 2004/05/08 20:36:02 rocky Exp $ +// test catalog number - no catalog after word CATALOG + +CATALOG + +TRACK AUDIO +NO COPY +FILE "cdda.bin" 1:0:0 diff --git a/test/bad-cat2.cue b/test/bad-cat2.cue new file mode 100644 index 00000000..90c0b9bc --- /dev/null +++ b/test/bad-cat2.cue @@ -0,0 +1,9 @@ +REM $Id: bad-cat2.cue,v 1.1 2004/07/09 20:47:08 rocky Exp $ +REM test catalog number. -- not enough digits + +CATALOG "167890123" + +FILE "cdda.bin" BINARY + +TRACK 01 AUDIO + INDEX 01 00:00:00 diff --git a/test/bad-cat2.toc b/test/bad-cat2.toc new file mode 100644 index 00000000..f1927cf5 --- /dev/null +++ b/test/bad-cat2.toc @@ -0,0 +1,8 @@ +// $Id: bad-cat2.toc,v 1.2 2004/07/09 20:47:08 rocky Exp $ +// test catalog number. -- not enough digits + +CATALOG 167890123 // Should be 13 digits + +TRACK AUDIO +NO COPY +FILE "cdda.bin" 1:0:0 diff --git a/test/bad-cat3.cue b/test/bad-cat3.cue new file mode 100644 index 00000000..0e65e3bf --- /dev/null +++ b/test/bad-cat3.cue @@ -0,0 +1,9 @@ +REM $Id: bad-cat3.cue,v 1.2 2004/07/10 01:18:02 rocky Exp $ +REM test catalog number. -- invalid decimal digit + +CATALOG 123456789b123 + +FILE "cdda.bin" BINARY + +TRACK 01 AUDIO + INDEX 01 00:00:00 diff --git a/test/bad-cat3.toc b/test/bad-cat3.toc new file mode 100644 index 00000000..1e389a34 --- /dev/null +++ b/test/bad-cat3.toc @@ -0,0 +1,8 @@ +// $Id: bad-cat3.toc,v 1.1 2004/05/08 20:36:02 rocky Exp $ +// test catalog number - non-digit catalog name + +CATALOG "123456789A123" + +TRACK AUDIO +NO COPY +FILE "cdda.bin" 1:0:0 diff --git a/test/bad-file.toc b/test/bad-file.toc new file mode 100644 index 00000000..5dfb1289 --- /dev/null +++ b/test/bad-file.toc @@ -0,0 +1,13 @@ +// $Id: bad-file.toc,v 1.1 2005/01/23 00:45:57 rocky Exp $ +// XA disk +// + +CD_ROM_XA + +TRACK MODE2_FORM2 +FILE "foo.bin" 00:00:00 00:13:57 + +TRACK MODE2_FORM1 +PREGAP 0:2:0 +FILE "foo.bin" 00:20:71 00:00:00 + diff --git a/test/bad-mode1.cue b/test/bad-mode1.cue new file mode 100644 index 00000000..589de652 --- /dev/null +++ b/test/bad-mode1.cue @@ -0,0 +1,7 @@ +REM $Id: bad-mode1.cue,v 1.1 2004/07/10 01:18:02 rocky Exp $ +REM "MODE1_FORM45" is not a valid mode. + +FILE "cdda.bin" BINARY + +TRACK 01 MODE3_FORM1 + INDEX 01 00:00:00 diff --git a/test/bad-mode1.toc b/test/bad-mode1.toc new file mode 100644 index 00000000..9dbc6459 --- /dev/null +++ b/test/bad-mode1.toc @@ -0,0 +1,5 @@ +// $Id: bad-mode1.toc,v 1.1 2004/05/07 10:57:50 rocky Exp $ +CD_DA + +TRACK MODE1_FORM45 // "MODE1_FORM45" is not a valid mode. +SILENCE 10:0:0 diff --git a/test/bad-msf-1.cue b/test/bad-msf-1.cue new file mode 100644 index 00000000..bea0ad6d --- /dev/null +++ b/test/bad-msf-1.cue @@ -0,0 +1,7 @@ +REM $Id: bad-msf-1.cue,v 1.1 2004/07/12 03:57:28 rocky Exp $ +REM bad MSF in second field - frame should be less than 75 + +FILE "cdda.bin" BINARY + +TRACK 01 AUDIO + INDEX 01 00:00:100 diff --git a/test/bad-msf-1.toc b/test/bad-msf-1.toc new file mode 100644 index 00000000..2d9f656e --- /dev/null +++ b/test/bad-msf-1.toc @@ -0,0 +1,6 @@ +// $Id: bad-msf-1.toc,v 1.1 2004/05/07 10:57:50 rocky Exp $ +// bad MSF in second field + +TRACK AUDIO +NO COPY // so that all CTL flags are 0 +FILE "cdda.bin" 0:0:75 // frame should be less than 75 diff --git a/test/bad-msf-2.cue b/test/bad-msf-2.cue new file mode 100644 index 00000000..fdd4bfc2 --- /dev/null +++ b/test/bad-msf-2.cue @@ -0,0 +1,7 @@ +REM $Id: bad-msf-2.cue,v 1.1 2004/07/10 01:18:02 rocky Exp $ +REM bad MSF in second field - seconds should be less than 60 + +FILE "cdda.bin" BINARY + +TRACK 01 AUDIO + INDEX 01 00:90:00 diff --git a/test/bad-msf-2.toc b/test/bad-msf-2.toc new file mode 100644 index 00000000..77873486 --- /dev/null +++ b/test/bad-msf-2.toc @@ -0,0 +1,6 @@ +// $Id: bad-msf-2.toc,v 1.1 2004/05/07 10:57:50 rocky Exp $ +// bad MSF in second field + +TRACK AUDIO +NO COPY // so that all CTL flags are 0 +FILE "cdda.bin" 0:60:0 // seconds should be less than 60 diff --git a/test/bad-msf-3.cue b/test/bad-msf-3.cue new file mode 100644 index 00000000..e02774bb --- /dev/null +++ b/test/bad-msf-3.cue @@ -0,0 +1,7 @@ +REM $Id: bad-msf-3.cue,v 1.1 2004/07/12 03:58:55 rocky Exp $ +REM bad MSF in second field + +FILE "cdda.bin" BINARY + +TRACK 01 AUDIO + INDEX 01 xx:yy:0 diff --git a/test/bad-msf-3.toc b/test/bad-msf-3.toc new file mode 100644 index 00000000..b1cfac1a --- /dev/null +++ b/test/bad-msf-3.toc @@ -0,0 +1,6 @@ +// $Id: bad-msf-3.toc,v 1.1 2004/07/10 01:18:02 rocky Exp $ +// bad MSF in second field + +TRACK AUDIO +NO COPY // so that all CTL flags are 0 +FILE "cdda.bin" xx:yy:zz // Should be digits diff --git a/test/cd-paranoia-log.right b/test/cd-paranoia-log.right new file mode 100644 index 00000000..7ad43a9e --- /dev/null +++ b/test/cd-paranoia-log.right @@ -0,0 +1,3 @@ +outputting to cdda.raw + (== PROGRESS == [] == :^D * ==) + diff --git a/test/cdda-mcn.right b/test/cdda-mcn.right new file mode 100644 index 00000000..dd53487d --- /dev/null +++ b/test/cdda-mcn.right @@ -0,0 +1,18 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ + +Disc mode is listed as: CD-DA +CD-ROM Track List (1 - 1) + #: MSF LSN Type Green? Copy? Channels Premphasis? + 1: 00:02:00 000000 audio false yes 0 no +170: 00:09:64 000589 leadout (1 MB raw, 1 MB formatted) +Media Catalog Number (MCN): 123456789ABCD +Last CD Session LSN: not supported by drive/driver +audio status: not implemented +__________________________________ +CD Analysis Report + +CD-TEXT for Disc: +CD-TEXT for Track 1: diff --git a/test/cdda-read.right b/test/cdda-read.right new file mode 100644 index 00000000..e5e583ea --- /dev/null +++ b/test/cdda-read.right @@ -0,0 +1,151 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +0x0000: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0010: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0020: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0030: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0040: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0050: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0060: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0070: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0080: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0090: 0000 0000 0000 0000 0000 0000 0000 0000 +0x00a0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x00b0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x00c0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x00d0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x00e0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x00f0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0100: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0110: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0120: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0130: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0140: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0150: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0160: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0170: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0180: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0190: 0000 0000 0000 0000 0000 0000 0000 0000 +0x01a0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x01b0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x01c0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x01d0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x01e0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x01f0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0200: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0210: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0220: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0230: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0240: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0250: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0260: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0270: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0280: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0290: 0000 0000 0000 0000 0000 0000 0000 0000 +0x02a0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x02b0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x02c0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x02d0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x02e0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x02f0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0300: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0310: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0320: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0330: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0340: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0350: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0360: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0370: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0380: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0390: 0000 0000 0000 0000 0000 0000 0000 0000 +0x03a0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x03b0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x03c0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x03d0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x03e0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x03f0: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0400: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0410: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0420: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0430: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0440: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0450: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0460: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0470: 0000 0000 0000 0000 0000 0000 0000 0000 +0x0480: 0000 0000 0000 0000 f0ff 0000 f0ff 0000 +0x0490: e3ff f0ff e3ff f0ff d8ff e3ff d8ff e3ff +0x04a0: dfff e8ff dfff e8ff f5ff fcff f5ff fcff +0x04b0: 0700 0c00 0700 0c00 1500 1800 1500 1800 +0x04c0: 2000 1100 2000 1100 2800 0b00 2800 0b00 +0x04d0: 1d00 0500 1d00 0500 1300 0000 1300 0000 +0x04e0: 0a00 0b00 0a00 0b00 1200 1300 1200 1300 +0x04f0: 1800 1900 1800 1900 0c00 0d00 0c00 0d00 +0x0500: 0200 0300 0200 0300 e9ff faff e9ff faff +0x0510: d5ff f2ff d5ff f2ff b5ff dbff b5ff dbff +0x0520: 9cff c8ff 9cff c8ff a9ff c9ff a9ff c9ff +0x0530: c4ff daff c4ff daff eaff f8ff eaff f8ff +0x0540: 1900 1000 1900 1000 3e00 2300 3e00 2300 +0x0550: 6b00 3100 6b00 3100 8d00 3b00 8d00 3b00 +0x0560: 7600 4200 7600 4200 6100 3600 6100 3600 +0x0570: 3e00 2b00 3e00 2b00 1000 1100 1000 1100 +0x0580: eaff fbff eaff fbff cbff e9ff cbff e9ff +0x0590: b2ff daff b2ff daff ceff deff ceff deff +0x05a0: e5ff f1ff e5ff f1ff f8ff 0000 f8ff 0000 +0x05b0: 0700 0c00 0700 0c00 1300 1500 1300 1500 +0x05c0: 0c00 0b00 0c00 0b00 e6ff f2ff e6ff f2ff +0x05d0: c7ff ddff c7ff ddff aeff ccff aeff ccff +0x05e0: 8aff bfff 8aff bfff 6eff b5ff 6eff b5ff +0x05f0: 99ff ceff 99ff ceff d0ff e3ff d0ff e3ff +0x0600: 0e00 0400 0e00 0400 8000 3e00 8000 3e00 +0x0610: da00 6c00 da00 6c00 1f01 8f00 1f01 8f00 +0x0620: 4201 a900 4201 a900 2901 9b00 2901 9b00 +0x0630: d000 6d00 d000 6d00 7100 3500 7100 3500 +0x0640: 0000 0600 0000 0600 64ff bfff 64ff bfff +0x0650: e7fe 86ff e7fe 86ff a7fe 59ff a7fe 59ff +0x0660: 98fe 47ff 98fe 47ff b1fe 4bff b1fe 4bff +0x0670: eafe 71ff eafe 71ff 5cff a2ff 5cff a2ff +0x0680: ceff ebff ceff ebff 3c00 2600 3c00 2600 +0x0690: 9400 5500 9400 5500 d900 7900 d900 7900 +0x06a0: bd00 6400 bd00 6400 a300 5100 a300 5100 +0x06b0: 5f00 3000 5f00 3000 1300 1400 1300 1400 +0x06c0: 0700 fcff 0700 fcff 0d00 f8ff 0d00 f8ff +0x06d0: 1100 0400 1100 0400 4300 1d00 4300 1d00 +0x06e0: 6a00 3000 6a00 3000 8800 3e00 8800 3e00 +0x06f0: 8e00 4800 8e00 4800 6000 2f00 6000 2f00 +0x0700: e9ff f9ff e9ff f9ff 88ff cdff 88ff cdff +0x0710: 3bff 9aff 3bff 9aff ecfe 72ff ecfe 72ff +0x0720: d2fe 63ff d2fe 63ff 01ff 79ff 01ff 79ff +0x0730: 2bff 8cff 2bff 8cff 83ff bdff 83ff bdff +0x0740: fcff f5ff fcff f5ff 5e00 3200 5e00 3200 +0x0750: ac00 6200 ac00 6200 e800 8700 e800 8700 +0x0760: 0501 8200 0501 8200 d800 6b00 d800 6b00 +0x0770: 9d00 5600 9d00 5600 6a00 4300 6a00 4300 +0x0780: 3e00 2200 3e00 2200 1900 0600 1900 0600 +0x0790: eaff efff eaff efff e4ff ecff e4ff ecff +0x07a0: dfff e9ff dfff e9ff bbff e6ff bbff e6ff +0x07b0: 9eff d3ff 9eff d3ff 77ff c4ff 77ff c4ff +0x07c0: 39ff a8ff 39ff a8ff 19ff 92ff 19ff 92ff +0x07d0: 22ff 91ff 22ff 91ff 5cff b1ff 5cff b1ff +0x07e0: cdff ecff cdff ecff 7500 3c00 7500 3c00 +0x07f0: 1101 8c00 1101 8c00 8b01 ca00 8b01 ca00 +0x0800: e701 f900 e701 f900 da01 fb00 da01 fb00 +0x0810: 6401 b800 6401 b800 8a00 4500 8a00 4500 +0x0820: 92ff c4ff 92ff c4ff 8efe 4cff 8efe 4cff +0x0830: a6fd cdfe a6fd cdfe 09fd 7afe 09fd 7afe +0x0840: 21fd 8cfe 21fd 8cfe 9dfd d2fe 9dfd d2fe +0x0850: b3fe 67ff b3fe 67ff 3000 1600 3000 1600 +0x0860: e801 e800 e801 e800 4503 8f01 4503 8f01 +0x0870: 3004 2102 3004 2102 2404 1f02 2404 1f02 +0x0880: 4f03 a201 4f03 a201 b801 cf00 b801 cf00 +0x0890: e4ff e4ff e4ff e4ff bafd d7fe bafd d7fe +0x08a0: 00fc 01fe 00fc 01fe 60fb abfd 60fb abfd +0x08b0: 5efb 9cfd 5efb 9cfd 32fc 11fe 32fc 11fe +0x08c0: d5fd c7fe d5fd c7fe 0100 0e00 0100 0e00 +0x08d0: 0102 1701 0102 1701 9803 e901 9803 e901 +0x08e0: d404 8b02 d404 8b02 ea04 8f02 ea04 8f02 +0x08f0: d803 fc01 d803 fc01 4802 2a01 4802 2a01 +0x0900: 2100 0e00 2100 0e00 20fe 07ff 20fe 07ff +0x0910: 86fc 35fe 86fc 35fe acfb c3fd acfb c3fd +0x0920: c0fb ddfd c0fb ddfd b5fc 40fe b5fc 40fe + diff --git a/test/cdda.right b/test/cdda.right new file mode 100644 index 00000000..ed6ca17a --- /dev/null +++ b/test/cdda.right @@ -0,0 +1,20 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ + +Disc mode is listed as: CD-DA +CD-ROM Track List (1 - 1) + #: MSF LSN Type Green? Copy? Channels Premphasis? + 1: 00:02:00 000000 audio false yes 2 no +170: 00:06:02 000302 leadout (693 KB raw, 693 KB formatted) +Media Catalog Number (MCN): 0000010271955 +Last CD Session LSN: not supported by drive/driver +audio status: not implemented +__________________________________ +CD Analysis Report + +CD-TEXT for Disc: + PERFORMER: Richard Stallman + TITLE: Join us now we have the software +CD-TEXT for Track 1: diff --git a/test/cdtext.toc b/test/cdtext.toc new file mode 100644 index 00000000..93c8000b --- /dev/null +++ b/test/cdtext.toc @@ -0,0 +1,48 @@ +CD_DA + +// global CD-TEXT data + +CD_TEXT { + + // Mapping from language number (0..7) used in 'LANGUAGE' statements + // to language code. +/// LANGUAGE_MAP { +/// 0 : EN // 9 is the code for ENGLISH, +/// // I don't know any other language code, yet +/// } + + // Language number should always start with 0 + LANGUAGE 0 { + // Required fields - at least all CD-TEXT CDs I've seen so far have them. + TITLE "CD Title" + PERFORMER "Performer" + DISC_ID "XY12345" + UPC_EAN "" // usually empty + + // Further possible items, all of them are optional + ARRANGER "" + SONGWRITER "" + MESSAGE "" + GENRE "" // I'm not sure if this should be really ascii data + } +} + + +TRACK AUDIO +// track specific CD-TEXT data +CD_TEXT { + LANGUAGE 0 { + // if an item is defined for one track it should be defined for all tracks + TITLE "Track Title" + + PERFORMER "Performer" + ISRC "US-XX1-98-01234" + + ARRANGER "" + SONGWRITER "" + MESSAGE "" + } +} + +SILENCE 1:0:0 + diff --git a/test/check_cd_read.sh b/test/check_cd_read.sh new file mode 100755 index 00000000..fe312564 --- /dev/null +++ b/test/check_cd_read.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# $Id: check_cd_read.sh,v 1.12 2008/03/22 18:08:25 karl Exp $ +# +# Copyright (C) 2003, 2005, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Tests to see that CD reading is correct (via cd-read). + +if test -z $srcdir ; then + srcdir=`pwd` +fi + +if test "X$top_builddir" = "X" ; then + top_builddir=`pwd`/.. +fi + +. ${top_builddir}/test/check_common_fn + +if test ! -x ../src/cd-read ; then + exit 77 +fi + +BASE=`basename $0 .sh` + +fname=cdda +testnum=CD-DA +opts="-c ${srcdir}/${fname}.cue --mode=red --just-hex --start=0" +test_cd_read "$opts" ${fname}-read.dump ${srcdir}/${fname}-read.right +RC=$? +check_result $RC "cd-read CUE test $testnum" "cd-read $opts" + +fname=isofs-m1 +testnum=MODE1 +opts="-i ${srcdir}/${fname}.cue --mode m1f1 -s 26 -n 2" +test_cd_read "$opts" ${fname}-read.dump ${srcdir}/${fname}-read.right +RC=$? +check_result $RC "cd-read CUE test $testnum" "$CD_READ $opts" + +exit $RC + +#;;; Local Variables: *** +#;;; mode:shell-script *** +#;;; eval: (sh-set-shell "bash") *** +#;;; End: *** diff --git a/test/check_common_fn.in b/test/check_common_fn.in new file mode 100755 index 00000000..a175682a --- /dev/null +++ b/test/check_common_fn.in @@ -0,0 +1,185 @@ +# $Id: check_common_fn.in,v 1.14 2008/10/17 01:51:43 rocky Exp $ +# +# Copyright (C) 2003, 2004, 2005, 2006, 2008 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Common routines and setup for regression testing. +SKIP_TEST_EXITCODE=77 + +# Some output changes depending on TZ and locale. Set this so we get known +# results +TZ=CUT +LC_TIME='en_US' +export TZ LC_TIME + +check_result() { + RC=$1 + shift + msg=$1 + shift + cmdline=$* + if test $RC -ne 0 ; then + if test $RC -ne $SKIP_TEST_EXITCODE ; then + echo "$0: $msg failed in comparing output." + if test -n "$cmdline" ; then + echo "$0: failed command:" + echo " $cmdline" + fi + exit $RC + else + echo "$0: $msg skipped." + fi + else + echo "$0: $msg ok." + fi +} + +test_cdinfo() { + + opts="$1" + outfile="$2" + rightfile="$3" + + CD_INFO="../src/cd-info@EXEEXT@" + + if [ ! -x ${CD_INFO} ]; then + echo "$0: No ${CD_INFO}" + return 1 + fi + + if ${CD_INFO} --no-header ${opts} >${outfile} 2>&1 ; then + if test "@DIFF@" != no; then + if @DIFF@ @DIFF_OPTS@ ${outfile} ${rightfile} ; then + rm -f $outfile + return 0 + else + return 3 + fi + else + echo "$0: No diff(1) or cmp(1) found - cannot test ${CD_INFO}" + rm -f $outfile + return 77 + fi + else + echo "$0 failed running: ${CD_INFO} ${opts}" + return 2 + fi + +} + +test_iso_info() { + + opts="$1" + outfile="$2" + rightfile="$3" + + ISO_INFO="../src/iso-info@EXEEXT@" + + if [ ! -x ${ISO_INFO} ]; then + echo "$0: No ${ISO_INFO}" + return 1 + fi + + cmdline="${ISO_INFO} --no-header ${opts}" + if $cmdline >${outfile} 2>&1 ; then + if test "/usr/bin/diff" != no; then + if @DIFF@ @DIFF_OPTS@ ${outfile} ${rightfile} ; then + rm -f $outfile + return 0 + else + return 3 + fi + else + echo "$0: No diff(1) or cmp(1 found - cannot test ${ISO_INFO}" + rm -f $outfile + return 77 + fi + else + echo "$0 failed running: $cmdline" + return 2 + fi + +} + +test_iso_read() { + + opts="$1" + outfile="$2" + rightfile="$3" + + ISO_READ="../src/iso-read@EXEEXT@" + + if [ ! -x ${ISO_READ} ]; then + echo "$0: No ${ISO_READ}" + return 1 + fi + + if ${ISO_READ} ${opts} -o ${outfile} 2>&1 ; then + if test "@DIFF@" != no; then + if @DIFF@ @DIFF_OPTS@ ${outfile} ${rightfile} ; then + rm -f $outfile + return 0 + else + return 3 + fi + else + echo "$0: No diff(1) or cmp(1 found - cannot test ${ISO_READ}" + rm -f $outfile + return 77 + fi + else + echo "$0 failed running: ${ISO_READ} ${opts} -o ${outfile}" + return 2 + fi + +} + +test_cd_read() { + + opts="$1" + outfile="$2" + rightfile="$3" + + CD_READ="../src/cd-read@EXEEXT@" + + if [ ! -x ${CD_READ} ]; then + echo "$0: No ${CD_READ}" + return 1 + fi + + if ${CD_READ} --no-header ${opts} >${outfile} 2>&1 ; then + if test "@DIFF@" != no; then + if @DIFF@ @DIFF_OPTS@ ${outfile} ${rightfile} ; then + rm -f $outfile + return 0 + else + return 3 + fi + else + echo "$0: No diff(1) or cmp(1 found - cannot test ${CD_READ}" + rm -f $outfile + return 77 + fi + else + echo "$0 failed running: ${CD_READ} ${opts}" + return 2 + fi + +} + +#;;; Local Variables: *** +#;;; mode:shell-script *** +#;;; eval: (sh-set-shell "bash") *** +#;;; End: *** diff --git a/test/check_cue.sh.in b/test/check_cue.sh.in new file mode 100644 index 00000000..8f669a7e --- /dev/null +++ b/test/check_cue.sh.in @@ -0,0 +1,125 @@ +#!/bin/sh +#$Id: check_cue.sh.in,v 1.31 2007/12/28 02:11:01 rocky Exp $ +# Tests to see that BIN/CUE and cdrdao TOC file iamge reading is correct +# (via cd-info). + +if test "@VCDINFO_LIBS@X" != "X" ; then + vcd_opt='--no-vcd' +fi + +if test "X$srcdir" = "X" ; then + srcdir=`pwd` +fi + +if test "X$top_srcdir" = "X" ; then + top_srcdir=`pwd`/.. +fi + +if test "X$top_builddir" = "X" ; then + top_builddir=`pwd`/.. +fi + +. ${top_builddir}/test/check_common_fn + +if test ! -x $top_srcdir/src/cd-info ; then + exit 77 +fi + +BASE=`basename $0 .sh` + +fname=cdda +testnum=CD-DA +opts="--quiet --no-device-info --cue-file ${srcdir}/${fname}.cue --no-cddb" +test_cdinfo "$opts" ${fname}.dump ${srcdir}/${fname}.right +RC=$? +check_result $RC "cd-info CUE test $testnum" "${CD_INFO} $opts" + + +opts="--quiet --no-device-info --bin-file ${srcdir}/${fname}.bin --no-cddb" +test_cdinfo "$opts" ${fname}.dump ${srcdir}/${fname}.right +RC=$? +check_result $RC "cd-info BIN test $testnum" "${CD_INFO} $opts" + +opts="--quiet --no-device-info --toc-file ${srcdir}/${fname}.toc --no-cddb" +test_cdinfo "$opts" ${fname}.dump ${srcdir}/${fname}.right +RC=$? +check_result $RC "cd-info TOC test $testnum" "${CD_INFO} $opts" + +fname=isofs-m1 +testnum='ISO 9660 mode1 CUE' +if test -f ${srcdir}/${fname}.bin ; then + if test -n "@HAVE_ROCK@"; then + opts="-q --no-device-info --no-disc-mode --cue-file ${srcdir}/${fname}.cue --iso9660" + test_cdinfo "$opts" ${fname}.dump ${srcdir}/${fname}.right + RC=$? + check_result $RC "cd-info Rock-Ridge CUE test $testnum" "${CD_INFO} $opts" + + opts="-q --no-device-info --no-disc-mode --no-rock-ridge --cue-file ${srcdir}/${fname}.cue --iso9660" + test_cdinfo "$opts" ${fname}.dump ${srcdir}/${fname}-no-rr.right + RC=$? + check_result $RC "cd-info no Rock-Ridge CUE test $testnum" "${CD_INFO} $opts" + fi + +else + echo "Don't see CUE file ${srcdir}/${fname}.bin. Test $testnum skipped." +fi + +if test -n "@HAVE_ROCK@"; then + testnum='ISO 9660 mode1 TOC' + if test -f ${srcdir}/${fname}.bin ; then + opts="-q --no-device-info --no-disc-mode --toc-file ${srcdir}/${fname}.toc --iso9660" + test_cdinfo "$opts" ${fname}.dump ${srcdir}/${fname}.right + RC=$? + check_result $RC "cd-info TOC test $testnum" "${CD_INFO} $opts" + else + echo "Don't see TOC file ${srcdir}/${fname}.bin. Test $testnum skipped." + fi +fi + +fname=vcd_demo +if test -z "@VCDINFO_LIBS@" ; then + right=${srcdir}/${fname}.right +else + right=${srcdir}/${fname}_vcdinfo.right +fi +testnum='Video CD' +if test -f ${srcdir}/${fname}.bin ; then + opts="-q --no-device-info --no-disc-mode -c ${srcdir}/${fname}.cue --iso9660" + test_cdinfo "$opts" ${fname}.dump $right + RC=$? + check_result $RC "cd-info CUE test $testnum" "${CD_INFO} $opts" + + if test -z "@VCDINFO_LIBS@" ; then + right=${srcdir}/${fname}.right + else + right=${srcdir}/${fname}_vcdinfo.right + fi + opts="-q --no-device-info --no-disc-mode -t ${srcdir}/${fname}.toc --iso9660" + if test -f ${srcdir}/${fname}.toc ; then + test_cdinfo "$opts" ${fname}.dump $right + RC=$? + check_result $RC "cd-info TOC test $testnum" "${CD_INFO} $opts" + else + echo "Don't see TOC file ${srcdir}/${fname}.toc. Test $testnum skipped." + fi +else + echo "Don't see CUE file ${srcdir}/${fname}.cue. Test $testnum skipped." +fi + +fname=svcd_ogt_test_ntsc +testnum='Super Video CD' +if test -f ${srcdir}/${fname}.bin ; then + opts="-q --no-device-info --no-disc-mode --cue-file ${srcdir}/${fname}.cue $vcd_opt --iso9660" + test_cdinfo "$opts" ${fname}.dump ${srcdir}/${fname}.right + RC=$? + check_result $RC "cd-info CUE test $testnum" "${CD_INFO} $opts" +else + echo "Don't see CUE file ${srcdir}/${fname}.bin. Test $testnum skipped." +fi + +exit $RC + +#;;; Local Variables: *** +#;;; mode:shell-script *** +#;;; eval: (sh-set-shell "bash") *** +#;;; End: *** diff --git a/test/check_fuzzyiso.sh b/test/check_fuzzyiso.sh new file mode 100755 index 00000000..e2ae3e4a --- /dev/null +++ b/test/check_fuzzyiso.sh @@ -0,0 +1,44 @@ +#!/bin/sh +#$Id: check_fuzzyiso.sh,v 1.6 2008/03/20 03:45:43 edsdead Exp $ + +if test "X$srcdir" = "X" ; then + srcdir=`pwd` +fi + +if test "X$top_srcdir" = "X" ; then + top_srcdir=`pwd`/.. +fi + +if test "X$top_builddir" = "X" ; then + top_builddir=`pwd`/.. +fi + +check_program="$top_builddir/example/isofuzzy" + +if test ! -x $check_program ; then + exit 77 +fi + +cd $srcdir; src_dir=`pwd` +for file in $src_dir/*.bin $src_dir/*.iso $src_dir/*.nrg ; do + case "$file" in + $src_dir/p1.nrg | $src_dir/p1.bin | $src_dir/cdda.bin | $src_dir/cdda-mcn.nrg | $src_dir/svcdgs.nrg ) + good_rc=1 + ;; + *) + good_rc=0 + ;; + esac + $check_program $file + if test $? -ne $good_rc ; then + echo "$0: failed running:" + echo " $check_program $file" + exit 1 + fi +done +exit 0 + +#;;; Local Variables: *** +#;;; mode:shell-script *** +#;;; eval: (sh-set-shell "bash") *** +#;;; End: *** diff --git a/test/check_iso.sh.in b/test/check_iso.sh.in new file mode 100644 index 00000000..ed4efbf2 --- /dev/null +++ b/test/check_iso.sh.in @@ -0,0 +1,63 @@ +#!/bin/sh +#$Id: check_iso.sh.in,v 1.15 2008/10/17 01:51:47 rocky Exp $ + +if test -z $srcdir ; then + srcdir=`pwd` +fi + +if test "X$top_builddir" = "X" ; then + top_builddir=`pwd`/.. +fi + +. ${top_builddir}/test/check_common_fn + +if test ! -x ../src/iso-info@EXEEXT@ ; then + exit 77 +fi + +BASE=`basename $0 .sh` +fname=copying + +opts="--quiet ${srcdir}/${fname}.iso --iso9660 " +test_iso_info "$opts" ${fname}.dump ${srcdir}/${fname}.right +RC=$? +check_result $RC 'iso-info basic test' "$ISO_INFO $opts" + +opts="--ignore --image ${srcdir}/${fname}.iso --extract $fname " +test_iso_read "$opts" ${fname} ${srcdir}/copying.gpl +RC=$? +check_result $RC 'iso-read basic test' "$ISO_READ $opts" + +if test -n "@HAVE_ROCK@"; then + fname=copying-rr + opts="--quiet ${srcdir}/${fname}.iso --iso9660 " + test_iso_info "$opts" ${fname}.dump ${srcdir}/${fname}.right + RC=$? + check_result $RC 'iso-info Rock Ridge test' "$ISO_INFO $opts" + + opts="--image ${srcdir}/${fname}.iso --extract COPYING" + test_iso_read "$opts" ${fname} ${srcdir}/copying-rr.gpl + RC=$? + check_result $RC 'iso-read RR test' "$ISO_READ $opts" +fi + +if test -n "@HAVE_JOLIET@" ; then + BASE=`basename $0 .sh` + fname=joliet + opts="--quiet ${srcdir}/${fname}.iso --iso9660 " + test_iso_info "$opts" ${fname}-nojoliet.dump ${srcdir}/${fname}.right + RC=$? + check_result $RC 'iso-info Joliet test' "$cmdline" + opts="--quiet ${srcdir}/${fname}.iso --iso9660 --no-joliet " + test_iso_info "$opts" ${fname}-nojoliet.dump \ + ${srcdir}/${fname}-nojoliet.right + RC=$? + check_result $RC 'iso-info --no-joliet test' "$cmdline" +fi + +exit $RC + +#;;; Local Variables: *** +#;;; mode:shell-script *** +#;;; eval: (sh-set-shell "bash") *** +#;;; End: *** diff --git a/test/check_nrg.sh.in b/test/check_nrg.sh.in new file mode 100755 index 00000000..f72cec7d --- /dev/null +++ b/test/check_nrg.sh.in @@ -0,0 +1,74 @@ +#!/bin/sh +#$Id: check_nrg.sh.in,v 1.17 2007/12/28 02:11:01 rocky Exp $ + +if test "@VCDINFO_LIBS@X" != "X" ; then + vcd_opt='--no-vcd' +fi + +if test "X$srcdir" = "X" ; then + srcdir=`pwd` +fi + +if test "X$top_srcdir" = "X" ; then + top_srcdir=`pwd`/.. +fi + +if test "X$top_builddir" = "X" ; then + top_builddir=`pwd`/.. +fi + +. ${top_builddir}/test/check_common_fn + +if test ! -x $top_srcdir/src/cd-info ; then + exit 77 +fi + +BASE=`basename $0 .sh` +test_name=videocd +opts="--quiet --no-device-info --nrg-file ${srcdir}/${test_name}.nrg $vcd_opt --iso9660" +test_cdinfo "$opts" ${test_name}.dump ${srcdir}/${test_name}.right +RC=$? +check_result $RC 'cd-info NRG test 1' "${CD_INFO} $opts" + +BASE=`basename $0 .sh` +nrg_file=${srcdir}/monvoisin.nrg + +if test -f $nrg_file ; then + test_cdinfo "-q --no-device-info --nrg-file $nrg_file $vcd_opt --iso9660 " \ + monvoisin.dump ${srcdir}/monvoisin.right + RC=$? + check_result $RC 'cd-info NRG test 2' +else + echo "Don't see NRG file ${nrg_file}. Test skipped." + exit 0 +fi + +test_name='svcdgs' +nrg_file=${srcdir}/${test_name}.nrg +opts="-q --no-device-info --nrg-file $nrg_file $vcd_opt --iso9660" +if test -f $nrg_file ; then + test_cdinfo "$opts" ${test_name}.dump ${srcdir}/${test_name}.right + RC=$? + check_result $RC "cd-info NRG $test_name" "${CD_INFO} $opts" +else + echo "Don't see NRG file ${nrg_file}. Test skipped." + exit $SKIP_TEST_EXITCODE +fi + +test_name='cdda-mcn' +nrg_file=${srcdir}/${test_name}.nrg +opts="-q --no-device-info --nrg-file $nrg_file --no-cddb" +if test -f $nrg_file ; then + test_cdinfo "$opts" ${test_name}.dump ${srcdir}/${test_name}.right + RC=$? + check_result $RC "cd-info NRG $test_name" "${CD_INFO} $opts" + exit $RC +else + echo "Don't see NRG file ${nrg_file}. Test skipped." + exit $SKIP_TEST_EXITCODE +fi + +#;;; Local Variables: *** +#;;; mode:shell-script *** +#;;; eval: (sh-set-shell "bash") *** +#;;; End: *** diff --git a/test/check_opts.sh b/test/check_opts.sh new file mode 100755 index 00000000..6cbfdd94 --- /dev/null +++ b/test/check_opts.sh @@ -0,0 +1,37 @@ +#!/bin/sh +#$Id: check_opts.sh,v 1.10 2007/12/28 02:11:01 rocky Exp $ +# Check cd-info options +if test -z "$srcdir" ; then + srcdir=`pwd` +fi + +if test "X$top_builddir" = "X" ; then + top_builddir=`pwd`/.. +fi + +. ${top_builddir}/test/check_common_fn + +if test ! -x ../src/cd-info ; then + exit 77 +fi + +BASE=`basename $0 .sh` + +fname=isofs-m1 +i=0 +for opt in '-T' '--no-tracks' '-A' '--no-analyze' '-I' '--no-ioctl' \ + '-q' '--quiet' ; do + testname=${BASE}$i + opts="--no-device-info --cue-file ${srcdir}/${fname}.cue $opt --quiet" + test_cdinfo "$opts" ${testname}.dump ${srcdir}/${testname}.right + RC=$? + check_result $RC "cd-info option test $opt" "../src/cd-info $opts" + i=`expr $i + 1` +done + +exit $RC + +#;;; Local Variables: *** +#;;; mode:shell-script *** +#;;; eval: (sh-set-shell "bash") *** +#;;; End: *** diff --git a/test/check_opts0.right b/test/check_opts0.right new file mode 100644 index 00000000..8af8ff71 --- /dev/null +++ b/test/check_opts0.right @@ -0,0 +1,18 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ + +Disc mode is listed as: CD-DATA (Mode 1) +Media Catalog Number (MCN): not available +Last CD Session LSN: not supported by drive/driver +__________________________________ +CD Analysis Report +CD-ROM with ISO 9660 filesystem +ISO 9660: 64 blocks, label `CDROM ' +Application: MKISOFS ISO 9660/HFS FILESYSTEM BUILDER & CDRECORD CD-R/DVD CREATOR (C) 1993 E.YOUNGDALE (C) 1997 J.PEARSON/J.SCHILLING +Preparer : +Publisher : +System : LINUX +Volume : CDROM +Volume Set : diff --git a/test/check_opts1.right b/test/check_opts1.right new file mode 100644 index 00000000..8af8ff71 --- /dev/null +++ b/test/check_opts1.right @@ -0,0 +1,18 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ + +Disc mode is listed as: CD-DATA (Mode 1) +Media Catalog Number (MCN): not available +Last CD Session LSN: not supported by drive/driver +__________________________________ +CD Analysis Report +CD-ROM with ISO 9660 filesystem +ISO 9660: 64 blocks, label `CDROM ' +Application: MKISOFS ISO 9660/HFS FILESYSTEM BUILDER & CDRECORD CD-R/DVD CREATOR (C) 1993 E.YOUNGDALE (C) 1997 J.PEARSON/J.SCHILLING +Preparer : +Publisher : +System : LINUX +Volume : CDROM +Volume Set : diff --git a/test/check_opts2.right b/test/check_opts2.right new file mode 100644 index 00000000..7723e0d1 --- /dev/null +++ b/test/check_opts2.right @@ -0,0 +1,12 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ + +Disc mode is listed as: CD-DATA (Mode 1) +CD-ROM Track List (1 - 1) + #: MSF LSN Type Green? Copy? + 1: 00:02:00 000000 data false no +170: 00:06:02 000302 leadout (693 KB raw, 604 KB formatted) +Media Catalog Number (MCN): not available +Last CD Session LSN: not supported by drive/driver diff --git a/test/check_opts3.right b/test/check_opts3.right new file mode 100644 index 00000000..7723e0d1 --- /dev/null +++ b/test/check_opts3.right @@ -0,0 +1,12 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ + +Disc mode is listed as: CD-DATA (Mode 1) +CD-ROM Track List (1 - 1) + #: MSF LSN Type Green? Copy? + 1: 00:02:00 000000 data false no +170: 00:06:02 000302 leadout (693 KB raw, 604 KB formatted) +Media Catalog Number (MCN): not available +Last CD Session LSN: not supported by drive/driver diff --git a/test/check_opts4.right b/test/check_opts4.right new file mode 100644 index 00000000..bd05bdc3 --- /dev/null +++ b/test/check_opts4.right @@ -0,0 +1,22 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ + +Disc mode is listed as: CD-DATA (Mode 1) +CD-ROM Track List (1 - 1) + #: MSF LSN Type Green? Copy? + 1: 00:02:00 000000 data false no +170: 00:06:02 000302 leadout (693 KB raw, 604 KB formatted) +Media Catalog Number (MCN): not available +Last CD Session LSN: not supported by drive/driver +__________________________________ +CD Analysis Report +CD-ROM with ISO 9660 filesystem +ISO 9660: 64 blocks, label `CDROM ' +Application: MKISOFS ISO 9660/HFS FILESYSTEM BUILDER & CDRECORD CD-R/DVD CREATOR (C) 1993 E.YOUNGDALE (C) 1997 J.PEARSON/J.SCHILLING +Preparer : +Publisher : +System : LINUX +Volume : CDROM +Volume Set : diff --git a/test/check_opts5.right b/test/check_opts5.right new file mode 100644 index 00000000..bd05bdc3 --- /dev/null +++ b/test/check_opts5.right @@ -0,0 +1,22 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ + +Disc mode is listed as: CD-DATA (Mode 1) +CD-ROM Track List (1 - 1) + #: MSF LSN Type Green? Copy? + 1: 00:02:00 000000 data false no +170: 00:06:02 000302 leadout (693 KB raw, 604 KB formatted) +Media Catalog Number (MCN): not available +Last CD Session LSN: not supported by drive/driver +__________________________________ +CD Analysis Report +CD-ROM with ISO 9660 filesystem +ISO 9660: 64 blocks, label `CDROM ' +Application: MKISOFS ISO 9660/HFS FILESYSTEM BUILDER & CDRECORD CD-R/DVD CREATOR (C) 1993 E.YOUNGDALE (C) 1997 J.PEARSON/J.SCHILLING +Preparer : +Publisher : +System : LINUX +Volume : CDROM +Volume Set : diff --git a/test/check_opts6.right b/test/check_opts6.right new file mode 100644 index 00000000..bd05bdc3 --- /dev/null +++ b/test/check_opts6.right @@ -0,0 +1,22 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ + +Disc mode is listed as: CD-DATA (Mode 1) +CD-ROM Track List (1 - 1) + #: MSF LSN Type Green? Copy? + 1: 00:02:00 000000 data false no +170: 00:06:02 000302 leadout (693 KB raw, 604 KB formatted) +Media Catalog Number (MCN): not available +Last CD Session LSN: not supported by drive/driver +__________________________________ +CD Analysis Report +CD-ROM with ISO 9660 filesystem +ISO 9660: 64 blocks, label `CDROM ' +Application: MKISOFS ISO 9660/HFS FILESYSTEM BUILDER & CDRECORD CD-R/DVD CREATOR (C) 1993 E.YOUNGDALE (C) 1997 J.PEARSON/J.SCHILLING +Preparer : +Publisher : +System : LINUX +Volume : CDROM +Volume Set : diff --git a/test/check_opts7.right b/test/check_opts7.right new file mode 100644 index 00000000..bd05bdc3 --- /dev/null +++ b/test/check_opts7.right @@ -0,0 +1,22 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ + +Disc mode is listed as: CD-DATA (Mode 1) +CD-ROM Track List (1 - 1) + #: MSF LSN Type Green? Copy? + 1: 00:02:00 000000 data false no +170: 00:06:02 000302 leadout (693 KB raw, 604 KB formatted) +Media Catalog Number (MCN): not available +Last CD Session LSN: not supported by drive/driver +__________________________________ +CD Analysis Report +CD-ROM with ISO 9660 filesystem +ISO 9660: 64 blocks, label `CDROM ' +Application: MKISOFS ISO 9660/HFS FILESYSTEM BUILDER & CDRECORD CD-R/DVD CREATOR (C) 1993 E.YOUNGDALE (C) 1997 J.PEARSON/J.SCHILLING +Preparer : +Publisher : +System : LINUX +Volume : CDROM +Volume Set : diff --git a/test/check_paranoia.sh.in b/test/check_paranoia.sh.in new file mode 100644 index 00000000..b955b7ba --- /dev/null +++ b/test/check_paranoia.sh.in @@ -0,0 +1,92 @@ +#!/bin/sh +# $Id: check_paranoia.sh.in,v 1.18 2008/10/17 01:51:47 rocky Exp $ +# Compare our cd-paranoia with known good results. + +if test "X$srcdir" = "X" ; then + srcdir=`pwd` +fi + +if test "X$top_builddir" = "X" ; then + top_builddir=`pwd`/.. +fi + +if test "X$top_srcdir" = "X" ; then + top_srcdir=`pwd`/.. +fi + +if test "@CMP@" != no -a "@BUILD_CD_PARANOIA_TRUE@"X = X ; then + cd_paranoia=$top_builddir/src/cd-paranoia/cd-paranoia@EXEEXT@ + $cd_paranoia -d $srcdir/cdda.cue -v -r -- "1-" + if test $? -ne 0 ; then + exit 6 + fi + dd bs=16 if=cdda.raw of=cdda-1.raw + dd bs=16 if=cdda.bin of=cdda-2.raw + if @CMP@ cdda-1.raw cdda-2.raw ; then + echo "** Raw cdda.bin extraction okay" + else + echo "** Raw cdda.bin extraction differ" + exit 3 + fi + mv cdda.raw cdda-good.raw + $cd_paranoia -d $srcdir/cdda.cue -x 64 -v -r -- "1-" + mv cdda.raw cdda-underrun.raw + $cd_paranoia -d $srcdir/cdda.cue -r -- "1-" + if test $? -ne 0 ; then + exit 6 + fi + if @CMP@ cdda-underrun.raw cdda-good.raw ; then + echo "** Under-run correction okay" + else + echo "** Under-run correction problem" + exit 3 + fi + # Start out with small jitter + $cd_paranoia -l ./cd-paranoia.log -d $srcdir/cdda.cue -x 5 -v -r -- "1-" + if test $? -ne 0 ; then + exit 6 + fi + mv cdda.raw cdda-jitter.raw + if @CMP@ cdda-jitter.raw cdda-good.raw ; then + echo "** Small jitter correction okay" + else + echo "** Small jitter correction problem" + exit 3 + fi + tail -3 ./cd-paranoia.log | sed -e's/\[.*\]/\[\]/' > ./cd-paranoia-filtered.log + if @CMP@ $srcdir/cd-paranoia-log.right ./cd-paranoia-filtered.log ; then + echo "** --log option okay" + rm ./cd-paranoia.log ./cd-paranoia-filtered.log + else + echo "** --log option problem" + exit 4 + fi + # A more massive set of failures: underrun + small jitter + $cd_paranoia -d $srcdir/cdda.cue -x 69 -v -r -- "1-" + if test $? -ne 0 ; then + exit 6 + fi + mv cdda.raw cdda-jitter.raw + if @CMP@ cdda-jitter.raw cdda-good.raw ; then + echo "** under-run + jitter correction okay" + else + echo "** under-run + jitter correction problem" + exit 3 + fi + ### FIXME: medium jitter is known to fail. Investigate. + ### FIXME: large jitter is known to fail. Investigate. + exit 0 +else + if test "@CMP@" != no ; then + echo "Don't see 'cmp' program. Test skipped." + else + echo "Don't see libcdio 'cd-paranoia' program. Test skipped." + fi + exit 77 +fi +fi +#;;; Local Variables: *** +#;;; mode:shell-script *** +#;;; eval: (sh-set-shell "bash") *** +#;;; End: *** + diff --git a/test/check_sizeof.c b/test/check_sizeof.c new file mode 100644 index 00000000..9c14d088 --- /dev/null +++ b/test/check_sizeof.c @@ -0,0 +1,76 @@ +/* + $Id: check_sizeof.c,v 1.5 2008/03/22 18:08:25 karl Exp $ + + Copyright (C) 2001, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include + +/* Private headers */ +#include "iso9660_private.h" + +#define CHECK_SIZEOF(typnam) { \ + printf ("checking sizeof (%s) ...", #typnam); \ + if (sizeof (typnam) != (typnam##_SIZEOF)) { \ + printf ("failed!\n==> sizeof (%s) == %d (but should be %d)\n", \ + #typnam, (int)sizeof(typnam), (int)(typnam##_SIZEOF)); \ + fail++; \ + } else { pass++; printf ("ok!\n"); } \ +} + +#define CHECK_SIZEOF_STRUCT(typnam) { \ + printf ("checking sizeof (struct %s) ...", #typnam); \ + if (sizeof (struct typnam) != (struct_##typnam##_SIZEOF)) { \ + printf ("failed!\n==> sizeof (struct %s) == %d (but should be %d)\n", \ + #typnam, (int)sizeof(struct typnam), (int)(struct_##typnam##_SIZEOF)); \ + fail++; \ + } else { pass++; printf ("ok!\n"); } \ +} + +int main (int argc, const char *argv[]) +{ + unsigned fail = 0, pass = 0; + + /* */ + CHECK_SIZEOF(msf_t); + + /* "iso9660_private.h" */ + CHECK_SIZEOF(iso_volume_descriptor_t); + CHECK_SIZEOF(iso9660_pvd_t); + CHECK_SIZEOF(iso_path_table_t); + CHECK_SIZEOF(iso9660_dir_t); + +#define iso9660_xa_t_SIZEOF 14 + + /* xa.h */ + CHECK_SIZEOF(iso9660_xa_t); + + if (fail) + return 1; + + return 0; +} diff --git a/test/copying-rr.gpl b/test/copying-rr.gpl new file mode 100644 index 00000000..d60c31a9 --- /dev/null +++ b/test/copying-rr.gpl @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/test/copying-rr.iso b/test/copying-rr.iso new file mode 100644 index 00000000..19429eb4 Binary files /dev/null and b/test/copying-rr.iso differ diff --git a/test/copying-rr.right b/test/copying-rr.right new file mode 100644 index 00000000..41115fd2 --- /dev/null +++ b/test/copying-rr.right @@ -0,0 +1,25 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ +ISO-9660 Information +/: + dr-xr-xr-x 4 0 0 [LSN 23] 2048 Oct 22 2004 02:21:14 . + dr-xr-xr-x 2 0 0 [LSN 23] 2048 Oct 22 2004 02:21:14 .. + dr-xr-xr-x 2 0 0 [LSN 24] 2048 Mar 05 2005 16:12:25 copy + lr-xr-xr-x 1 0 0 [LSN 27] 7 Mar 05 2005 15:26:00 Copy2 -> COPYING + -r--r--r-- 1 0 0 [LSN 27] 17992 Mar 05 2005 15:25:51 COPYING + br--r--r-- 1 0 0 [LSN 36] 0 Mar 05 2005 15:32:05 fd0 + dr-xr-xr-x 2 0 0 [LSN 25] 2048 Mar 05 2005 16:12:25 tmp + cr--r--r-- 1 0 0 [LSN 36] 0 Mar 05 2005 15:31:42 zero + +/copy/: + dr-xr-xr-x 2 0 0 [LSN 24] 2048 Mar 05 2005 16:12:25 . + dr-xr-xr-x 4 0 0 [LSN 23] 2048 Mar 05 2005 16:12:25 .. + lr-xr-xr-x 1 0 0 [LSN 36] 10 Mar 05 2005 15:27:05 COPYING -> ../COPYING + +/tmp/: + dr-xr-xr-x 2 0 0 [LSN 25] 2048 Mar 05 2005 16:12:25 . + dr-xr-xr-x 4 0 0 [LSN 23] 2048 Mar 05 2005 16:12:25 .. + lr-xr-xr-x 1 0 0 [LSN 36] 18 Mar 05 2005 15:51:05 COPYING -> ../copying/COPYING + diff --git a/test/copying.gpl b/test/copying.gpl new file mode 100644 index 00000000..623b6258 --- /dev/null +++ b/test/copying.gpl @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/test/copying.iso b/test/copying.iso new file mode 100644 index 00000000..5e57cd06 Binary files /dev/null and b/test/copying.iso differ diff --git a/test/copying.right b/test/copying.right new file mode 100644 index 00000000..492128ff --- /dev/null +++ b/test/copying.right @@ -0,0 +1,10 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ +ISO-9660 Information +/: + d [LSN 23] 2048 Jan 05 2006 21:50:19 . + d [LSN 23] 2048 Jan 05 2006 21:50:19 .. + - [LSN 24] 18002 Jan 05 2006 21:46:30 copying + diff --git a/test/data1.toc b/test/data1.toc new file mode 100644 index 00000000..9c5285cc --- /dev/null +++ b/test/data1.toc @@ -0,0 +1,8 @@ +// $Id: data1.toc,v 1.1 2004/05/08 20:36:02 rocky Exp $ +// Just a single MODE1 track. + +CD_ROM + +TRACK MODE1 +SILENCE 0:59:74 + diff --git a/test/data2.toc b/test/data2.toc new file mode 100644 index 00000000..8504c0f6 --- /dev/null +++ b/test/data2.toc @@ -0,0 +1,13 @@ +// $Id +// MODE1 track followed by an AUDIO track + +CD_ROM + +TRACK MODE1 +FILE "isofs-m1.bin" 00:00:00 +ZERO 1:0:0 + +TRACK AUDIO +PREGAP 0:2:0 +FILE "cdda.bin" 00:00:00 + diff --git a/test/data5.toc b/test/data5.toc new file mode 100644 index 00000000..a3d38b9c --- /dev/null +++ b/test/data5.toc @@ -0,0 +1,13 @@ +// $Id: data5.toc,v 1.2 2005/01/23 00:45:57 rocky Exp $ +// XA disk +// + +CD_ROM_XA + +TRACK MODE2_FORM2 +FILE "isofs-m1.bin" 00:00:00 00:13:57 + +TRACK MODE2_FORM1 +PREGAP 0:2:0 +FILE "isofs-m1.bin" 00:20:71 00:00:00 + diff --git a/test/data6.toc b/test/data6.toc new file mode 100644 index 00000000..eee4834f --- /dev/null +++ b/test/data6.toc @@ -0,0 +1,8 @@ +// $Id: data6.toc,v 1.2 2005/01/23 00:45:57 rocky Exp $ +// MODE2 track +// + +CD_ROM + +TRACK MODE2 +FILE "isofs-m1.bin" 00:00:00 00:13:57 diff --git a/test/data7.toc b/test/data7.toc new file mode 100644 index 00000000..61e4bea3 --- /dev/null +++ b/test/data7.toc @@ -0,0 +1,13 @@ +// $Id: data7.toc,v 1.2 2005/01/23 00:45:57 rocky Exp $ +// Video CD +// + +CD_ROM_XA + +TRACK MODE1 // ISO filesystem +FILE "isofs-m1.bin" 00:00:00 00:13:57 + +TRACK MODE2_FORM_MIX // XA track with form 1 and form 2 sectors +PREGAP 0:2:0 +FILE "isofs-m1.bin" 00:20:71 00:00:00 + diff --git a/test/fsf.right b/test/fsf.right new file mode 100644 index 00000000..7d1ebf9c --- /dev/null +++ b/test/fsf.right @@ -0,0 +1,12 @@ +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 - 1) + #: MSF LSN Type + 1: 00:02:00 000000 audio +170: 01:50:20 008120 leadout +__________________________________ +try to find out what sort of CD this is +Audio CD, CDDB disc ID is 02006c01 diff --git a/test/isofs-m1-no-rr.right b/test/isofs-m1-no-rr.right new file mode 100644 index 00000000..762202fe --- /dev/null +++ b/test/isofs-m1-no-rr.right @@ -0,0 +1,33 @@ +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 - 1) + #: MSF LSN Type Green? Copy? + 1: 00:02:00 000000 data false no +170: 00:06:02 000302 leadout (693 KB raw, 604 KB formatted) +Media Catalog Number (MCN): not available +Last CD Session LSN: not supported by drive/driver +__________________________________ +CD Analysis Report +CD-ROM with ISO 9660 filesystem +ISO 9660: 64 blocks, label `CDROM ' +Application: MKISOFS ISO 9660/HFS FILESYSTEM BUILDER & CDRECORD CD-R/DVD CREATOR (C) 1993 E.YOUNGDALE (C) 1997 J.PEARSON/J.SCHILLING +Preparer : +Publisher : +System : LINUX +Volume : CDROM +Volume Set : +ISO9660 filesystem +/: + d [LSN 23] 2048 Apr 20 2003 11:26:46 . + d [LSN 23] 2048 Apr 20 2003 11:26:46 .. + - [LSN 26] 17992 Jul 29 2002 12:39:53 copying + d [LSN 24] 2048 Apr 20 2003 16:18:53 doc + +/doc/: + d [LSN 24] 2048 Apr 20 2003 16:18:53 . + d [LSN 23] 2048 Apr 20 2003 11:26:46 .. + - [LSN 35] 648 Apr 20 2003 16:18:53 readme.txt + diff --git a/test/isofs-m1-read.right b/test/isofs-m1-read.right new file mode 100644 index 00000000..3cbf0442 --- /dev/null +++ b/test/isofs-m1-read.right @@ -0,0 +1,261 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +0x0000: 0909 2020 2020 474e 5520 4745 4e45 5241 .. GNU GENERA +0x0010: 4c20 5055 424c 4943 204c 4943 454e 5345 L PUBLIC LICENSE +0x0020: 0a09 0920 2020 2020 2020 5665 7273 696f ... Versio +0x0030: 6e20 322c 204a 756e 6520 3139 3931 0a0a n 2, June 1991.. +0x0040: 2043 6f70 7972 6967 6874 2028 4329 2031 Copyright (C) 1 +0x0050: 3938 392c 2031 3939 3120 4672 6565 2053 989, 1991 Free S +0x0060: 6f66 7477 6172 6520 466f 756e 6461 7469 oftware Foundati +0x0070: 6f6e 2c20 496e 632e 0a20 2020 2020 3539 on, Inc.. 59 +0x0080: 2054 656d 706c 6520 506c 6163 652c 2053 Temple Place, S +0x0090: 7569 7465 2033 3330 2c20 426f 7374 6f6e uite 330, Boston +0x00a0: 2c20 4d41 2020 3032 3131 312d 3133 3037 , MA 02111-1307 +0x00b0: 2020 5553 410a 2045 7665 7279 6f6e 6520 USA. Everyone +0x00c0: 6973 2070 6572 6d69 7474 6564 2074 6f20 is permitted to +0x00d0: 636f 7079 2061 6e64 2064 6973 7472 6962 copy and distrib +0x00e0: 7574 6520 7665 7262 6174 696d 2063 6f70 ute verbatim cop +0x00f0: 6965 730a 206f 6620 7468 6973 206c 6963 ies. of this lic +0x0100: 656e 7365 2064 6f63 756d 656e 742c 2062 ense document, b +0x0110: 7574 2063 6861 6e67 696e 6720 6974 2069 ut changing it i +0x0120: 7320 6e6f 7420 616c 6c6f 7765 642e 0a0a s not allowed... +0x0130: 0909 0920 2020 2050 7265 616d 626c 650a ... Preamble. +0x0140: 0a20 2054 6865 206c 6963 656e 7365 7320 . The licenses +0x0150: 666f 7220 6d6f 7374 2073 6f66 7477 6172 for most softwar +0x0160: 6520 6172 6520 6465 7369 676e 6564 2074 e are designed t +0x0170: 6f20 7461 6b65 2061 7761 7920 796f 7572 o take away your +0x0180: 0a66 7265 6564 6f6d 2074 6f20 7368 6172 .freedom to shar +0x0190: 6520 616e 6420 6368 616e 6765 2069 742e e and change it. +0x01a0: 2020 4279 2063 6f6e 7472 6173 742c 2074 By contrast, t +0x01b0: 6865 2047 4e55 2047 656e 6572 616c 2050 he GNU General P +0x01c0: 7562 6c69 630a 4c69 6365 6e73 6520 6973 ublic.License is +0x01d0: 2069 6e74 656e 6465 6420 746f 2067 7561 intended to gua +0x01e0: 7261 6e74 6565 2079 6f75 7220 6672 6565 rantee your free +0x01f0: 646f 6d20 746f 2073 6861 7265 2061 6e64 dom to share and +0x0200: 2063 6861 6e67 6520 6672 6565 0a73 6f66 change free.sof +0x0210: 7477 6172 652d 2d74 6f20 6d61 6b65 2073 tware--to make s +0x0220: 7572 6520 7468 6520 736f 6674 7761 7265 ure the software +0x0230: 2069 7320 6672 6565 2066 6f72 2061 6c6c is free for all +0x0240: 2069 7473 2075 7365 7273 2e20 2054 6869 its users. Thi +0x0250: 730a 4765 6e65 7261 6c20 5075 626c 6963 s.General Public +0x0260: 204c 6963 656e 7365 2061 7070 6c69 6573 License applies +0x0270: 2074 6f20 6d6f 7374 206f 6620 7468 6520 to most of the +0x0280: 4672 6565 2053 6f66 7477 6172 650a 466f Free Software.Fo +0x0290: 756e 6461 7469 6f6e 2773 2073 6f66 7477 undation's softw +0x02a0: 6172 6520 616e 6420 746f 2061 6e79 206f are and to any o +0x02b0: 7468 6572 2070 726f 6772 616d 2077 686f ther program who +0x02c0: 7365 2061 7574 686f 7273 2063 6f6d 6d69 se authors commi +0x02d0: 7420 746f 0a75 7369 6e67 2069 742e 2020 t to.using it. +0x02e0: 2853 6f6d 6520 6f74 6865 7220 4672 6565 (Some other Free +0x02f0: 2053 6f66 7477 6172 6520 466f 756e 6461 Software Founda +0x0300: 7469 6f6e 2073 6f66 7477 6172 6520 6973 tion software is +0x0310: 2063 6f76 6572 6564 2062 790a 7468 6520 covered by.the +0x0320: 474e 5520 4c69 6272 6172 7920 4765 6e65 GNU Library Gene +0x0330: 7261 6c20 5075 626c 6963 204c 6963 656e ral Public Licen +0x0340: 7365 2069 6e73 7465 6164 2e29 2020 596f se instead.) Yo +0x0350: 7520 6361 6e20 6170 706c 7920 6974 2074 u can apply it t +0x0360: 6f0a 796f 7572 2070 726f 6772 616d 732c o.your programs, +0x0370: 2074 6f6f 2e0a 0a20 2057 6865 6e20 7765 too... When we +0x0380: 2073 7065 616b 206f 6620 6672 6565 2073 speak of free s +0x0390: 6f66 7477 6172 652c 2077 6520 6172 6520 oftware, we are +0x03a0: 7265 6665 7272 696e 6720 746f 2066 7265 referring to fre +0x03b0: 6564 6f6d 2c20 6e6f 740a 7072 6963 652e edom, not.price. +0x03c0: 2020 4f75 7220 4765 6e65 7261 6c20 5075 Our General Pu +0x03d0: 626c 6963 204c 6963 656e 7365 7320 6172 blic Licenses ar +0x03e0: 6520 6465 7369 676e 6564 2074 6f20 6d61 e designed to ma +0x03f0: 6b65 2073 7572 6520 7468 6174 2079 6f75 ke sure that you +0x0400: 0a68 6176 6520 7468 6520 6672 6565 646f .have the freedo +0x0410: 6d20 746f 2064 6973 7472 6962 7574 6520 m to distribute +0x0420: 636f 7069 6573 206f 6620 6672 6565 2073 copies of free s +0x0430: 6f66 7477 6172 6520 2861 6e64 2063 6861 oftware (and cha +0x0440: 7267 6520 666f 720a 7468 6973 2073 6572 rge for.this ser +0x0450: 7669 6365 2069 6620 796f 7520 7769 7368 vice if you wish +0x0460: 292c 2074 6861 7420 796f 7520 7265 6365 ), that you rece +0x0470: 6976 6520 736f 7572 6365 2063 6f64 6520 ive source code +0x0480: 6f72 2063 616e 2067 6574 2069 740a 6966 or can get it.if +0x0490: 2079 6f75 2077 616e 7420 6974 2c20 7468 you want it, th +0x04a0: 6174 2079 6f75 2063 616e 2063 6861 6e67 at you can chang +0x04b0: 6520 7468 6520 736f 6674 7761 7265 206f e the software o +0x04c0: 7220 7573 6520 7069 6563 6573 206f 6620 r use pieces of +0x04d0: 6974 0a69 6e20 6e65 7720 6672 6565 2070 it.in new free p +0x04e0: 726f 6772 616d 733b 2061 6e64 2074 6861 rograms; and tha +0x04f0: 7420 796f 7520 6b6e 6f77 2079 6f75 2063 t you know you c +0x0500: 616e 2064 6f20 7468 6573 6520 7468 696e an do these thin +0x0510: 6773 2e0a 0a20 2054 6f20 7072 6f74 6563 gs... To protec +0x0520: 7420 796f 7572 2072 6967 6874 732c 2077 t your rights, w +0x0530: 6520 6e65 6564 2074 6f20 6d61 6b65 2072 e need to make r +0x0540: 6573 7472 6963 7469 6f6e 7320 7468 6174 estrictions that +0x0550: 2066 6f72 6269 640a 616e 796f 6e65 2074 forbid.anyone t +0x0560: 6f20 6465 6e79 2079 6f75 2074 6865 7365 o deny you these +0x0570: 2072 6967 6874 7320 6f72 2074 6f20 6173 rights or to as +0x0580: 6b20 796f 7520 746f 2073 7572 7265 6e64 k you to surrend +0x0590: 6572 2074 6865 2072 6967 6874 732e 0a54 er the rights..T +0x05a0: 6865 7365 2072 6573 7472 6963 7469 6f6e hese restriction +0x05b0: 7320 7472 616e 736c 6174 6520 746f 2063 s translate to c +0x05c0: 6572 7461 696e 2072 6573 706f 6e73 6962 ertain responsib +0x05d0: 696c 6974 6965 7320 666f 7220 796f 7520 ilities for you +0x05e0: 6966 2079 6f75 0a64 6973 7472 6962 7574 if you.distribut +0x05f0: 6520 636f 7069 6573 206f 6620 7468 6520 e copies of the +0x0600: 736f 6674 7761 7265 2c20 6f72 2069 6620 software, or if +0x0610: 796f 7520 6d6f 6469 6679 2069 742e 0a0a you modify it... +0x0620: 2020 466f 7220 6578 616d 706c 652c 2069 For example, i +0x0630: 6620 796f 7520 6469 7374 7269 6275 7465 f you distribute +0x0640: 2063 6f70 6965 7320 6f66 2073 7563 6820 copies of such +0x0650: 6120 7072 6f67 7261 6d2c 2077 6865 7468 a program, wheth +0x0660: 6572 0a67 7261 7469 7320 6f72 2066 6f72 er.gratis or for +0x0670: 2061 2066 6565 2c20 796f 7520 6d75 7374 a fee, you must +0x0680: 2067 6976 6520 7468 6520 7265 6369 7069 give the recipi +0x0690: 656e 7473 2061 6c6c 2074 6865 2072 6967 ents all the rig +0x06a0: 6874 7320 7468 6174 0a79 6f75 2068 6176 hts that.you hav +0x06b0: 652e 2020 596f 7520 6d75 7374 206d 616b e. You must mak +0x06c0: 6520 7375 7265 2074 6861 7420 7468 6579 e sure that they +0x06d0: 2c20 746f 6f2c 2072 6563 6569 7665 206f , too, receive o +0x06e0: 7220 6361 6e20 6765 7420 7468 650a 736f r can get the.so +0x06f0: 7572 6365 2063 6f64 652e 2020 416e 6420 urce code. And +0x0700: 796f 7520 6d75 7374 2073 686f 7720 7468 you must show th +0x0710: 656d 2074 6865 7365 2074 6572 6d73 2073 em these terms s +0x0720: 6f20 7468 6579 206b 6e6f 7720 7468 6569 o they know thei +0x0730: 720a 7269 6768 7473 2e0a 0a20 2057 6520 r.rights... We +0x0740: 7072 6f74 6563 7420 796f 7572 2072 6967 protect your rig +0x0750: 6874 7320 7769 7468 2074 776f 2073 7465 hts with two ste +0x0760: 7073 3a20 2831 2920 636f 7079 7269 6768 ps: (1) copyrigh +0x0770: 7420 7468 6520 736f 6674 7761 7265 2c20 t the software, +0x0780: 616e 640a 2832 2920 6f66 6665 7220 796f and.(2) offer yo +0x0790: 7520 7468 6973 206c 6963 656e 7365 2077 u this license w +0x07a0: 6869 6368 2067 6976 6573 2079 6f75 206c hich gives you l +0x07b0: 6567 616c 2070 6572 6d69 7373 696f 6e20 egal permission +0x07c0: 746f 2063 6f70 792c 0a64 6973 7472 6962 to copy,.distrib +0x07d0: 7574 6520 616e 642f 6f72 206d 6f64 6966 ute and/or modif +0x07e0: 7920 7468 6520 736f 6674 7761 7265 2e0a y the software.. +0x07f0: 0a20 2041 6c73 6f2c 2066 6f72 2065 6163 . Also, for eac + +0x0000: 6820 6175 7468 6f72 2773 2070 726f 7465 h author's prote +0x0010: 6374 696f 6e20 616e 6420 6f75 7273 2c20 ction and ours, +0x0020: 7765 2077 616e 7420 746f 206d 616b 6520 we want to make +0x0030: 6365 7274 6169 6e0a 7468 6174 2065 7665 certain.that eve +0x0040: 7279 6f6e 6520 756e 6465 7273 7461 6e64 ryone understand +0x0050: 7320 7468 6174 2074 6865 7265 2069 7320 s that there is +0x0060: 6e6f 2077 6172 7261 6e74 7920 666f 7220 no warranty for +0x0070: 7468 6973 2066 7265 650a 736f 6674 7761 this free.softwa +0x0080: 7265 2e20 2049 6620 7468 6520 736f 6674 re. If the soft +0x0090: 7761 7265 2069 7320 6d6f 6469 6669 6564 ware is modified +0x00a0: 2062 7920 736f 6d65 6f6e 6520 656c 7365 by someone else +0x00b0: 2061 6e64 2070 6173 7365 6420 6f6e 2c20 and passed on, +0x00c0: 7765 0a77 616e 7420 6974 7320 7265 6369 we.want its reci +0x00d0: 7069 656e 7473 2074 6f20 6b6e 6f77 2074 pients to know t +0x00e0: 6861 7420 7768 6174 2074 6865 7920 6861 hat what they ha +0x00f0: 7665 2069 7320 6e6f 7420 7468 6520 6f72 ve is not the or +0x0100: 6967 696e 616c 2c20 736f 0a74 6861 7420 iginal, so.that +0x0110: 616e 7920 7072 6f62 6c65 6d73 2069 6e74 any problems int +0x0120: 726f 6475 6365 6420 6279 206f 7468 6572 roduced by other +0x0130: 7320 7769 6c6c 206e 6f74 2072 6566 6c65 s will not refle +0x0140: 6374 206f 6e20 7468 6520 6f72 6967 696e ct on the origin +0x0150: 616c 0a61 7574 686f 7273 2720 7265 7075 al.authors' repu +0x0160: 7461 7469 6f6e 732e 0a0a 2020 4669 6e61 tations... Fina +0x0170: 6c6c 792c 2061 6e79 2066 7265 6520 7072 lly, any free pr +0x0180: 6f67 7261 6d20 6973 2074 6872 6561 7465 ogram is threate +0x0190: 6e65 6420 636f 6e73 7461 6e74 6c79 2062 ned constantly b +0x01a0: 7920 736f 6674 7761 7265 0a70 6174 656e y software.paten +0x01b0: 7473 2e20 2057 6520 7769 7368 2074 6f20 ts. We wish to +0x01c0: 6176 6f69 6420 7468 6520 6461 6e67 6572 avoid the danger +0x01d0: 2074 6861 7420 7265 6469 7374 7269 6275 that redistribu +0x01e0: 746f 7273 206f 6620 6120 6672 6565 0a70 tors of a free.p +0x01f0: 726f 6772 616d 2077 696c 6c20 696e 6469 rogram will indi +0x0200: 7669 6475 616c 6c79 206f 6274 6169 6e20 vidually obtain +0x0210: 7061 7465 6e74 206c 6963 656e 7365 732c patent licenses, +0x0220: 2069 6e20 6566 6665 6374 206d 616b 696e in effect makin +0x0230: 6720 7468 650a 7072 6f67 7261 6d20 7072 g the.program pr +0x0240: 6f70 7269 6574 6172 792e 2020 546f 2070 oprietary. To p +0x0250: 7265 7665 6e74 2074 6869 732c 2077 6520 revent this, we +0x0260: 6861 7665 206d 6164 6520 6974 2063 6c65 have made it cle +0x0270: 6172 2074 6861 7420 616e 790a 7061 7465 ar that any.pate +0x0280: 6e74 206d 7573 7420 6265 206c 6963 656e nt must be licen +0x0290: 7365 6420 666f 7220 6576 6572 796f 6e65 sed for everyone +0x02a0: 2773 2066 7265 6520 7573 6520 6f72 206e 's free use or n +0x02b0: 6f74 206c 6963 656e 7365 6420 6174 2061 ot licensed at a +0x02c0: 6c6c 2e0a 0a20 2054 6865 2070 7265 6369 ll... The preci +0x02d0: 7365 2074 6572 6d73 2061 6e64 2063 6f6e se terms and con +0x02e0: 6469 7469 6f6e 7320 666f 7220 636f 7079 ditions for copy +0x02f0: 696e 672c 2064 6973 7472 6962 7574 696f ing, distributio +0x0300: 6e20 616e 640a 6d6f 6469 6669 6361 7469 n and.modificati +0x0310: 6f6e 2066 6f6c 6c6f 772e 0a0c 0a09 0920 on follow...... +0x0320: 2020 2047 4e55 2047 454e 4552 414c 2050 GNU GENERAL P +0x0330: 5542 4c49 4320 4c49 4345 4e53 450a 2020 UBLIC LICENSE. +0x0340: 2054 4552 4d53 2041 4e44 2043 4f4e 4449 TERMS AND CONDI +0x0350: 5449 4f4e 5320 464f 5220 434f 5059 494e TIONS FOR COPYIN +0x0360: 472c 2044 4953 5452 4942 5554 494f 4e20 G, DISTRIBUTION +0x0370: 414e 4420 4d4f 4449 4649 4341 5449 4f4e AND MODIFICATION +0x0380: 0a0a 2020 302e 2054 6869 7320 4c69 6365 .. 0. This Lice +0x0390: 6e73 6520 6170 706c 6965 7320 746f 2061 nse applies to a +0x03a0: 6e79 2070 726f 6772 616d 206f 7220 6f74 ny program or ot +0x03b0: 6865 7220 776f 726b 2077 6869 6368 2063 her work which c +0x03c0: 6f6e 7461 696e 730a 6120 6e6f 7469 6365 ontains.a notice +0x03d0: 2070 6c61 6365 6420 6279 2074 6865 2063 placed by the c +0x03e0: 6f70 7972 6967 6874 2068 6f6c 6465 7220 opyright holder +0x03f0: 7361 7969 6e67 2069 7420 6d61 7920 6265 saying it may be +0x0400: 2064 6973 7472 6962 7574 6564 0a75 6e64 distributed.und +0x0410: 6572 2074 6865 2074 6572 6d73 206f 6620 er the terms of +0x0420: 7468 6973 2047 656e 6572 616c 2050 7562 this General Pub +0x0430: 6c69 6320 4c69 6365 6e73 652e 2020 5468 lic License. Th +0x0440: 6520 2250 726f 6772 616d 222c 2062 656c e "Program", bel +0x0450: 6f77 2c0a 7265 6665 7273 2074 6f20 616e ow,.refers to an +0x0460: 7920 7375 6368 2070 726f 6772 616d 206f y such program o +0x0470: 7220 776f 726b 2c20 616e 6420 6120 2277 r work, and a "w +0x0480: 6f72 6b20 6261 7365 6420 6f6e 2074 6865 ork based on the +0x0490: 2050 726f 6772 616d 220a 6d65 616e 7320 Program".means +0x04a0: 6569 7468 6572 2074 6865 2050 726f 6772 either the Progr +0x04b0: 616d 206f 7220 616e 7920 6465 7269 7661 am or any deriva +0x04c0: 7469 7665 2077 6f72 6b20 756e 6465 7220 tive work under +0x04d0: 636f 7079 7269 6768 7420 6c61 773a 0a74 copyright law:.t +0x04e0: 6861 7420 6973 2074 6f20 7361 792c 2061 hat is to say, a +0x04f0: 2077 6f72 6b20 636f 6e74 6169 6e69 6e67 work containing +0x0500: 2074 6865 2050 726f 6772 616d 206f 7220 the Program or +0x0510: 6120 706f 7274 696f 6e20 6f66 2069 742c a portion of it, +0x0520: 0a65 6974 6865 7220 7665 7262 6174 696d .either verbatim +0x0530: 206f 7220 7769 7468 206d 6f64 6966 6963 or with modific +0x0540: 6174 696f 6e73 2061 6e64 2f6f 7220 7472 ations and/or tr +0x0550: 616e 736c 6174 6564 2069 6e74 6f20 616e anslated into an +0x0560: 6f74 6865 720a 6c61 6e67 7561 6765 2e20 other.language. +0x0570: 2028 4865 7265 696e 6166 7465 722c 2074 (Hereinafter, t +0x0580: 7261 6e73 6c61 7469 6f6e 2069 7320 696e ranslation is in +0x0590: 636c 7564 6564 2077 6974 686f 7574 206c cluded without l +0x05a0: 696d 6974 6174 696f 6e20 696e 0a74 6865 imitation in.the +0x05b0: 2074 6572 6d20 226d 6f64 6966 6963 6174 term "modificat +0x05c0: 696f 6e22 2e29 2020 4561 6368 206c 6963 ion".) Each lic +0x05d0: 656e 7365 6520 6973 2061 6464 7265 7373 ensee is address +0x05e0: 6564 2061 7320 2279 6f75 222e 0a0a 4163 ed as "you"...Ac +0x05f0: 7469 7669 7469 6573 206f 7468 6572 2074 tivities other t +0x0600: 6861 6e20 636f 7079 696e 672c 2064 6973 han copying, dis +0x0610: 7472 6962 7574 696f 6e20 616e 6420 6d6f tribution and mo +0x0620: 6469 6669 6361 7469 6f6e 2061 7265 206e dification are n +0x0630: 6f74 0a63 6f76 6572 6564 2062 7920 7468 ot.covered by th +0x0640: 6973 204c 6963 656e 7365 3b20 7468 6579 is License; they +0x0650: 2061 7265 206f 7574 7369 6465 2069 7473 are outside its +0x0660: 2073 636f 7065 2e20 2054 6865 2061 6374 scope. The act +0x0670: 206f 660a 7275 6e6e 696e 6720 7468 6520 of.running the +0x0680: 5072 6f67 7261 6d20 6973 206e 6f74 2072 Program is not r +0x0690: 6573 7472 6963 7465 642c 2061 6e64 2074 estricted, and t +0x06a0: 6865 206f 7574 7075 7420 6672 6f6d 2074 he output from t +0x06b0: 6865 2050 726f 6772 616d 0a69 7320 636f he Program.is co +0x06c0: 7665 7265 6420 6f6e 6c79 2069 6620 6974 vered only if it +0x06d0: 7320 636f 6e74 656e 7473 2063 6f6e 7374 s contents const +0x06e0: 6974 7574 6520 6120 776f 726b 2062 6173 itute a work bas +0x06f0: 6564 206f 6e20 7468 650a 5072 6f67 7261 ed on the.Progra +0x0700: 6d20 2869 6e64 6570 656e 6465 6e74 206f m (independent o +0x0710: 6620 6861 7669 6e67 2062 6565 6e20 6d61 f having been ma +0x0720: 6465 2062 7920 7275 6e6e 696e 6720 7468 de by running th +0x0730: 6520 5072 6f67 7261 6d29 2e0a 5768 6574 e Program)..Whet +0x0740: 6865 7220 7468 6174 2069 7320 7472 7565 her that is true +0x0750: 2064 6570 656e 6473 206f 6e20 7768 6174 depends on what +0x0760: 2074 6865 2050 726f 6772 616d 2064 6f65 the Program doe +0x0770: 732e 0a0a 2020 312e 2059 6f75 206d 6179 s... 1. You may +0x0780: 2063 6f70 7920 616e 6420 6469 7374 7269 copy and distri +0x0790: 6275 7465 2076 6572 6261 7469 6d20 636f bute verbatim co +0x07a0: 7069 6573 206f 6620 7468 6520 5072 6f67 pies of the Prog +0x07b0: 7261 6d27 730a 736f 7572 6365 2063 6f64 ram's.source cod +0x07c0: 6520 6173 2079 6f75 2072 6563 6569 7665 e as you receive +0x07d0: 2069 742c 2069 6e20 616e 7920 6d65 6469 it, in any medi +0x07e0: 756d 2c20 7072 6f76 6964 6564 2074 6861 um, provided tha +0x07f0: 7420 796f 750a 636f 6e73 7069 6375 6f75 t you.conspicuou + diff --git a/test/isofs-m1.bin b/test/isofs-m1.bin new file mode 100644 index 00000000..adaea5c3 Binary files /dev/null and b/test/isofs-m1.bin differ diff --git a/test/isofs-m1.cue b/test/isofs-m1.cue new file mode 100644 index 00000000..677a803f --- /dev/null +++ b/test/isofs-m1.cue @@ -0,0 +1,3 @@ +FILE "ISOFS-M1.BIN" BINARY + TRACK 01 MODE1/2352 + INDEX 01 00:00:00 diff --git a/test/isofs-m1.right b/test/isofs-m1.right new file mode 100644 index 00000000..312dc7a0 --- /dev/null +++ b/test/isofs-m1.right @@ -0,0 +1,33 @@ +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 - 1) + #: MSF LSN Type Green? Copy? + 1: 00:02:00 000000 data false no +170: 00:06:02 000302 leadout (693 KB raw, 604 KB formatted) +Media Catalog Number (MCN): not available +Last CD Session LSN: not supported by drive/driver +__________________________________ +CD Analysis Report +CD-ROM with ISO 9660 filesystem +ISO 9660: 64 blocks, label `CDROM ' +Application: MKISOFS ISO 9660/HFS FILESYSTEM BUILDER & CDRECORD CD-R/DVD CREATOR (C) 1993 E.YOUNGDALE (C) 1997 J.PEARSON/J.SCHILLING +Preparer : +Publisher : +System : LINUX +Volume : CDROM +Volume Set : +ISO9660 filesystem +/: + drwxrwxr-x 3 715 715 [LSN 23] 2048 Apr 20 2003 11:26:46 . + drwxrwxr-x 3 715 715 [LSN 23] 2048 Apr 20 2003 11:26:46 .. + -rw-r--r-- 1 715 715 [LSN 26] 17992 Jul 29 2002 12:39:53 COPYING + drwxrwxr-x 2 715 715 [LSN 24] 2048 Apr 20 2003 16:18:53 doc + +/doc/: + drwxrwxr-x 2 715 715 [LSN 24] 2048 Apr 20 2003 16:18:53 . + drwxrwxr-x 3 715 715 [LSN 23] 2048 Apr 20 2003 11:26:46 .. + -rw-rw-r-- 1 715 715 [LSN 35] 648 Apr 20 2003 16:18:53 readme.txt + diff --git a/test/isofs-m1.toc b/test/isofs-m1.toc new file mode 100644 index 00000000..b21572f7 --- /dev/null +++ b/test/isofs-m1.toc @@ -0,0 +1,4 @@ +CD_ROM + +TRACK MODE1_RAW +FILE "isofs-m1.bin" 00:00:00 00:00:00 diff --git a/test/joliet-nojoliet.right b/test/joliet-nojoliet.right new file mode 100644 index 00000000..e942d4c2 --- /dev/null +++ b/test/joliet-nojoliet.right @@ -0,0 +1,23 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ +ISO-9660 Information +/: + d [LSN 28] 2048 Oct 22 2004 22:44:59 . + d [LSN 28] 2048 Oct 22 2004 22:44:59 .. + d [LSN 29] 2048 Oct 22 2004 22:44:59 libcdio + +/libcdio/: + d [LSN 29] 2048 Oct 22 2004 22:44:59 . + d [LSN 28] 2048 Oct 22 2004 22:44:59 .. + - [LSN 34] 17992 Mar 12 2004 07:18:03 copying + - [LSN 43] 2156 Jun 26 2004 10:01:09 readme + - [LSN 45] 2849 Aug 12 2004 09:22:23 readme.libcdio + d [LSN 30] 2048 Oct 22 2004 22:44:59 test + +/libcdio/test/: + d [LSN 30] 2048 Oct 22 2004 22:44:59 . + d [LSN 29] 2048 Oct 22 2004 22:44:59 .. + - [LSN 47] 74 Jul 25 2004 09:52:32 isofs_m1.cue + diff --git a/test/joliet.iso b/test/joliet.iso new file mode 100644 index 00000000..00071060 Binary files /dev/null and b/test/joliet.iso differ diff --git a/test/joliet.right b/test/joliet.right new file mode 100644 index 00000000..8733d114 --- /dev/null +++ b/test/joliet.right @@ -0,0 +1,23 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ +ISO-9660 Information +/: + d [LSN 31] 2048 Oct 22 2004 22:44:59 . + d [LSN 31] 2048 Oct 22 2004 22:44:59 .. + d [LSN 32] 2048 Oct 22 2004 22:44:59 libcdio + +/libcdio/: + d [LSN 32] 2048 Oct 22 2004 22:44:59 . + d [LSN 31] 2048 Oct 22 2004 22:44:59 .. + - [LSN 34] 17992 Mar 12 2004 07:18:03 COPYING + - [LSN 43] 2156 Jun 26 2004 10:01:09 README + - [LSN 45] 2849 Aug 12 2004 09:22:23 README.libcdio + d [LSN 33] 2048 Oct 22 2004 22:44:59 test + +/libcdio/test/: + d [LSN 33] 2048 Oct 22 2004 22:44:59 . + d [LSN 32] 2048 Oct 22 2004 22:44:59 .. + - [LSN 47] 74 Jul 25 2004 09:52:32 isofs-m1.cue + diff --git a/test/monvoisin.right b/test/monvoisin.right new file mode 100644 index 00000000..c10fb230 --- /dev/null +++ b/test/monvoisin.right @@ -0,0 +1,68 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ + +Disc mode is listed as: CD DATA (Mode 2) +CD-ROM Track List (1 - 2) + #: MSF LSN Type Green? Copy? + 1: 00:02:00 000000 XA true no + 2: 00:18:51 001251 XA true no +170: 00:39:71 002846 leadout (6 MB raw, 6 MB formatted) +Media Catalog Number (MCN): not available +Last CD Session LSN: not supported by drive/driver +__________________________________ +CD Analysis Report +CD-ROM with CD-RTOS and ISO 9660 filesystem +ISO 9660: 1101 blocks, label `MONVOISIN ' +Application: CDI/CDI_VCD.APP;1 +Preparer : +Publisher : +System : CD-RTOS CD-BRIDGE +Volume : MONVOISIN +Volume Set : +ISO9660 filesystem +/: + d---1xrxrxr 0 0 [fn 00] [LSN 18] 2048 Jan 01 1970 00:00:00 . + d---1xrxrxr 0 0 [fn 00] [LSN 18] 2048 Jan 01 1970 00:00:00 .. + d---1xrxrxr 0 0 [fn 00] [LSN 19] 2048 Feb 19 2001 15:37:04 cdi + d---1xrxrxr 0 0 [fn 00] [LSN 20] 2048 Feb 19 2001 15:37:04 ext + d---1xrxrxr 0 0 [fn 00] [LSN 21] 2048 Feb 19 2001 15:37:04 mpegav + d---1xrxrxr 0 0 [fn 00] [LSN 22] 2048 Feb 19 2001 15:37:04 segment + d---1xrxrxr 0 0 [fn 00] [LSN 23] 2048 Feb 19 2001 15:37:04 vcd + +/CDI/: + d---1xrxrxr 0 0 [fn 00] [LSN 19] 2048 Jan 01 1970 00:00:00 . + d---1xrxrxr 0 0 [fn 00] [LSN 18] 2048 Jan 01 1970 00:00:00 .. + ---2-xrxrxr 0 0 [fn 01] [LSN 211] 1494332 ( 1315168) Feb 19 2001 15:37:04 cdi_imag.rtf + ----1xrxrxr 0 0 [fn 01] [LSN 854] 13616 Feb 19 2001 15:37:04 cdi_text.fnt + ----1xrxrxr 0 0 [fn 01] [LSN 861] 102400 Feb 19 2001 15:37:04 cdi_vcd.app + ----1xrxrxr 0 0 [fn 01] [LSN 911] 279 Feb 19 2001 15:37:04 cdi_vcd.cfg + +/EXT/: + d---1xrxrxr 0 0 [fn 00] [LSN 20] 2048 Jan 01 1970 00:00:00 . + d---1xrxrxr 0 0 [fn 00] [LSN 18] 2048 Jan 01 1970 00:00:00 .. + ----1xrxrxr 0 0 [fn 01] [LSN 912] 65536 Feb 19 2001 15:37:04 lot_x.vcd + ----1xrxrxr 0 0 [fn 01] [LSN 944] 24 Feb 19 2001 15:37:04 psd_x.vcd + ----1xrxrxr 0 0 [fn 01] [LSN 945] 114 Feb 19 2001 15:37:04 scandata.dat + +/MPEGAV/: + d---1xrxrxr 0 0 [fn 00] [LSN 21] 2048 Jan 01 1970 00:00:00 . + d---1xrxrxr 0 0 [fn 00] [LSN 18] 2048 Jan 01 1970 00:00:00 .. + ---2-xrxrxr 0 0 [fn 01] [LSN 1251] 3655652 ( 3221504) Feb 19 2001 15:37:59 avseq01.dat + +/SEGMENT/: + d---1xrxrxr 0 0 [fn 00] [LSN 22] 2048 Jan 01 1970 00:00:00 . + d---1xrxrxr 0 0 [fn 00] [LSN 18] 2048 Jan 01 1970 00:00:00 .. + +/VCD/: + d---1xrxrxr 0 0 [fn 00] [LSN 23] 2048 Jan 01 1970 00:00:00 . + d---1xrxrxr 0 0 [fn 00] [LSN 18] 2048 Jan 01 1970 00:00:00 .. + ----1xrxrxr 0 0 [fn 00] [LSN 151] 2048 Feb 19 2001 15:37:04 entries.vcd + ----1xrxrxr 0 0 [fn 00] [LSN 150] 2048 Feb 19 2001 15:37:04 info.vcd + ----1xrxrxr 0 0 [fn 00] [LSN 152] 65536 Feb 19 2001 15:37:04 lot.vcd + ----1xrxrxr 0 0 [fn 00] [LSN 184] 24 Feb 19 2001 15:37:04 psd.vcd + +XA sectors Video CD +session #2 starts at track 2, LSN: 1251, ISO 9660 blocks: 1101 +ISO 9660: 1101 blocks, label `MONVOISIN ' diff --git a/test/p1.bin b/test/p1.bin new file mode 100644 index 00000000..b1ba1f5e Binary files /dev/null and b/test/p1.bin differ diff --git a/test/p1.cue b/test/p1.cue new file mode 100644 index 00000000..40855d6e --- /dev/null +++ b/test/p1.cue @@ -0,0 +1,12 @@ +TITLE "Join us now we have the software" +CATALOG 0000010271955 +PERFORMER "Richard Stallman" +FILE "BOING.BIN" BINARY + TRACK 01 AUDIO + FLAGS DCP + INDEX 00 00:00:00 + INDEX 01 00:01:00 + TRACK 02 AUDIO + FLAGS DCP + INDEX 00 00:02:00 + INDEX 01 00:03:00 diff --git a/test/p1.nrg b/test/p1.nrg new file mode 100644 index 00000000..ba76e8be Binary files /dev/null and b/test/p1.nrg differ diff --git a/test/svcd_ogt_test_ntsc.right b/test/svcd_ogt_test_ntsc.right new file mode 100644 index 00000000..e7e30b53 --- /dev/null +++ b/test/svcd_ogt_test_ntsc.right @@ -0,0 +1,59 @@ +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 Green? Copy? + 1: 00:02:00 000000 XA true yes + 2: 00:09:01 000526 XA true yes +170: 00:56:56 004106 leadout (9 MB raw, 9 MB formatted) +Media Catalog Number (MCN): not available +Last CD Session LSN: not supported by drive/driver +__________________________________ +CD Analysis Report +CD-ROM with CD-RTOS and ISO 9660 filesystem +ISO 9660: 376 blocks, label `SVCD_OGT_TEST_NTSC ' +Application: +Preparer : GNU VCDIMAGER 0.7.12 LINUX-GNU/I386 +Publisher : +System : CD-RTOS CD-BRIDGE +Volume : SVCD_OGT_TEST_NTSC +Volume Set : +ISO9660 filesystem +/: + d---1xrxrxr 0 0 [fn 00] [LSN 18] 2048 Jul 14 1978 00:00:00 . + d---1xrxrxr 0 0 [fn 00] [LSN 18] 2048 Jul 14 1978 00:00:00 .. + d---1xrxrxr 0 0 [fn 00] [LSN 19] 2048 Jul 14 1978 00:00:00 ext + d---1xrxrxr 0 0 [fn 00] [LSN 20] 2048 Jul 14 1978 00:00:00 mpeg2 + d---1xrxrxr 0 0 [fn 00] [LSN 21] 2048 Jul 14 1978 00:00:00 segment + d---1xrxrxr 0 0 [fn 00] [LSN 22] 2048 Jul 14 1978 00:00:00 svcd + +/EXT/: + d---1xrxrxr 0 0 [fn 00] [LSN 19] 2048 Jul 14 1978 00:00:00 . + d---1xrxrxr 0 0 [fn 00] [LSN 18] 2048 Jul 14 1978 00:00:00 .. + ----1xrxrxr 0 0 [fn 00] [LSN 375] 201 Jul 14 1978 00:00:00 scandata.dat + +/MPEG2/: + d---1xrxrxr 0 0 [fn 00] [LSN 20] 2048 Jul 14 1978 00:00:00 . + d---1xrxrxr 0 0 [fn 00] [LSN 18] 2048 Jul 14 1978 00:00:00 .. + ---2-xrxrxr 0 0 [fn 00] [LSN 526] 7971320 ( 7024640) Jul 14 1978 00:00:00 avseq01.mpg + +/SEGMENT/: + d---1xrxrxr 0 0 [fn 00] [LSN 21] 2048 Jul 14 1978 00:00:00 . + d---1xrxrxr 0 0 [fn 00] [LSN 18] 2048 Jul 14 1978 00:00:00 .. + ---2-xrxrxr 0 0 [fn 00] [LSN 225] 183596 ( 161792) Jul 14 1978 00:00:00 item0001.mpg + +/SVCD/: + d---1xrxrxr 0 0 [fn 00] [LSN 22] 2048 Jul 14 1978 00:00:00 . + d---1xrxrxr 0 0 [fn 00] [LSN 18] 2048 Jul 14 1978 00:00:00 .. + ----1xrxrxr 0 0 [fn 00] [LSN 151] 2048 Jul 14 1978 00:00:00 entries.svd + ----1xrxrxr 0 0 [fn 00] [LSN 150] 2048 Jul 14 1978 00:00:00 info.svd + ----1xrxrxr 0 0 [fn 00] [LSN 152] 65536 Jul 14 1978 00:00:00 lot.svd + ----1xrxrxr 0 0 [fn 00] [LSN 184] 40 Jul 14 1978 00:00:00 psd.svd + ----1xrxrxr 0 0 [fn 00] [LSN 186] 190 Jul 14 1978 00:00:00 search.dat + ----1xrxrxr 0 0 [fn 00] [LSN 185] 2048 Jul 14 1978 00:00:00 tracks.svd + +XA sectors Super Video CD (SVCD) or Chaoji Video CD (CVD) +session #2 starts at track 2, LSN: 526, ISO 9660 blocks: 376 +ISO 9660: 376 blocks, label `SVCD_OGT_TEST_NTSC ' diff --git a/test/svcdgs.right b/test/svcdgs.right new file mode 100644 index 00000000..6ce2c235 --- /dev/null +++ b/test/svcdgs.right @@ -0,0 +1,82 @@ +This is free software; see the source for copying conditions. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. +__________________________________ + +Disc mode is listed as: CD DATA (Mode 2) +CD-ROM Track List (1 - 2) + #: MSF LSN Type Green? Copy? + 1: 00:02:00 000000 XA true no + 2: 00:22:53 001553 XA true no +170: 01:17:62 005687 leadout (12 MB raw, 12 MB formatted) +Media Catalog Number (MCN): +Last CD Session LSN: not supported by drive/driver +__________________________________ +CD Analysis Report +CD-ROM with ISO 9660 filesystem +ISO 9660: 6610 blocks, label `SVCD ' +Application: I-author v1.0, EnReach, Inc. (C) Copyright 1998, Kernel writen by Hujianjun +Preparer : EnReach +Publisher : EnReach +System : S_VCD30 DISC of EnReach +Volume : SVCD +Volume Set : SVCD01 +ISO9660 filesystem +/: + d---1xrxrxr 0 0 [fn 00] [LSN 20] 2048 Jan 06 2001 21:29:31 . + d---1xrxrxr 0 0 [fn 00] [LSN 20] 2048 Jan 06 2001 21:29:31 .. + d---1xrxrxr 0 0 [fn 00] [LSN 21] 2048 Jan 06 2001 21:29:31 autorun + d---1xrxrxr 0 0 [fn 00] [LSN 22] 2048 Jan 06 2001 21:29:31 data + d---1xrxrxr 0 0 [fn 00] [LSN 23] 2048 Jan 06 2001 21:29:31 ext + d---1xrxrxr 0 0 [fn 00] [LSN 24] 2048 Jan 06 2001 21:29:31 mpegav + d---1xrxrxr 0 0 [fn 00] [LSN 25] 2048 Jan 06 2001 21:29:31 segment + d---1xrxrxr 0 0 [fn 00] [LSN 26] 2048 Jan 06 2001 21:29:31 svcd + d---1xrxrxr 0 0 [fn 00] [LSN 27] 2048 Jan 06 2001 21:29:31 vmp + +/AUTORUN/: + d---1xrxrxr 0 0 [fn 00] [LSN 21] 2048 Jan 06 2001 21:29:31 . + d---1xrxrxr 0 0 [fn 00] [LSN 20] 2048 Jan 06 2001 21:29:31 .. + +/DATA/: + d---1xrxrxr 0 0 [fn 00] [LSN 22] 2048 Jan 06 2001 21:29:31 . + d---1xrxrxr 0 0 [fn 00] [LSN 20] 2048 Jan 06 2001 21:29:31 .. + d---1xrxrxr 0 0 [fn 00] [LSN 300] 2048 Jan 06 2001 21:29:31 svcddata + +/DATA/SVCDDATA/: + d---1xrxrxr 0 0 [fn 00] [LSN 300] 2048 Jan 06 2001 21:29:31 . + d---1xrxrxr 0 0 [fn 00] [LSN 22] 2048 Jan 06 2001 21:29:31 .. + +/EXT/: + d---1xrxrxr 0 0 [fn 00] [LSN 23] 2048 Jan 06 2001 21:29:31 . + d---1xrxrxr 0 0 [fn 00] [LSN 20] 2048 Jan 06 2001 21:29:31 .. + +/MPEGAV/: + d---1xrxrxr 0 0 [fn 00] [LSN 24] 2048 Jan 06 2001 21:29:31 . + d---1xrxrxr 0 0 [fn 00] [LSN 20] 2048 Jan 06 2001 21:29:31 .. + ---2--r-r-r 0 0 [fn 01] [LSN 463] 13909140 ( 12257280) Jan 06 2001 21:29:31 avseq01.mpg + +/SEGMENT/: + d---1xrxrxr 0 0 [fn 00] [LSN 25] 2048 Jan 06 2001 21:29:31 . + d---1xrxrxr 0 0 [fn 00] [LSN 20] 2048 Jan 06 2001 21:29:31 .. + +/SVCD/: + d---1xrxrxr 0 0 [fn 00] [LSN 26] 2048 Jan 06 2001 21:29:31 . + d---1xrxrxr 0 0 [fn 00] [LSN 20] 2048 Jan 06 2001 21:29:31 .. + ----1xr-rxr 0 0 [fn 00] [LSN 151] 2048 Jan 06 2001 21:29:31 entries.svd + ----1xr-rxr 0 0 [fn 00] [LSN 150] 2048 Jan 06 2001 21:29:31 info.svd + ----1xr-rxr 0 0 [fn 00] [LSN 152] 65536 Jan 06 2001 21:29:31 lot.svd + ----1xr-rxr 0 0 [fn 00] [LSN 184] 16 Jan 06 2001 21:29:31 psd.svd + ----1xr-rxr 0 0 [fn 00] [LSN 188] 427 Jan 06 2001 21:29:31 search.dat + ----1xr-rxr 0 0 [fn 00] [LSN 186] 4096 Jan 06 2001 21:29:31 spicontx.svd + ----1xr-rxr 0 0 [fn 00] [LSN 185] 2048 Jan 06 2001 21:29:31 tracks.svd + +/VMP/: + d---1xrxrxr 0 0 [fn 00] [LSN 27] 2048 Jan 06 2001 21:29:31 . + d---1xrxrxr 0 0 [fn 00] [LSN 20] 2048 Jan 06 2001 21:29:31 .. + d---1xrxrxr 0 0 [fn 00] [LSN 28] 2048 Jan 06 2001 21:29:31 svcdj + +/VMP/SVCDJ/: + d---1xrxrxr 0 0 [fn 00] [LSN 28] 2048 Jan 06 2001 21:29:31 . + d---1xrxrxr 0 0 [fn 00] [LSN 27] 2048 Jan 06 2001 21:29:31 .. + +XA sectors Chaoji Video CD (CVD) diff --git a/test/t1.toc b/test/t1.toc new file mode 100644 index 00000000..1ad10d59 --- /dev/null +++ b/test/t1.toc @@ -0,0 +1,5 @@ +// simplest cue sheet + +TRACK AUDIO +NO COPY // so that all CTL flags are 0 +SILENCE 0:0:74 // frames are just under 75 diff --git a/test/t2.toc b/test/t2.toc new file mode 100644 index 00000000..edd5fedc --- /dev/null +++ b/test/t2.toc @@ -0,0 +1,6 @@ +// additional pre-gap for track 1 + +TRACK AUDIO +NO COPY // so that all CTL flags are 0 +FILE "cdda.bin" 0:59:0 // Seconds are just under 60 +START 0:10:0 // 10 second pre-gap diff --git a/test/t3.toc b/test/t3.toc new file mode 100644 index 00000000..cacce970 --- /dev/null +++ b/test/t3.toc @@ -0,0 +1,11 @@ +// simplest cue sheet for two tracks + +TRACK AUDIO +NO COPY // so that all CTL flags are 0 +FILE "cdda.bin" 180:0:0 // 180 for number of minutes is okay, although + // it is not for a second or a frame value. + +TRACK AUDIO +NO COPY // so that all CTL flags are 0 +FILE "cdda.bin" 1:0:0 + diff --git a/test/t4.toc b/test/t4.toc new file mode 100644 index 00000000..48f3c5f2 --- /dev/null +++ b/test/t4.toc @@ -0,0 +1,8 @@ +// non default CTL flag of track 1 + +TRACK AUDIO +COPY +PRE_EMPHASIS +FOUR_CHANNEL_AUDIO +FILE "cdda.bin" 1:0:0 + diff --git a/test/t5.toc b/test/t5.toc new file mode 100644 index 00000000..b662c0be --- /dev/null +++ b/test/t5.toc @@ -0,0 +1,8 @@ +// test catalog number + +CATALOG "1234567890123" + +TRACK AUDIO +NO COPY +FILE "cdda.bin" 1:0:0 + diff --git a/test/t6.toc b/test/t6.toc new file mode 100644 index 00000000..b959a365 --- /dev/null +++ b/test/t6.toc @@ -0,0 +1,7 @@ +// test ISRC code + + +TRACK AUDIO +NO COPY +ISRC "DEMUA9800001" +FILE "cdda.bin" 1:0:0 diff --git a/test/t7.toc b/test/t7.toc new file mode 100644 index 00000000..d87d3c05 --- /dev/null +++ b/test/t7.toc @@ -0,0 +1,12 @@ +// check two tracks with pre-gap and ISRC + + +TRACK AUDIO +NO COPY +FILE "cdda.bin" 1:0:0 + +TRACK AUDIO +NO COPY +ISRC "DEMUA9800001" +FILE "cdda.bin" 1:0:0 +START 0:5:0 // pre-gap diff --git a/test/t8.toc b/test/t8.toc new file mode 100644 index 00000000..793d423d --- /dev/null +++ b/test/t8.toc @@ -0,0 +1,14 @@ +// check index increments + + +TRACK AUDIO +NO COPY +FILE "cdda.bin" 0:10:0 +INDEX 1:0:0 +INDEX 2:0:0 + + +TRACK AUDIO +NO COPY +FILE "cdda.bin" 0:10:0 +START 0:5:0 // pre-gap diff --git a/test/t9.toc b/test/t9.toc new file mode 100644 index 00000000..9bd3453b --- /dev/null +++ b/test/t9.toc @@ -0,0 +1,31 @@ +CD_DA +// check mulitple tracks +CATALOG "0724385356926" + +TRACK AUDIO +ISRC "USEM39600078" +COPY +FILE "cdda.bin" 0:10:0 +START 0:5:0 + +TRACK AUDIO +NO COPY +ISRC "USEM39600079" +FILE "cdda.bin" 0:10:0 +START 0:2:0 + +TRACK AUDIO +COPY +FILE "cdda.bin" 0:10:0 +START 0:1:30 + +TRACK AUDIO +NO COPY +START 0:0:0 +FILE "cdda.bin" 0:10:0 + +TRACK AUDIO +ISRC "EDUMA9892346" +NO COPY +FILE "cdda.bin" 0:10:0 +START 0:2:1 diff --git a/test/testassert.c b/test/testassert.c new file mode 100644 index 00000000..4a696657 --- /dev/null +++ b/test/testassert.c @@ -0,0 +1,35 @@ +/* + $Id: testassert.c,v 1.2 2008/03/22 18:08:25 karl Exp $ + + Copyright (C) 2001, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* Private headers */ +#include "cdio_assert.h" + +int +main (int argc, const char *argv[]) +{ + cdio_assert (argc < 2); + + cdio_assert_not_reached (); + + return 0; +} diff --git a/test/testbincue.c.in b/test/testbincue.c.in new file mode 100644 index 00000000..1c2f66e0 --- /dev/null +++ b/test/testbincue.c.in @@ -0,0 +1,125 @@ +/* -*- C -*- + $Id: testbincue.c.in,v 1.2 2008/03/22 18:08:25 karl Exp $ + + Copyright (C) 2004, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + Regression test for cdio_binfile(). +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include + +#ifndef TEST_DIR +#define TEST_DIR "@srcdir@" +#endif + +#define NUM_GOOD_CUES 2 +#define NUM_BAD_CUES 7 +int +main(int argc, const char *argv[]) +{ + const char *cue_file[NUM_GOOD_CUES] = { + "cdda.cue", + "isofs-m1.cue", + }; + + const char *badcue_file[NUM_BAD_CUES] = { + "bad-cat1.cue", + "bad-cat2.cue", + "bad-cat3.cue", + "bad-mode1.cue", + "bad-msf-1.cue", + "bad-msf-2.cue", + "bad-msf-3.cue", + }; + int ret=0; + unsigned int i; + char psz_cuefile[500]; + + psz_cuefile[sizeof(psz_cuefile)-1] = '\0'; + cdio_loglevel_default = (argc > 1) ? CDIO_LOG_DEBUG : CDIO_LOG_INFO; + for (i=0; i + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + Regression test for cdio_get_devices, cdio_get_devices_with_cap(), + and cdio_free_device_list() +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include + +static void +log_handler (cdio_log_level_t level, const char message[]) +{ + switch(level) { + case CDIO_LOG_DEBUG: + case CDIO_LOG_INFO: + return; + default: + printf("cdio %d message: %s\n", level, message); + } +} + +static bool +is_in(char **file_list, const char *file) +{ + char **p; + for (p = file_list; p != NULL && *p != NULL; p++) { + if (strcmp(*p, file) == 0) { + printf("File %s found as expected\n", file); + return true; + } + } + printf("Can't find file %s in list\n", file); + return false; +} + +int +main(int argc, const char *argv[]) +{ + char **nrg_images=NULL; + char **bincue_images=NULL; + char **imgs; + char **c; + unsigned int i; + int ret=0; + + const char *cue_files[2] = {"cdda.cue", "isofs-m1.cue"}; + const char *nrg_files[1] = {"videocd.nrg"}; + + cdio_log_set_handler (log_handler); + + if (! (cdio_have_driver(DRIVER_NRG) && cdio_have_driver(DRIVER_BINCUE)) ) { + printf("You don't have enough drivers for this test\n"); + exit(77); + } + + nrg_images = cdio_get_devices(DRIVER_NRG); + + for (imgs=nrg_images; *imgs != NULL; imgs++) { + printf("NRG image %s\n", *imgs); + } + + if (!is_in(nrg_images, nrg_files[0])) { + cdio_free_device_list(nrg_images); + return 10; + } + + bincue_images = cdio_get_devices(DRIVER_BINCUE); + + for (imgs=bincue_images; *imgs != NULL; imgs++) { + printf("bincue image %s\n", *imgs); + } + + for (i=0; i<2; i++) { + if (is_in(bincue_images, cue_files[i])) { + printf("%s parses as a CDRWIN BIN/CUE csheet.\n", cue_files[i]); + } else { + printf("%s doesn't parse as a CDRWIN BIN/CUE csheet.\n", cue_files[i]); + ret = i+1; + } + } + + if (ret != 0) return ret; + + printf("-----\n"); + printf("ISO 9660 images...\n"); + imgs = NULL; + /* Print out a list of CDDA-drives. */ + imgs = cdio_get_devices_with_cap(bincue_images, CDIO_FS_ISO_9660, false); + + if (NULL == imgs || *imgs == NULL) { + printf("Failed to find an ISO 9660 image\n"); + return 11; + } + + for( c = imgs; *c != NULL; c++ ) { + printf("%s\n", *c); + } + + cdio_free_device_list(imgs); + free(imgs); + + + printf("-----\n"); + printf("CD-DA images...\n"); + imgs = NULL; + /* Print out a list of CDDA-drives. */ + imgs = cdio_get_devices_with_cap(bincue_images, CDIO_FS_AUDIO, false); + + if (NULL == imgs || *imgs == NULL) { + printf("Failed to find CDDA image\n"); + return 12; + } + + for( c = imgs; *c != NULL; c++ ) { + printf("%s\n", *c); + } + + cdio_free_device_list(imgs); + free(imgs); + + + printf("-----\n"); + imgs = NULL; + printf("VCD images...\n"); + /* Print out a list of CD-drives with VCD's in them. */ + imgs = cdio_get_devices_with_cap(nrg_images, +(CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD|CDIO_FS_ANAL_VIDEOCD|CDIO_FS_UNKNOWN), + true); + if (NULL == imgs || *imgs == NULL) { + printf("Failed to find VCD image\n"); + return 13; + } + + for( c = imgs; *c != NULL; c++ ) { + printf("image: %s\n", *c); + } + + cdio_free_device_list(imgs); + free(imgs); + + imgs = NULL; + /* Print out a list of CDDA-drives. */ + imgs = cdio_get_devices_with_cap(bincue_images, CDIO_FS_HIGH_SIERRA, false); + + if (NULL != imgs && *imgs != NULL) { + printf("Found erroneous High Sierra image\n"); + return 14; + } + + imgs = NULL; + /* Print out a list of CDDA-drives. */ + imgs = cdio_get_devices_with_cap(bincue_images, CDIO_FS_UFS, true); + + if (NULL != imgs && *imgs != NULL) { + printf("Found erroneous UFS image\n"); + return 15; + } + + cdio_free_device_list(nrg_images); + free(nrg_images); + cdio_free_device_list(bincue_images); + free(bincue_images); + cdio_free_device_list(imgs); + free(imgs); + + return 0; + +} diff --git a/test/testischar.c b/test/testischar.c new file mode 100644 index 00000000..e1be8f80 --- /dev/null +++ b/test/testischar.c @@ -0,0 +1,70 @@ +/* + $Id: testischar.c,v 1.3 2008/03/22 18:08:25 karl Exp $ + + Copyright (C) 2001, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* Tests ISO9660 character sets. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +int +main (int argc, const char *argv[]) +{ + int i, j; + + printf (" "); + + for (j = 0; j < 0x10; j++) + printf (" %1.1x", j); + + printf (" |"); + + for (j = 0; j < 0x10; j++) + printf (" %1.1x", j); + + printf ("\n"); + + for (i = 0; i < 0x10; i++) + { + + printf ("%1.1x ", i); + + for (j = 0; j < 0x10; j++) + { + int c = (j << 4) + i; + + printf (" %c", iso9660_is_dchar (c) ? c : ' '); + } + + printf (" |"); + + for (j = 0; j < 0x10; j++) + { + int c = (j << 4) + i; + + printf (" %c", iso9660_isachar (c) ? c : ' '); + } + + printf ("\n"); + } + + return 0; +} diff --git a/test/testiso9660.c b/test/testiso9660.c new file mode 100644 index 00000000..de42edf0 --- /dev/null +++ b/test/testiso9660.c @@ -0,0 +1,290 @@ +/* + $Id: testiso9660.c,v 1.23 2008/06/03 08:40:15 rocky Exp $ + + Copyright (C) 2003, 2006, 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* Tests ISO9660 library routines. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#include + +static bool +time_compare(struct tm *p_tm1, struct tm *p_tm2) +{ + bool okay = true; + if (!p_tm1) { + printf("get time is NULL\n"); + return false; + } + if (!p_tm2) { + printf("set time is NULL\n"); + return false; + } + if (p_tm1->tm_year != p_tm2->tm_year) { + printf("Years aren't equal. get: %d, set %d\n", + p_tm1->tm_year, p_tm2->tm_year); + okay=false; + } + if (p_tm1->tm_mon != p_tm2->tm_mon) { + printf("Months aren't equal. get: %d, set %d\n", + p_tm1->tm_mon, p_tm2->tm_mon); + okay=false; + } + if (p_tm1->tm_mday != p_tm2->tm_mday) { + printf("Month days aren't equal. get: %d, set %d\n", + p_tm1->tm_mday, p_tm2->tm_mday); + okay=false; + } + if (p_tm1->tm_min != p_tm2->tm_min) { + printf("minutes aren't equal. get: %d, set %d\n", + p_tm1->tm_min, p_tm2->tm_min); + okay=false; + } + if (p_tm1->tm_hour != p_tm2->tm_hour) { + printf("hours aren't equal. get: %d, set %d\n", + p_tm1->tm_hour, p_tm2->tm_hour); + okay=false; + } + if (p_tm1->tm_sec != p_tm2->tm_sec) { + printf("seconds aren't equal. get: %d, set %d\n", + p_tm1->tm_sec, p_tm2->tm_sec); + okay=false; + } + if (p_tm1->tm_wday != p_tm2->tm_wday) { + printf("Week days aren't equal. get: %d, set %d\n", + p_tm1->tm_wday, p_tm2->tm_wday); + okay=false; + } + if (p_tm1->tm_yday != p_tm2->tm_yday) { + printf("Year days aren't equal. get: %d, set %d\n", + p_tm1->tm_yday, p_tm2->tm_yday); + okay=false; + } +#if FIXED + if (p_tm1->tm_isdst != p_tm2->tm_isdst) { + printf("Is daylight savings times aren't equal. get: %d, set %d\n", + p_tm1->tm_isdst, p_tm2->tm_isdst); + okay=false; + } +#endif +#ifdef HAVE_TM_GMTOFF + if (p_tm1->tm_gmtoff != p_tm2->tm_gmtoff) { + printf("GMT offsets aren't equal. get: %ld, set %ld\n", + p_tm1->tm_gmtoff, p_tm2->tm_gmtoff); + okay=false; + } + if (p_tm1 != p_tm2 && p_tm1 && p_tm2) { +#ifdef FIXED + if (strcmp(p_tm1->tm_zone, p_tm2->tm_zone) != 0) { + printf("Time Zone values. get: %s, set %s\n", + p_tm1->tm_zone, p_tm2->tm_zone); + /* Argh... sometimes GMT is converted to UTC. So + Let's not call this a failure if everything else was okay. + */ + } +#endif + } +#endif + return okay; +} + +int +main (int argc, const char *argv[]) +{ + int c; + int i; + int i_bad = 0; + char dst[100]; + char *dst_p; + int achars[] = {'!', '"', '%', '&', '(', ')', '*', '+', ',', '-', '.', + '/', '?', '<', '=', '>'}; + + /********************************************* + * Test ACHAR and DCHAR + *********************************************/ + + for (c='A'; c<='Z'; c++ ) { + if (!iso9660_is_dchar(c)) { + printf("Failed iso9660_is_dchar test on %c\n", c); + i_bad++; + } + if (!iso9660_is_achar(c)) { + printf("Failed iso9660_is_achar test on %c\n", c); + i_bad++; + } + } + + if (i_bad) return i_bad; + + for (c='0'; c<='9'; c++ ) { + if (!iso9660_is_dchar(c)) { + printf("Failed iso9660_is_dchar test on %c\n", c); + i_bad++; + } + if (!iso9660_is_achar(c)) { + printf("Failed iso9660_is_achar test on %c\n", c); + i_bad++; + } + } + + if (i_bad) return i_bad; + + for (i=0; i<=13; i++ ) { + c=achars[i]; + if (iso9660_is_dchar(c)) { + printf("Should not pass iso9660_is_dchar test on %c\n", c); + i_bad++; + } + if (!iso9660_is_achar(c)) { + printf("Failed iso9660_is_achar test on symbol %c\n", c); + i_bad++; + } + } + + if (i_bad) return i_bad; + + /********************************************* + * Test iso9660_strncpy_pad + *********************************************/ + + dst_p = iso9660_strncpy_pad(dst, "1_3", 5, ISO9660_DCHARS); + if ( 0 != strncmp(dst, "1_3 ", 5) ) { + printf("Failed iso9660_strncpy_pad DCHARS\n"); + return 31; + } + dst_p = iso9660_strncpy_pad(dst, "ABC!123", 2, ISO9660_ACHARS); + if ( 0 != strncmp(dst, "AB", 2) ) { + printf("Failed iso9660_strncpy_pad ACHARS truncation\n"); + return 32; + } + + /********************************************* + * Test iso9660_dirname_valid_p + *********************************************/ + + if ( iso9660_dirname_valid_p("/NOGOOD") ) { + printf("/NOGOOD should fail iso9660_dirname_valid_p\n"); + return 33; + } + if ( iso9660_dirname_valid_p("LONGDIRECTORY/NOGOOD") ) { + printf("LONGDIRECTORY/NOGOOD should fail iso9660_dirname_valid_p\n"); + return 34; + } + if ( !iso9660_dirname_valid_p("OKAY/DIR") ) { + printf("OKAY/DIR should pass iso9660_dirname_valid_p\n"); + return 35; + } + if ( iso9660_dirname_valid_p("OKAY/FILE.EXT") ) { + printf("OKAY/FILENAME.EXT should fail iso9660_dirname_valid_p\n"); + return 36; + } + + /********************************************* + * Test iso9660_pathname_valid_p + *********************************************/ + + if ( !iso9660_pathname_valid_p("OKAY/FILE.EXT") ) { + printf("OKAY/FILE.EXT should pass iso9660_dirname_valid_p\n"); + return 37; + } + if ( iso9660_pathname_valid_p("OKAY/FILENAMETOOLONG.EXT") ) { + printf("OKAY/FILENAMETOOLONG.EXT should fail iso9660_dirname_valid_p\n"); + return 38; + } + if ( iso9660_pathname_valid_p("OKAY/FILE.LONGEXT") ) { + printf("OKAY/FILE.LONGEXT should fail iso9660_dirname_valid_p\n"); + return 39; + } + + dst_p = iso9660_pathname_isofy ("this/file.ext", 1); + if ( 0 != strncmp(dst_p, "this/file.ext;1", 16) ) { + printf("Failed iso9660_pathname_isofy\n"); + free(dst_p); + return 40; + } + free(dst_p); + + /********************************************* + * Test get/set date + *********************************************/ + + { + struct tm *p_tm, tm; + iso9660_dtime_t dtime; + iso9660_ltime_t ltime; + time_t now = time(NULL); + + memset(&dtime, 0, sizeof(dtime)); + p_tm = localtime(&now); + iso9660_set_dtime(p_tm, &dtime); + iso9660_get_dtime(&dtime, true, &tm); + if ( !time_compare(p_tm, &tm) ) { + return 41; + } + p_tm = gmtime(&now); + iso9660_set_dtime(p_tm, &dtime); + if (!iso9660_get_dtime(&dtime, false, &tm)) { + printf("Error returned by iso9660_get_dtime\n"); + return 42; + } + + if ( !time_compare(p_tm, &tm) ) { + printf("GMT time retrieved with iso9660_get_dtime() not same as that\n"); + printf("set with iso9660_set_dtime().\n"); + return 43; + } + + { + p_tm = localtime(&now); + iso9660_set_ltime(p_tm, <ime); + + if (!iso9660_get_ltime(<ime, &tm)) { + printf("Problem running iso9660_get_ltime\n"); + return 44; + } + + if ( ! time_compare(p_tm, &tm) ) { + printf("local time retrieved with iso9660_get_ltime() not\n"); + printf("same as that set with iso9660_set_ltime().\n"); + return 45; + } + + p_tm = gmtime(&now); + iso9660_set_ltime(p_tm, <ime); + iso9660_get_ltime(<ime, &tm); + if ( ! time_compare(p_tm, &tm) ) { + printf("GMT time retrieved with iso9660_get_ltime() not\n"); + printf("same as that set with iso9660_set_ltime().\n"); + return 46; + } + } + } + + return 0; +} diff --git a/test/testisocd.c b/test/testisocd.c new file mode 100644 index 00000000..fa1d9e35 --- /dev/null +++ b/test/testisocd.c @@ -0,0 +1,153 @@ +/* $Id: testisocd.c,v 1.6 2008/03/22 18:08:25 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Tests reading ISO 9660 info from a CD. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "portable.h" + +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define SKIP_TEST_RC 77 + +int +main(int argc, const char *argv[]) +{ + char **ppsz_cd_drives; /* List of all drives with an ISO9660 filesystem. */ + driver_id_t driver_id; /* Driver ID found */ + char *psz_drive; /* Name of drive */ + CdIo_t *p_cdio; + + /* See if we can find a device with a loaded CD-DA in it. If successful + drive_id will be set. */ + ppsz_cd_drives = + cdio_get_devices_with_cap_ret(NULL, CDIO_FS_ANAL_ISO9660_ANY, + true, &driver_id); + + if (ppsz_cd_drives && ppsz_cd_drives[0]) { + /* Found such a CD-ROM with an ISO 9660 filesystem. Use the first drive in + the list. */ + psz_drive = strdup(ppsz_cd_drives[0]); + /* Don't need a list of CD's with CD-DA's any more. */ + cdio_free_device_list(ppsz_cd_drives); + } else { + printf("Unable find or access a CD-ROM drive with an ISO-9660 " + "filesystem.\n"); + exit(SKIP_TEST_RC); + } + + p_cdio = cdio_open (psz_drive, driver_id); + if (!p_cdio) { + fprintf(stderr, "Sorry, couldn't open %s\n", psz_drive); + return 1; + } else { + /* You make get different results looking up "/" versus "/." and the + latter may give more complete information. "/" will take information + from the PVD only, whereas "/." will force a directory read of "/" and + find "." and in that Rock-Ridge information might be found which fills + in more stat information that iso9660_fs_find_lsn also will find. + . Ideally iso9660_fs_stat should be fixed. */ + iso9660_stat_t *p_statbuf = iso9660_fs_stat (p_cdio, "/."); + + if (NULL == p_statbuf) { + fprintf(stderr, + "Could not get ISO-9660 file information for file /.\n"); + cdio_destroy(p_cdio); + exit(2); + } else { + /* Now try getting the statbuf another way */ + char buf[ISO_BLOCKSIZE]; + char *psz_path = NULL; + const lsn_t i_lsn = p_statbuf->lsn; + const iso9660_stat_t *p_statbuf2 = iso9660_fs_find_lsn (p_cdio, i_lsn); + const iso9660_stat_t *p_statbuf3 = + iso9660_fs_find_lsn_with_path (p_cdio, i_lsn, &psz_path); + + /* Compare the two statbufs. */ +#if 0 + if (0 != memcmp(p_statbuf, p_statbuf2, sizeof(iso9660_stat_t))) { +#else + if (p_statbuf->lsn != p_statbuf2->lsn || + p_statbuf->size != p_statbuf2->size || + p_statbuf->type != p_statbuf2->type) { +#endif + fprintf(stderr, "File stat information between fs_stat and " + "fs_find_lsn isn't the same\n"); + exit(3); + } + + if (0 != memcmp(p_statbuf3, p_statbuf2, sizeof(iso9660_stat_t))) { + fprintf(stderr, "File stat information between fs_find_lsn and " + "fs_find_lsn_with_path isn't the same\n"); + exit(4); + } + + if (psz_path != NULL) { + if (0 != strncmp("/./", psz_path, strlen("/./"))) { + fprintf(stderr, "Path returned for fs_find_lsn_with_path " + "is not correct should be /./, is %s\n", psz_path); + exit(5); + free(psz_path); + } + } else { + fprintf(stderr, "Path returned for fs_find_lsn_with_path is NULL\n"); + exit(6); + } + + /* Try reading from the directory. */ + memset (buf, 0, ISO_BLOCKSIZE); + if ( 0 != cdio_read_data_sectors (p_cdio, buf, i_lsn, ISO_BLOCKSIZE, 1) ) + { + fprintf(stderr, "Error reading ISO 9660 file at lsn %lu\n", + (long unsigned int) p_statbuf->lsn); + exit(7); + } + exit(0); + } + } + + exit(0); +} diff --git a/test/testisocd2.c.in b/test/testisocd2.c.in new file mode 100644 index 00000000..12475d47 --- /dev/null +++ b/test/testisocd2.c.in @@ -0,0 +1,134 @@ +/* $Id: testisocd2.c.in,v 1.2 2008/03/22 18:08:25 karl Exp $ + + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Tests reading ISO 9660 info from an ISO 9660 image. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "portable.h" + +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +/* Set up a CD-DA image to test on which is in the libcdio distribution. */ +#define ISO9660_IMAGE_PATH "@srcdir@/" +#define ISO9660_IMAGE ISO9660_IMAGE_PATH "copying.iso" + +#define SKIP_TEST_RC 77 + +int +main(int argc, const char *argv[]) +{ + iso9660_t *p_iso; + + p_iso = iso9660_open (ISO9660_IMAGE); + if (!p_iso) { + fprintf(stderr, "Sorry, couldn't open ISO9660 image %s\n", + ISO9660_IMAGE); + return 1; + } else { + /* You make get different results looking up "/" versus "/." and the + latter may give more complete information. "/" will take information + from the PVD only, whereas "/." will force a directory read of "/" and + find "." and in that Rock-Ridge information might be found which fills + in more stat information that iso9660_fs_find_lsn also will find. + . Ideally iso9660_fs_stat should be fixed. */ + iso9660_stat_t *p_statbuf = iso9660_ifs_stat (p_iso, "/."); + + if (NULL == p_statbuf) { + fprintf(stderr, + "Could not get ISO-9660 file information for file /.\n"); + iso9660_close(p_iso); + exit(2); + } else { + /* Now try getting the statbuf another way */ + char buf[ISO_BLOCKSIZE]; + char *psz_path = NULL; + const lsn_t i_lsn = p_statbuf->lsn; + const iso9660_stat_t *p_statbuf2 = iso9660_ifs_find_lsn (p_iso, i_lsn); + const iso9660_stat_t *p_statbuf3 = + iso9660_ifs_find_lsn_with_path (p_iso, i_lsn, &psz_path); + + /* Compare the two statbufs. */ + if (p_statbuf->lsn != p_statbuf2->lsn || + p_statbuf->size != p_statbuf2->size || + p_statbuf->type != p_statbuf2->type) { + + fprintf(stderr, "File stat information between fs_stat and " + "iso9660_ifs_find_lsn isn't the same\n"); + exit(3); + } + + if (p_statbuf3->lsn != p_statbuf2->lsn || + p_statbuf3->size != p_statbuf2->size || + p_statbuf3->type != p_statbuf2->type) { + exit(4); + } + + if (psz_path != NULL) { + if (0 != strncmp("/./", psz_path, strlen("/./"))) { + fprintf(stderr, "Path returned for ifs_find_lsn_with_path " + "is not correct should be /./, is %s\n", psz_path); + exit(5); + } + free(psz_path); + } else { + fprintf(stderr, "Path returned for fs_find_lsn_with_path is NULL\n"); + exit(6); + } + + /* Try reading from the directory. */ + memset (buf, 0, ISO_BLOCKSIZE); + if ( ISO_BLOCKSIZE != iso9660_iso_seek_read (p_iso, buf, i_lsn, 1) ) + { + fprintf(stderr, "Error reading ISO 9660 file at lsn %lu\n", + (long unsigned int) p_statbuf->lsn); + exit(7); + } + exit(0); + } + } + + exit(0); +} diff --git a/test/testnrg.c.in b/test/testnrg.c.in new file mode 100644 index 00000000..eaa42274 --- /dev/null +++ b/test/testnrg.c.in @@ -0,0 +1,91 @@ +/* -*- C -*- + $Id: testnrg.c.in,v 1.1 2008/03/22 22:43:56 rocky Exp $ + + Copyright (C) 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + Regression test for cdio_nrgfile(). +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include + +#ifndef TEST_DIR +#define TEST_DIR "@srcdir@" +#endif + +#define NUM_FIELDS 2 + +int +main(int argc, const char *argv[]) +{ + char psz_nrgfile[500]; + CdIo_t *p_cdio; + const char *cdtext_check[NUM_FIELDS] = { + "Richard Stallman", + "Join us now we have the software" + }; + const int cdtext_fields[NUM_FIELDS] = {CDTEXT_PERFORMER, CDTEXT_TITLE}; + + cdio_loglevel_default = (argc > 1) ? CDIO_LOG_DEBUG : CDIO_LOG_INFO; + /* snprintf(psz_nrgfile, sizeof(psz_nrgfile)-1, + "%s/%s", TEST_DIR, cue_file[i]); + */ + if (!cdio_have_driver(DRIVER_NRG)) return(77); + + snprintf(psz_nrgfile, sizeof(psz_nrgfile)-1, "%s/%s", + TEST_DIR, "./p1.nrg"); + + p_cdio = cdio_open_nrg(psz_nrgfile); + if (!p_cdio) { + printf("Can't open Nero image file: %s.\n", psz_nrgfile); + return(1); + } + { + unsigned int i; + cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio, 0); + if (!p_cdtext) return(1); + for (i=0; ifield[cdtext_fields[i]]; + if (!psz_field) + return(2); + if (0 != strncmp(psz_field, cdtext_check[i], strlen(cdtext_check[i]))) { + printf("CD-Text compare mismatch.\n"); + printf("expected:\n\t'%s'\ngot:\n\t'%s'\n", + cdtext_check[i], psz_field); + return(3); + } + } + } + cdio_destroy(p_cdio); + + return 0; +} diff --git a/test/testparanoia.c b/test/testparanoia.c new file mode 100644 index 00000000..a85604f2 --- /dev/null +++ b/test/testparanoia.c @@ -0,0 +1,192 @@ +/* + $Id: testparanoia.c,v 1.14 2008/06/19 15:44:31 flameeyes Exp $ + + Copyright (C) 2005, 2006, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Simple program to show using libcdio's version of cdparanoia. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef WORDS_BIGENDIAN +#define BIGENDIAN 1 +#else +#define BIGENDIAN 0 +#endif + +#define SKIP_TEST_RC 77 + +#define MAX_SECTORS 50 +static uint8_t audio_buf[MAX_SECTORS][CDIO_CD_FRAMESIZE_RAW] = { {0}, }; +static paranoia_cb_mode_t audio_status[MAX_SECTORS]; +static unsigned int i = 0; + +static void +callback(long int inpos, paranoia_cb_mode_t function) +{ + audio_status[i] = function; +} + +int +main(int argc, const char *argv[]) +{ + cdrom_drive_t *d = NULL; /* Place to store handle given by cd-parapnioa. */ + driver_id_t driver_id; + char **ppsz_cd_drives; /* List of all drives with a loaded CDDA in it. */ + int i_rc=0; + + /* See if we can find a device with a loaded CD-DA in it. If successful + drive_id will be set. */ + ppsz_cd_drives = cdio_get_devices_with_cap_ret(NULL, CDIO_FS_AUDIO, false, + &driver_id); + + if (ppsz_cd_drives && *ppsz_cd_drives) { + /* Found such a CD-ROM with a CD-DA loaded. Use the first drive in + the list. */ + d=cdda_identify(*ppsz_cd_drives, 1, NULL); + } else { + printf("Unable find or access a CD-ROM drive with an audio CD in it.\n"); + exit(SKIP_TEST_RC); + } + + /** We had a bug in is_device when driver_id == DRIVER_UNKNOWN or + DRIVER_DEVICE. Let's make sure we've fixed that problem. **/ + if (!cdio_is_device(*ppsz_cd_drives, DRIVER_UNKNOWN) || + !cdio_is_device(*ppsz_cd_drives, DRIVER_DEVICE)) + exit(99); + + /* Don't need a list of CD's with CD-DA's any more. */ + cdio_free_device_list(ppsz_cd_drives); + + /* We'll set for verbose paranoia messages. */ + cdda_verbose_set(d, CDDA_MESSAGE_PRINTIT, CDDA_MESSAGE_PRINTIT); + + if ( 0 != cdio_cddap_open(d) ) { + printf("Unable to open disc.\n"); + exit(SKIP_TEST_RC); + } + + /* Okay now set up to read up to the first 300 frames of the first + audio track of the Audio CD. */ + { + cdrom_paranoia_t *p = paranoia_init(d); + lsn_t i_first_lsn = cdda_disc_firstsector(d); + + if ( -1 == i_first_lsn ) { + printf("Trouble getting starting LSN\n"); + } else { + lsn_t i_lsn; /* Current LSN to read */ + lsn_t i_last_lsn = cdda_disc_lastsector(d); + unsigned int i_sectors = i_last_lsn - i_first_lsn + 1; + unsigned int j; + unsigned int i_good = 0; + unsigned int i_bad = 0; + + /* Set reading mode for full paranoia, but allow skipping sectors. */ + paranoia_modeset(p, PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP); + + for ( j=0; j<10; j++ ) { + + /* Pick a place to start reading. */ + i_lsn = i_first_lsn + (rand() % i_sectors); + paranoia_seek(p, i_lsn, SEEK_SET); + + printf("Testing %d sectors starting at %ld\n", + MAX_SECTORS, (long int) i_lsn); + for ( i = 0; + i < MAX_SECTORS && i_lsn <= i_last_lsn; + i++, i_lsn++ ) { + /* read a sector */ + int16_t *p_readbuf = paranoia_read(p, callback); + char *psz_err=cdio_cddap_errors(d); + char *psz_mes=cdio_cddap_messages(d); + + memcpy(audio_buf[i], p_readbuf, CDIO_CD_FRAMESIZE_RAW); + + if (psz_mes || psz_err) + printf("%s%s\n", psz_mes ? psz_mes: "", psz_err ? psz_err: ""); + + if (psz_err) free(psz_err); + if (psz_mes) free(psz_mes); + if( !p_readbuf ) { + printf("paranoia read error. Stopping.\n"); + goto out; + } + } + + /* Compare with the sectors from paranoia. */ + i_lsn -= MAX_SECTORS; + for ( i = 0; i < MAX_SECTORS; i++, i_lsn++ ) { + uint8_t readbuf[CDIO_CD_FRAMESIZE_RAW] = {0,}; + if ( PARANOIA_CB_READ == audio_status[i] || + PARANOIA_CB_VERIFY == audio_status[i] ) { + /* We read the block via paranoia without an error. */ + + if ( 0 == cdio_read_audio_sector(d->p_cdio, readbuf, i_lsn) ) { + if ( BIGENDIAN != d->bigendianp ) { + /* We will compare in the slow, pedantic way*/ + int j; + for (j=0; j < CDIO_CD_FRAMESIZE_RAW ; j +=2) { + if (audio_buf[i][j] != readbuf[j+1] && + audio_buf[i][j+1] != readbuf[j] ) { + printf("LSN %ld doesn't match\n", (long int) i_lsn); + i_bad++; + } else { + i_good++; + } + } + } else { + if ( 0 != memcmp(audio_buf[i], readbuf, + CDIO_CD_FRAMESIZE_RAW) ) { + printf("LSN %ld doesn't match\n", (long int) i_lsn); + i_bad++; + } else { + i_good++; + } + } + } + } else { + printf("Skipping LSN %ld because of status: %s\n", + (long int) i_lsn, paranoia_cb_mode2str[audio_status[i]]); + } + } + } + printf("%u sectors compared okay %u sectors were different\n", + i_good, i_bad); + if (i_bad > i_good) i_rc = 1; + } + out: paranoia_free(p); + } + + cdio_cddap_close(d); + + exit(i_rc); +} + diff --git a/test/testpregap.c.in b/test/testpregap.c.in new file mode 100644 index 00000000..a224e082 --- /dev/null +++ b/test/testpregap.c.in @@ -0,0 +1,122 @@ +/* + $Id: testpregap.c.in,v 1.2 2008/06/10 00:45:09 pjcreath Exp $ + + Copyright (C) 2003, 2004, 2005 Rocky Bernstein + Copyright (C) 2008 Robert W. Fuller + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + Regression test for cdio_get_pregap_lsn() +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include + +#ifndef TEST_DIR +#define TEST_DIR "@srcdir@" +#endif + +static void +log_handler (cdio_log_level_t level, const char message[]) +{ + switch(level) { + case CDIO_LOG_DEBUG: + case CDIO_LOG_INFO: + return; + default: + printf("cdio %d message: %s\n", level, message); + } +} + +typedef struct _pregap_list_t { + + const char *image; + track_t track; + lsn_t pregap; + +} pregap_list_t; + +static pregap_list_t pregapList[] = +{ + { "@srcdir@/t2.toc", 1, 4425 }, + { "@srcdir@/t2.toc", 2, CDIO_INVALID_LSN }, + { "@srcdir@/p1.cue", 1, 0 }, + { "@srcdir@/p1.cue", 2, 150 }, + { "@srcdir@/p1.cue", 3, CDIO_INVALID_LSN }, +/* { "p1.nrg", 1, 0 }, Nero did not create the proper pre-gap - bleh */ + { "@srcdir@/p1.nrg", 2, 225 }, + { "@srcdir@/p1.nrg", 3, CDIO_INVALID_LSN } +}; + +#define NELEMS(v) (sizeof(v) / sizeof(v[0])) + +/* gcc -Wall -I../include testpregap.c ../lib/driver/.libs/libcdio.a */ + +int +main(int argc, const char *argv[]) +{ + CdIo_t *cdObj; + const char *image; + lsn_t pregap; + int i; + int rc = 0; + + cdio_log_set_handler (log_handler); + + if (! (cdio_have_driver(DRIVER_NRG) && cdio_have_driver(DRIVER_BINCUE) + && cdio_have_driver(DRIVER_CDRDAO)) ) { + printf("You don't have enough drivers for this test\n"); + exit(77); + } + + for (i = 0; i < NELEMS(pregapList); ++i) { + + image = pregapList[i].image; + + cdObj = cdio_open(image, DRIVER_UNKNOWN); + if (!cdObj) { + printf("unrecognized image: %s\n", image); + return 50; + } + + pregap = cdio_get_track_pregap_lsn(cdObj, pregapList[i].track); + if (pregap != pregapList[i].pregap) { + printf("%s should have had pregap of lsn=%d instead of lsn=%d\n", + image, pregapList[i].pregap, pregap); + rc = i + 1; + } else { + printf("%s had expected pregap\n", image); + } + + cdio_destroy(cdObj); + } + + return rc; +} diff --git a/test/testtoc.c b/test/testtoc.c new file mode 100644 index 00000000..af60dbe7 --- /dev/null +++ b/test/testtoc.c @@ -0,0 +1,113 @@ +/* + $Id: testtoc.c,v 1.9 2008/03/22 18:08:25 karl Exp $ + + Copyright (C) 2004, 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + Regression test for cdio_tocfile. +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include + +#ifndef TEST_DIR +#define TEST_DIR "." +#endif + +#define NUM_GOOD_TOCS 16 +#define NUM_BAD_TOCS 8 +int +main(int argc, const char *argv[]) +{ + const char *toc_file[NUM_GOOD_TOCS] = { + "cdtext.toc", + "t1.toc", + "t2.toc", + "t3.toc", + "t4.toc", + "t5.toc", + "t6.toc", + "t7.toc", + "t8.toc", + "t9.toc", + "data1.toc", + "data2.toc", + "data5.toc", + "data6.toc", + "data7.toc", + "vcd2.toc", + }; + + const char *badtoc_file[NUM_BAD_TOCS] = { + "bad-msf-1.toc", + "bad-msf-2.toc", + "bad-msf-3.toc", + "bad-cat1.toc", + "bad-cat2.toc", + "bad-cat3.toc", + "bad-file.toc", + "bad-mode1.toc" + }; + int ret=0; + unsigned int i; + char psz_tocfile[500]; + + psz_tocfile[sizeof(psz_tocfile)-1] = '\0'; + + cdio_loglevel_default = (argc > 1) ? CDIO_LOG_DEBUG : CDIO_LOG_INFO; + for (i=0; i