From 21e92dcf3ee78d217f782565eeed44a55c8720a0 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Tue, 15 Aug 2006 20:37:04 +0000 Subject: [PATCH] Initial import --- trunk/AUTHORS | 5 + trunk/COPYING | 280 +++++++ trunk/COPYRIGHT | 18 + trunk/Makefile.am | 192 +++++ trunk/acinclude.m4 | 37 + trunk/bootstrap | 6 + trunk/configure.ac | 103 +++ trunk/doc/Makefile | 4 + trunk/doc/comments | 52 ++ trunk/doc/doxygen.conf.in | 186 +++++ trunk/libburn-1.pc.in | 11 + trunk/libburn/Makefile | 4 + trunk/libburn/Makefile.am | 65 ++ trunk/libburn/async.c | 191 +++++ trunk/libburn/async.h | 8 + trunk/libburn/crc.c | 122 +++ trunk/libburn/crc.h | 9 + trunk/libburn/debug.c | 35 + trunk/libburn/debug.h | 8 + trunk/libburn/drive.c | 369 +++++++++ trunk/libburn/drive.h | 53 ++ trunk/libburn/error.h | 8 + trunk/libburn/file.c | 187 +++++ trunk/libburn/file.h | 22 + trunk/libburn/init.c | 33 + trunk/libburn/init.h | 8 + trunk/libburn/lec.c | 451 +++++++++++ trunk/libburn/lec.h | 12 + trunk/libburn/libburn.h | 928 ++++++++++++++++++++++ trunk/libburn/message.c | 108 +++ trunk/libburn/message.h | 19 + trunk/libburn/mmc.c | 506 ++++++++++++ trunk/libburn/mmc.h | 37 + trunk/libburn/null.c | 27 + trunk/libburn/null.h | 10 + trunk/libburn/options.c | 169 ++++ trunk/libburn/options.h | 78 ++ trunk/libburn/read.c | 264 +++++++ trunk/libburn/read.h | 14 + trunk/libburn/sbc.c | 40 + trunk/libburn/sbc.h | 11 + trunk/libburn/sector.c | 649 ++++++++++++++++ trunk/libburn/sector.h | 31 + trunk/libburn/sg.c | 334 ++++++++ trunk/libburn/sg.h | 19 + trunk/libburn/source.c | 36 + trunk/libburn/source.h | 8 + trunk/libburn/spc.c | 405 ++++++++++ trunk/libburn/spc.h | 25 + trunk/libburn/structure.c | 305 ++++++++ trunk/libburn/structure.h | 70 ++ trunk/libburn/toc.c | 130 ++++ trunk/libburn/toc.h | 48 ++ trunk/libburn/transport.h | 162 ++++ trunk/libburn/util.c | 44 ++ trunk/libburn/util.h | 8 + trunk/libburn/write.c | 529 +++++++++++++ trunk/libburn/write.h | 25 + trunk/libisofs-1.pc.in | 11 + trunk/libisofs/Makefile | 4 + trunk/libisofs/Makefile.am | 44 ++ trunk/libisofs/ecma119.c | 1506 ++++++++++++++++++++++++++++++++++++ trunk/libisofs/ecma119.h | 220 ++++++ trunk/libisofs/errors.c | 14 + trunk/libisofs/errors.h | 19 + trunk/libisofs/libisofs.h | 243 ++++++ trunk/libisofs/rockridge.c | 299 +++++++ trunk/libisofs/rockridge.h | 30 + trunk/libisofs/struct.c | 340 ++++++++ trunk/libisofs/struct.h | 77 ++ trunk/libisofs/susp.c | 312 ++++++++ trunk/libisofs/susp.h | 75 ++ trunk/libisofs/test.c | 138 ++++ trunk/libisofs/tree.c | 409 ++++++++++ trunk/libisofs/tree.h | 259 +++++++ trunk/libisofs/util.c | 710 +++++++++++++++++ trunk/libisofs/util.h | 156 ++++ trunk/libisofs/volume.c | 72 ++ trunk/libisofs/volume.h | 55 ++ trunk/libisofs/writer.c | 1178 ++++++++++++++++++++++++++++ trunk/libisofs/writer.h | 103 +++ trunk/test/Makefile | 4 + trunk/test/blank.c | 100 +++ trunk/test/burn.c | 4 + trunk/test/burniso.c | 147 ++++ trunk/test/devices.c | 39 + trunk/test/iso.c | 138 ++++ trunk/test/iso.py | 297 +++++++ trunk/test/master.c | 126 +++ trunk/test/poll.c | 78 ++ trunk/test/rip.c | 54 ++ trunk/test/structest.c | 48 ++ trunk/test/toc.c | 103 +++ trunk/test/tree.py | 77 ++ trunk/test/tree.pyc | Bin 0 -> 3547 bytes trunk/version.h.in | 3 + 96 files changed, 15010 insertions(+) create mode 100644 trunk/AUTHORS create mode 100644 trunk/COPYING create mode 100644 trunk/COPYRIGHT create mode 100644 trunk/Makefile.am create mode 100644 trunk/acinclude.m4 create mode 100755 trunk/bootstrap create mode 100644 trunk/configure.ac create mode 100644 trunk/doc/Makefile create mode 100644 trunk/doc/comments create mode 100644 trunk/doc/doxygen.conf.in create mode 100644 trunk/libburn-1.pc.in create mode 100644 trunk/libburn/Makefile create mode 100644 trunk/libburn/Makefile.am create mode 100644 trunk/libburn/async.c create mode 100644 trunk/libburn/async.h create mode 100644 trunk/libburn/crc.c create mode 100644 trunk/libburn/crc.h create mode 100644 trunk/libburn/debug.c create mode 100644 trunk/libburn/debug.h create mode 100644 trunk/libburn/drive.c create mode 100644 trunk/libburn/drive.h create mode 100644 trunk/libburn/error.h create mode 100644 trunk/libburn/file.c create mode 100644 trunk/libburn/file.h create mode 100644 trunk/libburn/init.c create mode 100644 trunk/libburn/init.h create mode 100644 trunk/libburn/lec.c create mode 100644 trunk/libburn/lec.h create mode 100644 trunk/libburn/libburn.h create mode 100644 trunk/libburn/message.c create mode 100644 trunk/libburn/message.h create mode 100644 trunk/libburn/mmc.c create mode 100644 trunk/libburn/mmc.h create mode 100644 trunk/libburn/null.c create mode 100644 trunk/libburn/null.h create mode 100644 trunk/libburn/options.c create mode 100644 trunk/libburn/options.h create mode 100644 trunk/libburn/read.c create mode 100644 trunk/libburn/read.h create mode 100644 trunk/libburn/sbc.c create mode 100644 trunk/libburn/sbc.h create mode 100644 trunk/libburn/sector.c create mode 100644 trunk/libburn/sector.h create mode 100644 trunk/libburn/sg.c create mode 100644 trunk/libburn/sg.h create mode 100644 trunk/libburn/source.c create mode 100644 trunk/libburn/source.h create mode 100644 trunk/libburn/spc.c create mode 100644 trunk/libburn/spc.h create mode 100644 trunk/libburn/structure.c create mode 100644 trunk/libburn/structure.h create mode 100644 trunk/libburn/toc.c create mode 100644 trunk/libburn/toc.h create mode 100644 trunk/libburn/transport.h create mode 100644 trunk/libburn/util.c create mode 100644 trunk/libburn/util.h create mode 100644 trunk/libburn/write.c create mode 100644 trunk/libburn/write.h create mode 100644 trunk/libisofs-1.pc.in create mode 100755 trunk/libisofs/Makefile create mode 100755 trunk/libisofs/Makefile.am create mode 100755 trunk/libisofs/ecma119.c create mode 100755 trunk/libisofs/ecma119.h create mode 100755 trunk/libisofs/errors.c create mode 100755 trunk/libisofs/errors.h create mode 100755 trunk/libisofs/libisofs.h create mode 100755 trunk/libisofs/rockridge.c create mode 100755 trunk/libisofs/rockridge.h create mode 100755 trunk/libisofs/struct.c create mode 100755 trunk/libisofs/struct.h create mode 100755 trunk/libisofs/susp.c create mode 100755 trunk/libisofs/susp.h create mode 100755 trunk/libisofs/test.c create mode 100755 trunk/libisofs/tree.c create mode 100755 trunk/libisofs/tree.h create mode 100755 trunk/libisofs/util.c create mode 100755 trunk/libisofs/util.h create mode 100755 trunk/libisofs/volume.c create mode 100755 trunk/libisofs/volume.h create mode 100755 trunk/libisofs/writer.c create mode 100755 trunk/libisofs/writer.h create mode 100644 trunk/test/Makefile create mode 100644 trunk/test/blank.c create mode 100644 trunk/test/burn.c create mode 100644 trunk/test/burniso.c create mode 100644 trunk/test/devices.c create mode 100644 trunk/test/iso.c create mode 100644 trunk/test/iso.py create mode 100644 trunk/test/master.c create mode 100644 trunk/test/poll.c create mode 100644 trunk/test/rip.c create mode 100644 trunk/test/structest.c create mode 100644 trunk/test/toc.c create mode 100644 trunk/test/tree.py create mode 100644 trunk/test/tree.pyc create mode 100644 trunk/version.h.in diff --git a/trunk/AUTHORS b/trunk/AUTHORS new file mode 100644 index 00000000..aa702922 --- /dev/null +++ b/trunk/AUTHORS @@ -0,0 +1,5 @@ +Developers: + +Mario Đanić +Luke Biddell +Anant Narayanan diff --git a/trunk/COPYING b/trunk/COPYING new file mode 100644 index 00000000..5a965fbc --- /dev/null +++ b/trunk/COPYING @@ -0,0 +1,280 @@ + 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 diff --git a/trunk/COPYRIGHT b/trunk/COPYRIGHT new file mode 100644 index 00000000..070ee8c0 --- /dev/null +++ b/trunk/COPYRIGHT @@ -0,0 +1,18 @@ +Derek Foreman and Ben Jansens +Copyright (C) 2002-2004 Derek Foreman and Ben Jansens +Mario Đanić , Luke Biddell , Anant Narayanan +Copyright (C) 2006 Mario Đanić, Luke Biddell, Anant Narayanan + + This program is free software; you can 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 diff --git a/trunk/Makefile.am b/trunk/Makefile.am new file mode 100644 index 00000000..be0e1f45 --- /dev/null +++ b/trunk/Makefile.am @@ -0,0 +1,192 @@ +pkgconfigdir=$(libdir)/pkgconfig +libincludedir=$(includedir)/libburn/@BURN_MAJOR_VERSION@/libburn + +lib_LTLIBRARIES = libburn/libburn.la libisofs/libisofs.la + +## ========================================================================= ## + +# Build libraries +libburn_libburn_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) +libburn_libburn_la_SOURCES = \ + libburn/async.c \ + libburn/async.h \ + libburn/crc.c \ + libburn/crc.h \ + libburn/debug.c \ + libburn/debug.h \ + libburn/drive.c \ + libburn/drive.h \ + libburn/error.h \ + libburn/file.c \ + libburn/file.h \ + libburn/init.c \ + libburn/init.h \ + libburn/lec.c \ + libburn/lec.h \ + libburn/libburn.h \ + libburn/message.c \ + libburn/message.h \ + libburn/mmc.c \ + libburn/mmc.h \ + libburn/null.c \ + libburn/null.h \ + libburn/options.c \ + libburn/options.h \ + libburn/read.c \ + libburn/read.h \ + libburn/sbc.c \ + libburn/sbc.h \ + libburn/sector.c \ + libburn/sector.h \ + libburn/sg.c \ + libburn/sg.h \ + libburn/source.h \ + libburn/source.c \ + libburn/spc.c \ + libburn/spc.h \ + libburn/structure.c \ + libburn/structure.h \ + libburn/toc.c \ + libburn/toc.h \ + libburn/transport.h \ + libburn/util.c \ + libburn/util.h \ + libburn/write.c \ + libburn/write.h \ + version.h + +libisofs_libisofs_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) +libisofs_libisofs_la_SOURCES = \ + libisofs/errors.h \ + libisofs/errors.c \ + libisofs/tree.h \ + libisofs/tree.c \ + libisofs/volume.h \ + libisofs/volume.c \ + libisofs/util.h \ + libisofs/util.c \ + libisofs/ecma119.c \ + libisofs/ecma119.h \ + libisofs/struct.h \ + libisofs/struct.c \ + libisofs/susp.h \ + libisofs/susp.c \ + libisofs/rockridge.h \ + libisofs/rockridge.c \ + libisofs/libisofs.h + +libinclude_HEADERS = \ + libburn/libburn.h \ + libisofs/libisofs.h + +## ========================================================================= ## + +## Build test applications +noinst_PROGRAMS = \ + test/blank \ + test/burn \ + test/burniso \ + test/devices \ + test/iso \ + test/master \ + test/poll \ + test/rip \ + test/toc \ + test/structest + +test_devices_CPPFLAGS = -Ilibburn +test_devices_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_devices_SOURCES = test/devices.c +test_poll_CPPFLAGS = -Ilibburn +test_poll_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_poll_SOURCES = test/poll.c +test_toc_CPPFLAGS = -Ilibburn +test_toc_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_toc_SOURCES = test/toc.c +test_rip_CPPFLAGS = -Ilibburn +test_rip_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_rip_SOURCES = test/rip.c +test_burn_CPPFLAGS = -Ilibburn +test_burn_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_burn_SOURCES = test/burn.c +test_burniso_CPPFLAGS = -Ilibburn +test_burniso_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_burniso_SOURCES = test/burniso.c +test_master_CPPFLAGS = -Ilibburn +test_master_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_master_SOURCES = test/master.c +test_structest_CPPFLAGS = -Ilibburn +test_structest_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_structest_SOURCES = test/structest.c +test_blank_CPPFLAGS = -Ilibburn +test_blank_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_blank_SOURCES = test/blank.c +test_iso_CPPFLAGS = -Ilibisofs +test_iso_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +test_iso_SOURCES = test/iso.c + +## ========================================================================= ## + +## Build documentation (You need Doxygen for this to work) +webhost = http://libburn-api.pykix.org +webpath = / + +docdeps = \ + doc/doxygen.conf \ + doc/comments \ + libburn/libburn.h \ + libisofs/libisofs.h \ + test/burniso.c + +doc: doc/html + +doc/html: $(docdeps) + $(RM) -r $@ + doxygen doc/doxygen.conf + +doc-upload: doc/html + scp -r $ $1/$3" + old="$srcdir/$1/$2/$3" + new="$srcdir/$1/$3" + cat >$new <<__EOF__ +/* WARNING: This file was automatically generated! + * Original: $old + */ +__EOF__ + cat >>$new <$old +]) + +AC_DEFUN([TARGET_SHIZZLE], +[ + ARCH="" + + AC_MSG_CHECKING([target operating system]) + + case $target in + *-*-linux*) + ARCH=linux + ;; + *) + AC_ERROR([You are attempting to compile for an unsupported platform]) + ;; + esac + + AC_MSG_RESULT([$ARCH]) + + # this doesn't actually do anything yet.. but it will someday when we port + # libburn + + #COPY_ARCH_SRC(libburn, $ARCH, transport.c) +]) diff --git a/trunk/bootstrap b/trunk/bootstrap new file mode 100755 index 00000000..5c2990b9 --- /dev/null +++ b/trunk/bootstrap @@ -0,0 +1,6 @@ +#!/bin/sh -x + +aclocal-1.7 +libtoolize --copy --force +autoconf +automake-1.7 --foreign --add-missing --copy --include-deps diff --git a/trunk/configure.ac b/trunk/configure.ac new file mode 100644 index 00000000..100ac502 --- /dev/null +++ b/trunk/configure.ac @@ -0,0 +1,103 @@ +AC_INIT([libburn], [0.2.1], [http://libburn.pykix.org]) +AC_PREREQ([2.50]) +dnl AC_CONFIG_HEADER([config.h]) + +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +AM_INIT_AUTOMAKE([subdir-objects]) + +dnl Making releases: +dnl BURN_MICRO_VERSION += 1; +dnl BURN_INTERFACE_AGE += 1; +dnl BURN_BINARY_AGE += 1; +dnl if any functions have been added, set BURN_INTERFACE_AGE to 0. +dnl if backwards compatibility has been broken, +dnl set BURN_BINARY_AGE and BURN_INTERFACE_AGE to 0. +dnl +dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match +dnl +BURN_MAJOR_VERSION=0 +BURN_MINOR_VERSION=2 +BURN_MICRO_VERSION=1 +BURN_INTERFACE_AGE=0 +BURN_BINARY_AGE=0 +BURN_VERSION=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION.$BURN_MICRO_VERSION + +AC_SUBST(BURN_MAJOR_VERSION) +AC_SUBST(BURN_MINOR_VERSION) +AC_SUBST(BURN_MICRO_VERSION) +AC_SUBST(BURN_INTERFACE_AGE) +AC_SUBST(BURN_BINARY_AGE) +AC_SUBST(BURN_VERSION) + +dnl Libtool versioning +LT_RELEASE=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION +LT_CURRENT=`expr $BURN_MICRO_VERSION - $BURN_INTERFACE_AGE` +LT_REVISION=$BURN_INTERFACE_AGE +LT_AGE=`expr $BURN_BINARY_AGE - $BURN_INTERFACE_AGE` +LT_CURRENT_MINUS_AGE=`expr $LT_CURRENT - $LT_AGE` + +AC_SUBST(LT_RELEASE) +AC_SUBST(LT_CURRENT) +AC_SUBST(LT_REVISION) +AC_SUBST(LT_AGE) +AC_SUBST(LT_CURRENT_MINUS_AGE) + +AC_PREFIX_DEFAULT([/usr/local]) +test "$prefix" = "NONE" && prefix=$ac_default_prefix + +AM_MAINTAINER_MODE + +AM_PROG_CC_C_O +AC_C_CONST +AC_C_INLINE +AC_C_BIGENDIAN + +dnl Large file support +AC_SYS_LARGEFILE +AC_FUNC_FSEEKO +if test ! $ac_cv_func_fseeko; then + AC_ERROR([Libburn requires largefile support.]) +fi + +AC_PROG_LIBTOOL +AC_SUBST(LIBTOOL_DEPS) +LIBTOOL="$LIBTOOL --silent" + +AC_PROG_INSTALL + +AC_CHECK_HEADERS() + +THREAD_LIBS=-lpthread +AC_SUBST(THREAD_LIBS) + +TARGET_SHIZZLE + +dnl Add compiler-specific flags + +dnl See if the user wants aggressive optimizations of the code +AC_ARG_ENABLE(debug, +[ --enable-debug Disable aggressive optimizations [default=yes]], + , enable_debug=yes) +if test x$enable_debug != xyes; then + if test x$GCC = xyes; then + CFLAGS="$CFLAGS -O3" + CFLAGS="$CFLAGS -fexpensive-optimizations" + fi + CFLAGS="$CFLAGS -DNDEBUG" +else + if test x$GCC = xyes; then + CFLAGS="$CFLAGS -g -pedantic -Wall" + fi + CFLAGS="$CFLAGS -DDEBUG" +fi + +AC_CONFIG_FILES([ + Makefile + doc/doxygen.conf + version.h + libburn-1.pc + libisofs-1.pc + ]) +AC_OUTPUT diff --git a/trunk/doc/Makefile b/trunk/doc/Makefile new file mode 100644 index 00000000..062350dd --- /dev/null +++ b/trunk/doc/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/trunk/doc/comments b/trunk/doc/comments new file mode 100644 index 00000000..a107f44a --- /dev/null +++ b/trunk/doc/comments @@ -0,0 +1,52 @@ +/** + @author Dana Jansens, Derek Foreman + + @mainpage Libburn Documentation Index + + @section intro Introduction + + Libburn is an open source library suite for reading, mastering and writing + optical discs. + + @section using Using the library + + @subsection concepts Library concepts + + There are a few concepts to introduce in order for you to understand how to + use Libburn. So, lets start with them: + +
    +
  1. Read - The process of reading the data on a disc for storage or + copying. +
  2. Write - The process of writing data onto a disc to create a new + disc image or append to an existing one. +
  3. Drive - A Drive is a hardware device used to reading and/or writing + discs. CD burners and CD-ROMs are examples of Drives. +
+ + @subsection working Working with the library + + Now that you understand the above @ref concepts, you're ready to look at the + actual use of the library. + + In general, using the library to perform a process consists of the following + steps: + +
    +
  1. Initialize the library. (If not already done; a single instance of + the library can perform multiple operations simultaneously with + multiple drives.) +
  2. Scan for available Drives. +
  3. Choose and grab a Drive for reading/writing. +
  4. Fill in the options for the operation. +
  5. Wait for the operation to complete, displaying status along the way + if desired. +
  6. Release the Drive. +
  7. Destroy the library instance. (If you're done working with the + library.) +
+ + Here's a very simple example of burning an ISO file + + @include burniso.c +*/ diff --git a/trunk/doc/doxygen.conf.in b/trunk/doc/doxygen.conf.in new file mode 100644 index 00000000..2f5ab15e --- /dev/null +++ b/trunk/doc/doxygen.conf.in @@ -0,0 +1,186 @@ +# Doxyfile 1.2.18 + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = @PACKAGE_NAME@ +PROJECT_NUMBER = @PACKAGE_VERSION@ +OUTPUT_DIRECTORY = +OUTPUT_LANGUAGE = English +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = @top_srcdir@ +INTERNAL_DOCS = NO +STRIP_CODE_COMMENTS = NO +CASE_SENSE_NAMES = NO +SHORT_NAMES = NO +HIDE_SCOPE_NAMES = NO +VERBATIM_HEADERS = YES +SHOW_INCLUDE_FILES = YES +JAVADOC_AUTOBRIEF = YES +MULTILINE_CPP_IS_BRIEF = YES +DETAILS_AT_TOP = YES +INHERIT_DOCS = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 4 +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ALIASES = +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +SHOW_USED_FILES = YES +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = libburn libisofs doc +FILE_PATTERNS = libburn.h libisofs.h comments +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = test +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = OB OTK _ +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = doc/html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 200 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = letter +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_SCHEMA = +XML_DTD = +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = DOXYGEN +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +GRAPHICAL_HIERARCHY = NO +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/trunk/libburn-1.pc.in b/trunk/libburn-1.pc.in new file mode 100644 index 00000000..6a10a5c7 --- /dev/null +++ b/trunk/libburn-1.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libburn +Description: Disc reading/writing library +Version: @VERSION@ +Requires: +Libs: -L${libdir} -lburn @THREAD_LIBS@ +Cflags: -I${includedir}/libburn/@BURN_MAJOR_VERSION@ diff --git a/trunk/libburn/Makefile b/trunk/libburn/Makefile new file mode 100644 index 00000000..062350dd --- /dev/null +++ b/trunk/libburn/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/trunk/libburn/Makefile.am b/trunk/libburn/Makefile.am new file mode 100644 index 00000000..6301aa88 --- /dev/null +++ b/trunk/libburn/Makefile.am @@ -0,0 +1,65 @@ +pkgconfigdir=$(libdir)/pkgconfig +libincludedir=$(includedir)/libburn/@BURN_MAJOR_VERSION@ + +lib_LTLIBRARIES = libburn.la + +libburn_la_SOURCES = \ + async.c \ + async.h \ + crc.c \ + crc.h \ + debug.c \ + debug.h \ + drive.c \ + drive.h \ + file.c \ + file.h \ + init.c \ + init.h \ + lec.c \ + lec.h \ + message.c \ + message.h \ + mmc.c \ + mmc.h \ + null.c \ + null.h \ + options.c \ + options.h \ + read.c \ + read.h \ + sbc.c \ + sbc.h \ + sector.c \ + sector.h \ + sg.c \ + sg.h \ + spc.c \ + spc.h \ + source.h \ + source.c \ + structure.c \ + structure.h \ + toc.c \ + toc.h \ + transport.h \ + util.c \ + util.h \ + write.c \ + write.h + +libinclude_HEADERS = libburn.h + +## ========================================================================= ## +indent_files = $(libburn_la_SOURCES) + +indent: $(indent_files) + indent -bad -bap -nbbb -nbbo -nbc -bli0 -br -bls \ + -cdw -ce -cli0 -ncs -nbfda -i8 -l79 -lc79 \ + -lp -saf -sai -nprs -npsl -saw -sob -ss -ut \ + -sbi0 -nsc -ts8 -npcs -ncdb -fca \ + $^ + +.PHONY: indent + +## ========================================================================= ## diff --git a/trunk/libburn/async.c b/trunk/libburn/async.c new file mode 100644 index 00000000..0e01f543 --- /dev/null +++ b/trunk/libburn/async.c @@ -0,0 +1,191 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "libburn.h" +#include "transport.h" +#include "drive.h" +#include "write.h" +#include "options.h" +#include "async.h" + +#include +#include +#include + +#define SCAN_GOING() (workers && !workers->drive) + +typedef void *(*WorkerFunc) (void *); + +struct scan_opts +{ + struct burn_drive_info **drives; + unsigned int *n_drives; + + int done; +}; + +struct erase_opts +{ + struct burn_drive *drive; + int fast; +}; + +struct write_opts +{ + struct burn_drive *drive; + struct burn_write_opts *opts; + struct burn_disc *disc; +}; + +struct w_list +{ + struct burn_drive *drive; + pthread_t thread; + + struct w_list *next; + + union w_list_data + { + struct scan_opts scan; + struct erase_opts erase; + struct write_opts write; + } u; +}; + +static struct w_list *workers; + +static struct w_list *find_worker(struct burn_drive *d) +{ + struct w_list *a; + + for (a = workers; a; a = a->next) + if (a->drive == d) + return a; + return NULL; +} + +static void add_worker(struct burn_drive *d, WorkerFunc f, void *data) +{ + struct w_list *a; + struct w_list *tmp; + + a = malloc(sizeof(struct w_list)); + a->drive = d; + a->u = *(union w_list_data *)data; + + /* insert at front of the list */ + a->next = workers; + tmp = workers; + workers = a; + + if (d) + d->busy = BURN_DRIVE_SPAWNING; + + if (pthread_create(&a->thread, NULL, f, a)) { + free(a); + workers = tmp; + return; + } +} + +static void remove_worker(pthread_t th) +{ + struct w_list *a, *l = NULL; + + for (a = workers; a; l = a, a = a->next) + if (a->thread == th) { + if (l) + l->next = a->next; + else + workers = a->next; + free(a); + break; + } + assert(a != NULL); /* wasn't found.. this should not be possible */ +} + +static void *scan_worker_func(struct w_list *w) +{ + burn_drive_scan_sync(w->u.scan.drives, w->u.scan.n_drives); + w->u.scan.done = 1; + return NULL; +} + +int burn_drive_scan(struct burn_drive_info *drives[], unsigned int *n_drives) +{ + struct scan_opts o; + int ret = 0; + + /* cant be anything working! */ + assert(!(workers && workers->drive)); + + if (!workers) { + /* start it */ + o.drives = drives; + o.n_drives = n_drives; + o.done = 0; + add_worker(NULL, (WorkerFunc) scan_worker_func, &o); + } else if (workers->u.scan.done) { + /* its done */ + ret = workers->u.scan.done; + remove_worker(workers->thread); + assert(workers == NULL); + } else { + /* still going */ + } + return ret; +} + +static void *erase_worker_func(struct w_list *w) +{ + burn_disc_erase_sync(w->u.erase.drive, w->u.erase.fast); + remove_worker(pthread_self()); + return NULL; +} + +void burn_disc_erase(struct burn_drive *drive, int fast) +{ + struct erase_opts o; + + assert(drive); + assert(!SCAN_GOING()); + assert(!find_worker(drive)); + + o.drive = drive; + o.fast = fast; + add_worker(drive, (WorkerFunc) erase_worker_func, &o); +} + +static void *write_disc_worker_func(struct w_list *w) +{ + burn_disc_write_sync(w->u.write.opts, w->u.write.disc); + + /* the options are refcounted, free out ref count which we added below + */ + burn_write_opts_free(w->u.write.opts); + + remove_worker(pthread_self()); + return NULL; +} + +void burn_disc_write(struct burn_write_opts *opts, struct burn_disc *disc) +{ + struct write_opts o; + + assert(!SCAN_GOING()); + assert(!find_worker(opts->drive)); + o.drive = opts->drive; + o.opts = opts; + o.disc = disc; + + opts->refcount++; + + add_worker(opts->drive, (WorkerFunc) write_disc_worker_func, &o); +} + +void burn_async_join_all(void) +{ + void *ret; + + while (workers) + pthread_join(workers->thread, &ret); +} diff --git a/trunk/libburn/async.h b/trunk/libburn/async.h new file mode 100644 index 00000000..0e1d6677 --- /dev/null +++ b/trunk/libburn/async.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__ASYNC_H +#define BURN__ASYNC_H + +void burn_async_join_all(void); +struct burn_write_opts; +#endif /* BURN__ASYNC_H */ diff --git a/trunk/libburn/crc.c b/trunk/libburn/crc.c new file mode 100644 index 00000000..fddc5b4f --- /dev/null +++ b/trunk/libburn/crc.c @@ -0,0 +1,122 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "crc.h" + +static unsigned short ccitt_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +unsigned long crc32_table[256] = { + 0x00000000L, 0x90910101L, 0x91210201L, 0x01B00300L, + 0x92410401L, 0x02D00500L, 0x03600600L, 0x93F10701L, + 0x94810801L, 0x04100900L, 0x05A00A00L, 0x95310B01L, + 0x06C00C00L, 0x96510D01L, 0x97E10E01L, 0x07700F00L, + 0x99011001L, 0x09901100L, 0x08201200L, 0x98B11301L, + 0x0B401400L, 0x9BD11501L, 0x9A611601L, 0x0AF01700L, + 0x0D801800L, 0x9D111901L, 0x9CA11A01L, 0x0C301B00L, + 0x9FC11C01L, 0x0F501D00L, 0x0EE01E00L, 0x9E711F01L, + 0x82012001L, 0x12902100L, 0x13202200L, 0x83B12301L, + 0x10402400L, 0x80D12501L, 0x81612601L, 0x11F02700L, + 0x16802800L, 0x86112901L, 0x87A12A01L, 0x17302B00L, + 0x84C12C01L, 0x14502D00L, 0x15E02E00L, 0x85712F01L, + 0x1B003000L, 0x8B913101L, 0x8A213201L, 0x1AB03300L, + 0x89413401L, 0x19D03500L, 0x18603600L, 0x88F13701L, + 0x8F813801L, 0x1F103900L, 0x1EA03A00L, 0x8E313B01L, + 0x1DC03C00L, 0x8D513D01L, 0x8CE13E01L, 0x1C703F00L, + 0xB4014001L, 0x24904100L, 0x25204200L, 0xB5B14301L, + 0x26404400L, 0xB6D14501L, 0xB7614601L, 0x27F04700L, + 0x20804800L, 0xB0114901L, 0xB1A14A01L, 0x21304B00L, + 0xB2C14C01L, 0x22504D00L, 0x23E04E00L, 0xB3714F01L, + 0x2D005000L, 0xBD915101L, 0xBC215201L, 0x2CB05300L, + 0xBF415401L, 0x2FD05500L, 0x2E605600L, 0xBEF15701L, + 0xB9815801L, 0x29105900L, 0x28A05A00L, 0xB8315B01L, + 0x2BC05C00L, 0xBB515D01L, 0xBAE15E01L, 0x2A705F00L, + 0x36006000L, 0xA6916101L, 0xA7216201L, 0x37B06300L, + 0xA4416401L, 0x34D06500L, 0x35606600L, 0xA5F16701L, + 0xA2816801L, 0x32106900L, 0x33A06A00L, 0xA3316B01L, + 0x30C06C00L, 0xA0516D01L, 0xA1E16E01L, 0x31706F00L, + 0xAF017001L, 0x3F907100L, 0x3E207200L, 0xAEB17301L, + 0x3D407400L, 0xADD17501L, 0xAC617601L, 0x3CF07700L, + 0x3B807800L, 0xAB117901L, 0xAAA17A01L, 0x3A307B00L, + 0xA9C17C01L, 0x39507D00L, 0x38E07E00L, 0xA8717F01L, + 0xD8018001L, 0x48908100L, 0x49208200L, 0xD9B18301L, + 0x4A408400L, 0xDAD18501L, 0xDB618601L, 0x4BF08700L, + 0x4C808800L, 0xDC118901L, 0xDDA18A01L, 0x4D308B00L, + 0xDEC18C01L, 0x4E508D00L, 0x4FE08E00L, 0xDF718F01L, + 0x41009000L, 0xD1919101L, 0xD0219201L, 0x40B09300L, + 0xD3419401L, 0x43D09500L, 0x42609600L, 0xD2F19701L, + 0xD5819801L, 0x45109900L, 0x44A09A00L, 0xD4319B01L, + 0x47C09C00L, 0xD7519D01L, 0xD6E19E01L, 0x46709F00L, + 0x5A00A000L, 0xCA91A101L, 0xCB21A201L, 0x5BB0A300L, + 0xC841A401L, 0x58D0A500L, 0x5960A600L, 0xC9F1A701L, + 0xCE81A801L, 0x5E10A900L, 0x5FA0AA00L, 0xCF31AB01L, + 0x5CC0AC00L, 0xCC51AD01L, 0xCDE1AE01L, 0x5D70AF00L, + 0xC301B001L, 0x5390B100L, 0x5220B200L, 0xC2B1B301L, + 0x5140B400L, 0xC1D1B501L, 0xC061B601L, 0x50F0B700L, + 0x5780B800L, 0xC711B901L, 0xC6A1BA01L, 0x5630BB00L, + 0xC5C1BC01L, 0x5550BD00L, 0x54E0BE00L, 0xC471BF01L, + 0x6C00C000L, 0xFC91C101L, 0xFD21C201L, 0x6DB0C300L, + 0xFE41C401L, 0x6ED0C500L, 0x6F60C600L, 0xFFF1C701L, + 0xF881C801L, 0x6810C900L, 0x69A0CA00L, 0xF931CB01L, + 0x6AC0CC00L, 0xFA51CD01L, 0xFBE1CE01L, 0x6B70CF00L, + 0xF501D001L, 0x6590D100L, 0x6420D200L, 0xF4B1D301L, + 0x6740D400L, 0xF7D1D501L, 0xF661D601L, 0x66F0D700L, + 0x6180D800L, 0xF111D901L, 0xF0A1DA01L, 0x6030DB00L, + 0xF3C1DC01L, 0x6350DD00L, 0x62E0DE00L, 0xF271DF01L, + 0xEE01E001L, 0x7E90E100L, 0x7F20E200L, 0xEFB1E301L, + 0x7C40E400L, 0xECD1E501L, 0xED61E601L, 0x7DF0E700L, + 0x7A80E800L, 0xEA11E901L, 0xEBA1EA01L, 0x7B30EB00L, + 0xE8C1EC01L, 0x7850ED00L, 0x79E0EE00L, 0xE971EF01L, + 0x7700F000L, 0xE791F101L, 0xE621F201L, 0x76B0F300L, + 0xE541F401L, 0x75D0F500L, 0x7460F600L, 0xE4F1F701L, + 0xE381F801L, 0x7310F900L, 0x72A0FA00L, 0xE231FB01L, + 0x71C0FC00L, 0xE151FD01L, 0xE0E1FE01L, 0x7070FF00L +}; + +unsigned short crc_ccitt(unsigned char *q, int len) +{ + unsigned short crc = 0; + + while (len-- > 0) + crc = ccitt_table[(crc >> 8 ^ *q++) & 0xff] ^ (crc << 8); + return ~crc; +} +unsigned int crc_32(unsigned char *data, int len) +{ + unsigned int crc = 0; + + while (len-- > 0) + crc = crc32_table[(crc ^ *data++) & 0xffL] ^ (crc >> 8); + return crc; +} diff --git a/trunk/libburn/crc.h b/trunk/libburn/crc.h new file mode 100644 index 00000000..a4846a33 --- /dev/null +++ b/trunk/libburn/crc.h @@ -0,0 +1,9 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__CRC_H +#define BURN__CRC_H + +unsigned short crc_ccitt(unsigned char *, int len); +unsigned int crc_32(unsigned char *, int len); + +#endif /* BURN__CRC_H */ diff --git a/trunk/libburn/debug.c b/trunk/libburn/debug.c new file mode 100644 index 00000000..b4abab77 --- /dev/null +++ b/trunk/libburn/debug.c @@ -0,0 +1,35 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifdef WIN32 +#include +#endif + +#include +#include +#include "libburn.h" +#include "debug.h" + +static int burn_verbosity = 0; + +void burn_set_verbosity(int v) +{ + burn_verbosity = v; +} + +void burn_print(int level, const char *a, ...) +{ +#ifdef WIN32 + char debug_string_data[256]; +#endif + va_list vl; + + if (level <= burn_verbosity) { + va_start(vl, a); +#ifdef WIN32 + vsprintf(debug_string_data, a, vl); + OutputDebugString(debug_string_data); +#else + vfprintf(stderr, a, vl); +#endif + } +} diff --git a/trunk/libburn/debug.h b/trunk/libburn/debug.h new file mode 100644 index 00000000..b566de0e --- /dev/null +++ b/trunk/libburn/debug.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__DEBUG_H +#define BURN__DEBUG_H + +void burn_print(int level, const char *a, ...); + +#endif /* BURN__DEBUG_H */ diff --git a/trunk/libburn/drive.c b/trunk/libburn/drive.c new file mode 100644 index 00000000..a96816a4 --- /dev/null +++ b/trunk/libburn/drive.c @@ -0,0 +1,369 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "libburn.h" +#include "drive.h" +#include "transport.h" +#include "message.h" +#include "debug.h" +#include "init.h" +#include "toc.h" +#include "util.h" +#include "sg.h" +#include "structure.h" + +static struct burn_drive drive_array[255]; +static int drivetop = -1; + +void burn_drive_free(void) +{ + int i; + struct burn_drive *d; + + for (i = 0; i < drivetop + 1; i++) { + d = &drive_array[i]; + free((void *)d->idata); + free((void *)d->mdata); + free((void *)d->toc_entry); + free(d->devname); + } + drivetop = -1; + memset(drive_array, 0, sizeof(drive_array)); +} + +/* +void drive_read_lead_in(int dnum) +{ + mmc_read_lead_in(&drive_array[dnum], get_4k()); +} +*/ +unsigned int burn_drive_count(void) +{ + return drivetop + 1; +} + +int burn_drive_grab(struct burn_drive *d, int le) +{ + int errcode; + + if (!d->released) { + burn_print(1, "can't grab - already grabbed\n"); + return 0; + } + errcode = d->grab(d); + + if (errcode == 0) { + burn_print(1, "low level drive grab failed\n"); + return 0; + } + d->busy = BURN_DRIVE_GRABBING; + + if (le) + d->load(d); + + d->lock(d); + d->status = BURN_DISC_BLANK; + if (d->mdata->cdr_write || d->mdata->cdrw_write || + d->mdata->dvdr_write || d->mdata->dvdram_write) { + d->read_disc_info(d); + } else + d->read_toc(d); + d->busy = BURN_DRIVE_IDLE; + return 1; +} + +struct burn_drive *burn_drive_register(struct burn_drive *d) +{ + d->block_types[0] = 0; + d->block_types[1] = 0; + d->block_types[2] = 0; + d->block_types[3] = 0; + d->toc_temp = 0; + d->nwa = 0; + d->alba = 0; + d->rlba = 0; + d->cancel = 0; + d->busy = BURN_DRIVE_IDLE; + d->toc_entries = 0; + d->toc_entry = NULL; + d->disc = NULL; + d->erasable = 0; + memcpy(&drive_array[drivetop + 1], d, sizeof(struct burn_drive)); + pthread_mutex_init(&drive_array[drivetop + 1].access_lock, NULL); + return &drive_array[++drivetop]; +} + +void burn_drive_release(struct burn_drive *d, int le) +{ + if (d->released) + burn_print(1, "second release on drive!\n"); + assert(!d->busy); + if (le) + d->eject(d); + d->unlock(d); + + d->release(d); + + d->status = BURN_DISC_UNREADY; + d->released = 1; + if (d->toc_entry) + free(d->toc_entry); + d->toc_entry = NULL; + d->toc_entries = 0; + if (d->disc != NULL) { + burn_disc_free(d->disc); + d->disc = NULL; + } +} + +void burn_wait_all(void) +{ + unsigned int i; + int finished = 0; + struct burn_drive *d; + + while (!finished) { + finished = 1; + d = drive_array; + for (i = burn_drive_count(); i > 0; --i, ++d) { + assert(d->released); + } + if (!finished) + sleep(1); + } +} + +void burn_disc_erase_sync(struct burn_drive *d, int fast) +{ + burn_message_clear_queue(); + + burn_print(1, "erasing drive %s %s\n", d->idata->vendor, + d->idata->product); + + if (d->status != BURN_DISC_FULL) + return; + d->cancel = 0; + d->busy = BURN_DRIVE_ERASING; + d->erase(d, fast); + /* reset the progress */ + d->progress.session = 0; + d->progress.sessions = 1; + d->progress.track = 0; + d->progress.tracks = 1; + d->progress.index = 0; + d->progress.indices = 1; + d->progress.start_sector = 0; + d->progress.sectors = 0x10000; + d->progress.sector = 0; + /* read the initial 0 stage */ + while (!d->test_unit_ready(d) && d->get_erase_progress(d) == 0) + sleep(1); + while (!d->test_unit_ready(d) && + (d->progress.sector = d->get_erase_progress(d)) > 0) + sleep(1); + d->progress.sector = 0x10000; + d->busy = BURN_DRIVE_IDLE; +} + +enum burn_disc_status burn_disc_get_status(struct burn_drive *d) +{ + assert(!d->released); + return d->status; +} + +int burn_disc_erasable(struct burn_drive *d) +{ + return d->erasable; +} +enum burn_drive_status burn_drive_get_status(struct burn_drive *d, + struct burn_progress *p) +{ + if (p) { + memcpy(p, &(d->progress), sizeof(struct burn_progress)); + /* TODO: add mutex */ + } + return d->busy; +} + +void burn_drive_cancel(struct burn_drive *d) +{ + pthread_mutex_lock(&d->access_lock); + d->cancel = 1; + pthread_mutex_unlock(&d->access_lock); +} + +int burn_drive_get_block_types(struct burn_drive *d, + enum burn_write_types write_type) +{ + burn_print(12, "write type: %d\n", write_type); + assert( /* (write_type >= BURN_WRITE_PACKET) && */ + (write_type <= BURN_WRITE_RAW)); + return d->block_types[write_type]; +} + +static void strip_spaces(char *str) +{ + char *tmp; + + tmp = str + strlen(str) - 1; + while (isspace(*tmp)) + *(tmp--) = '\0'; + + tmp = str; + while (*tmp) { + if (isspace(*tmp) && isspace(*(tmp + 1))) { + char *tmp2; + + for (tmp2 = tmp + 1; *tmp2; ++tmp2) + *(tmp2 - 1) = *tmp2; + *(tmp2 - 1) = '\0'; + } else + ++tmp; + } +} + +static int drive_getcaps(struct burn_drive *d, struct burn_drive_info *out) +{ + struct scsi_inquiry_data *id; + + assert(d->idata); + assert(d->mdata); + if (!d->idata->valid || !d->mdata->valid) + return 0; + + id = (struct scsi_inquiry_data *)d->idata; + + memcpy(out->vendor, id->vendor, sizeof(id->vendor)); + strip_spaces(out->vendor); + memcpy(out->product, id->product, sizeof(id->product)); + strip_spaces(out->product); + memcpy(out->revision, id->revision, sizeof(id->revision)); + strip_spaces(out->revision); + strncpy(out->location, d->devname, 16); + out->location[16] = '\0'; + out->buffer_size = d->mdata->buffer_size; + out->read_dvdram = !!d->mdata->dvdram_read; + out->read_dvdr = !!d->mdata->dvdr_read; + out->read_dvdrom = !!d->mdata->dvdrom_read; + out->read_cdr = !!d->mdata->cdr_read; + out->read_cdrw = !!d->mdata->cdrw_read; + out->write_dvdram = !!d->mdata->dvdram_write; + out->write_dvdr = !!d->mdata->dvdr_write; + out->write_cdr = !!d->mdata->cdr_write; + out->write_cdrw = !!d->mdata->cdrw_write; + out->write_simulate = !!d->mdata->simulate; + out->c2_errors = !!d->mdata->c2_pointers; + out->drive = d; + /* update available block types for burners */ + if (out->write_dvdram || out->write_dvdr || + out->write_cdrw || out->write_cdr) + d->probe_write_modes(d); + out->tao_block_types = d->block_types[BURN_WRITE_TAO]; + out->sao_block_types = d->block_types[BURN_WRITE_SAO]; + out->raw_block_types = d->block_types[BURN_WRITE_RAW]; + out->packet_block_types = d->block_types[BURN_WRITE_PACKET]; + return 1; +} + +int burn_drive_scan_sync(struct burn_drive_info *drives[], + unsigned int *n_drives) +{ + /* state vars for the scan process */ + static int scanning = 0, scanned, found; + static unsigned num_scanned, count; + unsigned int i; + struct burn_drive *d; + + assert(burn_running); + + if (!scanning) { + scanning = 1; + /* make sure the drives aren't in use */ + burn_wait_all(); /* make sure the queue cleans up + before checking for the released + state */ + + d = drive_array; + count = burn_drive_count(); + for (i = 0; i < count; ++i, ++d) + assert(d->released == 1); + + /* refresh the lib's drives */ + sg_enumerate(); + ata_enumerate(); + count = burn_drive_count(); + if (count) + *drives = + malloc(sizeof(struct burn_drive_info) * count); + else + *drives = NULL; + *n_drives = scanned = found = num_scanned = 0; + } + + for (i = 0; i < count; ++i) { + if (scanned & (1 << i)) + continue; /* already scanned the device */ + + while (!drive_getcaps(&drive_array[i], + &(*drives)[num_scanned])) { + sleep(1); + } + scanned |= 1 << i; + found |= 1 << i; + num_scanned++; + (*n_drives)++; + } + + if (num_scanned == count) { + /* done scanning */ + scanning = 0; + return 1; + } + return 0; +} + +void burn_drive_info_free(struct burn_drive_info *info) +{ + free(info); + burn_drive_free(); +} + +struct burn_disc *burn_drive_get_disc(struct burn_drive *d) +{ + d->disc->refcnt++; + return d->disc; +} + +void burn_drive_set_speed(struct burn_drive *d, int r, int w) +{ + d->set_speed(d, r, w); +} + +int burn_msf_to_sectors(int m, int s, int f) +{ + return (m * 60 + s) * 75 + f; +} + +void burn_sectors_to_msf(int sectors, int *m, int *s, int *f) +{ + *m = sectors / (60 * 75); + *s = (sectors - *m * 60 * 75) / 75; + *f = sectors - *m * 60 * 75 - *s * 75; +} + +int burn_drive_get_read_speed(struct burn_drive *d) +{ + return d->mdata->max_read_speed; +} + +int burn_drive_get_write_speed(struct burn_drive *d) +{ + return d->mdata->max_write_speed; +} diff --git a/trunk/libburn/drive.h b/trunk/libburn/drive.h new file mode 100644 index 00000000..e6f7e27d --- /dev/null +++ b/trunk/libburn/drive.h @@ -0,0 +1,53 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __DRIVE +#define __DRIVE + +#include "libburn.h" +#include "toc.h" +#include "structure.h" + +struct burn_drive; +struct command; +struct mempage; + +#define LEAD_IN 1 +#define GAP 2 +#define USER_DATA 3 +#define LEAD_OUT 4 +#define SYNC 5 + +#define SESSION_LEADOUT_ENTRY(d,s) (d)->toc->session[(s)].leadout_entry + +#define CURRENT_SESSION_START(d) \ + burn_msf_to_lba(d->toc->session[d->currsession].start_m, \ + d->toc->session[d->currsession].start_s, \ + d->toc->session[d->currsession].start_f) + +#define SESSION_END(d,s) \ + TOC_ENTRY_PLBA((d)->toc, SESSION_LEADOUT_ENTRY((d), (s))) + +#define PREVIOUS_SESSION_END(d) \ + TOC_ENTRY_PLBA((d)->toc, SESSION_LEADOUT_ENTRY((d), (d)->currsession-1)) + +#define LAST_SESSION_END(d) \ + TOC_ENTRY_PLBA((d)->toc, \ + SESSION_LEADOUT_ENTRY((d), (d)->toc->sessions-1)) + +struct burn_drive *burn_drive_register(struct burn_drive *); + +unsigned int burn_drive_count(void); +void burn_wait_all(void); +int burn_sector_length_write(struct burn_drive *d); +int burn_track_control(struct burn_drive *d, int); +void burn_write_empty_sector(int fd); +void burn_write_empty_subcode(int fd); +void burn_drive_free(void); + +int burn_drive_scan_sync(struct burn_drive_info *drives[], + unsigned int *n_drives); +void burn_disc_erase_sync(struct burn_drive *d, int fast); +int burn_drive_get_block_types(struct burn_drive *d, + enum burn_write_types write_type); + +#endif /* __DRIVE */ diff --git a/trunk/libburn/error.h b/trunk/libburn/error.h new file mode 100644 index 00000000..74d4f68d --- /dev/null +++ b/trunk/libburn/error.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode; t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __ERROR_H +#define __ERROR_H + +#define BE_CANCELLED 1 + +#endif /* __ERROR_H */ diff --git a/trunk/libburn/file.c b/trunk/libburn/file.c new file mode 100644 index 00000000..809b8428 --- /dev/null +++ b/trunk/libburn/file.c @@ -0,0 +1,187 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include +#include "source.h" +#include "libburn.h" +#include "file.h" + +/* main channel data can be padded on read, but 0 padding the subs will make +an unreadable disc */ + + +/* This is a generic OS oriented function wrapper which compensates + shortcommings of read() in respect to a guaranteed amount of return data. + See man 2 read , paragraph "RETURN VALUE". + Possibly libburn/file.c is not the right storage location for this. + To make it ready for a move, this function is not declared static. +*/ +static int read_full_buffer(int fd, unsigned char *buffer, int size) +{ + int ret,summed_ret = 0; + + /* make safe against partial buffer returns */ + while (1) { + ret = read(fd, buffer + summed_ret, size - summed_ret); + if (ret <= 0) + break; + summed_ret += ret; + if (summed_ret >= size) + break; + } + if (ret < 0) /* error encountered. abort immediately */ + return ret; + return summed_ret; +} + + +static int file_read(struct burn_source *source, + unsigned char *buffer, + int size) +{ + struct burn_source_fd *fs = source->data; + + return read_full_buffer(fs->datafd, buffer, size); +} + +static int file_read_sub(struct burn_source *source, + unsigned char *buffer, + int size) +{ + struct burn_source_file *fs = source->data; + + return read_full_buffer(fs->subfd, buffer, size); +} + +static void file_free(struct burn_source *source) +{ + struct burn_source_file *fs = source->data; + + close(fs->datafd); + if (source->read_sub) + close(fs->subfd); + free(fs); +} + +static off_t file_size(struct burn_source *source) +{ + struct stat buf; + struct burn_source_file *fs = source->data; + + if (fstat(fs->datafd, &buf) == -1) + return (off_t) 0; + /* for now we keep it compatible to the old (int) return value */ + if(buf.st_size >= 1308622848) /* 2 GB - 800 MB to prevent rollover */ + return (off_t) 1308622848; + return (off_t) buf.st_size; +} + +struct burn_source *burn_file_source_new(const char *path, const char *subpath) +{ + struct burn_source_file *fs; + struct burn_source *src; + int fd1, fd2 = 0; + + if (!path) + return NULL; + fd1 = open(path, O_RDONLY); + if (fd1 == -1) + return NULL; + if (subpath) { + fd2 = open(subpath, O_RDONLY); + if (fd2 == -1) { + close(fd1); + return NULL; + } + } + fs = malloc(sizeof(struct burn_source_file)); + fs->datafd = fd1; + + if (subpath) + fs->subfd = fd2; + + src = burn_source_new(); + src->read = file_read; + if (subpath) + src->read_sub = file_read_sub; + + src->get_size = file_size; + src->free_data = file_free; + src->data = fs; + return src; +} + + +/* ------ provisory location for the new source subclass fd --------- */ + +static off_t fd_get_size(struct burn_source *source) +{ + struct stat buf; + struct burn_source_fd *fs = source->data; + + if (fs->fixed_size > 0) + return fs->fixed_size; + if (fstat(fs->datafd, &buf) == -1) + return (off_t) 0; + /* for now we keep it compatible to the old (int) return value */ + if (buf.st_size >= 1308622848) /* 2 GB - 800 MB to prevent rollover */ + return (off_t) 1308622848; + return buf.st_size; +} + +static int fd_read(struct burn_source *source, + unsigned char *buffer, + int size) +{ + struct burn_source_fd *fs = source->data; + + return read_full_buffer(fs->datafd, buffer, size); +} + + +static int fd_read_sub(struct burn_source *source, + unsigned char *buffer, + int size) +{ + struct burn_source_fd *fs = source->data; + + return read_full_buffer(fs->subfd, buffer, size); +} + + +static void fd_free_data(struct burn_source *source) +{ + struct burn_source_fd *fs = source->data; + + close(fs->datafd); + if (source->read_sub) + close(fs->subfd); + free(fs); +} + + +struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size) +{ + struct burn_source_fd *fs; + struct burn_source *src; + + if (datafd == -1) + return NULL; + fs = malloc(sizeof(struct burn_source_fd)); + fs->datafd = datafd; + fs->subfd = subfd; + fs->fixed_size = size; + + src = burn_source_new(); + src->read = fd_read; + if(subfd != -1) + src->read = fd_read_sub; + src->get_size = fd_get_size; + src->free_data = fd_free_data; + src->data = fs; + return src; +} + diff --git a/trunk/libburn/file.h b/trunk/libburn/file.h new file mode 100644 index 00000000..96c1a692 --- /dev/null +++ b/trunk/libburn/file.h @@ -0,0 +1,22 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__FILE_H +#define BURN__FILE_H + +struct burn_source_file +{ + int datafd; + int subfd; +}; + + +/* ------ provisory location for the new source subclass fd --------- */ + +struct burn_source_fd +{ + int datafd; + int subfd; + off_t fixed_size; +}; + +#endif /* LIBBURN__FILE_H */ diff --git a/trunk/libburn/init.c b/trunk/libburn/init.c new file mode 100644 index 00000000..a6f39412 --- /dev/null +++ b/trunk/libburn/init.c @@ -0,0 +1,33 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include "init.h" +#include "sg.h" +#include "error.h" +#include "libburn.h" +#include "drive.h" + +int burn_running = 0; + +int burn_initialize(void) +{ + if (burn_running) + return 1; + + burn_running = 1; + return 1; +} + +void burn_finish(void) +{ + assert(burn_running); + + burn_wait_all(); + + burn_drive_free(); + + burn_running = 0; +} diff --git a/trunk/libburn/init.h b/trunk/libburn/init.h new file mode 100644 index 00000000..c3e9b9a8 --- /dev/null +++ b/trunk/libburn/init.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__INIT_H +#define BURN__INIT_H + +extern int burn_running; + +#endif /* BURN__INIT_H */ diff --git a/trunk/libburn/lec.c b/trunk/libburn/lec.c new file mode 100644 index 00000000..9141593a --- /dev/null +++ b/trunk/libburn/lec.c @@ -0,0 +1,451 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* borrowed HEAVILY from cdrdao */ + +#include +#include "lec.h" + +#define LEC_HEADER_OFFSET 12 +#define LEC_MODE1_P_PARITY_OFFSET 2076 +#define LEC_MODE1_Q_PARITY_OFFSET 2248 + +static unsigned char gf8_ilog[255] = { + 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, + 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, + 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, + 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, + 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, + 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, + 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, + 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, + 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, + 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, + 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, + 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, + 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, + 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, + 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, + 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, + 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, + 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, + 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, +}; +static unsigned char gf8_log[256] = { + 0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, + 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, + 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, + 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, + 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253, + 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, + 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, + 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, + 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, + 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, + 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, + 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, + 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, + 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, + 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, + 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, + 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, + 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, + 173, 232, 116, 214, 244, 234, 168, 80, 88, 175, +}; +static unsigned char gf8_q_coeffs[2][45] = { + {97, 251, 133, 60, 82, 160, 155, 201, 8, 112, 246, 11, 21, 42, 157, + 169, 80, 174, 232, 230, 172, 211, 241, 18, 68, 216, 44, 121, 9, 200, + 75, 103, 221, 252, 96, 176, 88, 167, 114, 76, 199, 26, 1, 0, 0}, + {190, 96, 250, 132, 59, 81, 159, 154, 200, 7, 111, 245, 10, 20, 41, + 156, 168, 79, 173, 231, 229, 171, 210, 240, 17, 67, 215, 43, 120, 8, + 199, 74, 102, 220, 251, 95, 175, 87, 166, 113, 75, 198, 25, 0, 0} +}; +static unsigned char gf8_p_coeffs[2][26] = { + {230, 172, 211, 241, 18, 68, 216, 44, 121, 9, 200, 75, 103, 221, 252, + 96, 176, 88, 167, 114, 76, 199, 26, 1, 0, 0}, + {231, 229, 171, 210, 240, 17, 67, 215, 43, 120, 8, 199, 74, 102, 220, + 251, 95, 175, 87, 166, 113, 75, 198, 25, 0, 0} +}; + +static unsigned char yellowbook_scrambler[2340] = { + 1, 128, 0, 96, 0, 40, 0, 30, 128, 8, 96, 6, 168, 2, 254, 129, 128, 96, + 96, 40, 40, 30, 158, + 136, 104, 102, 174, 170, 252, 127, 1, 224, 0, 72, 0, 54, 128, 22, 224, + 14, 200, 4, 86, 131, 126, 225, + 224, 72, 72, 54, 182, 150, 246, 238, 198, 204, 82, 213, 253, 159, 1, + 168, 0, 126, 128, 32, 96, 24, 40, + 10, 158, 135, 40, 98, 158, 169, 168, 126, 254, 160, 64, 120, 48, 34, + 148, 25, 175, 74, 252, 55, 1, 214, + 128, 94, 224, 56, 72, 18, 182, 141, 182, 229, 182, 203, 54, 215, 86, + 222, 190, 216, 112, 90, 164, 59, 59, + 83, 83, 125, 253, 225, 129, 136, 96, 102, 168, 42, 254, 159, 0, 104, 0, + 46, 128, 28, 96, 9, 232, 6, + 206, 130, 212, 97, 159, 104, 104, 46, 174, 156, 124, 105, 225, 238, + 200, 76, 86, 181, 254, 247, 0, 70, 128, + 50, 224, 21, 136, 15, 38, 132, 26, 227, 75, 9, 247, 70, 198, 178, 210, + 245, 157, 135, 41, 162, 158, 249, + 168, 66, 254, 177, 128, 116, 96, 39, 104, 26, 174, 139, 60, 103, 81, + 234, 188, 79, 49, 244, 20, 71, 79, + 114, 180, 37, 183, 91, 54, 187, 86, 243, 126, 197, 224, 83, 8, 61, 198, + 145, 146, 236, 109, 141, 237, 165, + 141, 187, 37, 179, 91, 53, 251, 87, 3, 126, 129, 224, 96, 72, 40, 54, + 158, 150, 232, 110, 206, 172, 84, + 125, 255, 97, 128, 40, 96, 30, 168, 8, 126, 134, 160, 98, 248, 41, 130, + 158, 225, 168, 72, 126, 182, 160, + 118, 248, 38, 194, 154, 209, 171, 28, 127, 73, 224, 54, 200, 22, 214, + 142, 222, 228, 88, 75, 122, 183, 99, + 54, 169, 214, 254, 222, 192, 88, 80, 58, 188, 19, 49, 205, 212, 85, + 159, 127, 40, 32, 30, 152, 8, 106, + 134, 175, 34, 252, 25, 129, 202, 224, 87, 8, 62, 134, 144, 98, 236, 41, + 141, 222, 229, 152, 75, 42, 183, + 95, 54, 184, 22, 242, 142, 197, 164, 83, 59, 125, 211, 97, 157, 232, + 105, 142, 174, 228, 124, 75, 97, 247, + 104, 70, 174, 178, 252, 117, 129, 231, 32, 74, 152, 55, 42, 150, 159, + 46, 232, 28, 78, 137, 244, 102, 199, + 106, 210, 175, 29, 188, 9, 177, 198, 244, 82, 199, 125, 146, 161, 173, + 184, 125, 178, 161, 181, 184, 119, 50, + 166, 149, 186, 239, 51, 12, 21, 197, 207, 19, 20, 13, 207, 69, 148, 51, + 47, 85, 220, 63, 25, 208, 10, + 220, 7, 25, 194, 138, 209, 167, 28, 122, 137, 227, 38, 201, 218, 214, + 219, 30, 219, 72, 91, 118, 187, 102, + 243, 106, 197, 239, 19, 12, 13, 197, 197, 147, 19, 45, 205, 221, 149, + 153, 175, 42, 252, 31, 1, 200, 0, + 86, 128, 62, 224, 16, 72, 12, 54, 133, 214, 227, 30, 201, 200, 86, 214, + 190, 222, 240, 88, 68, 58, 179, + 83, 53, 253, 215, 1, 158, 128, 104, 96, 46, 168, 28, 126, 137, 224, + 102, 200, 42, 214, 159, 30, 232, 8, + 78, 134, 180, 98, 247, 105, 134, 174, 226, 252, 73, 129, 246, 224, 70, + 200, 50, 214, 149, 158, 239, 40, 76, + 30, 181, 200, 119, 22, 166, 142, 250, 228, 67, 11, 113, 199, 100, 82, + 171, 125, 191, 97, 176, 40, 116, 30, + 167, 72, 122, 182, 163, 54, 249, 214, 194, 222, 209, 152, 92, 106, 185, + 239, 50, 204, 21, 149, 207, 47, 20, + 28, 15, 73, 196, 54, 211, 86, 221, 254, 217, 128, 90, 224, 59, 8, 19, + 70, 141, 242, 229, 133, 139, 35, + 39, 89, 218, 186, 219, 51, 27, 85, 203, 127, 23, 96, 14, 168, 4, 126, + 131, 96, 97, 232, 40, 78, 158, + 180, 104, 119, 110, 166, 172, 122, 253, 227, 1, 137, 192, 102, 208, 42, + 220, 31, 25, 200, 10, 214, 135, 30, + 226, 136, 73, 166, 182, 250, 246, 195, 6, 209, 194, 220, 81, 153, 252, + 106, 193, 239, 16, 76, 12, 53, 197, + 215, 19, 30, 141, 200, 101, 150, 171, 46, 255, 92, 64, 57, 240, 18, + 196, 13, 147, 69, 173, 243, 61, 133, + 209, 163, 28, 121, 201, 226, 214, 201, 158, 214, 232, 94, 206, 184, 84, + 114, 191, 101, 176, 43, 52, 31, 87, + 72, 62, 182, 144, 118, 236, 38, 205, 218, 213, 155, 31, 43, 72, 31, + 118, 136, 38, 230, 154, 202, 235, 23, + 15, 78, 132, 52, 99, 87, 105, 254, 174, 192, 124, 80, 33, 252, 24, 65, + 202, 176, 87, 52, 62, 151, 80, + 110, 188, 44, 113, 221, 228, 89, 139, 122, 231, 99, 10, 169, 199, 62, + 210, 144, 93, 172, 57, 189, 210, 241, + 157, 132, 105, 163, 110, 249, 236, 66, 205, 241, 149, 132, 111, 35, + 108, 25, 237, 202, 205, 151, 21, 174, 143, + 60, 100, 17, 235, 76, 79, 117, 244, 39, 7, 90, 130, 187, 33, 179, 88, + 117, 250, 167, 3, 58, 129, 211, + 32, 93, 216, 57, 154, 146, 235, 45, 143, 93, 164, 57, 187, 82, 243, + 125, 133, 225, 163, 8, 121, 198, 162, + 210, 249, 157, 130, 233, 161, 142, 248, 100, 66, 171, 113, 191, 100, + 112, 43, 100, 31, 107, 72, 47, 118, 156, + 38, 233, 218, 206, 219, 20, 91, 79, 123, 116, 35, 103, 89, 234, 186, + 207, 51, 20, 21, 207, 79, 20, 52, + 15, 87, 68, 62, 179, 80, 117, 252, 39, 1, 218, 128, 91, 32, 59, 88, 19, + 122, 141, 227, 37, 137, 219, + 38, 219, 90, 219, 123, 27, 99, 75, 105, 247, 110, 198, 172, 82, 253, + 253, 129, 129, 160, 96, 120, 40, 34, + 158, 153, 168, 106, 254, 175, 0, 124, 0, 33, 192, 24, 80, 10, 188, 7, + 49, 194, 148, 81, 175, 124, 124, + 33, 225, 216, 72, 90, 182, 187, 54, 243, 86, 197, 254, 211, 0, 93, 192, + 57, 144, 18, 236, 13, 141, 197, + 165, 147, 59, 45, 211, 93, 157, 249, 169, 130, 254, 225, 128, 72, 96, + 54, 168, 22, 254, 142, 192, 100, 80, + 43, 124, 31, 97, 200, 40, 86, 158, 190, 232, 112, 78, 164, 52, 123, 87, + 99, 126, 169, 224, 126, 200, 32, + 86, 152, 62, 234, 144, 79, 44, 52, 29, 215, 73, 158, 182, 232, 118, + 206, 166, 212, 122, 223, 99, 24, 41, + 202, 158, 215, 40, 94, 158, 184, 104, 114, 174, 165, 188, 123, 49, 227, + 84, 73, 255, 118, 192, 38, 208, 26, + 220, 11, 25, 199, 74, 210, 183, 29, 182, 137, 182, 230, 246, 202, 198, + 215, 18, 222, 141, 152, 101, 170, 171, + 63, 63, 80, 16, 60, 12, 17, 197, 204, 83, 21, 253, 207, 1, 148, 0, 111, + 64, 44, 48, 29, 212, 9, + 159, 70, 232, 50, 206, 149, 148, 111, 47, 108, 28, 45, 201, 221, 150, + 217, 174, 218, 252, 91, 1, 251, 64, + 67, 112, 49, 228, 20, 75, 79, 119, 116, 38, 167, 90, 250, 187, 3, 51, + 65, 213, 240, 95, 4, 56, 3, + 82, 129, 253, 160, 65, 184, 48, 114, 148, 37, 175, 91, 60, 59, 81, 211, + 124, 93, 225, 249, 136, 66, 230, + 177, 138, 244, 103, 7, 106, 130, 175, 33, 188, 24, 113, 202, 164, 87, + 59, 126, 147, 96, 109, 232, 45, 142, + 157, 164, 105, 187, 110, 243, 108, 69, 237, 243, 13, 133, 197, 163, 19, + 57, 205, 210, 213, 157, 159, 41, 168, + 30, 254, 136, 64, 102, 176, 42, 244, 31, 7, 72, 2, 182, 129, 182, 224, + 118, 200, 38, 214, 154, 222, 235, + 24, 79, 74, 180, 55, 55, 86, 150, 190, 238, 240, 76, 68, 53, 243, 87, + 5, 254, 131, 0, 97, 192, 40, + 80, 30, 188, 8, 113, 198, 164, 82, 251, 125, 131, 97, 161, 232, 120, + 78, 162, 180, 121, 183, 98, 246, 169, + 134, 254, 226, 192, 73, 144, 54, 236, 22, 205, 206, 213, 148, 95, 47, + 120, 28, 34, 137, 217, 166, 218, 250, + 219, 3, 27, 65, 203, 112, 87, 100, 62, 171, 80, 127, 124, 32, 33, 216, + 24, 90, 138, 187, 39, 51, 90, + 149, 251, 47, 3, 92, 1, 249, 192, 66, 208, 49, 156, 20, 105, 207, 110, + 212, 44, 95, 93, 248, 57, 130, + 146, 225, 173, 136, 125, 166, 161, 186, 248, 115, 2, 165, 193, 187, 16, + 115, 76, 37, 245, 219, 7, 27, 66, + 139, 113, 167, 100, 122, 171, 99, 63, 105, 208, 46, 220, 28, 89, 201, + 250, 214, 195, 30, 209, 200, 92, 86, + 185, 254, 242, 192, 69, 144, 51, 44, 21, 221, 207, 25, 148, 10, 239, + 71, 12, 50, 133, 213, 163, 31, 57, + 200, 18, 214, 141, 158, 229, 168, 75, 62, 183, 80, 118, 188, 38, 241, + 218, 196, 91, 19, 123, 77, 227, 117, + 137, 231, 38, 202, 154, 215, 43, 30, 159, 72, 104, 54, 174, 150, 252, + 110, 193, 236, 80, 77, 252, 53, 129, + 215, 32, 94, 152, 56, 106, 146, 175, 45, 188, 29, 177, 201, 180, 86, + 247, 126, 198, 160, 82, 248, 61, 130, + 145, 161, 172, 120, 125, 226, 161, 137, 184, 102, 242, 170, 197, 191, + 19, 48, 13, 212, 5, 159, 67, 40, 49, + 222, 148, 88, 111, 122, 172, 35, 61, 217, 209, 154, 220, 107, 25, 239, + 74, 204, 55, 21, 214, 143, 30, 228, + 8, 75, 70, 183, 114, 246, 165, 134, 251, 34, 195, 89, 145, 250, 236, + 67, 13, 241, 197, 132, 83, 35, 125, + 217, 225, 154, 200, 107, 22, 175, 78, 252, 52, 65, 215, 112, 94, 164, + 56, 123, 82, 163, 125, 185, 225, 178, + 200, 117, 150, 167, 46, 250, 156, 67, 41, 241, 222, 196, 88, 83, 122, + 189, 227, 49, 137, 212, 102, 223, 106, + 216, 47, 26, 156, 11, 41, 199, 94, 210, 184, 93, 178, 185, 181, 178, + 247, 53, 134, 151, 34, 238, 153, 140, + 106, 229, 239, 11, 12, 7, 69, 194, 179, 17, 181, 204, 119, 21, 230, + 143, 10, 228, 7, 11, 66, 135, 113, + 162, 164, 121, 187, 98, 243, 105, 133, 238, 227, 12, 73, 197, 246, 211, + 6, 221, 194, 217, 145, 154, 236, 107, + 13, 239, 69, 140, 51, 37, 213, 219, 31, 27, 72, 11, 118, 135, 102, 226, + 170, 201, 191, 22, 240, 14, 196, + 4, 83, 67, 125, 241, 225, 132, 72, 99, 118, 169, 230, 254, 202, 192, + 87, 16, 62, 140, 16, 101, 204, 43, + 21, 223, 79, 24, 52, 10, 151, 71, 46, 178, 156, 117, 169, 231, 62, 202, + 144, 87, 44, 62, 157, 208, 105, + 156, 46, 233, 220, 78, 217, 244, 90, 199, 123, 18, 163, 77, 185, 245, + 178, 199, 53, 146, 151, 45, 174, 157, + 188, 105, 177, 238, 244, 76, 71, 117, 242, 167, 5, 186, 131, 51, 33, + 213, 216, 95, 26, 184, 11, 50, 135, + 85, 162, 191, 57, 176, 18, 244, 13, 135, 69, 162, 179, 57, 181, 210, + 247, 29, 134, 137, 162, 230, 249, 138, + 194, 231, 17, 138, 140, 103, 37, 234, 155, 15, 43, 68, 31, 115, 72, 37, + 246, 155, 6, 235, 66, 207, 113, + 148, 36, 111, 91, 108, 59, 109, 211, 109, 157, 237, 169, 141, 190, 229, + 176, 75, 52, 55, 87, 86, 190, 190, + 240, 112, 68, 36, 51, 91, 85, 251, 127, 3, 96, 1, 232, 0, 78, 128, 52, + 96, 23, 104, 14, 174, 132, + 124, 99, 97, 233, 232, 78, 206, 180, 84, 119, 127, 102, 160, 42, 248, + 31, 2, 136, 1, 166, 128, 122, 224, + 35, 8, 25, 198, 138, 210, 231, 29, 138, 137, 167, 38, 250, 154, 195, + 43, 17, 223, 76, 88, 53, 250, 151, + 3, 46, 129, 220, 96, 89, 232, 58, 206, 147, 20, 109, 207, 109, 148, 45, + 175, 93, 188, 57, 177, 210, 244, + 93, 135, 121, 162, 162, 249, 185, 130, 242, 225, 133, 136, 99, 38, 169, + 218, 254, 219, 0, 91, 64, 59, 112, + 19, 100, 13, 235, 69, 143, 115, 36, 37, 219, 91, 27, 123, 75, 99, 119, + 105, 230, 174, 202, 252, 87, 1, + 254, 128, 64, 96, 48, 40, 20, 30, 143, 72, 100, 54, 171, 86, 255, 126, + 192, 32, 80, 24, 60, 10, 145, + 199, 44, 82, 157, 253, 169, 129, 190, 224, 112, 72, 36, 54, 155, 86, + 235, 126, 207, 96, 84, 40, 63, 94, + 144, 56, 108, 18, 173, 205, 189, 149, 177, 175, 52, 124, 23, 97, 206, + 168, 84, 126, 191, 96, 112, 40, 36, + 30, 155, 72, 107, 118, 175, 102, 252, 42, 193, 223, 16, 88, 12, 58, + 133, 211, 35, 29, 217, 201, 154, 214, + 235, 30, 207, 72, 84, 54, 191, 86, 240, 62, 196, 16, 83, 76, 61, 245, + 209, 135, 28, 98, 137, 233, 166, + 206, 250, 212, 67, 31, 113, 200, 36, 86, 155, 126, 235, 96, 79, 104, + 52, 46, 151, 92, 110, 185, 236, 114, + 205, 229, 149, 139, 47, 39, 92, 26, 185, 203, 50, 215, 85, 158, 191, + 40, 112, 30, 164, 8, 123, 70, 163, + 114, 249, 229, 130, 203, 33, 151, 88, 110, 186, 172, 115, 61, 229, 209, + 139, 28, 103, 73, 234, 182, 207, 54, + 212, 22, 223, 78, 216, 52, 90, 151, 123, 46, 163, 92, 121, 249, 226, + 194, 201, 145, 150, 236, 110, 205, 236, + 85, 141, 255, 37, 128, 27, 32, 11, 88, 7, 122, 130, 163, 33, 185, 216, + 114, 218, 165, 155, 59, 43, 83, + 95, 125, 248, 33, 130, 152, 97, 170, 168, 127, 62, 160, 16, 120, 12, + 34, 133, 217, 163, 26, 249, 203, 2, + 215, 65, 158, 176, 104, 116, 46, 167, 92, 122, 185, 227, 50, 201, 213, + 150, 223, 46, 216, 28, 90, 137, 251, + 38, 195, 90, 209, 251, 28, 67, 73, 241, 246, 196, 70, 211, 114, 221, + 229, 153, +}; + +void scramble(unsigned char *inout) +{ + unsigned char *r = inout + 12; + unsigned char *s = yellowbook_scrambler; + unsigned int i; + + for (i = 2340; i; i--) { + *r++ ^= *s++; + } +} + +/* Calculate the P parities for the sector. + * The 43 P vectors of length 24 are combined with the GF8_P_COEFFS. + */ +void parity_p(unsigned char *sector) +{ + int i, j; + unsigned char p0_msb, p1_msb; + unsigned char p0_lsb, p1_lsb; + unsigned char *p_msb_start, *p_lsb_start; + unsigned char *p_msb, *p_lsb; + unsigned char *coeffs0, *coeffs1; + unsigned char *p0, *p1; + unsigned char d; + unsigned short c; + + p_lsb_start = sector + LEC_HEADER_OFFSET; + p_msb_start = sector + LEC_HEADER_OFFSET + 1; + + p1 = sector + LEC_MODE1_P_PARITY_OFFSET; + p0 = sector + LEC_MODE1_P_PARITY_OFFSET + 2 * 43; + + for (i = 0; i <= 42; i++) { + p_lsb = p_lsb_start; + p_msb = p_msb_start; + + coeffs0 = gf8_p_coeffs[0]; + coeffs1 = gf8_p_coeffs[1]; + + p0_lsb = p1_lsb = p0_msb = p1_msb = 0; + + for (j = 0; j <= 23; j++) { + d = *p_lsb; + + if (d != 0) { + c = gf8_log[d] + *coeffs0; + if (c >= 255) + c -= 255; + p0_lsb ^= gf8_ilog[c]; + + c = gf8_log[d] + *coeffs1; + if (c >= 255) + c -= 255; + p1_lsb ^= gf8_ilog[c]; + } + + d = *p_msb; + + if (d != 0) { + c = gf8_log[d] + *coeffs0; + if (c >= 255) + c -= 255; + p0_msb ^= gf8_ilog[c]; + + c = gf8_log[d] + *coeffs1; + if (c >= 255) + c -= 255; + p1_msb ^= gf8_ilog[c]; + } + + coeffs0++; + coeffs1++; + + p_lsb += 2 * 43; + p_msb += 2 * 43; + } + + *p0 = p0_lsb; + *(p0 + 1) = p0_msb; + + *p1 = p1_lsb; + *(p1 + 1) = p1_msb; + + p0 += 2; + p1 += 2; + + p_lsb_start += 2; + p_msb_start += 2; + } +} + +/* Calculate the Q parities for the sector. + * The 26 Q vectors of length 43 are combined with the GF8_Q_COEFFS. + */ +void parity_q(unsigned char *sector) +{ + int i, j; + unsigned char q0_msb, q1_msb; + unsigned char q0_lsb, q1_lsb; + unsigned char *q_msb_start, *q_lsb_start; + unsigned char *q_msb, *q_lsb; + unsigned char *coeffs0, *coeffs1; + unsigned char *q0, *q1, *q_start; + unsigned char d; + unsigned short c; + + q_lsb_start = sector + LEC_HEADER_OFFSET; + q_msb_start = sector + LEC_HEADER_OFFSET + 1; + + q_start = sector + LEC_MODE1_Q_PARITY_OFFSET; + q1 = sector + LEC_MODE1_Q_PARITY_OFFSET; + q0 = sector + LEC_MODE1_Q_PARITY_OFFSET + 2 * 26; + + for (i = 0; i <= 25; i++) { + q_lsb = q_lsb_start; + q_msb = q_msb_start; + + coeffs0 = gf8_q_coeffs[0]; + coeffs1 = gf8_q_coeffs[1]; + + q0_lsb = q1_lsb = q0_msb = q1_msb = 0; + + for (j = 0; j <= 42; j++) { + d = *q_lsb; + + if (d != 0) { + c = gf8_log[d] + *coeffs0; + if (c >= 255) + c -= 255; + q0_lsb ^= gf8_ilog[c]; + + c = gf8_log[d] + *coeffs1; + if (c >= 255) + c -= 255; + q1_lsb ^= gf8_ilog[c]; + } + + d = *q_msb; + + if (d != 0) { + c = gf8_log[d] + *coeffs0; + if (c >= 255) + c -= 255; + q0_msb ^= gf8_ilog[c]; + + c = gf8_log[d] + *coeffs1; + if (c >= 255) + c -= 255; + q1_msb ^= gf8_ilog[c]; + } + + coeffs0++; + coeffs1++; + + q_lsb += 2 * 44; + q_msb += 2 * 44; + + if (q_lsb >= q_start) { + q_msb -= 2 * 1118; + q_lsb -= 2 * 1118; + } + } + + *q0 = q0_lsb; + *(q0 + 1) = q0_msb; + + *q1 = q1_lsb; + *(q1 + 1) = q1_msb; + + q0 += 2; + q1 += 2; + + q_lsb_start += 2 * 43; + q_msb_start += 2 * 43; + } +} diff --git a/trunk/libburn/lec.h b/trunk/libburn/lec.h new file mode 100644 index 00000000..f6980301 --- /dev/null +++ b/trunk/libburn/lec.h @@ -0,0 +1,12 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __LEC +#define __LEC + +#define RS_L12_BITS 8 + +void scramble(unsigned char *); +void parity_p(unsigned char *in); +void parity_q(unsigned char *in); + +#endif /* __LEC */ diff --git a/trunk/libburn/libburn.h b/trunk/libburn/libburn.h new file mode 100644 index 00000000..7506ed3b --- /dev/null +++ b/trunk/libburn/libburn.h @@ -0,0 +1,928 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef LIBBURN_H +#define LIBBURN_H + +/* Needed for off_t which is the (POSIX-ly) appropriate type for + expressing a file or stream size. + + XXX we should enforce 64-bitness for off_t +*/ +#include + +#ifndef DOXYGEN + +#if defined(__cplusplus) +#define BURN_BEGIN_DECLS \ + namespace burn { \ + extern "C" { +#define BURN_END_DECLS \ + } \ + } +#else +#define BURN_BEGIN_DECLS +#define BURN_END_DECLS +#endif + +BURN_BEGIN_DECLS + +#endif + +/** References a physical drive in the system */ +struct burn_drive; + +/** References a whole disc */ +struct burn_disc; + +/** References a single session on a disc */ +struct burn_session; + +/** References a single track on a disc */ +struct burn_track; + +/** Session format for normal audio or data discs */ +#define BURN_CDROM 0 +/** Session format for obsolete CD-I discs */ +#define BURN_CDI 0x10 +/** Session format for CDROM-XA discs */ +#define BURN_CDXA 0x20 + +#define BURN_POS_END 100 + +/** Mask for mode bits */ +#define BURN_MODE_BITS 127 + +/** Track mode - mode 0 data + 0 bytes of user data. it's all 0s. mode 0. get it? HAH +*/ +#define BURN_MODE0 (1 << 0) +/** Track mode - mode "raw" - all 2352 bytes supplied by app + FOR DATA TRACKS ONLY! +*/ +#define BURN_MODE_RAW (1 << 1) +/** Track mode - mode 1 data + 2048 bytes user data, and all the LEC money can buy +*/ +#define BURN_MODE1 (1 << 2) +/** Track mode - mode 2 data + defaults to formless, 2336 bytes of user data, unprotected + | with a data form if required. +*/ +#define BURN_MODE2 (1 << 3) +/** Track mode modifier - Form 1, | with MODE2 for reasonable results + 2048 bytes of user data, 4 bytes of subheader +*/ +#define BURN_FORM1 (1 << 4) +/** Track mode modifier - Form 2, | with MODE2 for reasonable results + lots of user data. not much LEC. +*/ +#define BURN_FORM2 (1 << 5) +/** Track mode - audio + 2352 bytes per sector. may be | with 4ch or preemphasis. + NOT TO BE CONFUSED WITH BURN_MODE_RAW +*/ +#define BURN_AUDIO (1 << 6) +/** Track mode modifier - 4 channel audio. */ +#define BURN_4CH (1 << 7) +/** Track mode modifier - Digital copy permitted, can be set on any track.*/ +#define BURN_COPY (1 << 8) +/** Track mode modifier - 50/15uS pre-emphasis */ +#define BURN_PREEMPHASIS (1 << 9) +/** Input mode modifier - subcodes present packed 16 */ +#define BURN_SUBCODE_P16 (1 << 10) +/** Input mode modifier - subcodes present packed 96 */ +#define BURN_SUBCODE_P96 (1 << 11) +/** Input mode modifier - subcodes present raw 96 */ +#define BURN_SUBCODE_R96 (1 << 12) + +/** Possible disc writing style/modes */ +enum burn_write_types +{ + /** Packet writing. + currently unsupported + */ + BURN_WRITE_PACKET, + /** Track At Once recording. + 2s gaps between tracks, no fonky lead-ins + */ + BURN_WRITE_TAO, + /** Session At Once. + block type MUST be BURN_BLOCK_SAO + */ + BURN_WRITE_SAO, + /** Raw disc at once recording. + all subcodes must be provided by lib or user + only raw block types are supported + */ + BURN_WRITE_RAW +}; + +/** Data format to send to the drive */ +enum burn_block_types +{ + /** sync, headers, edc/ecc provided by lib/user */ + BURN_BLOCK_RAW0 = 1, + /** sync, headers, edc/ecc and p/q subs provided by lib/user */ + BURN_BLOCK_RAW16 = 2, + /** sync, headers, edc/ecc and packed p-w subs provided by lib/user */ + BURN_BLOCK_RAW96P = 4, + /** sync, headers, edc/ecc and raw p-w subs provided by lib/user */ + BURN_BLOCK_RAW96R = 8, + /** only 2048 bytes of user data provided by lib/user */ + BURN_BLOCK_MODE1 = 256, + /** 2336 bytes of user data provided by lib/user */ + BURN_BLOCK_MODE2R = 512, + /** 2048 bytes of user data provided by lib/user + subheader provided in write parameters + are we ever going to support this shit? I vote no. + (supposed to be supported on all drives...) + */ + BURN_BLOCK_MODE2_PATHETIC = 1024, + /** 2048 bytes of data + 8 byte subheader provided by lib/user + hey, this is also dumb + */ + BURN_BLOCK_MODE2_LAME = 2048, + /** 2324 bytes of data provided by lib/user + subheader provided in write parameters + no sir, I don't like it. + */ + BURN_BLOCK_MODE2_OBSCURE = 4096, + /** 2332 bytes of data supplied by lib/user + 8 bytes sub header provided in write parameters + this is the second least suck mode2, and is mandatory for + all drives to support. + */ + BURN_BLOCK_MODE2_OK = 8192, + /** SAO block sizes are based on cue sheet, so use this. */ + BURN_BLOCK_SAO = 16384 +}; + +/** Possible status' of the drive in regard to the disc in it. */ +enum burn_disc_status +{ + /** The current status is not yet known */ + BURN_DISC_UNREADY, + /** The drive holds a blank disc */ + BURN_DISC_BLANK, + /** There is no disc at all in the drive */ + BURN_DISC_EMPTY, + /** There is an incomplete disc in the drive */ + BURN_DISC_APPENDABLE, + /** There is a disc with data on it in the drive */ + BURN_DISC_FULL +}; + +/** Possible types of messages form the library. */ +enum burn_message_type +{ + /** Diagnostic/Process information. For the curious user. */ + BURN_MESSAGE_INFO, + /** A warning regarding a possible problem. The user should probably + be notified, but its not fatal. */ + BURN_MESSAGE_WARNING, + /** An error message. This usually means the current process will be + aborted, and the user should definately see these. */ + BURN_MESSAGE_ERROR +}; + +/** Possible information messages */ +enum burn_message_info +{ + BURN_INFO_FOO +}; + +/** Possible warning messages */ +enum burn_message_warning +{ + BURN_WARNING_FOO +}; + +/** Possible error messages */ +enum burn_message_error +{ + BURN_ERROR_CANCELLED +}; + +/** Possible data source return values */ +enum burn_source_status +{ + /** The source is ok */ + BURN_SOURCE_OK, + /** The source is at end of file */ + BURN_SOURCE_EOF, + /** The source is unusable */ + BURN_SOURCE_FAILED +}; + + +/** Possible busy states for a drive */ +enum burn_drive_status +{ + /** The drive is not in an operation */ + BURN_DRIVE_IDLE, + /** The library is spawning the processes to handle a pending + operation (A read/write/etc is about to start but hasn't quite + yet) */ + BURN_DRIVE_SPAWNING, + /** The drive is reading data from a disc */ + BURN_DRIVE_READING, + /** The drive is writing data to a disc */ + BURN_DRIVE_WRITING, + /** The drive is writing Lead-In */ + BURN_DRIVE_WRITING_LEADIN, + /** The drive is writing Lead-Out */ + BURN_DRIVE_WRITING_LEADOUT, + /** The drive is erasing a disc */ + BURN_DRIVE_ERASING, + /** The drive is being grabbed */ + BURN_DRIVE_GRABBING +}; + +/** Information about a track on a disc - this is from the q sub channel of the + lead-in area of a disc. The documentation here is very terse. + See a document such as mmc3 for proper information. +*/ +struct burn_toc_entry +{ + /** Session the track is in */ + unsigned char session; + /** Type of data. for this struct to be valid, it must be 1 */ + unsigned char adr; + /** Type of data in the track */ + unsigned char control; + /** Zero. Always. Really. */ + unsigned char tno; + /** Track number or special information */ + unsigned char point; + unsigned char min; + unsigned char sec; + unsigned char frame; + unsigned char zero; + /** Track start time minutes for normal tracks */ + unsigned char pmin; + /** Track start time seconds for normal tracks */ + unsigned char psec; + /** Track start time frames for normal tracks */ + unsigned char pframe; +}; + + +/** Data source for tracks */ +struct burn_source { + /** Reference count for the data source. Should be 1 when a new source + is created. Increment it to take a reference for yourself. Use + burn_source_free to destroy your reference to it. */ + int refcount; + + /** Read data from the source */ + int (*read)(struct burn_source *, + unsigned char *buffer, + int size); + + /** Read subchannel data from the source (NULL if lib generated) */ + int (*read_sub)(struct burn_source *, + unsigned char *buffer, + int size); + + /** Get the size of the source's data */ + off_t (*get_size)(struct burn_source *); + + /** Clean up the source specific data */ + void (*free_data)(struct burn_source *); + + /** Next source, for when a source runs dry and padding is disabled + THIS IS AUTOMATICALLY HANDLED, DO NOT TOUCH + */ + struct burn_source *next; + + /** Source specific data */ + void *data; +}; + + +/** Information on a drive in the system */ +struct burn_drive_info +{ + /** Name of the vendor of the drive */ + char vendor[9]; + /** Name of the drive */ + char product[17]; + /** Revision of the drive */ + char revision[5]; + /** Location of the drive in the filesystem. */ + char location[17]; + + /** Can the drive read DVD-RAM discs */ + unsigned int read_dvdram:1; + /** Can the drive read DVD-R discs */ + unsigned int read_dvdr:1; + /** Can the drive read DVD-ROM discs */ + unsigned int read_dvdrom:1; + /** Can the drive read CD-R discs */ + unsigned int read_cdr:1; + /** Can the drive read CD-RW discs */ + unsigned int read_cdrw:1; + + /** Can the drive write DVD-RAM discs */ + unsigned int write_dvdram:1; + /** Can the drive write DVD-R discs */ + unsigned int write_dvdr:1; + /** Can the drive write CD-R discs */ + unsigned int write_cdr:1; + /** Can the drive write CD-RW discs */ + unsigned int write_cdrw:1; + + /** Can the drive simulate a write */ + unsigned int write_simulate:1; + + /** Can the drive report C2 errors */ + unsigned int c2_errors:1; + + /** The size of the drive's buffer (in kilobytes) */ + int buffer_size; + /** + * The supported block types in tao mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int tao_block_types; + /** + * The supported block types in sao mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int sao_block_types; + /** + * The supported block types in raw mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int raw_block_types; + /** + * The supported block types in packet mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int packet_block_types; + + /** The value by which this drive can be indexed when using functions + in the library. This is the value to pass to all libbburn functions + that operate on a drive. */ + struct burn_drive *drive; +}; + +/** Messages from the library */ +struct burn_message +{ + /** The drive associated with the message. NULL if the error is not + related to a specific drive. */ + struct burn_drive *drive; + + /** The type of message this is. See message_type for details. */ + enum burn_message_type type; + + /** The actual message */ + union detail { + struct { + enum burn_message_info message; + } info; + struct { + enum burn_message_warning message; + } warning; + struct { + enum burn_message_error message; + } error; + } detail; +}; + +/** Operation progress report. All values are 0 based indices. + * */ +struct burn_progress { + /** The total number of sessions */ + int sessions; + /** Current session.*/ + int session; + /** The total number of tracks */ + int tracks; + /** Current track. */ + int track; + /** The total number of indices */ + int indices; + /** Curent index. */ + int index; + /** The starting logical block address */ + int start_sector; + /** The number of sector */ + int sectors; + /** The current sector being processed */ + int sector; +}; + +/** Initialize the library. + This must be called before using any other functions in the library. It + may be called more than once with no effect. + If is possible to 'restart' the library by shutting it down and + re-initializing it, though there is no good reason to do that. + @return Nonzero if the library was able to initialize; zero if + initialization failed. +*/ +int burn_initialize(void); + +/** Shutdown the library. + This should be called before exiting your application. Make sure that all + drives you have grabbed are released before calling this. +*/ +void burn_finish(void); + +/** Set the verbosity level of the library. The default value is 0, which means + that nothing is output on stderr. The more you increase this, the more + debug output should be displayed on stderr for you. + @param level The verbosity level desired. 0 for nothing, higher positive + values for more information output. +*/ +void burn_set_verbosity(int level); + +/** Returns a newly allocated burn_message structure. This message should be + freed with burn_message_free() when you are finished with it. + @return A message or NULL when there are no more messages to retrieve. +*/ +struct burn_message* burn_get_message(void); + +/** Frees a burn_message structure */ +void burn_message_free(struct burn_message *msg); + +/** Scans for drives. This function MUST be called until it returns nonzero. + No drives can be in use when this is called or it will assert. + All drive pointers are invalidated by using this function. Do NOT store + drive pointers across calls to this function or death AND pain will ensue. + When the app is done with the burn_drive_info array, it must be freed with + burn_drive_info_free() + @param drives Returns an array of drives (cdroms/burners). The returned + array should be freed when it is no longer needed, and + before calling this function again to rescan. + @param n_drives Returns the number of hardware drives in @c drives. + @return Zero while scanning is not complete; non-zero when it is finished. +*/ +int burn_drive_scan(struct burn_drive_info *drives[], + unsigned int *n_drives); +/** Frees a burn_drive_info array returned by burn_drive_scan + @param info The array to free +*/ +void burn_drive_info_free(struct burn_drive_info *info); + +/** Grab a drive. This must be done before the drive can be used (for reading, + writing, etc). It may be neccesary to call this function more than once + to grab a drive. See burn_grab for details. + @param drive The drive to grab. This is found in a returned + burn_drive_info struct. + @param load Nonzero to make the drive attempt to load a disc (close its + tray door, etc). + @return 1 if the drive has been grabbed, else 0 +*/ +int burn_drive_grab(struct burn_drive *drive, int load); + +/** Release a drive. This should not be done until the drive is no longer + busy (see burn_drive_get_status). + @param drive The drive to release. + @param eject Nonzero to make the drive eject the disc in it. +*/ +void burn_drive_release(struct burn_drive *drive, int eject); + +/** Returns what kind of disc a drive is holding. This function may need to be + called more than once to get a proper status from it. See burn_status + for details. + @param drive The drive to query for a disc. + @return The status of the drive, or what kind of disc is in it. +*/ +enum burn_disc_status burn_disc_get_status(struct burn_drive *drive); + +/** Tells whether a disc can be erased or not + @return Non-zero means erasable +*/ +int burn_disc_erasable(struct burn_drive *d); + +/** Returns the progress and status of a drive. + @param drive The drive to query busy state for. + @param p Returns the progress of the operation, NULL if you don't care + @return the current status of the drive. See also burn_drive_status. +*/ +enum burn_drive_status burn_drive_get_status(struct burn_drive *drive, + struct burn_progress *p); + +/** Creates a write_opts struct for burning to the specified drive + must be freed with burn_write_opts_free + @param drive The drive to write with + @return The write_opts +*/ +struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive); + +/** Frees a write_opts struct created with burn_write_opts_new + @param opts write_opts to free +*/ +void burn_write_opts_free(struct burn_write_opts *opts); + +/** Creates a write_opts struct for reading from the specified drive + must be freed with burn_write_opts_free + @param drive The drive to read from + @return The read_opts +*/ +struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive); + +/** Frees a read_opts struct created with burn_read_opts_new + @param opts write_opts to free +*/ +void burn_read_opts_free(struct burn_read_opts *opts); + +/** Erase a disc in the drive. The drive must be grabbed successfully BEFORE + calling this functions. Always ensure that the drive reports a status of + BURN_DISC_FULL before calling this function. An erase operation is not + cancellable, as control of the operation is passed wholly to the drive and + there is no way to interrupt it safely. + @param drive The drive with which to erase a disc. + @param fast Nonzero to do a fast erase, where only the disc's headers are + erased; zero to erase the entire disc. +*/ +void burn_disc_erase(struct burn_drive *drive, int fast); + +/** Read a disc from the drive and write it to an fd pair. The drive must be + grabbed successfully BEFORE calling this function. Always ensure that the + drive reports a status of BURN_DISC_FULL before calling this function. + @param drive The drive from which to read a disc. + @param o The options for the read operation. +*/ +void burn_disc_read(struct burn_drive *drive, const struct burn_read_opts *o); + +/** Write a disc in the drive. The drive must be grabbed successfully BEFORE + calling this function. Always ensure that the drive reports a status of + BURN_DISC_BLANK or BURN_STATUS_FULL (to append a new session to the + disc) before calling this function. + @param o The options for the writing operation. + @param disc The struct burn_disc * that described the disc to be created +*/ +void burn_disc_write(struct burn_write_opts *o, struct burn_disc *disc); + +/** Cancel an operation on a drive. + This will only work when the drive's busy state is BURN_DRIVE_READING or + BURN_DRIVE_WRITING. + @param drive The drive on which to cancel the current operation. +*/ +void burn_drive_cancel(struct burn_drive *drive); + +/** Convert a minute-second-frame (MSF) value to sector count + @param m Minute component + @param s Second component + @param f Frame component + @return The sector count +*/ +int burn_msf_to_sectors(int m, int s, int f); + +/** Convert a sector count to minute-second-frame (MSF) + @param sectors The sector count + @param m Returns the minute component + @param s Returns the second component + @param f Returns the frame component +*/ +void burn_sectors_to_msf(int sectors, int *m, int *s, int *f); + +/** Convert a minute-second-frame (MSF) value to an lba + @param m Minute component + @param s Second component + @param f Frame component + @return The lba +*/ +int burn_msf_to_lba(int m, int s, int f); + +/** Convert an lba to minute-second-frame (MSF) + @param lba The lba + @param m Returns the minute component + @param s Returns the second component + @param f Returns the frame component +*/ +void burn_lba_to_msf(int lba, int *m, int *s, int *f); + +/** Create a new disc (for DAO recording)*/ +struct burn_disc *burn_disc_create(void); + +/** Delete disc and decrease the reference count on all its sessions + @param d The disc to be freed +*/ +void burn_disc_free(struct burn_disc *d); + +/** Create a new session (For SAO at once recording, or to be added to a + disc for DAO) +*/ +struct burn_session *burn_session_create(void); + +/** Free a session (and decrease reference count on all tracks inside) + @param s Session to be freed +*/ +void burn_session_free(struct burn_session *s); + +/** Add a session to a disc at a specific position, increasing the + sessions's reference count. + @param d Disc to add the session to + @param s Session to add to the disc + @param pos position to add at (BURN_POS_END is "at the end") + @return 0 for failure, 1 for success +*/ +int burn_disc_add_session(struct burn_disc *d, struct burn_session *s, + unsigned int pos); + +/** Remove a session from a disc + @param d Disc to remove session from + @param s Session pointer to find and remove +*/ +int burn_disc_remove_session(struct burn_disc *d, struct burn_session *s); + + +/** Create a track (for TAO recording, or to put in a session) */ +struct burn_track *burn_track_create(void); + +/** Free a track + @param t Track to free +*/ +void burn_track_free(struct burn_track *t); + +/** Add a track to a session at specified position + @param s Session to add to + @param t Track to insert in session + @param pos position to add at (BURN_POS_END is "at the end") + @return 0 for failure, 1 for success +*/ +int burn_session_add_track(struct burn_session *s, struct burn_track *t, + unsigned int pos); + +/** Remove a track from a session + @param s Session to remove track from + @param t Track pointer to find and remove + @return 0 for failure, 1 for success +*/ +int burn_session_remove_track(struct burn_session *s, struct burn_track *t); + + +/** Define the data in a track + @param t the track to define + @param offset The lib will write this many 0s before start of data + @param tail The number of extra 0s to write after data + @param pad 1 means the lib should pad the last sector with 0s if the + track isn't exactly sector sized. (otherwise the lib will + begin reading from the next track) + @param mode data format (bitfield) +*/ +void burn_track_define_data(struct burn_track *t, int offset, int tail, + int pad, int mode); + +/** Set the ISRC details for a track + @param t The track to change + @param country the 2 char country code. Each character must be + only numbers or letters. + @param owner 3 char owner code. Each character must be only numbers + or letters. + @param year 2 digit year. A number in 0-99 (Yep, not Y2K friendly). + @param serial 5 digit serial number. A number in 0-99999. +*/ +void burn_track_set_isrc(struct burn_track *t, char *country, char *owner, + unsigned char year, unsigned int serial); + +/** Disable ISRC parameters for a track + @param t The track to change +*/ +void burn_track_clear_isrc(struct burn_track *t); + +/** Hide the first track in the "pre gap" of the disc + @param s session to change + @param onoff 1 to enable hiding, 0 to disable +*/ +void burn_session_hide_first_track(struct burn_session *s, int onoff); + +/** Get the drive's disc struct - free when done + @param d drive to query + @return the disc struct +*/ +struct burn_disc *burn_drive_get_disc(struct burn_drive *d); + +/** Set the track's data source + @param t The track to set the data source for + @param s The data source to use for the contents of the track + @return An error code stating if the source is ready for use for + writing the track, or if an error occured + +*/ +enum burn_source_status burn_track_set_source(struct burn_track *t, + struct burn_source *s); + +/** Free a burn_source (decrease its refcount and maybe free it) + @param s Source to free +*/ +void burn_source_free(struct burn_source *s); + +/** Creates a data source for an image file (and maybe subcode file) */ +struct burn_source *burn_file_source_new(const char *path, + const char *subpath); + +/** Creates a data source for an image file (resp. a track) from an open + readable filedescriptor, an eventually open readable subcodes file + descriptor and eventually a fixed size in bytes. + @param datafd The source of data. + @param subfd The eventual source for subcodes. Not used if -1. + @param size The eventual fixed size of eventually both fds. + If this value is 0, the size will be determined from datafd. +*/ +struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size); + +/** Tells how long a track will be on disc */ +int burn_track_get_sectors(struct burn_track *); + + +/** Sets drive read and write speed + @param d The drive to set speed for + @param read Read speed in k/s (0 is max) + @param write Write speed in k/s (0 is max) +*/ +void burn_drive_set_speed(struct burn_drive *d, int read, int write); + +/* these are for my debugging, they will disappear */ +void burn_structure_print_disc(struct burn_disc *d); +void burn_structure_print_session(struct burn_session *s); +void burn_structure_print_track(struct burn_track *t); + +/** Sets the write type for the write_opts struct + @param opts The write opts to change + @param write_type The write type to use + @param block_type The block type to use + @return Returns 1 on success and 0 on failure. +*/ +int burn_write_opts_set_write_type(struct burn_write_opts *opts, + enum burn_write_types write_type, + int block_type); + +/** Supplies toc entries for writing - not normally required for cd mastering + @param opts The write opts to change + @param count The number of entries + @param toc_entries +*/ +void burn_write_opts_set_toc_entries(struct burn_write_opts *opts, + int count, + struct burn_toc_entry *toc_entries); + +/** Sets the session format for a disc + @param opts The write opts to change + @param format The session format to set +*/ +void burn_write_opts_set_format(struct burn_write_opts *opts, int format); + +/** Sets the simulate value for the write_opts struct + @param opts The write opts to change + @param sim If non-zero, the drive will perform a simulation instead of a burn + @return Returns 1 on success and 0 on failure. +*/ +int burn_write_opts_set_simulate(struct burn_write_opts *opts, int sim); + +/** Controls buffer underrun prevention + @param opts The write opts to change + @param underrun_proof if non-zero, buffer underrun protection is enabled + @return Returns 1 on success and 0 on failure. +*/ +int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts, + int underrun_proof); + +/** Sets whether to use opc or not with the write_opts struct + @param opts The write opts to change + @param opc If non-zero, optical power calibration will be performed at + start of burn + +*/ +void burn_write_opts_set_perform_opc(struct burn_write_opts *opts, int opc); + +void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts, int has_mediacatalog); + +void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, unsigned char mediacatalog[13]); + +/** Sets whether to read in raw mode or not + @param opts The read opts to change + @param raw_mode If non-zero, reading will be done in raw mode, so that everything in the data tracks on the + disc is read, including headers. +*/ +void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw_mode); + +/** Sets whether to report c2 errors or not + @param opts The read opts to change + @param c2errors If non-zero, report c2 errors. +*/ +void burn_read_opts_set_c2errors(struct burn_read_opts *opts, int c2errors); + +/** Sets whether to read subcodes from audio tracks or not + @param opts The read opts to change + @param subcodes_audio If non-zero, read subcodes from audio tracks on the disc. +*/ +void burn_read_opts_read_subcodes_audio(struct burn_read_opts *opts, + int subcodes_audio); + +/** Sets whether to read subcodes from data tracks or not + @param opts The read opts to change + @param subcodes_data If non-zero, read subcodes from data tracks on the disc. +*/ +void burn_read_opts_read_subcodes_data(struct burn_read_opts *opts, + int subcodes_data); + +/** Sets whether to recover errors if possible + @param opts The read opts to change + @param hardware_error_recovery If non-zero, attempt to recover errors if possible. +*/ +void burn_read_opts_set_hardware_error_recovery(struct burn_read_opts *opts, + int hardware_error_recovery); + +/** Sets whether to report recovered errors or not + @param opts The read opts to change + @param report_recovered_errors If non-zero, recovered errors will be reported. +*/ +void burn_read_opts_report_recovered_errors(struct burn_read_opts *opts, + int report_recovered_errors); + +/** Sets whether blocks with unrecoverable errors should be read or not + @param opts The read opts to change + @param transfer_damaged_blocks If non-zero, blocks with unrecoverable errors will still be read. +*/ +void burn_read_opts_transfer_damaged_blocks(struct burn_read_opts *opts, + int transfer_damaged_blocks); + +/** Sets the number of retries to attempt when trying to correct an error + @param opts The read opts to change + @param hardware_error_retries The number of retries to attempt when correcting an error. +*/ +void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts, + unsigned char hardware_error_retries); + +/** Gets the maximum write speed for a drive + @param d Drive to query + @return Maximum write speed in K/s +*/ +int burn_drive_get_write_speed(struct burn_drive *d); + +/** Gets the maximum read speed for a drive + @param d Drive to query + @return Maximum read speed in K/s +*/ +int burn_drive_get_read_speed(struct burn_drive *d); + +/** Gets a copy of the toc_entry structure associated with a track + @param t Track to get the entry from + @param entry Struct for the library to fill out +*/ +void burn_track_get_entry(struct burn_track *t, struct burn_toc_entry *entry); + +/** Gets a copy of the toc_entry structure associated with a session's lead out + @param s Session to get the entry from + @param entry Struct for the library to fill out +*/ +void burn_session_get_leadout_entry(struct burn_session *s, + struct burn_toc_entry *entry); + +/** Gets an array of all the sessions for the disc + THIS IS NO LONGER VALID AFTER YOU ADD OR REMOVE A SESSION + @param d Disc to get session array for + @param num Returns the number of sessions in the array + @return array of sessions +*/ +struct burn_session **burn_disc_get_sessions(struct burn_disc *d, + int *num); + +int burn_disc_get_sectors(struct burn_disc *d); + +/** Gets an array of all the tracks for a session + THIS IS NO LONGER VALID AFTER YOU ADD OR REMOVE A TRACK + @param s session to get track array for + @param num Returns the number of tracks in the array + @return array of tracks +*/ +struct burn_track **burn_session_get_tracks(struct burn_session *s, + int *num); + +int burn_session_get_sectors(struct burn_session *s); + +/** Gets the mode of a track + @param track the track to query + @return the track's mode +*/ +int burn_track_get_mode(struct burn_track *track); + +/** Returns whether the first track of a session is hidden in the pregap + @param session the session to query + @return non-zero means the first track is hidden +*/ +int burn_session_get_hidefirst(struct burn_session *session); + +/** Returns the library's version in its parts + @param major The major version number + @param minor The minor version number + @param micro The micro version number +*/ +void burn_version(int *major, int *minor, int *micro); + +#ifndef DOXYGEN + +BURN_END_DECLS + +#endif + +#endif /*LIBBURN_H*/ diff --git a/trunk/libburn/message.c b/trunk/libburn/message.c new file mode 100644 index 00000000..71f35bc9 --- /dev/null +++ b/trunk/libburn/message.c @@ -0,0 +1,108 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "message.h" +#include "libburn.h" +#include "debug.h" + +#include + +struct message_list +{ + struct message_list *next; + + struct burn_message *msg; +}; + +static struct message_list *queue; + +void burn_message_free(struct burn_message *msg) +{ + free(msg); +} + +void burn_message_clear_queue(void) +{ + struct burn_message *msg; + + if ((msg = burn_get_message())) { + burn_print(0, + "YOU HAVE MESSAGES QUEUED FROM THE LAST OPERATION. " + "YOU SHOULD BE GRABBING THEM ALL!\n"); + do { + burn_message_free(msg); + } while ((msg = burn_get_message())); + } +} + +struct burn_message *burn_get_message() +{ + struct burn_message *msg = NULL; + + if (queue) { + struct message_list *next; + + next = queue->next; + msg = queue->msg; + free(queue); + queue = next; + } + + return msg; +} + +static void queue_push_tail(struct burn_message *msg) +{ + struct message_list *node; + + node = malloc(sizeof(struct message_list)); + node->next = NULL; + node->msg = msg; + + if (!queue) + queue = node; + else { + struct message_list *it; + + for (it = queue; it->next; it = it->next) ; + it->next = node; + } +} + +void burn_message_info_new(struct burn_drive *drive, + enum burn_message_info message) +{ + struct burn_message *msg; + + msg = malloc(sizeof(struct burn_message)); + msg->drive = drive; + msg->type = BURN_MESSAGE_INFO; + msg->detail.info.message = message; + + queue_push_tail(msg); +} + +void burn_message_warning_new(struct burn_drive *drive, + enum burn_message_info message) +{ + struct burn_message *msg; + + msg = malloc(sizeof(struct burn_message)); + msg->drive = drive; + msg->type = BURN_MESSAGE_WARNING; + msg->detail.warning.message = message; + + queue_push_tail(msg); +} + +void burn_message_error_new(struct burn_drive *drive, + enum burn_message_info message) +{ + struct burn_message *msg; + + msg = malloc(sizeof(struct burn_message)); + msg->drive = drive; + msg->type = BURN_MESSAGE_ERROR; + msg->detail.error.message = message; + + queue_push_tail(msg); +} diff --git a/trunk/libburn/message.h b/trunk/libburn/message.h new file mode 100644 index 00000000..32613cde --- /dev/null +++ b/trunk/libburn/message.h @@ -0,0 +1,19 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __MESSAGE +#define __MESSAGE + +#include "libburn.h" + +void burn_message_clear_queue(void); + +void burn_message_info_new(struct burn_drive *drive, + enum burn_message_info message); + +void burn_message_warning_new(struct burn_drive *drive, + enum burn_message_info message); + +void burn_message_error_new(struct burn_drive *drive, + enum burn_message_info message); + +#endif diff --git a/trunk/libburn/mmc.c b/trunk/libburn/mmc.c new file mode 100644 index 00000000..efc0b662 --- /dev/null +++ b/trunk/libburn/mmc.c @@ -0,0 +1,506 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include +#include +#include "error.h" +#include "sector.h" +#include "libburn.h" +#include "transport.h" +#include "mmc.h" +#include "spc.h" +#include "drive.h" +#include "debug.h" +#include "toc.h" +#include "structure.h" +#include "options.h" + +static unsigned char MMC_GET_TOC[] = { 0x43, 2, 2, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_GET_ATIP[] = { 0x43, 2, 4, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_GET_DISC_INFO[] = + { 0x51, 0, 0, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_READ_CD[] = { 0xBE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_ERASE[] = { 0xA1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_SEND_OPC[] = { 0x54, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_SET_SPEED[] = + { 0xBB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_WRITE_12[] = + { 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_WRITE_10[] = { 0x2A, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_GET_CONFIGURATION[] = + { 0x46, 0, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_SYNC_CACHE[] = { 0x35, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_GET_EVENT[] = { 0x4A, 1, 0, 0, 16, 0, 0, 0, 8, 0 }; +static unsigned char MMC_CLOSE[] = { 0x5B, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_TRACK_INFO[] = { 0x52, 0, 0, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_SEND_CUE_SHEET[] = + { 0x5D, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +void mmc_send_cue_sheet(struct burn_drive *d, struct cue_sheet *s) +{ + struct buffer buf; + struct command c; + + c.retry = 1; + c.oplen = sizeof(MMC_SEND_CUE_SHEET); + memcpy(c.opcode, MMC_SEND_CUE_SHEET, sizeof(MMC_SEND_CUE_SHEET)); + c.page = &buf; + c.page->bytes = s->count * 8; + c.page->sectors = 0; + c.opcode[6] = (c.page->bytes >> 16) & 0xFF; + c.opcode[7] = (c.page->bytes >> 8) & 0xFF; + c.opcode[8] = c.page->bytes & 0xFF; + c.dir = TO_DRIVE; + memcpy(c.page->data, s->data, c.page->bytes); + d->issue_command(d, &c); +} + +int mmc_get_nwa(struct burn_drive *d) +{ + struct buffer buf; + struct command c; + unsigned char *data; + + c.retry = 1; + c.oplen = sizeof(MMC_TRACK_INFO); + memcpy(c.opcode, MMC_TRACK_INFO, sizeof(MMC_TRACK_INFO)); + c.opcode[1] = 1; + c.opcode[5] = 0xFF; + c.page = &buf; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + data = c.page->data; + return (data[12] << 24) + (data[13] << 16) + + (data[14] << 8) + data[15]; +} + +void mmc_close_disc(struct burn_drive *d, struct burn_write_opts *o) +{ + assert(o->drive == d); + o->multi = 0; + spc_select_write_params(d, o); + mmc_close(d, 1, 0); +} + +void mmc_close_session(struct burn_drive *d, struct burn_write_opts *o) +{ + assert(o->drive == d); + o->multi = 3; + spc_select_write_params(d, o); + mmc_close(d, 1, 0); +} + +void mmc_close(struct burn_drive *d, int session, int track) +{ + struct command c; + + c.retry = 1; + c.oplen = sizeof(MMC_CLOSE); + memcpy(c.opcode, MMC_CLOSE, sizeof(MMC_CLOSE)); + c.opcode[2] = session | !!track; + c.opcode[4] = track >> 8; + c.opcode[5] = track & 0xFF; + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void mmc_get_event(struct burn_drive *d) +{ + struct buffer buf; + struct command c; + + c.retry = 1; + c.oplen = sizeof(MMC_GET_EVENT); + memcpy(c.opcode, MMC_GET_EVENT, sizeof(MMC_GET_EVENT)); + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + burn_print(12, "0x%x:0x%x:0x%x:0x%x\n", + c.page->data[0], c.page->data[1], c.page->data[2], + c.page->data[3]); + burn_print(12, "event: %d:%d:%d:%d\n", c.page->data[4], + c.page->data[5], c.page->data[6], c.page->data[7]); +} + +void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf) +{ + struct command c; + int len; + + len = buf->sectors; + assert(buf->bytes >= buf->sectors); /* can be == at 0... */ + burn_print(100, "trying to write %d at %d\n", len, start); + memcpy(c.opcode, MMC_WRITE_12, sizeof(MMC_WRITE_12)); + c.retry = 1; + c.oplen = sizeof(MMC_WRITE_12); + c.opcode[2] = start >> 24; + c.opcode[3] = (start >> 16) & 0xFF; + c.opcode[4] = (start >> 8) & 0xFF; + c.opcode[5] = start & 0xFF; + c.opcode[6] = len >> 24; + c.opcode[7] = (len >> 16) & 0xFF; + c.opcode[8] = (len >> 8) & 0xFF; + c.opcode[9] = len & 0xFF; + c.page = buf; + c.dir = TO_DRIVE; + + d->issue_command(d, &c); +} + +int mmc_write(struct burn_drive *d, int start, struct buffer *buf) +{ + int cancelled; + struct command c; + int len; + + pthread_mutex_lock(&d->access_lock); + cancelled = d->cancel; + pthread_mutex_unlock(&d->access_lock); + + if (cancelled) + return BE_CANCELLED; + + len = buf->sectors; + assert(buf->bytes >= buf->sectors); /* can be == at 0... */ + burn_print(100, "trying to write %d at %d\n", len, start); + memcpy(c.opcode, MMC_WRITE_10, sizeof(MMC_WRITE_10)); + c.retry = 1; + c.oplen = sizeof(MMC_WRITE_10); + c.opcode[2] = start >> 24; + c.opcode[3] = (start >> 16) & 0xFF; + c.opcode[4] = (start >> 8) & 0xFF; + c.opcode[5] = start & 0xFF; + c.opcode[6] = 0; + c.opcode[7] = (len >> 8) & 0xFF; + c.opcode[8] = len & 0xFF; + c.page = buf; + c.dir = TO_DRIVE; +/* + burn_print(12, "%d, %d, %d, %d - ", c->opcode[2], c->opcode[3], c->opcode[4], c->opcode[5]); + burn_print(12, "%d, %d, %d, %d\n", c->opcode[6], c->opcode[7], c->opcode[8], c->opcode[9]); +*/ + +/* write(fileno(stderr), c.page->data, c.page->bytes);*/ + + d->issue_command(d, &c); + return 0; +} + +void mmc_read_toc(struct burn_drive *d) +{ +/* read full toc, all sessions, in m/s/f form, 4k buffer */ + struct burn_track *track; + struct burn_session *session; + struct buffer buf; + struct command c; + int dlen; + int i; + unsigned char *tdata; + + memcpy(c.opcode, MMC_GET_TOC, sizeof(MMC_GET_TOC)); + c.retry = 1; + c.oplen = sizeof(MMC_GET_TOC); + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + if (c.error) { + d->busy = BURN_DRIVE_IDLE; + return; + } + + dlen = c.page->data[0] * 256 + c.page->data[1]; + d->toc_entries = (dlen - 2) / 11; +/* + some drives fail this check. + + assert(((dlen - 2) % 11) == 0); +*/ + d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry)); + tdata = c.page->data + 4; + + burn_print(12, "TOC:\n"); + + d->disc = burn_disc_create(); + + for (i = 0; i < c.page->data[3]; i++) { + session = burn_session_create(); + burn_disc_add_session(d->disc, session, BURN_POS_END); + burn_session_free(session); + } + for (i = 0; i < d->toc_entries; i++, tdata += 11) { + burn_print(12, "S %d, PT %d, TNO %d : ", tdata[0], tdata[3], + tdata[2]); + burn_print(12, "(%d:%d:%d)", tdata[8], tdata[9], tdata[10]); + burn_print(12, "A(%d:%d:%d)", tdata[4], tdata[5], tdata[6]); + burn_print(12, " - control %d, adr %d\n", tdata[1] & 0xF, + tdata[1] >> 4); + + if (tdata[3] == 1) { + if (burn_msf_to_lba(tdata[8], tdata[9], tdata[10])) { + d->disc->session[0]->hidefirst = 1; + track = burn_track_create(); + burn_session_add_track(d->disc-> + session[tdata[0] - 1], + track, BURN_POS_END); + burn_track_free(track); + + } + } + if (tdata[3] < 100) { + track = burn_track_create(); + burn_session_add_track(d->disc->session[tdata[0] - 1], + track, BURN_POS_END); + track->entry = &d->toc_entry[i]; + burn_track_free(track); + } + d->toc_entry[i].session = tdata[0]; + d->toc_entry[i].adr = tdata[1] >> 4; + d->toc_entry[i].control = tdata[1] & 0xF; + d->toc_entry[i].tno = tdata[2]; + d->toc_entry[i].point = tdata[3]; + d->toc_entry[i].min = tdata[4]; + d->toc_entry[i].sec = tdata[5]; + d->toc_entry[i].frame = tdata[6]; + d->toc_entry[i].zero = tdata[7]; + d->toc_entry[i].pmin = tdata[8]; + d->toc_entry[i].psec = tdata[9]; + d->toc_entry[i].pframe = tdata[10]; + if (tdata[3] == 0xA0) + d->disc->session[tdata[0] - 1]->firsttrack = tdata[8]; + if (tdata[3] == 0xA1) + d->disc->session[tdata[0] - 1]->lasttrack = tdata[8]; + if (tdata[3] == 0xA2) + d->disc->session[tdata[0] - 1]->leadout_entry = + &d->toc_entry[i]; + } + if (d->status != BURN_DISC_APPENDABLE) + d->status = BURN_DISC_FULL; + toc_find_modes(d); +} + +void mmc_read_disc_info(struct burn_drive *d) +{ + struct buffer buf; + unsigned char *data; + struct command c; + + memcpy(c.opcode, MMC_GET_DISC_INFO, sizeof(MMC_GET_DISC_INFO)); + c.retry = 1; + c.oplen = sizeof(MMC_GET_DISC_INFO); + c.page = &buf; + c.page->sectors = 0; + c.page->bytes = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + if (c.error) { + d->busy = BURN_DRIVE_IDLE; + return; + } + + data = c.page->data; + d->erasable = !!(data[2] & 16); + switch (data[2] & 3) { + case 0: + d->toc_entries = 0; + d->start_lba = burn_msf_to_lba(data[17], data[18], data[19]); + d->end_lba = burn_msf_to_lba(data[21], data[22], data[23]); + d->status = BURN_DISC_BLANK; + break; + case 1: + d->status = BURN_DISC_APPENDABLE; + case 2: + mmc_read_toc(d); + break; + } +} + +void mmc_read_atip(struct burn_drive *d) +{ + struct buffer buf; + struct command c; + + memcpy(c.opcode, MMC_GET_ATIP, sizeof(MMC_GET_ATIP)); + c.retry = 1; + c.oplen = sizeof(MMC_GET_ATIP); + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + burn_print(1, "atip shit for you\n"); +} + +void mmc_read_sectors(struct burn_drive *d, + int start, + int len, + const struct burn_read_opts *o, struct buffer *buf) +{ + int temp; + int errorblock, req; + struct command c; + + assert(len >= 0); +/* if the drive isn't busy, why the hell are we here? */ + assert(d->busy); + burn_print(12, "reading %d from %d\n", len, start); + memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD)); + c.retry = 1; + c.oplen = sizeof(MMC_READ_CD); + temp = start; + c.opcode[5] = temp & 0xFF; + temp >>= 8; + c.opcode[4] = temp & 0xFF; + temp >>= 8; + c.opcode[3] = temp & 0xFF; + temp >>= 8; + c.opcode[2] = temp & 0xFF; + c.opcode[8] = len & 0xFF; + len >>= 8; + c.opcode[7] = len & 0xFF; + len >>= 8; + c.opcode[6] = len & 0xFF; + req = 0xF8; + if (d->busy == BURN_DRIVE_GRABBING || o->report_recovered_errors) + req |= 2; + + c.opcode[10] = 0; +/* always read the subcode, throw it away later, since we don't know + what we're really reading +*/ + if (d->busy == BURN_DRIVE_GRABBING || (o->subcodes_audio) + || (o->subcodes_data)) + c.opcode[10] = 1; + + c.opcode[9] = req; + c.page = buf; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + if (c.error) { + burn_print(12, "got an error over here\n"); + burn_print(12, "%d, %d, %d, %d\n", c.sense[3], c.sense[4], + c.sense[5], c.sense[6]); + errorblock = + (c.sense[3] << 24) + (c.sense[4] << 16) + + (c.sense[5] << 8) + c.sense[6]; + c.page->sectors = errorblock - start + 1; + burn_print(1, "error on block %d\n", errorblock); + burn_print(12, "error on block %d\n", errorblock); + burn_print(12, "returning %d sectors\n", c.page->sectors); + } +} + +void mmc_erase(struct burn_drive *d, int fast) +{ + struct command c; + + memcpy(c.opcode, MMC_ERASE, sizeof(MMC_ERASE)); + c.opcode[1] = 16; /* IMMED set to 1 */ + c.opcode[1] |= !!fast; + c.retry = 1; + c.oplen = sizeof(MMC_ERASE); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void mmc_read_lead_in(struct burn_drive *d, struct buffer *buf) +{ + int len; + struct command c; + + len = buf->sectors; + memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD)); + c.retry = 1; + c.oplen = sizeof(MMC_READ_CD); + c.opcode[5] = 0; + c.opcode[4] = 0; + c.opcode[3] = 0; + c.opcode[2] = 0xF0; + c.opcode[8] = 1; + c.opcode[7] = 0; + c.opcode[6] = 0; + c.opcode[9] = 0; + c.opcode[10] = 2; + c.page = buf; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); +} + +void mmc_perform_opc(struct burn_drive *d) +{ + struct command c; + + memcpy(c.opcode, MMC_SEND_OPC, sizeof(MMC_SEND_OPC)); + c.retry = 1; + c.oplen = sizeof(MMC_SEND_OPC); + c.opcode[1] = 1; + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void mmc_set_speed(struct burn_drive *d, int r, int w) +{ + struct command c; + + memcpy(c.opcode, MMC_SET_SPEED, sizeof(MMC_SET_SPEED)); + c.retry = 1; + c.oplen = sizeof(MMC_SET_SPEED); + c.opcode[2] = r >> 8; + c.opcode[3] = r & 0xFF; + c.opcode[4] = w >> 8; + c.opcode[5] = w & 0xFF; + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void mmc_get_configuration(struct burn_drive *d) +{ + struct buffer buf; + int len; + struct command c; + + memcpy(c.opcode, MMC_GET_CONFIGURATION, sizeof(MMC_GET_CONFIGURATION)); + c.retry = 1; + c.oplen = sizeof(MMC_GET_CONFIGURATION); + c.page = &buf; + c.page->sectors = 0; + c.page->bytes = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + burn_print(1, "got it back\n"); + len = (c.page->data[0] << 24) + + (c.page->data[1] << 16) + + (c.page->data[2] << 8) + + c.page->data[3]; + burn_print(1, "all %d bytes of it\n", len); + burn_print(1, "%d, %d, %d, %d\n", + c.page->data[0], + c.page->data[1], c.page->data[2], c.page->data[3]); +} + +void mmc_sync_cache(struct burn_drive *d) +{ + struct command c; + + memcpy(c.opcode, MMC_SYNC_CACHE, sizeof(MMC_SYNC_CACHE)); + c.retry = 1; + c.oplen = sizeof(MMC_SYNC_CACHE); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} diff --git a/trunk/libburn/mmc.h b/trunk/libburn/mmc.h new file mode 100644 index 00000000..95275b7c --- /dev/null +++ b/trunk/libburn/mmc.h @@ -0,0 +1,37 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __MMC +#define __MMC + +struct burn_drive; +struct burn_write_opts; +struct command; +struct buffer; +struct cue_sheet; + +/* MMC commands */ + +void mmc_read(struct burn_drive *); +void mmc_close_session(struct burn_drive *, struct burn_write_opts *); +void mmc_close_disc(struct burn_drive *, struct burn_write_opts *); +void mmc_close(struct burn_drive *, int session, int track); +void mmc_get_event(struct burn_drive *); +int mmc_write(struct burn_drive *, int start, struct buffer *buf); +void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf); +void mmc_sync_cache(struct burn_drive *); +void mmc_load(struct burn_drive *); +void mmc_eject(struct burn_drive *); +void mmc_erase(struct burn_drive *, int); +void mmc_read_toc(struct burn_drive *); +void mmc_read_disc_info(struct burn_drive *); +void mmc_read_atip(struct burn_drive *); +void mmc_read_sectors(struct burn_drive *, + int, + int, const struct burn_read_opts *, struct buffer *); +void mmc_set_speed(struct burn_drive *, int, int); +void mmc_read_lead_in(struct burn_drive *, struct buffer *); +void mmc_perform_opc(struct burn_drive *); +void mmc_get_configuration(struct burn_drive *); +int mmc_get_nwa(struct burn_drive *); +void mmc_send_cue_sheet(struct burn_drive *, struct cue_sheet *); +#endif /*__MMC*/ diff --git a/trunk/libburn/null.c b/trunk/libburn/null.c new file mode 100644 index 00000000..415e0a49 --- /dev/null +++ b/trunk/libburn/null.c @@ -0,0 +1,27 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "null.h" +#include "libburn.h" +#include + +#include +int null_read(struct burn_source *source, unsigned char *buffer, int size) +{ + memset(buffer, 0, size); + return size; +} + +struct burn_source *burn_null_source_new(void) +{ + struct burn_source *src; + + src = malloc(sizeof(struct burn_source)); + src->refcount = 1; + src->read = null_read; + src->read_sub = NULL; + + src->get_size = 0; + src->free_data = NULL; + src->data = NULL; + return src; +} diff --git a/trunk/libburn/null.h b/trunk/libburn/null.h new file mode 100644 index 00000000..1a7aae33 --- /dev/null +++ b/trunk/libburn/null.h @@ -0,0 +1,10 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__NULL_H +#define BURN__NULL_H + +struct burn_source; +int null_read(struct burn_source *source, unsigned char *buffer, int size); +struct burn_source *burn_null_source_new(void); + +#endif /* LIBBURN__NULL_H */ diff --git a/trunk/libburn/options.c b/trunk/libburn/options.c new file mode 100644 index 00000000..938a23c1 --- /dev/null +++ b/trunk/libburn/options.c @@ -0,0 +1,169 @@ +#include "libburn.h" +#include "options.h" +#include "transport.h" + +#include +#include +#include +struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive) +{ + struct burn_write_opts *opts; + + opts = malloc(sizeof(struct burn_write_opts)); + opts->drive = drive; + opts->refcount = 1; + opts->write_type = BURN_WRITE_TAO; + opts->block_type = BURN_BLOCK_MODE1; + opts->toc_entry = NULL; + opts->toc_entries = 0; + opts->simulate = 0; + opts->underrun_proof = drive->mdata->underrun_proof; + opts->perform_opc = 1; + opts->has_mediacatalog = 0; + opts->format = BURN_CDROM; + opts->multi = 0; + opts->control = 0; + return opts; +} + +void burn_write_opts_free(struct burn_write_opts *opts) +{ + if (--opts->refcount <= 0) + free(opts); +} + +struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive) +{ + struct burn_read_opts *opts; + + opts = malloc(sizeof(struct burn_read_opts)); + opts->drive = drive; + opts->refcount = 1; + opts->raw = 0; + opts->c2errors = 0; + opts->subcodes_audio = 0; + opts->subcodes_data = 0; + opts->hardware_error_recovery = 0; + opts->report_recovered_errors = 0; + opts->transfer_damaged_blocks = 0; + opts->hardware_error_retries = 3; + + return opts; +} + +void burn_read_opts_free(struct burn_read_opts *opts) +{ + if (--opts->refcount <= 0) + free(opts); +} + +int burn_write_opts_set_write_type(struct burn_write_opts *opts, + enum burn_write_types write_type, + int block_type) +{ + if ((write_type == BURN_WRITE_SAO && block_type == BURN_BLOCK_SAO) || + (opts->drive->block_types[write_type] & block_type)) { + opts->write_type = write_type; + opts->block_type = block_type; + return 1; + } + assert(0); + return 0; +} + +void burn_write_opts_set_toc_entries(struct burn_write_opts *opts, int count, + struct burn_toc_entry *toc_entries) +{ + opts->toc_entries = count; + opts->toc_entry = malloc(count * sizeof(struct burn_toc_entry)); + memcpy(opts->toc_entry, &toc_entries, + sizeof(struct burn_toc_entry) * count); +} + +void burn_write_opts_set_format(struct burn_write_opts *opts, int format) +{ + opts->format = format; +} + +int burn_write_opts_set_simulate(struct burn_write_opts *opts, int sim) +{ + if (opts->drive->mdata->simulate) { + opts->simulate = sim; + return 1; + } + return 0; +} + +int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts, + int underrun_proof) +{ + if (opts->drive->mdata->underrun_proof) { + opts->underrun_proof = underrun_proof; + return 1; + } + return 0; +} + +void burn_write_opts_set_perform_opc(struct burn_write_opts *opts, int opc) +{ + opts->perform_opc = opc; +} + +void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts, + int has_mediacatalog) +{ + opts->has_mediacatalog = has_mediacatalog; +} + +void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, + unsigned char mediacatalog[13]) +{ + memcpy(opts->mediacatalog, &mediacatalog, 13); +} + +void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw) +{ + opts->raw = raw; +} + +void burn_read_opts_set_c2errors(struct burn_read_opts *opts, int c2errors) +{ + opts->c2errors = c2errors; +} + +void burn_read_opts_read_subcodes_audio(struct burn_read_opts *opts, + int subcodes_audio) +{ + opts->subcodes_audio = subcodes_audio; +} + +void burn_read_opts_read_subcodes_data(struct burn_read_opts *opts, + int subcodes_data) +{ + opts->subcodes_data = subcodes_data; +} + +void burn_read_opts_set_hardware_error_recovery(struct burn_read_opts *opts, + int hardware_error_recovery) +{ + opts->hardware_error_recovery = hardware_error_recovery; +} + +void burn_read_opts_report_recovered_errors(struct burn_read_opts *opts, + int report_recovered_errors) +{ + opts->report_recovered_errors = report_recovered_errors; +} + +void burn_read_opts_transfer_damaged_blocks(struct burn_read_opts *opts, + int transfer_damaged_blocks) +{ + opts->transfer_damaged_blocks = transfer_damaged_blocks; +} + +void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts, + unsigned char + hardware_error_retries) +{ + opts->hardware_error_retries = hardware_error_retries; +} diff --git a/trunk/libburn/options.h b/trunk/libburn/options.h new file mode 100644 index 00000000..aebfdb5d --- /dev/null +++ b/trunk/libburn/options.h @@ -0,0 +1,78 @@ +#ifndef BURN__OPTIONS_H +#define BURN__OPTIONS_H + +#include "libburn.h" + +/** Options for disc writing operations. This should be created with + burn_write_opts_new() and freed with burn_write_opts_free(). */ +struct burn_write_opts +{ + /** Drive the write opts are good for */ + struct burn_drive *drive; + + /** For internal use. */ + int refcount; + + /** The method/style of writing to use. */ + enum burn_write_types write_type; + /** format of the data to send to the drive */ + enum burn_block_types block_type; + + /** Number of toc entries. if this is 0, they will be auto generated*/ + int toc_entries; + /** Toc entries for the disc */ + struct burn_toc_entry *toc_entry; + + /** Simulate the write so that the disc is not actually written */ + unsigned int simulate:1; + /** If available, enable a drive feature which prevents buffer + underruns if not enough data is available to keep up with the + drive. */ + unsigned int underrun_proof:1; + /** Perform calibration of the drive's laser before beginning the + write. */ + unsigned int perform_opc:1; + /** A disc can have a media catalog number */ + int has_mediacatalog; + unsigned char mediacatalog[13]; + /** Session format */ + int format; + /* internal use only */ + unsigned char control; + unsigned char multi; +}; + +/** Options for disc reading operations. This should be created with + burn_read_opts_new() and freed with burn_read_opts_free(). */ +struct burn_read_opts +{ + /** Drive the read opts are good for */ + struct burn_drive *drive; + + /** For internal use. */ + int refcount; + + /** Read in raw mode, so that everything in the data tracks on the + disc is read, including headers. Not needed if just reading a + filesystem off a disc, but it should usually be used when making a + disc image or copying a disc. */ + unsigned int raw:1; + /** Report c2 errors. Useful for statistics reporting */ + unsigned int c2errors:1; + /** Read subcodes from audio tracks on the disc */ + unsigned int subcodes_audio:1; + /** Read subcodes from data tracks on the disc */ + unsigned int subcodes_data:1; + /** Have the drive recover errors if possible */ + unsigned int hardware_error_recovery:1; + /** Report errors even when they were recovered from */ + unsigned int report_recovered_errors:1; + /** Read blocks even when there are unrecoverable errors in them */ + unsigned int transfer_damaged_blocks:1; + + /** The number of retries the hardware should make to correct + errors. */ + unsigned char hardware_error_retries; +}; + +#endif /* BURN__OPTIONS_H */ diff --git a/trunk/libburn/read.c b/trunk/libburn/read.c new file mode 100644 index 00000000..4417ef1d --- /dev/null +++ b/trunk/libburn/read.c @@ -0,0 +1,264 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include +#include +#include +#include "sector.h" +#include "libburn.h" +#include "drive.h" +#include "transport.h" +#include "message.h" +#include "crc.h" +#include "debug.h" +#include "init.h" +#include "lec.h" +#include "toc.h" +#include "util.h" +#include "sg.h" +#include "read.h" +#include "options.h" + +void burn_disc_read(struct burn_drive *d, const struct burn_read_opts *o) +{ +#if 0 + int i, end, maxsects, finish; + int seclen; + int drive_lba; + unsigned short crc; + unsigned char fakesub[96]; + struct buffer page; + int speed; + + assert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000)); + + assert(!d->busy); + assert(d->toc->valid); + assert(o->datafd != -1); +/* XXX not sure this is a good idea. copy it? */ +/* XXX also, we have duplicated data now, do we remove the fds from struct +drive, or only store a subset of the _opts structs in drives */ + + /* set the speed on the drive */ + speed = o->speed > 0 ? o->speed : d->mdata->max_read_speed; + d->set_speed(d, speed, 0); + + d->params.retries = o->hardware_error_retries; + d->send_parameters(d, o); + + d->cancel = 0; + d->busy = BURN_DRIVE_READING; + d->currsession = 0; +/* drive_lba = 232000; + d->currtrack = 18; +*/ + d->currtrack = 0; + drive_lba = 0; +/* XXX removal of this line obviously breaks * + d->track_end = burn_track_end(d, d->currsession, d->currtrack);*/ + printf("track ends at %d\n", d->track_end); + page.sectors = 0; + page.bytes = 0; + + if (o->subfd != -1) { + memset(fakesub, 0xFF, 12); + memset(fakesub + 12, 0, 84); + fakesub[13] = 1; + fakesub[14] = 1; + fakesub[20] = 2; + fakesub[12] = (d->toc->toc_entry[0].control << 4) + + d->toc->toc_entry[0].adr; + crc = crc_ccitt(fakesub + 12, 10); + fakesub[22] = crc >> 8; + fakesub[23] = crc & 0xFF; + write(o->subfd, fakesub, 96); + } + while (1) { + seclen = burn_sector_length_read(d, o); + + burn_print(12, "received %d blocks\n", page.sectors); + for (i = 0; i < page.sectors; i++) { + burn_packet_process(d, page.data + seclen * i, o); + d->track_end--; + drive_lba++; + } + + if ((d->cancel) || (drive_lba == LAST_SESSION_END(d))) { + burn_print(1, "finished or cancelled\n"); + d->busy = BURN_DRIVE_IDLE; + if (!d->cancel) + d->toc->complete = 1; + return; + } +/* XXX: removal of this line obviously breaks * + end = burn_track_end(d, d->currsession, d->currtrack); */ + + if (drive_lba == end) { + d->currtrack++; + if (d->currtrack > + d->toc->session[d->currsession].lasttrack) { + d->currsession++; + burn_print(12, "session switch to %d\n", + d->currsession); + burn_print(12, "skipping a lead out\n"); + drive_lba = CURRENT_SESSION_START(d); + burn_print(12, "new lba %d\n", drive_lba); +/* XXX more of the same + end = burn_track_end(d, d->currsession, + d->currtrack); +*/ } + burn_print(12, "track switch to %d\n", d->currtrack); + } + + page.sectors = 0; + page.bytes = 0; + + maxsects = BUFFER_SIZE / seclen; + finish = end - drive_lba; + + d->track_end = finish; + + page.sectors = (finish < maxsects) ? finish : maxsects; + printf("reading %d sectors from %d\n", page.sectors, + drive_lba); + d->read_sectors(d, drive_lba, page.sectors, o, &page); + printf("Read %d\n", page.sectors); + } +#endif +} +int burn_sector_length_read(struct burn_drive *d, + const struct burn_read_opts *o) +{ + int dlen = 2352; + int data; + +/*XXX how do we handle this crap now?*/ +/* data = d->toc->track[d->currtrack].toc_entry->control & 4;*/ + data = 1; + if (o->report_recovered_errors) + dlen += 294; + if ((o->subcodes_data) && data) + dlen += 96; + if ((o->subcodes_audio) && !data) + dlen += 96; + return dlen; +} + +static int bitcount(unsigned char *data, int n) +{ + int i, j, count = 0; + unsigned char tem; + + for (i = 0; i < n; i++) { + tem = data[i]; + for (j = 0; j < 8; j++) { + count += tem & 1; + tem >>= 1; + } + } + return count; +} + +void burn_packet_process(struct burn_drive *d, unsigned char *data, + const struct burn_read_opts *o) +{ + unsigned char sub[96]; + unsigned short crc; + int ptr = 2352, i, j, code, fb; + int audio = 1; + + if (o->c2errors) { + fb = bitcount(data + ptr, 294); + if (fb) { + burn_print(1, "%d damaged bits\n", + bitcount(data + ptr, 294)); + burn_print(1, "sending error on %s %s\n", + d->idata->vendor, d->idata->product); + /* XXX send a burn_message! burn_message_error(d, + something); */ + } + ptr += 294; + } +/* + if (d->toc->track[d->currtrack].mode == BURN_MODE_UNINITIALIZED) { + if ((d->toc->track[d->currtrack].toc_entry->control & 4) == 0) + d->toc->track[d->currtrack].mode = BURN_MODE_AUDIO; + else + switch (data[15]) { + case 0: + d->toc->track[d->currtrack].mode = BURN_MODE0; + break; + case 1: + d->toc->track[d->currtrack].mode = BURN_MODE1; + break; + case 2: + d->toc->track[d->currtrack].mode = + BURN_MODE2_FORMLESS; + break; + } + } +*/ + if ((audio && o->subcodes_audio) + || (!audio && o->subcodes_data)) { + memset(sub, 0, sizeof(sub)); + for (i = 0; i < 12; i++) { + for (j = 0; j < 8; j++) { + for (code = 0; code < 8; code++) { + sub[code * 12 + i] <<= 1; + if (data[ptr + j + i * 8] & + (1 << (7 - code))) + sub[code * 12 + i]++; + } + } + } + crc = (*(sub + 22) << 8) + *(sub + 23); + if (crc != crc_ccitt(sub + 12, 10)) { + burn_print(1, "sending error on %s %s\n", + d->idata->vendor, d->idata->product); +/* e = burn_error(); + e->drive = d; +*/ + burn_print(1, "crc mismatch in Q\n"); + } + /* else process_q(d, sub + 12); */ + /* + if (o->subfd != -1) write(o->subfd, sub, 96); */ + } +/* + if ((d->track_end <= 150) + && (drive_lba + 150 < CURRENT_SESSION_END(d)) + && (TOC_ENTRY(d->toc, d->currtrack).control == 4) + && (TOC_ENTRY(d->toc, d->currtrack + 1).control == 0)) { + burn_print(12, "pregap : %d\n", d->track_end); + write(o->binfd, zeros, 2352); + +#warning XXX WHERE ARE MY SUBCODES + } else +*//* write(o->datafd, data, 2352); */ +} + +/* so yeah, when you uncomment these, make them write zeros insted of crap +static void write_empty_sector(int fd) +{ + char sec[2352]; + + burn_print(1, "writing an 'empty' sector\n"); + write(fd, sec, 2352); +} + +static void write_empty_subcode(int fd) +{ + char sub[96]; + + write(fd, sub, 96); +} + +static void flipq(unsigned char *sub) +{ + *(sub + 12 + 10) = ~*(sub + 12 + 10); + *(sub + 12 + 11) = ~*(sub + 12 + 11); +} +*/ diff --git a/trunk/libburn/read.h b/trunk/libburn/read.h new file mode 100644 index 00000000..fc079583 --- /dev/null +++ b/trunk/libburn/read.h @@ -0,0 +1,14 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __LIBBURN_READ +#define __LIBBURN_READ + +struct burn_drive; +struct burn_read_opts; + +int burn_sector_length_read(struct burn_drive *d, + const struct burn_read_opts *o); +void burn_packet_process(struct burn_drive *d, unsigned char *data, + const struct burn_read_opts *o); + +#endif /* __LIBBURN_READ */ diff --git a/trunk/libburn/sbc.c b/trunk/libburn/sbc.c new file mode 100644 index 00000000..dcd64ae0 --- /dev/null +++ b/trunk/libburn/sbc.c @@ -0,0 +1,40 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* scsi block commands */ + +#include +#include +#include + +#include "transport.h" +#include "sbc.h" +#include "options.h" + +/* spc command set */ +static char SBC_LOAD[] = { 0x1b, 0, 0, 0, 3, 0 }; +static char SBC_UNLOAD[] = { 0x1b, 0, 0, 0, 2, 0 }; + +void sbc_load(struct burn_drive *d) +{ + struct command c; + + memcpy(c.opcode, SBC_LOAD, sizeof(SBC_LOAD)); + c.retry = 1; + c.oplen = sizeof(SBC_LOAD); + c.dir = NO_TRANSFER; + c.page = NULL; + d->issue_command(d, &c); +} + +void sbc_eject(struct burn_drive *d) +{ + struct command c; + + c.page = NULL; + memcpy(c.opcode, SBC_UNLOAD, sizeof(SBC_UNLOAD)); + c.oplen = 1; + c.oplen = sizeof(SBC_UNLOAD); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} diff --git a/trunk/libburn/sbc.h b/trunk/libburn/sbc.h new file mode 100644 index 00000000..3ca028cf --- /dev/null +++ b/trunk/libburn/sbc.h @@ -0,0 +1,11 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SBC +#define __SBC + +struct burn_drive; + +void sbc_load(struct burn_drive *); +void sbc_eject(struct burn_drive *); + +#endif /* __SBC */ diff --git a/trunk/libburn/sector.c b/trunk/libburn/sector.c new file mode 100644 index 00000000..2d7ce17b --- /dev/null +++ b/trunk/libburn/sector.c @@ -0,0 +1,649 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include "error.h" +#include "options.h" +#include "transport.h" +#include "libburn.h" +#include "drive.h" +#include "sector.h" +#include "crc.h" +#include "debug.h" +#include "lec.h" +#include "toc.h" +#include "write.h" + +/*static unsigned char isrc[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";*/ + +#define sector_common(X) d->alba++; d->rlba X; + +static void uncook_subs(unsigned char *dest, unsigned char *source) +{ + int i, j, code; + + memset(dest, 0, 96); + + for (i = 0; i < 12; i++) { + for (j = 0; j < 8; j++) { + for (code = 0; code < 8; code++) { + if (source[code * 12 + i] & 0x80) + dest[j + i * 8] |= (1 << (7 - code)); + source[code * 12 + i] <<= 1; + } + } + } +} + +/* 0 means "same as inmode" */ +static int get_outmode(struct burn_write_opts *o) +{ + if (o->write_type == BURN_WRITE_SAO) + return 0; + else + switch (o->block_type) { + case BURN_BLOCK_RAW0: + return BURN_MODE_RAW; + case BURN_BLOCK_RAW16: + return BURN_MODE_RAW | BURN_SUBCODE_P16; + case BURN_BLOCK_RAW96P: + return BURN_MODE_RAW | BURN_SUBCODE_P96; + case BURN_BLOCK_RAW96R: + return BURN_MODE_RAW | BURN_SUBCODE_R96; + case BURN_BLOCK_MODE1: + return BURN_MODE1; + } + assert(0); /* return BURN_MODE_UNIMPLEMENTED :) */ +} + +static void get_bytes(struct burn_track *track, int count, unsigned char *data) +{ + int valid, shortage, curr; + +/* no track pointer means we're just generating 0s */ + if (!track) { + memset(data, 0, count); + return; + } + +/* first we use up any offset */ + valid = track->offset - track->offsetcount; + if (valid > count) + valid = count; + + if (valid) { + track->offsetcount += valid; + memset(data, 0, valid); + } + shortage = count - valid; + + if (!shortage) + return; + +/* Next we use source data */ + curr = valid; + if (!track->eos) { + valid = track->source->read(track->source, data + curr, count - curr); + } else valid = 0; + + if (valid == -1) { + track->eos = 1; + valid = 0; + } + + curr += valid; + shortage = count - curr; + + if (!shortage) + return; + +/* Before going to the next track, we run through any tail */ + + valid = track->tail - track->tailcount; + if (valid > count - curr) + valid = count - curr; + + if (valid) { + track->tailcount += valid; + memset(data + curr, 0, valid); + } + curr += valid; + shortage -= valid; + + if (!shortage) + return; + +/* If we're still short, and there's a "next" pointer, we pull from that. + if that depletes, we'll just fill with 0s. +*/ + if (track->source->next) { + struct burn_source *src; + printf("pulling from next track\n"); + src = track->source->next; + valid = src->read(src, data + curr, shortage); + if (valid > 0) { + shortage -= valid; + curr += valid; + } + } + if (!shortage) + return; + memset(data + curr, 0, shortage); +} +static unsigned char *get_sector(struct burn_write_opts *opts, int inmode) +{ + struct burn_drive *d = opts->drive; + struct buffer *out = d->buffer; + int outmode; + int seclen; + unsigned char *ret; + + outmode = get_outmode(opts); + if (outmode == 0) + outmode = inmode; + + seclen = burn_sector_length(outmode) + burn_subcode_length(outmode); + + if (out->bytes + (seclen) >= BUFFER_SIZE) { + int err; + err = d->write(d, d->nwa, out); + if (err == BE_CANCELLED) + return NULL; + d->nwa += out->sectors; + out->bytes = 0; + out->sectors = 0; + } + + ret = out->data + out->bytes; + out->bytes += seclen; + out->sectors++; + + return ret; +} + +/* either inmode == outmode, or outmode == raw. anything else is bad news */ +static void convert_data(struct burn_write_opts *o, struct burn_track *track, + int inmode, unsigned char *data) +{ + int outlen, inlen; + int offset = -1; + int outmode; + + outmode = get_outmode(o); + if (outmode == 0) + outmode = inmode; + + outlen = burn_sector_length(outmode); + inlen = burn_sector_length(inmode); + assert(outlen >= inlen); + + if ((outmode & BURN_MODE_BITS) == (inmode & BURN_MODE_BITS)) { + get_bytes(track, inlen, data); + return; + } + + assert(outmode & BURN_MODE_RAW); + + if (inmode & BURN_MODE1) + offset = 16; + if (inmode & BURN_MODE_RAW) + offset = 0; + if (inmode & BURN_AUDIO) + offset = 0; + assert(offset != -1); + get_bytes(track, inlen, data + offset); +} +static void convert_subs(struct burn_write_opts *o, int inmode, + unsigned char *subs, unsigned char *sector) +{ + unsigned char *out; + int outmode; + + outmode = get_outmode(o); + if (outmode == 0) + outmode = inmode; + sector += burn_sector_length(outmode); +/* XXX for sao with subs, we'd need something else... */ + + switch (o->block_type) { + case BURN_BLOCK_RAW96R: + uncook_subs(sector, subs); + break; + + case BURN_BLOCK_RAW16: + memcpy(sector, subs + 12, 12); + out = sector + 12; + out[0] = 0; + out[1] = 0; + out[2] = 0; +/*XXX find a better way to deal with partially damaged P channels*/ + if (subs[2] != 0) + out[3] = 0x80; + else + out[3] = 0; + out = sector + 10; + + out[0] = ~out[0]; + out[1] = ~out[1]; + break; + } +} + +static void subcode_toc(struct burn_drive *d, int mode, unsigned char *data) +{ + unsigned char *q; + int track; + int crc; + int min, sec, frame; + + track = d->toc_temp / 3; + memset(data, 0, 96); + q = data + 12; + + burn_lba_to_msf(d->rlba, &min, &sec, &frame); +/*XXX track numbers are BCD +a0 - 1st track ctrl +a1 - last track ctrl +a2 - lout ctrl +*/ + q[0] = (d->toc_entry[track].control << 4) + 1; + q[1] = 0; + if (d->toc_entry[track].point < 100) + q[2] = dec_to_bcd(d->toc_entry[track].point); + else + q[2] = d->toc_entry[track].point; + q[3] = dec_to_bcd(min); + q[4] = dec_to_bcd(sec); + q[5] = dec_to_bcd(frame); + q[6] = 0; + q[7] = dec_to_bcd(d->toc_entry[track].pmin); + q[8] = dec_to_bcd(d->toc_entry[track].psec); + q[9] = dec_to_bcd(d->toc_entry[track].pframe); + crc = crc_ccitt(q, 10); + q[10] = crc >> 8; + q[11] = crc & 0xFF; + d->toc_temp++; + d->toc_temp %= (d->toc_entries * 3); +} + +int sector_toc(struct burn_write_opts *o, int mode) +{ + struct burn_drive *d = o->drive; + unsigned char *data; + unsigned char subs[96]; + + data = get_sector(o, mode); + if (!data) + return 0; + convert_data(o, NULL, mode, data); + subcode_toc(d, mode, subs); + convert_subs(o, mode, subs, data); + sector_headers(o, data, mode, 1); + sector_common(++) + return 1; +} + +int sector_pregap(struct burn_write_opts *o, + unsigned char tno, unsigned char control, int mode) +{ + struct burn_drive *d = o->drive; + unsigned char *data; + unsigned char subs[96]; + + data = get_sector(o, mode); + if (!data) + return 0; + convert_data(o, NULL, mode, data); + subcode_user(o, subs, tno, control, 0, NULL, 1); + convert_subs(o, mode, subs, data); + sector_headers(o, data, mode, 0); + sector_common(--) + return 1; +} + +int sector_postgap(struct burn_write_opts *o, + unsigned char tno, unsigned char control, int mode) +{ + struct burn_drive *d = o->drive; + unsigned char subs[96]; + unsigned char *data; + + data = get_sector(o, mode); + if (!data) + return 0; + convert_data(o, NULL, mode, data); +/* use last index in track */ + subcode_user(o, subs, tno, control, 1, NULL, 1); + convert_subs(o, mode, subs, data); + sector_headers(o, data, mode, 0); + sector_common(++) + return 1; +} + +static void subcode_lout(struct burn_write_opts *o, unsigned char control, + unsigned char *data) +{ + struct burn_drive *d = o->drive; + unsigned char *q; + int crc; + int rmin, min, rsec, sec, rframe, frame; + + memset(data, 0, 96); + q = data + 12; + + burn_lba_to_msf(d->alba, &min, &sec, &frame); + burn_lba_to_msf(d->rlba, &rmin, &rsec, &rframe); + + if (((rmin == 0) && (rsec == 0) && (rframe == 0)) || + ((rsec >= 2) && !((rframe / 19) % 2))) + memset(data, 0xFF, 12); + q[0] = (control << 4) + 1; + q[1] = 0xAA; + q[2] = 0x01; + q[3] = dec_to_bcd(rmin); + q[4] = dec_to_bcd(rsec); + q[5] = dec_to_bcd(rframe); + q[6] = 0; + q[7] = dec_to_bcd(min); + q[8] = dec_to_bcd(sec); + q[9] = dec_to_bcd(frame); + crc = crc_ccitt(q, 10); + q[10] = crc >> 8; + q[11] = crc & 0xFF; +} + +static char char_to_isrc(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'Z') + return 0x11 + (c - 'A'); + if (c >= 'a' && c <= 'z') + return 0x11 + (c - 'a'); + assert(0); + return 0; +} + +void subcode_user(struct burn_write_opts *o, unsigned char *subcodes, + unsigned char tno, unsigned char control, + unsigned char indx, struct isrc *isrc, int psub) +{ + struct burn_drive *d = o->drive; + unsigned char *p, *q; + int crc; + int m, s, f, c, qmode; /* 1, 2 or 3 */ + + memset(subcodes, 0, 96); + + p = subcodes; + if ((tno == 1) && (d->rlba == -150)) + memset(p, 0xFF, 12); + + if (psub) + memset(p, 0xFF, 12); + q = subcodes + 12; + + qmode = 1; + /* every 1 in 10 we can do something different */ + if (d->rlba % 10 == 0) { + /* each of these can occur 1 in 100 */ + if ((d->rlba / 10) % 10 == 0) { + if (o->has_mediacatalog) + qmode = 2; + } else if ((d->rlba / 10) % 10 == 1) { + if (isrc && isrc->has_isrc) + qmode = 3; + } + } + + assert(qmode == 1 || qmode == 2 || qmode == 3); + + switch (qmode) { + case 1: + q[1] = dec_to_bcd(tno); /* track number */ + q[2] = dec_to_bcd(indx); /* index XXX read this shit + from the track array */ + burn_lba_to_msf(d->rlba, &m, &s, &f); + q[3] = dec_to_bcd(m); /* rel min */ + q[4] = dec_to_bcd(s); /* rel sec */ + q[5] = dec_to_bcd(f); /* rel frame */ + q[6] = 0; /* zero */ + burn_lba_to_msf(d->alba, &m, &s, &f); + q[7] = dec_to_bcd(m); /* abs min */ + q[8] = dec_to_bcd(s); /* abs sec */ + q[9] = dec_to_bcd(f); /* abs frame */ + break; + case 2: + /* media catalog number */ + q[1] = (o->mediacatalog[0] << 4) + o->mediacatalog[1]; + q[2] = (o->mediacatalog[2] << 4) + o->mediacatalog[3]; + q[3] = (o->mediacatalog[4] << 4) + o->mediacatalog[5]; + q[4] = (o->mediacatalog[6] << 4) + o->mediacatalog[7]; + q[5] = (o->mediacatalog[8] << 4) + o->mediacatalog[9]; + q[6] = (o->mediacatalog[10] << 4) + o->mediacatalog[11]; + q[7] = o->mediacatalog[12] << 4; + + q[8] = 0; + burn_lba_to_msf(d->alba, &m, &s, &f); + q[9] = dec_to_bcd(f); /* abs frame */ + break; + case 3: + c = char_to_isrc(isrc->country[0]); + /* top 6 bits of [1] is the first country code */ + q[1] = c << 2; + c = char_to_isrc(isrc->country[1]); + /* bottom 2 bits of [1] is part of the second country code */ + q[1] += (c >> 4); + /* top 4 bits if [2] is the rest of the second country code */ + q[2] = c << 4; + + c = char_to_isrc(isrc->owner[0]); + /* bottom 4 bits of [2] is part of the first owner code */ + q[2] += (c >> 2); + /* top 2 bits of [3] is the rest of the first owner code */ + q[3] = c << 6; + c = char_to_isrc(isrc->owner[1]); + /* bottom 6 bits of [3] is the entire second owner code */ + q[3] += c; + c = char_to_isrc(isrc->owner[2]); + /* top 6 bits of [4] are the third owner code */ + q[4] = c << 2; + + /* [5] is the year in 2 BCD numbers */ + q[5] = dec_to_bcd(isrc->year % 100); + /* [6] is the first 2 digits in the serial */ + q[6] = dec_to_bcd(isrc->serial % 100); + /* [7] is the next 2 digits in the serial */ + q[7] = dec_to_bcd((isrc->serial / 100) % 100); + /* the top 4 bits of [8] is the last serial digit, the rest is + zeros */ + q[8] = dec_to_bcd((isrc->serial / 10000) % 10) << 4; + burn_lba_to_msf(d->alba, &m, &s, &f); + q[9] = dec_to_bcd(f); /* abs frame */ + break; + } + q[0] = (control << 4) + qmode; + + crc = crc_ccitt(q, 10); + q[10] = crc >> 8; + q[11] = crc & 0xff; +} + +int sector_lout(struct burn_write_opts *o, unsigned char control, int mode) +{ + struct burn_drive *d = o->drive; + unsigned char subs[96]; + unsigned char *data; + + data = get_sector(o, mode); + if (!data) + return 0; + convert_data(o, NULL, mode, data); + subcode_lout(o, control, subs); + convert_subs(o, mode, subs, data); + sector_headers(o, data, mode, 0); + sector_common(++) + return 1; +} + +int sector_data(struct burn_write_opts *o, struct burn_track *t, int psub) +{ + struct burn_drive *d = o->drive; + unsigned char subs[96]; + unsigned char *data; + + data = get_sector(o, t->mode); + if (!data) + return 0; + convert_data(o, t, t->mode, data); + + if (!t->source->read_sub) + subcode_user(o, subs, t->entry->point, + t->entry->control, 1, &t->isrc, psub); + else if (!t->source->read_sub(t->source, subs, 96)) + subcode_user(o, subs, t->entry->point, + t->entry->control, 1, &t->isrc, psub); + convert_subs(o, t->mode, subs, data); + + sector_headers(o, data, t->mode, 0); + sector_common(++) + return 1; +} + +int burn_msf_to_lba(int m, int s, int f) +{ + if (m < 90) + return (m * 60 + s) * 75 + f - 150; + else + return (m * 60 + s) * 75 + f - 450150; +} + +void burn_lba_to_msf(int lba, int *m, int *s, int *f) +{ + if (lba >= -150) { + *m = (lba + 150) / (60 * 75); + *s = (lba + 150 - *m * 60 * 75) / 75; + *f = lba + 150 - *m * 60 * 75 - *s * 75; + } else { + *m = (lba + 450150) / (60 * 75); + *s = (lba + 450150 - *m * 60 * 75) / 75; + *f = lba + 450150 - *m * 60 * 75 - *s * 75; + } +} + +int dec_to_bcd(int d) +{ + int top, bottom; + + top = d / 10; + bottom = d - (top * 10); + return (top << 4) + bottom; +} + +void sector_headers(struct burn_write_opts *o, unsigned char *out, + int mode, int leadin) +{ + struct burn_drive *d = o->drive; + unsigned int crc; + int min, sec, frame; + int modebyte = -1; + + if (mode & BURN_AUDIO) /* no headers for "audio" */ + return; + if (o->write_type == BURN_WRITE_SAO) + return; + if (mode & BURN_MODE1) + modebyte = 1; + + assert(modebyte == 1); + + out[0] = 0; + memset(out + 1, 0xFF, 10); /* sync */ + out[11] = 0; + + if (leadin) { + burn_lba_to_msf(d->rlba, &min, &sec, &frame); + out[12] = dec_to_bcd(min) + 0xA0; + out[13] = dec_to_bcd(sec); + out[14] = dec_to_bcd(frame); + out[15] = modebyte; + } else { + burn_lba_to_msf(d->alba, &min, &sec, &frame); + out[12] = dec_to_bcd(min); + out[13] = dec_to_bcd(sec); + out[14] = dec_to_bcd(frame); + out[15] = modebyte; + } + if (mode & BURN_MODE1) { + crc = crc_32(out, 2064); + out[2064] = crc & 0xFF; + crc >>= 8; + out[2065] = crc & 0xFF; + crc >>= 8; + out[2066] = crc & 0xFF; + crc >>= 8; + out[2067] = crc & 0xFF; + } + if (mode & BURN_MODE1) { + memset(out + 2068, 0, 8); + parity_p(out); + parity_q(out); + } + scramble(out); +} + +#if 0 +void process_q(struct burn_drive *d, unsigned char *q) +{ + unsigned char i[5]; + int mode; + + mode = q[0] & 0xF; +/* burn_print(12, "mode: %d : ", mode);*/ + switch (mode) { + case 1: +/* burn_print(12, "tno = %d : ", q[1]); + burn_print(12, "index = %d\n", q[2]); +*/ + /* q[1] is the track number (starting at 1) q[2] is the index + number (starting at 0) */ +#warning this is totally bogus + if (q[1] - 1 > 99) + break; + if (q[2] > d->toc->track[q[1] - 1].indices) { + burn_print(12, "new index at %d\n", d->alba); + d->toc->track[q[1] - 1].index[q[2]] = d->alba; + d->toc->track[q[1] - 1].indices++; + } + break; + case 2: + /* XXX dont ignore these */ + break; + case 3: +/* burn_print(12, "ISRC data in mode 3 q\n");*/ + i[0] = isrc[(q[1] << 2) >> 2]; +/* burn_print(12, "0x%x 0x%x 0x%x 0x%x 0x%x\n", q[1], q[2], q[3], q[4], q[5]); + burn_print(12, "ISRC - %c%c%c%c%c\n", i[0], i[1], i[2], i[3], i[4]); +*/ + break; + default: + assert(0); + } +} +#endif + +/* this needs more info. subs in the data? control/adr? */ +#warning sector_identify needs to be written +int sector_identify(unsigned char *data) +{ + scramble(data); +/* +check mode byte for 1 or 2 +test parity to see if it's a valid sector +if invalid, return BURN_MODE_AUDIO; +else return mode byte (what about mode 2 formless? heh) +*/ + return BURN_MODE1; +} diff --git a/trunk/libburn/sector.h b/trunk/libburn/sector.h new file mode 100644 index 00000000..91b68d3b --- /dev/null +++ b/trunk/libburn/sector.h @@ -0,0 +1,31 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SECTOR +#define __SECTOR + +#include "libburn.h" +#include "transport.h" + +struct burn_drive; +struct isrc; + +int dec_to_bcd(int); + +int sector_toc(struct burn_write_opts *, int mode); +int sector_pregap(struct burn_write_opts *, unsigned char tno, + unsigned char control, int mode); +int sector_postgap(struct burn_write_opts *, unsigned char tno, + unsigned char control, int mode); +int sector_lout(struct burn_write_opts *, unsigned char control, int mode); +int sector_data(struct burn_write_opts *, struct burn_track *t, int psub); +void sector_headers(struct burn_write_opts *, unsigned char *, + int mode, int leadin); +void subcode_user(struct burn_write_opts *, unsigned char *s, + unsigned char tno, unsigned char control, + unsigned char index, struct isrc *isrc, int psub); + +int sector_identify(unsigned char *); + +void process_q(struct burn_drive *d, unsigned char *q); + +#endif /* __SECTOR */ diff --git a/trunk/libburn/sg.c b/trunk/libburn/sg.c new file mode 100644 index 00000000..cbcbf1e9 --- /dev/null +++ b/trunk/libburn/sg.c @@ -0,0 +1,334 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "transport.h" +#include "drive.h" +#include "sg.h" +#include "spc.h" +#include "mmc.h" +#include "sbc.h" +#include "debug.h" +#include "toc.h" +#include "util.h" + +static void enumerate_common(char *fname); + +static int sgio_test(int fd) +{ + unsigned char test_ops[] = { 0, 0, 0, 0, 0, 0 }; + sg_io_hdr_t s; + + memset(&s, 0, sizeof(sg_io_hdr_t)); + s.interface_id = 'S'; + s.dxfer_direction = SG_DXFER_NONE; + s.cmd_len = 6; + s.cmdp = test_ops; + s.timeout = 12345; + return ioctl(fd, SG_IO, &s); +} + +void ata_enumerate(void) +{ + struct hd_driveid tm; + int i, fd; + char fname[10]; + + for (i = 0; i < 26; i++) { + sprintf(fname, "/dev/hd%c", 'a' + i); + /* open O_RDWR so we don't think read only drives are + in some way useful + */ + fd = open(fname, O_RDWR | O_NONBLOCK); + if (fd == -1) + continue; + + /* found a drive */ + ioctl(fd, HDIO_GET_IDENTITY, &tm); + + /* not atapi */ + if (!(tm.config & 0x8000) || (tm.config & 0x4000)) { + close(fd); + continue; + } + + /* if SG_IO fails on an atapi device, we should stop trying to + use hd* devices */ + if (sgio_test(fd) == -1) { + close(fd); + return; + } + close(fd); + enumerate_common(fname); + } +} + +void sg_enumerate(void) +{ + struct sg_scsi_id sid; + int i, fd; + char fname[10]; + + for (i = 0; i < 32; i++) { + sprintf(fname, "/dev/sg%d", i); + /* open RDWR so we don't accidentally think read only drives + are in some way useful + */ + fd = open(fname, O_RDWR); + if (fd == -1) + continue; + + /* found a drive */ + ioctl(fd, SG_GET_SCSI_ID, &sid); + close(fd); + if (sid.scsi_type != TYPE_ROM) + continue; + + enumerate_common(fname); + } +} + +static void enumerate_common(char *fname) +{ + struct burn_drive *t; + struct burn_drive out; + + out.devname = burn_strdup(fname); + out.fd = -1337; + + out.grab = sg_grab; + out.release = sg_release; + out.issue_command = sg_issue_command; + out.getcaps = spc_getcaps; + out.released = 1; + out.status = BURN_DISC_UNREADY; + + out.eject = sbc_eject; + out.load = sbc_load; + out.lock = spc_prevent; + out.unlock = spc_allow; + out.read_disc_info = spc_sense_write_params; + out.get_erase_progress = spc_get_erase_progress; + out.test_unit_ready = spc_test_unit_ready; + out.probe_write_modes = spc_probe_write_modes; + out.read_toc = mmc_read_toc; + out.write = mmc_write; + out.erase = mmc_erase; + out.read_sectors = mmc_read_sectors; + out.perform_opc = mmc_perform_opc; + out.set_speed = mmc_set_speed; + out.send_parameters = spc_select_error_params; + out.send_write_parameters = spc_select_write_params; + out.send_cue_sheet = mmc_send_cue_sheet; + out.sync_cache = mmc_sync_cache; + out.get_nwa = mmc_get_nwa; + out.close_disc = mmc_close_disc; + out.close_session = mmc_close_session; + out.idata = malloc(sizeof(struct scsi_inquiry_data)); + out.idata->valid = 0; + out.mdata = malloc(sizeof(struct scsi_mode_data)); + out.mdata->valid = 0; + memset(&out.params, 0, sizeof(struct params)); + t = burn_drive_register(&out); + +/* try to get the drive info */ + if (sg_grab(t)) { + burn_print(2, "getting drive info\n"); + t->getcaps(t); + t->unlock(t); + t->released = 1; + } else { + burn_print(2, "unable to grab new located drive\n"); + } + +} + +/* + we use the sg reference count to decide whether we can use the + drive or not. + if refcount is not one, drive is open somewhere else. +*/ +int sg_grab(struct burn_drive *d) +{ + int fd, count; + + fd = open(d->devname, O_RDWR | O_NONBLOCK); + assert(fd != -1337); + if (-1 != fd) { +/* er = ioctl(fd, SG_GET_ACCESS_COUNT, &count);*/ + count = 1; + if (1 == count) { + d->fd = fd; + fcntl(fd, F_SETOWN, getpid()); + d->released = 0; + return 1; + } + burn_print(1, "could not acquire drive - already open\n"); + close(fd); + return 0; + } + burn_print(1, "could not acquire drive\n"); + return 0; +} + +/* + non zero return means you still have the drive and it's not + in a state to be released? (is that even possible?) +*/ + +int sg_release(struct burn_drive *d) +{ + if (d->fd < 1) { + burn_print(1, "release an ungrabbed drive. die\n"); + return 0; + } + close(d->fd); + d->fd = -1337; + return 0; +} + +int sg_issue_command(struct burn_drive *d, struct command *c) +{ + int done = 0; + int err; + sg_io_hdr_t s; + + c->error = 0; +/* +this is valid during the mode probe in scan + if (d->fd < 1 || d->released) { + burn_print(1, + "command issued on ungrabbed drive, chaos.\n"); + burn_print(1, "fd = %d, released = %d\n", d->fd, + d->released); + } +*/ + memset(&s, 0, sizeof(sg_io_hdr_t)); + + s.interface_id = 'S'; + + if (c->dir == TO_DRIVE) + s.dxfer_direction = SG_DXFER_TO_DEV; + else if (c->dir == FROM_DRIVE) + s.dxfer_direction = SG_DXFER_FROM_DEV; + else if (c->dir == NO_TRANSFER) { + s.dxfer_direction = SG_DXFER_NONE; + assert(!c->page); + } + s.cmd_len = c->oplen; + s.cmdp = c->opcode; + s.mx_sb_len = 32; + s.sbp = c->sense; + memset(c->sense, 0, sizeof(c->sense)); + s.timeout = 200000; + if (c->page) { + s.dxferp = c->page->data; + if (c->dir == FROM_DRIVE) { + s.dxfer_len = BUFFER_SIZE; +/* touch page so we can use valgrind */ + memset(c->page->data, 0, BUFFER_SIZE); + } else { + assert(c->page->bytes > 0); + s.dxfer_len = c->page->bytes; + } + } else { + s.dxferp = NULL; + s.dxfer_len = 0; + } + s.usr_ptr = c; + + do { + err = ioctl(d->fd, SG_IO, &s); + assert(err != -1); + if (s.sb_len_wr) { + if (!c->retry) { + c->error = 1; + return 1; + } + switch (scsi_error(d, s.sbp, s.sb_len_wr)) { + case RETRY: + done = 0; + break; + case FAIL: + done = 1; + c->error = 1; + break; + } + } else { + done = 1; + } + } while (!done); + return 1; +} + +enum response scsi_error(struct burn_drive *d, unsigned char *sense, + int senselen) +{ + int key, asc, ascq; + + senselen = senselen; + key = sense[2]; + asc = sense[12]; + ascq = sense[13]; + + burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n", + key, asc, ascq, d->idata->vendor, d->idata->product); + + switch (asc) { + case 0: + burn_print(12, "NO ERROR!\n"); + return RETRY; + + case 2: + burn_print(1, "not ready\n"); + return RETRY; + case 4: + burn_print(1, + "logical unit is in the process of becoming ready\n"); + return RETRY; + case 0x20: + if (key == 5) + burn_print(1, "bad opcode\n"); + return FAIL; + case 0x21: + burn_print(1, "invalid address or something\n"); + return FAIL; + case 0x24: + if (key == 5) + burn_print(1, "invalid field in cdb\n"); + else + break; + return FAIL; + case 0x26: + if ( key == 5 ) + burn_print( 1, "invalid field in parameter list\n" ); + return FAIL; + case 0x28: + if (key == 6) + burn_print(1, + "Not ready to ready change, medium may have changed\n"); + else + break; + return RETRY; + case 0x3A: + burn_print(12, "Medium not present in %s %s\n", + d->idata->vendor, d->idata->product); + + d->status = BURN_DISC_EMPTY; + return FAIL; + } + burn_print(1, "unknown failure\n"); + burn_print(1, "key:0x%x, asc:0x%x, ascq:0x%x\n", key, asc, ascq); + return FAIL; +} diff --git a/trunk/libburn/sg.h b/trunk/libburn/sg.h new file mode 100644 index 00000000..8f431c5f --- /dev/null +++ b/trunk/libburn/sg.h @@ -0,0 +1,19 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SG +#define __SG + +struct burn_drive; +struct command; + +enum response +{ RETRY, FAIL }; + +void sg_enumerate(void); +void ata_enumerate(void); +int sg_grab(struct burn_drive *); +int sg_release(struct burn_drive *); +int sg_issue_command(struct burn_drive *, struct command *); +enum response scsi_error(struct burn_drive *, unsigned char *, int); + +#endif /* __SG */ diff --git a/trunk/libburn/source.c b/trunk/libburn/source.c new file mode 100644 index 00000000..fd0c56b6 --- /dev/null +++ b/trunk/libburn/source.c @@ -0,0 +1,36 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include "libburn.h" +#include "source.h" +#include "structure.h" + +void burn_source_free(struct burn_source *src) +{ + if (--src->refcount < 1) { + if (src->free_data) + src->free_data(src); + free(src); + } +} + +enum burn_source_status burn_track_set_source(struct burn_track *t, + struct burn_source *s) +{ + if (!s->read) + return BURN_SOURCE_FAILED; + s->refcount++; + t->source = s; + return BURN_SOURCE_OK; +} + +struct burn_source *burn_source_new(void) +{ + struct burn_source *out; + + out = malloc(sizeof(struct burn_source)); + memset(out, 0, sizeof(struct burn_source)); + out->refcount = 1; + return out; +} diff --git a/trunk/libburn/source.h b/trunk/libburn/source.h new file mode 100644 index 00000000..e0a69aa1 --- /dev/null +++ b/trunk/libburn/source.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SOURCE +#define __SOURCE + +struct burn_source *burn_source_new(void); + +#endif /*__SOURCE*/ diff --git a/trunk/libburn/spc.c b/trunk/libburn/spc.c new file mode 100644 index 00000000..89fd5de6 --- /dev/null +++ b/trunk/libburn/spc.c @@ -0,0 +1,405 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* scsi primary commands */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "transport.h" +#include "spc.h" +#include "mmc.h" +#include "sbc.h" +#include "drive.h" +#include "debug.h" +#include "options.h" + +/* spc command set */ +static unsigned char SPC_INQUIRY[] = { 0x12, 0, 0, 0, 255, 0 }; + +/*static char SPC_TEST[]={0,0,0,0,0,0};*/ +static unsigned char SPC_PREVENT[] = { 0x1e, 0, 0, 0, 1, 0 }; +static unsigned char SPC_ALLOW[] = { 0x1e, 0, 0, 0, 0, 0 }; +static unsigned char SPC_MODE_SENSE[] = { 0x5a, 0, 0, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char SPC_MODE_SELECT[] = + { 0x55, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char SPC_REQUEST_SENSE[] = { 0x03, 0, 0, 0, 18, 0 }; +static unsigned char SPC_TEST_UNIT_READY[] = { 0x00, 0, 0, 0, 0, 0 }; + +int spc_test_unit_ready(struct burn_drive *d) +{ + struct command c; + + c.retry = 0; + c.oplen = sizeof(SPC_TEST_UNIT_READY); + memcpy(c.opcode, SPC_TEST_UNIT_READY, sizeof(SPC_TEST_UNIT_READY)); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); + if (c.error) + return (c.sense[2] & 0xF) == 0; + return 1; +} + +void spc_request_sense(struct burn_drive *d, struct buffer *buf) +{ + struct command c; + + c.retry = 0; + c.oplen = sizeof(SPC_REQUEST_SENSE); + memcpy(c.opcode, SPC_REQUEST_SENSE, sizeof(SPC_REQUEST_SENSE)); + c.page = buf; + c.page->sectors = 0; + c.page->bytes = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); +} + +int spc_get_erase_progress(struct burn_drive *d) +{ + struct buffer b; + + spc_request_sense(d, &b); + return (b.data[16] << 8) | b.data[17]; +} + +void spc_inquiry(struct burn_drive *d) +{ + struct buffer buf; + struct scsi_inquiry_data *id; + struct command c; + + memcpy(c.opcode, SPC_INQUIRY, sizeof(SPC_INQUIRY)); + c.retry = 1; + c.oplen = sizeof(SPC_INQUIRY); + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + id = (struct scsi_inquiry_data *)d->idata; + id->vendor[8] = 0; + id->product[16] = 0; + id->revision[4] = 0; + + memcpy(id->vendor, c.page->data + 8, 8); + memcpy(id->product, c.page->data + 16, 16); + memcpy(id->revision, c.page->data + 32, 4); + + id->valid = 1; + return; +} + +void spc_prevent(struct burn_drive *d) +{ + struct command c; + + memcpy(c.opcode, SPC_PREVENT, sizeof(SPC_PREVENT)); + c.retry = 1; + c.oplen = sizeof(SPC_PREVENT); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void spc_allow(struct burn_drive *d) +{ + struct command c; + + memcpy(c.opcode, SPC_ALLOW, sizeof(SPC_ALLOW)); + c.retry = 1; + c.oplen = sizeof(SPC_ALLOW); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void spc_sense_caps(struct burn_drive *d) +{ + struct buffer buf; + struct scsi_mode_data *m; + int size; + unsigned char *page; + struct command c; + + memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SENSE); + c.opcode[2] = 0x2A; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + size = c.page->data[0] * 256 + c.page->data[1]; + m = d->mdata; + page = c.page->data + 8; + + m->buffer_size = page[12] * 256 + page[13]; + m->dvdram_read = page[2] & 32; + m->dvdram_write = page[3] & 32; + m->dvdr_read = page[2] & 16; + m->dvdr_write = page[3] & 16; + m->dvdrom_read = page[2] & 8; + m->simulate = page[3] & 4; + m->cdrw_read = page[2] & 2; + m->cdrw_write = page[3] & 2; + m->cdr_read = page[2] & 1; + m->cdr_write = page[3] & 1; + m->max_read_speed = page[8] * 256 + page[9]; + m->cur_read_speed = page[14] * 256 + page[15]; + m->max_write_speed = page[18] * 256 + page[19]; + m->cur_write_speed = page[20] * 256 + page[21]; + m->c2_pointers = page[5] & 16; + m->valid = 1; + m->underrun_proof = page[4] & 128; +} + +void spc_sense_error_params(struct burn_drive *d) +{ + struct buffer buf; + struct scsi_mode_data *m; + int size; + unsigned char *page; + struct command c; + + memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SENSE); + c.opcode[2] = 0x01; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + size = c.page->data[0] * 256 + c.page->data[1]; + m = d->mdata; + page = c.page->data + 8; + d->params.retries = page[3]; + m->retry_page_length = page[1]; + m->retry_page_valid = 1; +} + +void spc_select_error_params(struct burn_drive *d, + const struct burn_read_opts *o) +{ + struct buffer buf; + struct command c; + + memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SELECT); + c.opcode[8] = 8 + 2 + d->mdata->retry_page_length; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + assert(d->mdata->valid); + memset(c.page->data, 0, 8 + 2 + d->mdata->retry_page_length); + c.page->bytes = 8 + 2 + d->mdata->retry_page_length; + c.page->data[8] = 1; + c.page->data[9] = d->mdata->retry_page_length; + if (o->transfer_damaged_blocks) + c.page->data[10] |= 32; + if (o->report_recovered_errors) + c.page->data[10] |= 4; + if (!o->hardware_error_recovery) + c.page->data[10] |= 1; +/*burn_print(1, "error parameter 0x%x\n", c->page->data[10]);*/ + c.page->data[11] = d->params.retries; + c.dir = TO_DRIVE; + d->issue_command(d, &c); +} + +void spc_sense_write_params(struct burn_drive *d) +{ + struct buffer buf; + struct scsi_mode_data *m; + int size; + unsigned char *page; + struct command c; + + assert(d->mdata->cdr_write || d->mdata->cdrw_write || + d->mdata->dvdr_write || d->mdata->dvdram_write); + + memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SENSE); + c.opcode[2] = 0x05; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + size = c.page->data[0] * 256 + c.page->data[1]; + m = d->mdata; + page = c.page->data + 8; + burn_print(1, "write page length 0x%x\n", page[1]); + m->write_page_length = page[1]; + m->write_page_valid = 1; + mmc_read_disc_info(d); +} + +void spc_select_write_params(struct burn_drive *d, + const struct burn_write_opts *o) +{ + struct buffer buf; + struct command c; + int bufe, sim; + + assert(o->drive == d); + memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SELECT); + c.opcode[8] = 8 + 2 + d->mdata->write_page_length; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + assert(d->mdata->valid); + memset(c.page->data, 0, 8 + 2 + d->mdata->write_page_length); + c.page->bytes = 8 + 2 + d->mdata->write_page_length; + c.page->data[8] = 5; + c.page->data[9] = d->mdata->write_page_length; + + burn_print(12, "using write page length %d (valid %d)\n", + d->mdata->write_page_length, d->mdata->write_page_valid); + bufe = o->underrun_proof; + sim = o->simulate; + c.page->data[10] = (bufe << 6) + + (sim << 4) + + o->write_type; + c.page->data[11] = (o->multi << 6) | o->control; + c.page->data[12] = spc_block_type(o->block_type); + c.page->data[22] = 0; + c.page->data[23] = 150; /* audio pause length */ +/*XXX need session format! */ + c.dir = TO_DRIVE; + d->issue_command(d, &c); +} + +void spc_getcaps(struct burn_drive *d) +{ + spc_inquiry(d); + spc_sense_caps(d); + spc_sense_error_params(d); +} + +/* +only called when a blank is present, so we set type to blank +(on the last pass) + +don't check totally stupid modes (raw/raw0) +some drives say they're ok, and they're not. +*/ + +void spc_probe_write_modes(struct burn_drive *d) +{ + struct buffer buf; + int try_write_type = 1; + int try_block_type = 0; + int key, asc, ascq; + struct command c; + + while (try_write_type != 4) { + burn_print(9, "trying %d, %d\n", try_write_type, + try_block_type); + memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SELECT); + c.opcode[8] = 8 + 2 + 0x32; + c.page = &buf; + + memset(c.page->data, 0, 8 + 2 + 0x32); + c.page->bytes = 8 + 2 + 0x32; + + c.page->data[8] = 5; + c.page->data[9] = 0x32; + c.page->data[10] = try_write_type; + if (try_block_type > 4) + c.page->data[11] = 4; + else + c.page->data[11] = 0; + c.page->data[12] = try_block_type; + c.page->data[23] = 150; + c.dir = TO_DRIVE; + d->issue_command(d, &c); + + key = c.sense[2]; + asc = c.sense[12]; + ascq = c.sense[13]; + + if (key) + burn_print(7, "%d not supported\n", try_block_type); + else { + burn_print(7, "%d:%d SUPPORTED MODE!\n", + try_write_type, try_block_type); + if (try_write_type == 2) /* sao */ + d->block_types[try_write_type] = + BURN_BLOCK_SAO; + else + d->block_types[try_write_type] |= + 1 << try_block_type; + } + switch (try_block_type) { + case 0: + case 1: + case 2: + try_block_type++; + break; + case 3: + try_block_type = 8; + break; + case 8: + case 9: + case 10: + case 11: + case 12: + try_block_type++; + break; + case 13: + try_block_type = 0; + try_write_type++; + break; + default: + return; + } + } +} + +int spc_block_type(enum burn_block_types b) +{ + switch (b) { + case BURN_BLOCK_SAO: + return 0; /* ignored bitz */ + case BURN_BLOCK_RAW0: + return 0; + case BURN_BLOCK_RAW16: + return 1; + case BURN_BLOCK_RAW96P: + return 2; + case BURN_BLOCK_RAW96R: + return 3; + case BURN_BLOCK_MODE1: + return 8; + case BURN_BLOCK_MODE2R: + return 9; + case BURN_BLOCK_MODE2_PATHETIC: + return 10; + case BURN_BLOCK_MODE2_LAME: + return 11; + case BURN_BLOCK_MODE2_OBSCURE: + return 12; + case BURN_BLOCK_MODE2_OK: + return 13; + } + assert(0); +} diff --git a/trunk/libburn/spc.h b/trunk/libburn/spc.h new file mode 100644 index 00000000..2bca4ded --- /dev/null +++ b/trunk/libburn/spc.h @@ -0,0 +1,25 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SPC +#define __SPC + +#include "libburn.h" + +void spc_inquiry(struct burn_drive *); +void spc_prevent(struct burn_drive *); +void spc_allow(struct burn_drive *); +void spc_sense_caps(struct burn_drive *); +void spc_sense_error_params(struct burn_drive *); +void spc_select_error_params(struct burn_drive *, + const struct burn_read_opts *); +void spc_getcaps(struct burn_drive *d); +void spc_sense_write_params(struct burn_drive *); +void spc_select_write_params(struct burn_drive *, + const struct burn_write_opts *); +void spc_probe_write_modes(struct burn_drive *); +void spc_request_sense(struct burn_drive *d, struct buffer *buf); +int spc_block_type(enum burn_block_types b); +int spc_get_erase_progress(struct burn_drive *d); +int spc_test_unit_ready(struct burn_drive *d); + +#endif /*__SPC*/ diff --git a/trunk/libburn/structure.c b/trunk/libburn/structure.c new file mode 100644 index 00000000..6719ab2e --- /dev/null +++ b/trunk/libburn/structure.c @@ -0,0 +1,305 @@ +#include +#include +#include +#include +#include "libburn.h" +#include "structure.h" +#include "write.h" +#include "debug.h" + +#define RESIZE(TO, NEW, pos) {\ + void *tmp;\ +\ + assert(!(pos > BURN_POS_END));\ + if (pos == BURN_POS_END)\ + pos = TO->NEW##s;\ + if (pos > TO->NEW##s)\ + return 0;\ +\ + tmp = realloc(TO->NEW, sizeof(struct NEW *) * (TO->NEW##s + 1));\ + if (!tmp)\ + return 0;\ + TO->NEW = tmp;\ + memmove(TO->NEW + pos + 1, TO->NEW + pos,\ + sizeof(struct NEW *) * (TO->NEW##s - pos));\ + TO->NEW##s++;\ +} + +struct burn_disc *burn_disc_create(void) +{ + struct burn_disc *d; + d = malloc(sizeof(struct burn_disc)); + memset(d, 0, sizeof(struct burn_disc)); + d->refcnt = 1; + d->sessions = 0; + d->session = NULL; + return d; +} + +void burn_disc_free(struct burn_disc *d) +{ + d->refcnt--; + if (d->refcnt == 0) { + /* dec refs on all elements */ + int i; + + for (i = 0; i < d->sessions; i++) + burn_session_free(d->session[i]); + free(d->session); + free(d); + } +} + +struct burn_session *burn_session_create(void) +{ + struct burn_session *s; + s = malloc(sizeof(struct burn_session)); + memset(s, 0, sizeof(struct burn_session)); + s->refcnt = 1; + s->tracks = 0; + s->track = NULL; + s->hidefirst = 0; + return s; +} + +void burn_session_hide_first_track(struct burn_session *s, int onoff) +{ + s->hidefirst = onoff; +} + +void burn_session_free(struct burn_session *s) +{ + s->refcnt--; + if (s->refcnt == 0) { + /* dec refs on all elements */ + int i; + + for (i = 0; i < s->tracks; i++) + burn_track_free(s->track[i]); + free(s->track); + free(s); + } + +} + +int burn_disc_add_session(struct burn_disc *d, struct burn_session *s, + unsigned int pos) +{ + RESIZE(d, session, pos); + d->session[pos] = s; + s->refcnt++; + return 1; +} + +struct burn_track *burn_track_create(void) +{ + struct burn_track *t; + t = malloc(sizeof(struct burn_track)); + memset(t, 0, sizeof(struct burn_track)); + t->refcnt = 1; + t->indices = 0; + t->offset = 0; + t->offsetcount = 0; + t->tail = 0; + t->tailcount = 0; + t->mode = BURN_MODE1; + t->isrc.has_isrc = 0; + t->pad = 1; + t->entry = NULL; + t->source = NULL; + t->postgap = 0; + t->pregap1 = 0; + t->pregap2 = 0; + return t; +} + +void burn_track_free(struct burn_track *t) +{ + t->refcnt--; + if (t->refcnt == 0) { + /* dec refs on all elements */ + if (t->source) + burn_source_free(t->source); + free(t); + } +} + +int burn_session_add_track(struct burn_session *s, struct burn_track *t, + unsigned int pos) +{ + RESIZE(s, track, pos); + s->track[pos] = t; + t->refcnt++; + return 1; +} + +int burn_session_remove_track(struct burn_session *s, struct burn_track *t) +{ + struct burn_track **tmp; + int i, pos = -1; + + assert(s->track != NULL); + + burn_track_free(t); + + /* Find the position */ + for (i = 0; i < s->tracks; i++) { + if (t == s->track[i]) + pos = i; + } + + if (pos == -1) + return 0; + + /* Is it the last track? */ + if (pos != s->tracks) { + memmove(s->track[pos], s->track[pos + 1], + sizeof(struct burn_track *) * (s->tracks - (pos + 1))); + } + + s->tracks--; + tmp = realloc(s->track, sizeof(struct burn_track *) * s->tracks); + if (!tmp) + return 0; + s->track = tmp; + return 1; +} + +void burn_structure_print_disc(struct burn_disc *d) +{ + int i; + + burn_print(12, "This disc has %d sessions\n", d->sessions); + for (i = 0; i < d->sessions; i++) { + burn_structure_print_session(d->session[i]); + } +} +void burn_structure_print_session(struct burn_session *s) +{ + int i; + + burn_print(12, " Session has %d tracks\n", s->tracks); + for (i = 0; i < s->tracks; i++) { + burn_structure_print_track(s->track[i]); + } +} +void burn_structure_print_track(struct burn_track *t) +{ + burn_print(12, "(%p) track size %d sectors\n", t, + burn_track_get_sectors(t)); +} + +void burn_track_define_data(struct burn_track *t, int offset, int tail, + int pad, int mode) +{ + t->offset = offset; + t->pad = pad; + t->mode = mode; + t->tail = tail; +} + +void burn_track_set_isrc(struct burn_track *t, char *country, char *owner, + unsigned char year, unsigned int serial) +{ + int i; + + t->isrc.has_isrc = 1; + for (i = 0; i < 2; ++i) { + assert((country[i] >= '0' || country[i] < '9') && + (country[i] >= 'a' || country[i] < 'z') && + (country[i] >= 'A' || country[i] < 'Z')); + t->isrc.country[i] = country[i]; + } + for (i = 0; i < 3; ++i) { + assert((owner[i] >= '0' || owner[i] < '9') && + (owner[i] >= 'a' || owner[i] < 'z') && + (owner[i] >= 'A' || owner[i] < 'Z')); + t->isrc.owner[i] = owner[i]; + } + assert(year <= 99); + t->isrc.year = year; + assert(serial <= 99999); + t->isrc.serial = serial; +} + +void burn_track_clear_isrc(struct burn_track *t) +{ + t->isrc.has_isrc = 0; +} + +int burn_track_get_sectors(struct burn_track *t) +{ + int size; + int sectors, seclen; + + seclen = burn_sector_length(t->mode); + size = t->offset + t->source->get_size(t->source) + t->tail; + sectors = size / seclen; + if (size % seclen) + sectors++; + burn_print(1, "%d sectors of %d length\n", sectors, seclen); + return sectors; +} + +int burn_track_get_shortage(struct burn_track *t) +{ + int size; + int seclen; + + seclen = burn_sector_length(t->mode); + size = t->offset + t->source->get_size(t->source) + t->tail; + if (size % seclen) + return seclen - size % seclen; + return 0; +} + +int burn_session_get_sectors(struct burn_session *s) +{ + int sectors = 0, i; + + for (i = 0; i < s->tracks; i++) + sectors += burn_track_get_sectors(s->track[i]); + return sectors; +} + +int burn_disc_get_sectors(struct burn_disc *d) +{ + int sectors = 0, i; + + for (i = 0; i < d->sessions; i++) + sectors += burn_session_get_sectors(d->session[i]); + return sectors; +} + +void burn_track_get_entry(struct burn_track *t, struct burn_toc_entry *entry) +{ + memcpy(entry, t->entry, sizeof(struct burn_toc_entry)); +} + +void burn_session_get_leadout_entry(struct burn_session *s, + struct burn_toc_entry *entry) +{ + memcpy(entry, s->leadout_entry, sizeof(struct burn_toc_entry)); +} + +struct burn_session **burn_disc_get_sessions(struct burn_disc *d, int *num) +{ + *num = d->sessions; + return d->session; +} + +struct burn_track **burn_session_get_tracks(struct burn_session *s, int *num) +{ + *num = s->tracks; + return s->track; +} + +int burn_track_get_mode(struct burn_track *track) +{ + return track->mode; +} + +int burn_session_get_hidefirst(struct burn_session *session) +{ + return session->hidefirst; +} diff --git a/trunk/libburn/structure.h b/trunk/libburn/structure.h new file mode 100644 index 00000000..61f083f1 --- /dev/null +++ b/trunk/libburn/structure.h @@ -0,0 +1,70 @@ +#ifndef BURN__STRUCTURE_H +#define BURN__STRUCTURE_H + +struct isrc +{ + int has_isrc; + char country[2]; /* each must be 0-9, A-Z */ + char owner[3]; /* each must be 0-9, A-Z */ + unsigned char year; /* must be 0-99 */ + unsigned int serial; /* must be 0-99999 */ +}; + +struct burn_track +{ + int refcnt; + struct burn_toc_entry *entry; + unsigned char indices; + /* lba address of the index */ + unsigned int index[99]; + /** number of 0 bytes to write before data */ + int offset; + /** how much offset has been used */ + int offsetcount; + /** Number of zeros to write after data */ + int tail; + /** how much tail has been used */ + int tailcount; + /** 1 means Pad with zeros, 0 means start reading the next track */ + int pad; + /** Data source */ + struct burn_source *source; + /** End of Source flag */ + int eos; + /** The audio/data mode for the entry. Derived from control and + possibly from reading the track's first sector. */ + int mode; + /** The track contains interval one of a pregap */ + int pregap1; + /** The track contains interval two of a pregap */ + int pregap2; + /** The track contains a postgap */ + int postgap; + struct isrc isrc; +}; + +struct burn_session +{ + unsigned char firsttrack; + unsigned char lasttrack; + int hidefirst; + unsigned char start_m; + unsigned char start_s; + unsigned char start_f; + struct burn_toc_entry *leadout_entry; + + int tracks; + struct burn_track **track; + int refcnt; +}; + +struct burn_disc +{ + int sessions; + struct burn_session **session; + int refcnt; +}; + +int burn_track_get_shortage(struct burn_track *t); + +#endif /* BURN__STRUCTURE_H */ diff --git a/trunk/libburn/toc.c b/trunk/libburn/toc.c new file mode 100644 index 00000000..8d0d3fd3 --- /dev/null +++ b/trunk/libburn/toc.c @@ -0,0 +1,130 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include "toc.h" +#include "transport.h" +#include "libburn.h" +#include "sector.h" +#include "options.h" + +#if 0 +static void write_clonecd2(volatile struct toc *toc, int f); + +static void write_clonecd2(volatile struct toc *toc, int f) +{ + int i; + + /* header */ + dprintf(f, "[CloneCD]\r\n"); + dprintf(f, "Version=2\r\n"); + dprintf(f, "\r\n"); + + /* disc data */ + dprintf(f, "[Disc]\r\n"); + + dprintf(f, "TocEntries=%d\r\n", toc->toc_entries); + dprintf(f, "Sessions=%d\r\n", toc->sessions); + dprintf(f, "DataTracksScrambled=%d\r\n", toc->datatracksscrambled); + dprintf(f, "CDTextLength=%d\r\n", toc->cdtextlength); + dprintf(f, "\r\n"); + + /* session data */ + for (i = 0; i < toc->sessions; ++i) { + dprintf(f, "[Session %d]\r\n", i + 1); + + { + int m; + + switch (toc->session[i].track[0]->mode) { + case BURN_MODE_RAW_DATA: + case BURN_MODE_AUDIO: + m = 0; + break; + case BURN_MODE0: + m = 1; + break; + case BURN_MODE1: + case BURN_MODE2_FORMLESS: + case BURN_MODE2_FORM1: + case BURN_MODE2_FORM2: + case BURN_MODE_UNINITIALIZED: + assert(0); /* unhandled! find out ccd's + value for these modes! */ + } + dprintf(f, "PreGapMode=%d\r\n", m); + } + dprintf(f, "\r\n"); + } + + for (i = 0; i < toc->toc_entries; ++i) { + dprintf(f, "[Entry %d]\r\n", i); + + dprintf(f, "Session=%d\r\n", toc->toc_entry[i].session); + dprintf(f, "Point=0x%02x\r\n", toc->toc_entry[i].point); + dprintf(f, "ADR=0x%02x\r\n", toc->toc_entry[i].adr); + dprintf(f, "Control=0x%02x\r\n", toc->toc_entry[i].control); + dprintf(f, "TrackNo=%d\r\n", toc->toc_entry[i].tno); + dprintf(f, "AMin=%d\r\n", toc->toc_entry[i].min); + dprintf(f, "ASec=%d\r\n", toc->toc_entry[i].sec); + dprintf(f, "AFrame=%d\r\n", toc->toc_entry[i].frame); + dprintf(f, "ALBA=%d\r\n", + burn_msf_to_lba(toc->toc_entry[i].min, + toc->toc_entry[i].sec, + toc->toc_entry[i].frame)); + dprintf(f, "Zero=%d\r\n", toc->toc_entry[i].zero); + dprintf(f, "PMin=%d\r\n", toc->toc_entry[i].pmin); + dprintf(f, "PSec=%d\r\n", toc->toc_entry[i].psec); + dprintf(f, "PFrame=%d\r\n", toc->toc_entry[i].pframe); + dprintf(f, "PLBA=%d\r\n", + burn_msf_to_lba(toc->toc_entry[i].pmin, + toc->toc_entry[i].psec, + toc->toc_entry[i].pframe)); + dprintf(f, "\r\n"); + } +} +#endif + +void toc_find_modes(struct burn_drive *d) +{ + struct burn_read_opts o; + int lba; + int i, j; + struct buffer mem; + struct burn_toc_entry *e; + + assert(d->busy); + + mem.bytes = 0; + mem.sectors = 1; + o.raw = 1; + o.c2errors = 0; + o.subcodes_audio = 1; + o.subcodes_data = 1; + o.hardware_error_recovery = 1; + o.report_recovered_errors = 0; + o.transfer_damaged_blocks = 1; + o.hardware_error_retries = 1; + + for (i = 0; i < d->disc->sessions; i++) + for (j = 0; j < d->disc->session[i]->tracks; j++) { + struct burn_track *t = d->disc->session[i]->track[j]; + + e = t->entry; + if (!e) + lba = 0; + else + lba = burn_msf_to_lba(e->pmin, e->psec, + e->pframe); +/* XXX | in the subcodes if appropriate! */ + if (e && !(e->control & 4)) { + t->mode = BURN_AUDIO; + } else { + mem.sectors = 1; + d->read_sectors(d, lba, mem.sectors, &o, &mem); + t->mode = sector_identify(mem.data); + } + } +} diff --git a/trunk/libburn/toc.h b/trunk/libburn/toc.h new file mode 100644 index 00000000..1d804e75 --- /dev/null +++ b/trunk/libburn/toc.h @@ -0,0 +1,48 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __TOC_H +#define __TOC_H + +struct command; + +#include "libburn.h" +#include "structure.h" + +/* return if a given entry refers to a track position */ +#define TOC_ENTRY_IS_TRACK(drive, entrynum) \ + ((drive)->toc_entry[entrynum].point < 100) + +/* return if a given entry is in audio or data format */ +#define TOC_ENTRY_IS_AUDIO(drive, entrynum) \ + (~(drive)->toc_entry[entrynum].control & 4) + +/* return the point value for a given entry number */ +#define TOC_POINT(drive, entrynum) ((drive)->toc_entry[entrynum].point) + +/* return the track struct for a given entry number */ +#define TOC_TRACK(drive, entrynum) \ + ((drive)->track[TOC_POINT(drive, entrynum) - 1]) + +/* return the lba of a toc entry */ +#define TOC_ENTRY_PLBA(drive, entrynum) \ + burn_msf_to_lba((drive)->toc_entry[(entrynum)].pmin, \ + (drive)->toc_entry[(entrynum)].psec, \ + (drive)->toc_entry[(entrynum)].pframe) + +/* flags for the q subchannel control field */ +#define TOC_CONTROL_AUDIO (0) +#define TOC_CONTROL_DATA (1 << 2) +#define TOC_CONTROL_AUDIO_TWO_CHANNELS (0) +#define TOC_CONTROL_AUDIO_FOUR_CHANNELS (1 << 3) +#define TOC_CONTROL_AUDIO_PRE_EMPHASIS (1 << 0) +#define TOC_CONTROL_DATA_RECORDED_UNINTERRUPTED (0) +#define TOC_CONTROL_DATA_RECORDED_INCREMENT (1 << 0) +#define TOC_CONTROL_COPY_PROHIBITED (0) +#define TOC_CONTROL_COPY_PERMITTED (1 << 1) + +/** read a sector from each track on disc to determine modes + @param d The drive. +*/ +void toc_find_modes(struct burn_drive *d); + +#endif /*__TOC_H*/ diff --git a/trunk/libburn/transport.h b/trunk/libburn/transport.h new file mode 100644 index 00000000..2f7ee1a0 --- /dev/null +++ b/trunk/libburn/transport.h @@ -0,0 +1,162 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __TRANSPORT +#define __TRANSPORT + +#include "libburn.h" + +#include +/* sg data structures */ +#include +#include +#include + +/* kludge! glibc headers don't define all the SCSI shit that we use! */ +#ifndef SG_GET_ACCESS_COUNT +# define SG_GET_ACCESS_COUNT 0x2289 +#endif + +#define BUFFER_SIZE 65536 + +enum transfer_direction +{ TO_DRIVE, FROM_DRIVE, NO_TRANSFER }; + +/* end of sg data structures */ + +/* generic 'drive' data structures */ + +struct cue_sheet +{ + int count; + unsigned char *data; +}; + +struct params +{ + int speed; + int retries; +}; + +struct buffer +{ + unsigned char data[BUFFER_SIZE]; + int sectors; + int bytes; +}; + +struct command +{ + unsigned char opcode[16]; + int oplen; + int dir; + unsigned char sense[128]; + int error; + int retry; + struct buffer *page; +}; + +struct scsi_inquiry_data +{ + char vendor[9]; + char product[17]; + char revision[5]; + int valid; +}; + +struct scsi_mode_data +{ + int buffer_size; + int dvdram_read; + int dvdram_write; + int dvdr_read; + int dvdr_write; + int dvdrom_read; + int cdrw_read; + int cdrw_write; + int cdr_read; + int cdr_write; + int simulate; + int max_read_speed; + int max_write_speed; + int cur_read_speed; + int cur_write_speed; + int retry_page_length; + int retry_page_valid; + int write_page_length; + int write_page_valid; + int c2_pointers; + int valid; + int underrun_proof; +}; + +struct burn_drive +{ + int host; + int id; + int channel; + int lun; + char *devname; + int fd; + + pthread_mutex_t access_lock; + + enum burn_disc_status status; + int erasable; + volatile int released; + int nwa; /* next writeable address */ + int alba; /* absolute lba */ + int rlba; /* relative lba in section */ + int start_lba; + int end_lba; + int toc_temp; + struct burn_disc *disc; /* disc structure */ + int block_types[4]; + struct buffer *buffer; + struct burn_progress progress; + + volatile int cancel; + volatile enum burn_drive_status busy; +/* transport functions */ + int (*grab) (struct burn_drive *); + int (*release) (struct burn_drive *); + int (*issue_command) (struct burn_drive *, struct command *); + +/* lower level functions */ + void (*erase) (struct burn_drive *, int); + void (*getcaps) (struct burn_drive *); + int (*write) (struct burn_drive *, int, struct buffer *); + void (*read_toc) (struct burn_drive *); + void (*lock) (struct burn_drive *); + void (*unlock) (struct burn_drive *); + void (*eject) (struct burn_drive *); + void (*load) (struct burn_drive *); + void (*read_disc_info) (struct burn_drive *); + void (*read_sectors) (struct burn_drive *, + int start, + int len, + const struct burn_read_opts *, struct buffer *); + void (*perform_opc) (struct burn_drive *); + void (*set_speed) (struct burn_drive *, int, int); + void (*send_parameters) (struct burn_drive *, + const struct burn_read_opts *); + void (*send_write_parameters) (struct burn_drive *, + const struct burn_write_opts *); + void (*send_cue_sheet) (struct burn_drive *, struct cue_sheet *); + void (*sync_cache) (struct burn_drive *); + int (*get_erase_progress) (struct burn_drive *); + int (*get_nwa) (struct burn_drive *); + void (*close_disc) (struct burn_drive * d, struct burn_write_opts * o); + void (*close_session) (struct burn_drive * d, + struct burn_write_opts * o); + int (*test_unit_ready) (struct burn_drive * d); + void (*probe_write_modes) (struct burn_drive * d); + struct params params; + struct scsi_inquiry_data *idata; + struct scsi_mode_data *mdata; + int toc_entries; + struct burn_toc_entry *toc_entry; +}; + +/* end of generic 'drive' data structures */ + +#endif /* __TRANSPORT */ diff --git a/trunk/libburn/util.c b/trunk/libburn/util.c new file mode 100644 index 00000000..cad28587 --- /dev/null +++ b/trunk/libburn/util.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include "../version.h" +#include "util.h" +#include "libburn.h" + +char *burn_strdup(char *s) +{ + char *ret; + int l; + + assert(s); + + l = strlen(s) + 1; + ret = malloc(l); + memcpy(ret, s, l); + + return ret; +} + +char *burn_strndup(char *s, int n) +{ + char *ret; + int l; + + assert(s); + assert(n > 0); + + l = strlen(s); + ret = malloc(l < n ? l : n); + + memcpy(ret, s, l < n - 1 ? l : n - 1); + ret[n - 1] = '\0'; + + return ret; +} + +void burn_version(int *major, int *minor, int *micro) +{ + *major = BURN_MAJOR_VERSION; + *minor = BURN_MINOR_VERSION; + *micro = BURN_MICRO_VERSION; +} diff --git a/trunk/libburn/util.h b/trunk/libburn/util.h new file mode 100644 index 00000000..4e74895d --- /dev/null +++ b/trunk/libburn/util.h @@ -0,0 +1,8 @@ +#ifndef __UTIL +#define __UTIL + +char *burn_strdup(char *s); + +char *burn_strndup(char *s, int n); + +#endif diff --git a/trunk/libburn/write.c b/trunk/libburn/write.c new file mode 100644 index 00000000..fca62b69 --- /dev/null +++ b/trunk/libburn/write.c @@ -0,0 +1,529 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include +#include +#include +#include "error.h" +#include "sector.h" +#include "libburn.h" +#include "drive.h" +#include "transport.h" +#include "message.h" +#include "crc.h" +#include "debug.h" +#include "init.h" +#include "lec.h" +#include "toc.h" +#include "util.h" +#include "sg.h" +#include "write.h" +#include "options.h" + +static int type_to_ctrl(int mode) +{ + int ctrl = 0; + + int data = BURN_MODE2 | BURN_MODE1 | BURN_MODE0; + + if (mode & data) { + ctrl |= 4; + } else if (mode & BURN_AUDIO) { + if (mode & BURN_4CH) + ctrl |= 8; + if (mode & BURN_PREEMPHASIS) + ctrl |= 1; + } else + assert(0); + + if (mode & BURN_COPY) + ctrl |= 2; + + return ctrl; +} + +/* only the ctrl nibble is set here (not adr) */ +static void type_to_form(int mode, unsigned char *ctladr, int *form) +{ + *ctladr = type_to_ctrl(mode) << 4; + + if (mode & BURN_AUDIO) + *form = 0; + if (mode & BURN_MODE0) + assert(0); + if (mode & BURN_MODE1) + *form = 0x10; + if (mode & BURN_MODE2) + assert(0); /* XXX someone's gonna want this sometime */ + if (mode & BURN_MODE_RAW) + *form = 0; + if (mode & BURN_SUBCODE_P16) /* must be expanded to R96 */ + *form |= 0x40; + if (mode & BURN_SUBCODE_P96) + *form |= 0xC0; + if (mode & BURN_SUBCODE_R96) + *form |= 0x40; +} + +int burn_write_flush(struct burn_write_opts *o) +{ + struct burn_drive *d = o->drive; + + if (d->buffer->bytes && !d->cancel) { + int err; + err = d->write(d, d->nwa, d->buffer); + if (err == BE_CANCELLED) + return 0; + d->nwa += d->buffer->sectors; + } + d->sync_cache(d); + return 1; +} + +static void print_cue(struct cue_sheet *sheet) +{ + int i; + unsigned char *unit; + + printf("\n"); + printf("ctladr|trno|indx|form|scms| msf\n"); + printf("------+----+----+----+----+--------\n"); + for (i = 0; i < sheet->count; i++) { + unit = sheet->data + 8 * i; + printf(" %1X %1X | %02X | %02X | %02X | %02X |", + (unit[0] & 0xf0) >> 4, unit[0] & 0xf, unit[1], unit[2], + unit[3], unit[4]); + printf("%02X:%02X:%02X\n", unit[5], unit[6], unit[7]); + } +} + +static void add_cue(struct cue_sheet *sheet, unsigned char ctladr, + unsigned char tno, unsigned char indx, + unsigned char form, unsigned char scms, int lba) +{ + unsigned char *unit; + unsigned char *ptr; + int m, s, f; + + burn_lba_to_msf(lba, &m, &s, &f); + + sheet->count++; + ptr = realloc(sheet->data, sheet->count * 8); + assert(ptr); + sheet->data = ptr; + unit = sheet->data + (sheet->count - 1) * 8; + unit[0] = ctladr; + unit[1] = tno; + unit[2] = indx; + unit[3] = form; + unit[4] = scms; + unit[5] = m; + unit[6] = s; + unit[7] = f; +} + +struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o, + struct burn_session *session) +{ + int i, m, s, f, form, pform, runtime = -150; + unsigned char ctladr; + struct burn_drive *d; + struct burn_toc_entry *e; + struct cue_sheet *sheet; + struct burn_track **tar = session->track; + int ntr = session->tracks; + int rem = 0; + + d = o->drive; + + sheet = malloc(sizeof(struct cue_sheet)); + sheet->data = NULL; + sheet->count = 0; + + type_to_form(tar[0]->mode, &ctladr, &form); + add_cue(sheet, ctladr | 1, 0, 0, 1, 0, runtime); + add_cue(sheet, ctladr | 1, 1, 0, form, 0, runtime); + runtime += 150; + + burn_print(1, "toc for %d tracks:\n", ntr); + d->toc_entries = ntr + 3; + assert(d->toc_entry == NULL); + d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry)); + e = d->toc_entry; + memset((void *)e, 0, d->toc_entries * sizeof(struct burn_toc_entry)); + e[0].point = 0xA0; + if (tar[0]->mode & BURN_AUDIO) + e[0].control = TOC_CONTROL_AUDIO; + else + e[0].control = TOC_CONTROL_DATA; + e[0].pmin = 1; + e[0].psec = o->format; + e[0].adr = 1; + e[1].point = 0xA1; + e[1].pmin = ntr; + e[1].adr = 1; + if (tar[ntr - 1]->mode & BURN_AUDIO) + e[1].control = TOC_CONTROL_AUDIO; + else + e[1].control = TOC_CONTROL_DATA; + e[2].point = 0xA2; + e[2].control = e[1].control; + e[2].adr = 1; + + tar[0]->pregap2 = 1; + pform = form; + for (i = 0; i < ntr; i++) { + type_to_form(tar[i]->mode, &ctladr, &form); + if (pform != form) { + add_cue(sheet, ctladr | 1, i + 1, 0, form, 0, runtime); + runtime += 150; +/* XXX fix pregap interval 1 for data tracks */ +// if (!(form & BURN_AUDIO)) +// tar[i]->pregap1 = 1; + tar[i]->pregap2 = 1; + } +/* XXX HERE IS WHERE WE DO INDICES IN THE CUE SHEET */ +/* XXX and we should make sure the gaps conform to ecma-130... */ + tar[i]->entry = &e[3 + i]; + e[3 + i].point = i + 1; + burn_lba_to_msf(runtime, &m, &s, &f); + e[3 + i].pmin = m; + e[3 + i].psec = s; + e[3 + i].pframe = f; + e[3 + i].adr = 1; + e[3 + i].control = type_to_ctrl(tar[i]->mode); + burn_print(1, "track %d control %d\n", tar[i]->mode, + e[3 + i].control); + add_cue(sheet, ctladr | 1, i + 1, 1, form, 0, runtime); + runtime += burn_track_get_sectors(tar[i]); +/* if we're padding, we'll clear any current shortage. + if we're not, we'll slip toc entries by a sector every time our + shortage is more than a sector +XXX this is untested :) +*/ + if (!tar[i]->pad) { + rem += burn_track_get_shortage(tar[i]); + if (i +1 != ntr) + tar[i]->source->next = tar[i+1]->source; + } else if (rem) { + rem = 0; + runtime++; + } + if (rem > burn_sector_length(tar[i]->mode)) { + rem -= burn_sector_length(tar[i]->mode); + runtime--; + } + pform = form; + } + burn_lba_to_msf(runtime, &m, &s, &f); + e[2].pmin = m; + e[2].psec = s; + e[2].pframe = f; + burn_print(1, "run time is %d (%d:%d:%d)\n", runtime, m, s, f); + for (i = 0; i < d->toc_entries; i++) + burn_print(1, "point %d (%02d:%02d:%02d)\n", + d->toc_entry[i].point, d->toc_entry[i].pmin, + d->toc_entry[i].psec, d->toc_entry[i].pframe); + add_cue(sheet, ctladr | 1, 0xAA, 1, 1, 0, runtime); + return sheet; +} + +int burn_sector_length(int tracktype) +{ + if (tracktype & BURN_AUDIO) + return 2352; + if (tracktype & BURN_MODE_RAW) + return 2352; + if (tracktype & BURN_MODE1) + return 2048; + assert(0); + return 12345; +} + +int burn_subcode_length(int tracktype) +{ + if (tracktype & BURN_SUBCODE_P16) + return 16; + if ((tracktype & BURN_SUBCODE_P96) || (tracktype & BURN_SUBCODE_R96)) + return 96; + return 0; +} + +int burn_write_leadin(struct burn_write_opts *o, + struct burn_session *s, int first) +{ + struct burn_drive *d = o->drive; + int count; + + d->busy = BURN_DRIVE_WRITING_LEADIN; + + burn_print(5, first ? " first leadin\n" : " leadin\n"); + + if (first) + count = 0 - d->alba - 150; + else + count = 4500; + + d->progress.start_sector = d->alba; + d->progress.sectors = count; + d->progress.sector = 0; + + while (count != 0) { + if (!sector_toc(o, s->track[0]->mode)) + return 0; + count--; + d->progress.sector++; + } + d->busy = BURN_DRIVE_WRITING; + return 1; +} + +int burn_write_leadout(struct burn_write_opts *o, + int first, unsigned char control, int mode) +{ + struct burn_drive *d = o->drive; + int count; + + d->busy = BURN_DRIVE_WRITING_LEADOUT; + + d->rlba = -150; + burn_print(5, first ? " first leadout\n" : " leadout\n"); + if (first) + count = 6750; + else + count = 2250; + d->progress.start_sector = d->alba; + d->progress.sectors = count; + d->progress.sector = 0; + + while (count != 0) { + if (!sector_lout(o, control, mode)) + return 0; + count--; + d->progress.sector++; + } + d->busy = BURN_DRIVE_WRITING; + return 1; +} + +int burn_write_session(struct burn_write_opts *o, struct burn_session *s) +{ + struct burn_drive *d = o->drive; + struct burn_track *prev = NULL, *next = NULL; + int i; + + d->rlba = 0; + burn_print(1, " writing a session\n"); + for (i = 0; i < s->tracks; i++) { + if (i > 0) + prev = s->track[i - 1]; + if (i + 1 < s->tracks) + next = s->track[i + 1]; + else + next = NULL; + + if (!burn_write_track(o, s, i)) + return 0; + } + return 1; +} + +int burn_write_track(struct burn_write_opts *o, struct burn_session *s, + int tnum) +{ + struct burn_track *t = s->track[tnum]; + struct burn_drive *d = o->drive; + int i, tmp = 0; + int sectors; + + d->rlba = -150; + +/* XXX for tao, we don't want the pregaps but still want post? */ + if (o->write_type != BURN_WRITE_TAO) { + if (t->pregap1) + d->rlba += 75; + if (t->pregap2) + d->rlba += 150; + + if (t->pregap1) { + struct burn_track *pt = s->track[tnum - 1]; + + if (tnum == 0) { + printf("first track should not have a pregap1\n"); + pt = t; + } + for (i = 0; i < 75; i++) + if (!sector_pregap(o, t->entry->point, + pt->entry->control, pt->mode)) + return 0; + } + if (t->pregap2) + for (i = 0; i < 150; i++) + if (!sector_pregap(o, t->entry->point, + t->entry->control, t->mode)) + return 0; + } else { + o->control = t->entry->control; + d->send_write_parameters(d, o); + } + +/* user data */ + sectors = burn_track_get_sectors(t); + + /* Update progress */ + d->progress.start_sector = d->nwa; + d->progress.sectors = sectors; + d->progress.sector = 0; + + burn_print(12, "track is %d sectors long\n", sectors); + + if (tnum == s->tracks) + tmp = sectors > 150 ? 150 : sectors; + + for (i = 0; i < sectors - tmp; i++) { + if (!sector_data(o, t, 0)) + return 0; + + /* update current progress */ + d->progress.sector++; + } + for (; i < sectors; i++) { + burn_print(1, "last track, leadout prep\n"); + if (!sector_data(o, t, 1)) + return 0; + + /* update progress */ + d->progress.sector++; + } + + if (t->postgap) + for (i = 0; i < 150; i++) + if (!sector_postgap(o, t->entry->point, t->entry->control, + t->mode)) + return 0; + i = t->offset; + if (o->write_type == BURN_WRITE_SAO) { + if (d->buffer->bytes) { + int err; + err = d->write(d, d->nwa, d->buffer); + if (err == BE_CANCELLED) + return 0; + d->nwa += d->buffer->sectors; + d->buffer->bytes = 0; + d->buffer->sectors = 0; + } + } + if (o->write_type == BURN_WRITE_TAO) + if (!burn_write_flush(o)) + return 0; + return 1; +} + +void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc) +{ + struct cue_sheet *sheet; + struct burn_drive *d = o->drive; + struct buffer buf; + struct burn_track *lt; + int first = 1, i; + int res; + + burn_message_clear_queue(); + + burn_print(1, "sync write of %d sessions\n", disc->sessions); + d->buffer = &buf; + memset(d->buffer, 0, sizeof(struct buffer)); + + d->rlba = -150; + + d->toc_temp = 9; + +/* Apparently some drives require this command to be sent, and a few drives +return crap. so we send the command, then ignore the result. +*/ + res = d->get_nwa(d); +/* printf("ignored nwa: %d\n", res);*/ + + d->alba = d->start_lba; + d->nwa = d->alba; + + if (o->write_type != BURN_WRITE_TAO) + d->send_write_parameters(d, o); + + /* init progress before showing the state */ + d->progress.session = 0; + d->progress.sessions = disc->sessions; + d->progress.track = 0; + d->progress.tracks = disc->session[0]->tracks; + /* TODO: handle indices */ + d->progress.index = 0; + d->progress.indices = disc->session[0]->track[0]->indices; + /* TODO: handle multissession discs */ + /* XXX: sectors are only set during write track */ + d->progress.start_sector = 0; + d->progress.sectors = 0; + d->progress.sector = 0; + + d->busy = BURN_DRIVE_WRITING; + + for (i = 0; i < disc->sessions; i++) { + /* update progress */ + d->progress.session = i; + d->progress.tracks = disc->session[i]->tracks; + + sheet = burn_create_toc_entries(o, disc->session[i]); +/* print_cue(sheet);*/ + if (o->write_type == BURN_WRITE_SAO) + d->send_cue_sheet(d, sheet); + free(sheet); + + if (o->write_type == BURN_WRITE_RAW) { + if (!burn_write_leadin(o, disc->session[i], first)) + goto fail; + } else { + if (first) { + d->nwa = -150; + d->alba = -150; + } else { + d->nwa += 4500; + d->alba += 4500; + } + } + if (!burn_write_session(o, disc->session[i])) + goto fail; + + lt = disc->session[i]->track[disc->session[i]->tracks - 1]; + if (o->write_type == BURN_WRITE_RAW) { + if (!burn_write_leadout(o, first, lt->entry->control, + lt->mode)) + goto fail; + } else { + if (!burn_write_flush(o)) + goto fail; + d->nwa += first ? 6750 : 2250; + d->alba += first ? 6750 : 2250; + } + if (first) + first = 0; + + /* XXX: currently signs an end of session */ + d->progress.sector = 0; + d->progress.start_sector = 0; + d->progress.sectors = 0; + } + if (o->write_type != BURN_WRITE_SAO) + if (!burn_write_flush(o)) + goto fail; + sleep(1); + + burn_print(1, "done\n"); + d->busy = BURN_DRIVE_IDLE; + +fail: + d->sync_cache(d); + burn_print(1, "done - failed\n"); + d->busy = BURN_DRIVE_IDLE; +} diff --git a/trunk/libburn/write.h b/trunk/libburn/write.h new file mode 100644 index 00000000..d7eb733f --- /dev/null +++ b/trunk/libburn/write.h @@ -0,0 +1,25 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__WRITE_H +#define BURN__WRITE_H + +struct cue_sheet; +struct burn_session; +struct burn_write_opts; +struct burn_disc; + +struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o, + struct burn_session *session); +int burn_sector_length(int trackmode); +int burn_subcode_length(int trackmode); +void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc); +int burn_write_leadin(struct burn_write_opts *o, + struct burn_session *s, int first); +int burn_write_leadout(struct burn_write_opts *o, + int first, unsigned char control, int mode); +int burn_write_session(struct burn_write_opts *o, struct burn_session *s); +int burn_write_track(struct burn_write_opts *o, struct burn_session *s, + int tnum); +int burn_write_flush(struct burn_write_opts *o); + +#endif /* BURN__WRITE_H */ diff --git a/trunk/libisofs-1.pc.in b/trunk/libisofs-1.pc.in new file mode 100644 index 00000000..fb21b560 --- /dev/null +++ b/trunk/libisofs-1.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libisofs +Description: ISO9660 filesystem creation library +Version: @VERSION@ +Requires: +Libs: -L${libdir} -lisofs +Cflags: -I${includedir}/libburn/@BURN_MAJOR_VERSION@ diff --git a/trunk/libisofs/Makefile b/trunk/libisofs/Makefile new file mode 100755 index 00000000..062350dd --- /dev/null +++ b/trunk/libisofs/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/trunk/libisofs/Makefile.am b/trunk/libisofs/Makefile.am new file mode 100755 index 00000000..042bc7ea --- /dev/null +++ b/trunk/libisofs/Makefile.am @@ -0,0 +1,44 @@ +pkgconfigdir=$(libdir)/pkgconfig +libincludedir=$(includedir)/libburn/@BURN_MAJOR_VERSION@ + +lib_LTLIBRARIES = libisofs.la + +libisofs_la_SOURCES = \ + errors.h \ + errors.c \ + tree.h \ + tree.c \ + volume.h \ + volume.c \ + util.h \ + util.c \ + ecma119.c \ + ecma119.h \ + struct.h \ + struct.c \ + susp.h \ + susp.c \ + rockridge.h \ + rockridge.c + +libinclude_HEADERS = libisofs.h + +noinst_PROGRAMS = test +test_SOURCES = test.c +test_LDADD = $(libisofs_la_OBJECTS) + +INCLUDES = -I.. + +## ========================================================================= ## +indent_files = $(libisofs_la_SOURCES) + +indent: $(indent_files) + indent -bad -bap -nbbb -nbbo -nbc -bli0 -br -bls \ + -cdw -ce -cli0 -ncs -nbfda -i8 -l79 -lc79 \ + -lp -saf -sai -nprs -npsl -saw -sob -ss -ut \ + -sbi0 -nsc -ts8 -npcs -ncdb -fca \ + $^ + +.PHONY: indent + +## ========================================================================= ## diff --git a/trunk/libisofs/ecma119.c b/trunk/libisofs/ecma119.c new file mode 100755 index 00000000..6df65eb8 --- /dev/null +++ b/trunk/libisofs/ecma119.c @@ -0,0 +1,1506 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include "tree.h" +#include "ecma119.h" +#include "volume.h" +#include "util.h" +#include "struct.h" +#include "rockridge.h" +#include "libisofs.h" +#include "libburn/libburn.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char* const ecma119_standard_id = "CD001"; + +/* Format definitions */ +const char* const ecma119_vol_fmt = "B5bB"; /* common between all vol descs */ + +const char* const ecma119_privol_fmt = + "B" /* volume desc type */ + "5b" /* standard id */ + "B" /* volume desc version */ + "x" + "32b" /* system id */ + "32b" /* volume id */ + "8x" + "=L" /* volume space size */ + "32x" + "=H" /* volume set size */ + "=H" /* volume sequence number */ + "=H" /* block size */ + "=L" /* path table size */ + "L" /* m path table 1 */ + ">L" /* m path table 2 */ + "34B" /* root directory record */ + "128b" /* volume id */ + "128b" /* publisher id */ + "128b" /* data preparer id */ + "128b" /* application id */ + "37b" /* copyright file id */ + "37b" /* abstract file id */ + "37b" /* bibliographic file id */ + "T" /* creation timestamp */ + "T" /* modification timestamp */ + "T" /* expiration timestamp */ + "T" /* effective timestamp */ + "B" /* file structure version */ + "x" + "512x" /* Application Use */ + "653x"; /* reserved */ + +const char* const ecma119_supvol_joliet_fmt = + "B" /* volume desc type */ + "5b" /* standard id */ + "B" /* volume desc version */ + "B" /* volume flags */ + "16h" /* system id */ + "16h" /* volume id */ + "8x" + "=L" /* volume space size */ + "32b" /* escape sequences */ + "=H" /* volume set size */ + "=H" /* volume sequence number */ + "=H" /* block size */ + "=L" /* path table size */ + "L" /* m path table 1 */ + ">L" /* m path table 2 */ + "34B" /* root directory record */ + "64h" /* volume id */ + "64h" /* publisher id */ + "64h" /* data preparer id */ + "64h" /* application id */ + "18hx" /* copyright file id */ + "18hx" /* abstract file id */ + "18hx" /* bibliographic file id */ + "T" /* creation timestamp */ + "T" /* modification timestamp */ + "T" /* expiration timestamp */ + "T" /* effective timestamp */ + "B" /* file structure version */ + "x" + "512x" /* Application Use */ + "653x"; /* reserved */ + +const char* const ecma119_dir_record_fmt = + "B" /* dir record length */ + "B" /* extended attribute length */ + "=L" /* block */ + "=L" /* size */ + "S" + "B" /* file flags */ + "B" /* file unit size */ + "B" /* interleave gap size */ + "=H" /* volume sequence number */ + "B"; /* name length */ + /* file id ansa SU field have variable length, so they don't get defined + * yet. */ + +/* abstract string functions away from char* to make Joliet stuff easier */ +typedef void (*copy_string)(unsigned char *dest, void *str, int maxlen); +typedef int (*node_namelen)(struct iso_tree_node *node); +typedef const void* (*node_getname)(struct iso_tree_node *node); + +/* because we don't write SUSP fields in the Joliet Directory Records, it helps + * to abstract away the task of getting the non_CE_len of a susp_info. + */ +typedef int (*susp_len)(struct susp_info*); +typedef int (*total_dirent_len)(struct iso_tree_node*); + +const char application_id[] = "LIBBURN SUITE (C) 2002 D.FOREMAN/B.JANSENS"; +const char system_id[] = "LINUX"; + +const uint16_t* application_id_joliet = (uint16_t*) "\0j\0a\0p\0p\0i\0d\0\0"; +const uint16_t* system_id_joliet = (uint16_t*) "\0j\0s\0y\0s\0i\0d\0\0"; + +/* since we have to pass things by address to iso_struct_pack, this makes + * it easier. */ +const uint8_t zero = 0; +const uint8_t one = 1; + +enum RecordType { + RECORD_TYPE_SELF, + RECORD_TYPE_PARENT, + RECORD_TYPE_NORMAL, + RECORD_TYPE_ROOT +}; + +/* layout functions */ +static void ecma119_reorganize_heirarchy(struct ecma119_write_target *target, + struct iso_tree_dir *dir); +static void ecma119_alloc_writer_data(struct ecma119_write_target *target, + struct iso_tree_dir *dir); +static void ecma119_setup_path_tables_iso(struct ecma119_write_target*); +static void ecma119_setup_path_tables_joliet(struct ecma119_write_target*); + +/* burn_source functions */ +static int ecma119_read(struct burn_source*, unsigned char*, int); +static int ecma119_get_size(struct burn_source*); +static void ecma119_free_data(struct burn_source*); + +/* Writers for the different write_states. */ +static void ecma119_write_system_area(struct ecma119_write_target*, + unsigned char *buf); +static void ecma119_write_privol_desc(struct ecma119_write_target*, + unsigned char *buf); +static void ecma119_write_supvol_desc_joliet(struct ecma119_write_target*, + unsigned char *buf); +static void ecma119_write_vol_desc_terminator(struct ecma119_write_target*, + unsigned char *buf); +static void ecma119_write_path_table(struct ecma119_write_target*, + unsigned char *buf, + int flags, + int m_type); +static void ecma119_write_dir_records(struct ecma119_write_target*, + unsigned char *buf, + int flags); +static void ecma119_write_files(struct ecma119_write_target*, + unsigned char *buf); + +/* helpers for the writers */ +static void ecma119_write_path_table_full(struct ecma119_write_target*, + unsigned char *buf, + int flags, + int mtype); +static unsigned char *ecma119_write_dir(struct ecma119_write_target*, + int flags, + struct iso_tree_dir*); +static void ecma119_write_dir_record(struct ecma119_write_target*, + unsigned char *, + int flags, + struct iso_tree_node*, + enum RecordType); +static void ecma119_write_dir_record_iso(struct ecma119_write_target *t, + uint8_t *buf, + struct iso_tree_node *node, + enum RecordType type); +static void ecma119_write_dir_record_joliet(struct ecma119_write_target *t, + uint8_t *buf, + struct iso_tree_node *node, + enum RecordType type); +static void ecma119_write_dir_record_noname(struct ecma119_write_target *t, + uint8_t *buf, + struct iso_tree_node *node, + enum RecordType type, + uint32_t block, + uint32_t size, + int joliet); + + +/* name-mangling routines */ + +/* return a newly allocated array of pointers to all the children, in + * in alphabetical order and with mangled names. */ +static struct iso_tree_node **ecma119_mangle_names(struct iso_tree_dir *dir); + +/* mangle a single filename according to where the last '.' is: + * \param name the name to mangle + * \param num_change the number of characters to mangle + * \param seq_num the string ("%0d", seq_num) to which it should + * be mangled + */ +static void ecma119_mangle_name(char *name, + int num_change, + int seq_num); + +/* mangle a single filename based on the explicit positions passed. That is, + * the difference between this and ecma119_mangle_name_iso is that this one + * doesn't look for the '.' character; it changes the character at the + * beginning of name. */ +static void ecma119_mangle_name_priv(char *name, + int num_change, + int seq_num); + +/* return a newly allocated array of pointers to all the children, in + * alphabetical order. The difference between this and ecma119_mangle_names + * is that this function sorts the files according to joliet names and it + * doesn't mangle the names. */ +static struct iso_tree_node **ecma119_sort_joliet(struct iso_tree_dir *dir); + +/* string abstraction functions */ +static int node_namelen_iso(struct iso_tree_node *node) +{ + return strlen(iso_tree_node_get_name(node, ISO_NAME_ISO)); +} + +static int node_namelen_joliet(struct iso_tree_node *node) +{ + const char *name = iso_tree_node_get_name(node, ISO_NAME_JOLIET); + + /* return length in bytes, not length in characters */ + return ucslen((uint16_t*)name) * 2; +} + +static const void *node_getname_iso(struct iso_tree_node *node) +{ + return iso_tree_node_get_name(node, ISO_NAME_ISO); +} + +static const void *node_getname_joliet(struct iso_tree_node *node) +{ + return iso_tree_node_get_name(node, ISO_NAME_JOLIET); +} + +static int susp_len_iso(struct susp_info *s) +{ + return s->non_CE_len; +} + +static int susp_len_joliet(struct susp_info *s) +{ + return 0; +} + +static int dirent_len_iso(struct iso_tree_node *n) +{ + return n->dirent_len + GET_NODE_INF(n)->susp.non_CE_len; +} + +static int dirent_len_joliet(struct iso_tree_node *n) +{ + return 34 + node_namelen_joliet(n); +} + +static void print_dir_info(struct iso_tree_dir *dir, + struct ecma119_write_target *t, + int spaces) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + int i; + + for (i=0; inon_CE_len=%d, " + "parent->non_CE_len=%d, len=%d, blk=%d, jblk=%d\n", + inf->susp.non_CE_len, + inf->susp.CE_len, + inf->self_susp.non_CE_len, + inf->parent_susp.non_CE_len, + inf->len, + (int)dir->block, + (int)inf->joliet_block); +} + +static void print_file_info(struct iso_tree_file *file, + struct ecma119_write_target *t, + int spaces) +{ + struct file_write_info *inf = GET_FILE_INF(file); + int i; + + for (i=0; idirent_len, + inf->susp.non_CE_len, + inf->susp.CE_len, + (int)file->attrib.st_size, + (int)file->block); +} + +static struct iso_tree_node **ecma119_mangle_names(struct iso_tree_dir *dir) +{ + size_t retsize = dir->nfiles + dir->nchildren; + struct iso_tree_node **ret = calloc(1, sizeof(void*) * retsize); + int i, j, k; + + j = 0; + for (i=0; infiles; i++) { + ret[j++] = ISO_NODE(dir->files[i]); + } + for (i=0; inchildren; i++) { + ret[j++] = ISO_NODE(dir->children[i]); + } + + qsort(ret, retsize, sizeof(void*), iso_node_cmp_iso); + + for (i=0; i= 10) { + return; + } + + overwritten = name[num_change]; + sprintf(fmt, "%%0%1dd", num_change); + sprintf(name, fmt, seq_num); + name[num_change] = overwritten; +} + +static struct iso_tree_node **ecma119_sort_joliet(struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + size_t retsize = dir->nfiles + inf->real_nchildren; + struct iso_tree_node **ret = malloc(retsize * sizeof(void*)); + int i, j; + + j = 0; + for (i=0; infiles; i++) { + ret[j++] = ISO_NODE(dir->files[i]); + } + for (i=0; ireal_nchildren; i++) { + ret[j++] = ISO_NODE(inf->real_children[i]); + } + qsort(ret, retsize, sizeof(void*), iso_node_cmp_joliet); + + return ret; +} + +struct ecma119_write_target *ecma119_target_new(struct iso_volset *volset, + int volnum) +{ + struct ecma119_write_target *t; + + assert(volnum < volset->volset_size); + + t = calloc(1, sizeof(struct ecma119_write_target)); + t->volset = volset; + t->volnum = volnum; + t->now = time(NULL); + t->block_size = 2048; + + iso_tree_sort(TARGET_ROOT(t)); + ecma119_alloc_writer_data(t, TARGET_ROOT(t)); + ecma119_reorganize_heirarchy(t, TARGET_ROOT(t)); + + return t; +} + +/** + * Write create all necessary SUSP fields for the given directory and + * initialise them. Any SUSP fields that require an offset won't be completed + * yet. Ensure that the size fields in the susp_info structs are correct. + */ +static void ecma119_susp_dir_layout(struct ecma119_write_target *target, + struct iso_tree_dir *dir, + int flags) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + struct susp_info *susp = &inf->susp; + susp->n_susp_fields = 0; + susp->susp_fields = NULL; + + if (!target->rockridge) { + /* since Rock Ridge is the only SUSP extension supported, + * no need to continue. */ + return; + } + + if (dir->depth == 1) { + susp_add_SP(target, dir); + susp_add_ER(target, dir); + } else { + rrip_add_NM(target, ISO_NODE(dir)); + rrip_add_TF(target, ISO_NODE(dir)); + } + rrip_add_PX_dir(target, dir); + + if (inf->real_parent != dir->parent) { + rrip_add_RE(target, ISO_NODE(dir)); + rrip_add_PL(target, dir); + } + susp_add_CE(target, ISO_NODE(dir)); +} + +/** + * Write create all necessary SUSP fields for the given file and + * initialise them. Any SUSP fields that require an offset won't be completed + * yet. Ensure that the size fields in the susp_info structs are correct. + */ +static void ecma119_susp_file_layout(struct ecma119_write_target *target, + struct iso_tree_file *file) +{ + struct file_write_info *inf = GET_FILE_INF(file); + + if (!target->rockridge) { + return; + } + + rrip_add_PX(target, ISO_NODE(file)); + rrip_add_NM(target, ISO_NODE(file)); + rrip_add_TF(target, ISO_NODE(file)); + + if (inf->real_me) { + rrip_add_CL(target, ISO_NODE(file)); + } + if (S_ISLNK(file->attrib.st_mode)) { + rrip_add_SL(target, ISO_NODE(file)); + } + if (S_ISCHR(file->attrib.st_mode) || S_ISBLK(file->attrib.st_mode)) { + rrip_add_PN(target, ISO_NODE(file)); + } + susp_add_CE(target, ISO_NODE(file)); +} + +/** + * Recursively allocate the writer_data pointer for each node in the tree. + * Also, save the current state of the tree in the inf->real_XXX pointers. + */ +static void ecma119_alloc_writer_data(struct ecma119_write_target *target, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf; + int i; + + inf = calloc(1, sizeof(struct dir_write_info)); + inf->real_parent = dir->parent; + inf->real_nchildren = dir->nchildren; + inf->real_children = malloc(sizeof(void*) * dir->nchildren); + memcpy(inf->real_children, dir->children, dir->nchildren*sizeof(void*)); + inf->real_depth = dir->depth; + + dir->writer_data = inf; + + for (i=0; inchildren; i++) { + ecma119_alloc_writer_data(target, dir->children[i]); + } + for (i=0; infiles; i++) { + dir->files[i]->writer_data = + calloc(1, sizeof(struct file_write_info)); + } +} + +/* ensure that the maximum height of the directory tree is 8, repositioning + * directories as necessary */ +static void ecma119_reorganize_heirarchy(struct ecma119_write_target *target, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + struct iso_tree_dir *root = TARGET_ROOT(target); + struct iso_tree_file *file; + struct file_write_info *finf; + + /* save this now in case a recursive call modifies this value */ + int nchildren = dir->nchildren; + int i; + + if (dir == root) { + dir->depth = 1; + } else { + dir->depth = dir->parent->depth + 1; + + assert(dir->depth <= 9); + if (dir->depth == 9) { + dir->depth = 2; + dir->parent = root; + root->nchildren++; + root->children = realloc(root->children, + root->nchildren * sizeof(void*)); + root->children[root->nchildren-1] = dir; + + /* no need to reshuffle the siblings since we know that + * _every_ sibling will also be relocated. */ + inf->real_parent->nchildren--; + if (!inf->real_parent->nchildren) { + free(inf->real_parent->children); + inf->real_parent->children = NULL; + } + /* insert a placeholder file for the CL field */ + file = iso_tree_add_new_file(inf->real_parent, + dir->name.full); + finf = calloc(1, sizeof(struct file_write_info)); + finf->real_me = dir; + file->writer_data = finf; + } + } + for (i=0; ichildren[i]); + } +} + +/* set directory sizes recursively. Also fill out the dirlist_len and + * filelist_len fields in the ecma119 writer. + */ +static void ecma119_target_rsize(struct ecma119_write_target *t, + struct iso_tree_dir *dir) +{ + int i; + struct dir_write_info *inf = GET_DIR_INF(dir); + struct node_write_info *cinf; + + t->dirlist_len++; + + /* work out the size of the dirents */ + if (dir->depth == 1) { + dir->dirent_len = 34; + } else { + dir->dirent_len = 33 + node_namelen_iso(ISO_NODE(dir)); + } + dir->dirent_len += dir->dirent_len % 2; + + for (i=0; infiles; i++) { + dir->files[i]->dirent_len = 33 + node_namelen_iso( + ISO_NODE(dir->files[i])); + dir->files[i]->dirent_len += dir->files[i]->dirent_len % 2; + + if (dir->files[i]->path && dir->files[i]->attrib.st_size) { + t->filelist_len++; + } + } + + /* layout all the susp entries and calculate the total size */ + ecma119_susp_dir_layout(t, dir, 0); + + inf->len = 34 + inf->self_susp.non_CE_len /* for "." and ".." */ + + 34 + inf->parent_susp.non_CE_len; + inf->susp_len = inf->susp.CE_len + + inf->self_susp.CE_len + + inf->parent_susp.CE_len; + + for (i=0; infiles; i++) { + ecma119_susp_file_layout(t, dir->files[i]); + cinf = GET_NODE_INF(dir->files[i]); + inf->len += dir->files[i]->dirent_len + cinf->susp.non_CE_len; + inf->susp_len += cinf->susp.CE_len; + } + for (i=0; inchildren; i++) { + ecma119_target_rsize(t, dir->children[i]); + cinf = GET_NODE_INF(dir->children[i]); + inf->len += dir->children[i]->dirent_len +cinf->susp.non_CE_len; + inf->susp_len += cinf->susp.CE_len; + } + dir->attrib.st_size = inf->len; /* the actual size of the data is + * inf->len + inf->susp_len because we + * append the CE data to the end of the + * directory. But the ISO volume + * doesn't need to know. + */ +} + +static void ecma119_target_rsize_joliet(struct ecma119_write_target *t, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + int i; + + inf->joliet_len = 34 + 34; /* for "." and ".." */ + for (i=0; infiles; i++) { + /* don't count files that are placeholders for Rock Ridge + * relocated directories */ + if (!GET_FILE_INF(dir->files[i])->real_me) { + inf->joliet_len += + dirent_len_joliet(ISO_NODE(dir->files[i])); + } + } + for (i=0; ireal_nchildren; i++) { + struct iso_tree_node *ch = ISO_NODE(inf->real_children[i]); + inf->joliet_len += dirent_len_joliet(ch); + ecma119_target_rsize_joliet(t, inf->real_children[i]); + } +} + +/* set directory positions recursively. Also fill out the dirlist in the + * ecma119_write_target */ +static void ecma119_target_dir_layout(struct ecma119_write_target *t, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + int i; + + t->dirlist[t->curfile++] = dir; + dir->block = t->curblock; + t->curblock += DIV_UP(inf->len + inf->susp_len, 2048); + + for (i=0; inchildren; i++) { + ecma119_target_dir_layout(t, dir->children[i]); + } +} + +/* same as ecma119_target_dir_layout, but for Joliet. */ +static void ecma119_target_dir_layout_joliet(struct ecma119_write_target *t, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + int i; + + t->dirlist_joliet[t->curfile++] = dir; + inf->joliet_block = t->curblock; + t->curblock += DIV_UP(inf->joliet_len, 2048); + + for (i=0; ireal_nchildren; i++) { + ecma119_target_dir_layout_joliet(t, inf->real_children[i]); + } +} + +/* set file positions recursively. Also fill in the filelist in the + * ecma119_write_target */ +static void ecma119_target_file_layout(struct ecma119_write_target *t, + struct iso_tree_dir *dir) +{ + int i; + + for (i=0; infiles; i++) { + if (dir->files[i]->path && dir->files[i]->attrib.st_size) { + t->filelist[t->curfile++] = dir->files[i]; + } + dir->files[i]->block = t->curblock; + t->curblock += DIV_UP(dir->files[i]->attrib.st_size, 2048); + } + for (i=0; inchildren; i++) { + ecma119_target_file_layout(t, dir->children[i]); + } +} + +void ecma119_target_layout(struct ecma119_write_target *t) +{ + ecma119_target_rsize(t, TARGET_ROOT(t)); + if (t->joliet) { + ecma119_target_rsize_joliet(t, TARGET_ROOT(t)); + } + ecma119_setup_path_tables_iso(t); + t->curblock = 16 /* for the system area */ + + 1 /* volume desc */ + + 1; /* volume desc terminator */ + if (t->joliet) { + t->curblock++; /* joliet supplementary volume desc */ + } + + t->l_path_table_pos = t->curblock; + t->curblock += DIV_UP(t->path_table_size, 2048); + t->m_path_table_pos = t->curblock; + t->curblock += DIV_UP(t->path_table_size, 2048); + + if (t->joliet) { + ecma119_setup_path_tables_joliet(t); + t->l_path_table_pos_joliet = t->curblock; + t->curblock += DIV_UP(t->path_table_size_joliet, 2048); + t->m_path_table_pos_joliet = t->curblock; + t->curblock += DIV_UP(t->path_table_size_joliet, 2048); + } + + t->dirlist = calloc(1, sizeof(void*) * t->dirlist_len); + t->filelist = calloc(1, sizeof(void*) * t->filelist_len); + t->curfile = 0; + ecma119_target_dir_layout(t, TARGET_ROOT(t)); + + if (t->joliet) { + t->curfile = 0; + t->dirlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len); + ecma119_target_dir_layout_joliet(t, TARGET_ROOT(t)); + } + + t->curfile = 0; + ecma119_target_file_layout(t, TARGET_ROOT(t)); + t->total_size = t->curblock * 2048; + t->vol_space_size = t->curblock; + + if (t->rockridge) { + susp_finalize(t, TARGET_ROOT(t)); + rrip_finalize(t, TARGET_ROOT(t)); + } + + iso_tree_print_verbose(TARGET_ROOT(t), + (print_dir_callback)print_dir_info, + (print_file_callback)print_file_info, + t, 0); + + /* prepare for writing */ + t->curblock = -1; + t->state = ECMA119_WRITE_SYSTEM_AREA; +} + +struct burn_source *iso_source_new_ecma119(struct iso_volset *volset, + int volnum, + int level, + int flags) +{ + struct burn_source *src = calloc(1, sizeof(struct burn_source)); + struct ecma119_write_target *t = ecma119_target_new(volset, volnum); + struct iso_volume *vol = volset->volume[volnum]; + + t->iso_level = level; + t->rockridge = (flags & ECMA119_ROCKRIDGE) ? 1:0; + t->joliet = (flags & ECMA119_JOLIET) ? 1:0; + + vol->iso_level = t->iso_level; + vol->rockridge = t->rockridge; + vol->joliet = t->joliet; + + ecma119_target_layout(t); + src->read = ecma119_read; + src->read_sub = NULL; + src->get_size = ecma119_get_size; + src->free_data = ecma119_free_data; + src->data = t; + return src; +} + +static int ecma119_read(struct burn_source *src, + unsigned char *data, + int size) +{ + struct ecma119_write_target *t = src->data; + + assert( src->read == ecma119_read && src->get_size == ecma119_get_size + && src->free_data == ecma119_free_data + && size == 2048); + + t->curblock++; + memset(data, 0, size); + switch(t->state) { + case ECMA119_WRITE_SYSTEM_AREA: + ecma119_write_system_area(t, data); + break; + case ECMA119_WRITE_PRI_VOL_DESC: + ecma119_write_privol_desc(t, data); + break; + case ECMA119_WRITE_SUP_VOL_DESC_JOLIET: + ecma119_write_supvol_desc_joliet(t, data); + break; + case ECMA119_WRITE_VOL_DESC_TERMINATOR: + ecma119_write_vol_desc_terminator(t, data); + break; + case ECMA119_WRITE_L_PATH_TABLE: + ecma119_write_path_table(t, data, 0, 0); + break; + case ECMA119_WRITE_M_PATH_TABLE: + ecma119_write_path_table(t, data, 0, 1); + break; + case ECMA119_WRITE_L_PATH_TABLE_JOLIET: + ecma119_write_path_table(t, data, ECMA119_JOLIET, 0); + break; + case ECMA119_WRITE_M_PATH_TABLE_JOLIET: + ecma119_write_path_table(t, data, ECMA119_JOLIET, 1); + break; + case ECMA119_WRITE_DIR_RECORDS: + ecma119_write_dir_records(t, data, 0); + break; + case ECMA119_WRITE_DIR_RECORDS_JOLIET: + ecma119_write_dir_records(t, data, ECMA119_JOLIET); + break; + case ECMA119_WRITE_FILES: + ecma119_write_files(t, data); + break; + case ECMA119_WRITE_DONE: + return 0; + default: + assert(0); + } + + return 2048; +} + +static int ecma119_get_size(struct burn_source *src) +{ + struct ecma119_write_target *t = src->data; + + assert( src->read == ecma119_read && src->get_size == ecma119_get_size + && src->free_data == ecma119_free_data); + + return t->total_size; +} + +/* free writer_data fields recursively */ +static void ecma119_free_writer_data(struct ecma119_write_target *t, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + int i; + + susp_free_fields(&inf->susp); + susp_free_fields(&inf->self_susp); + susp_free_fields(&inf->parent_susp); + + if (inf->real_children) { + free(inf->real_children); + } + + for (i=0; infiles; i++) { + struct file_write_info *finf = GET_FILE_INF(dir->files[i]); + susp_free_fields(&finf->susp); + } + for (i=0; inchildren; i++) { + ecma119_free_writer_data(t, dir->children[i]); + } +} + +static void ecma119_free_data(struct burn_source *src) +{ + struct ecma119_write_target *t = src->data; + + assert( src->read == ecma119_read && src->get_size == ecma119_get_size + && src->free_data == ecma119_free_data); + + if (t->filelist) free(t->filelist); + if (t->dirlist) free(t->dirlist); + if (t->pathlist) free(t->pathlist); + if (t->dirlist_joliet) free(t->dirlist_joliet); + if (t->pathlist_joliet) free(t->pathlist_joliet); + + ecma119_free_writer_data(t, TARGET_ROOT(t)); + + free(t); + src->data = NULL; + src->read = NULL; + src->get_size = NULL; + src->free_data = NULL; +} + +/*============================================================================*/ +/* Writing functions */ +/*============================================================================*/ + +static void ecma119_write_system_area(struct ecma119_write_target *t, + unsigned char *buf) +{ + if (t->curblock == 15) { + t->state = ECMA119_WRITE_PRI_VOL_DESC; + } +} + +static void ecma119_write_privol_desc(struct ecma119_write_target *t, + unsigned char *buf) +{ + struct iso_tree_node *root = ISO_NODE(TARGET_ROOT(t)); + struct iso_volume *vol = t->volset->volume[t->volnum]; + uint8_t one = 1; /* so that we can take &one */ + uint32_t zero = 0; + time_t never = -1; + uint8_t dir_record[34]; + + ecma119_write_dir_record(t, dir_record, 0, root, RECORD_TYPE_ROOT); + iso_struct_pack(ecma119_privol_fmt, buf, + &one, + "CD001", + &one, + system_id, + vol->volume_id.cstr, + &t->vol_space_size, + &t->volset->volset_size, + &t->volnum, + &t->block_size, + &t->path_table_size, + &t->l_path_table_pos, + &zero, + &t->m_path_table_pos, + &zero, + dir_record, + t->volset->volset_id.cstr, + vol->publisher_id.cstr, + vol->data_preparer_id.cstr, + application_id, + "", + "", + "", + &t->now, + &t->now, + &never, + &t->now, + &one); + + t->state = (t->joliet) ? ECMA119_WRITE_SUP_VOL_DESC_JOLIET : + ECMA119_WRITE_VOL_DESC_TERMINATOR; +} + +static void ecma119_write_supvol_desc_joliet(struct ecma119_write_target *t, + unsigned char *buf) +{ struct iso_tree_node *root = ISO_NODE(TARGET_ROOT(t)); + struct iso_volume *vol = t->volset->volume[t->volnum]; + uint8_t one = 1; /* so that we can take &one */ + uint8_t two = 2; + uint32_t zero = 0; + time_t never = -1; + uint8_t dir_record[34]; + + ecma119_write_dir_record(t, dir_record, ECMA119_JOLIET, + root, RECORD_TYPE_ROOT); + iso_struct_pack(ecma119_supvol_joliet_fmt, buf, + &two, + "CD001", + &one, + &zero, + system_id_joliet, + vol->volume_id.jstr, + &t->vol_space_size, + "%/E", + &t->volset->volset_size, + &t->volnum, + &t->block_size, + &t->path_table_size_joliet, + &t->l_path_table_pos_joliet, + &zero, + &t->m_path_table_pos_joliet, + &zero, + dir_record, + t->volset->volset_id.jstr, + vol->publisher_id.jstr, + vol->data_preparer_id.jstr, + application_id_joliet, + &zero, + &zero, + &zero, + &t->now, + &t->now, + &never, + &t->now, + &one); + + t->state = ECMA119_WRITE_VOL_DESC_TERMINATOR; +} + +static void ecma119_write_vol_desc_terminator(struct ecma119_write_target *t, + unsigned char *buf) +{ + buf[0] = 255; + strcpy((char*)&buf[1], "CD001"); + buf[6] = 1; + + t->state = ECMA119_WRITE_L_PATH_TABLE; +} + +/** + * Write a full path table to the buffer (it is assumed to be large enough). + * The path table will be broken into 2048-byte chunks later is necessary. + */ +static void ecma119_write_path_table_full(struct ecma119_write_target *t, + unsigned char *buf, + int flags, + int m_type) +{ + void (*write_int)(uint8_t*, uint32_t, int); + struct iso_tree_dir *dir; + const char *name; + int len, parent, i; + size_t off; + + node_namelen namelen; + node_getname getname; + off_t root_block; + struct iso_tree_dir **pathlist; + + if (flags & ECMA119_JOLIET) { + namelen = node_namelen_joliet; + getname = node_getname_joliet; + pathlist = t->pathlist_joliet; + root_block = GET_DIR_INF(pathlist[0])->joliet_block; + } else { + namelen = node_namelen_iso; + getname = node_getname_iso; + pathlist = t->pathlist; + root_block = pathlist[0]->block; + } + + write_int = m_type ? iso_msb : iso_lsb; + + /* write the root directory */ + buf[0] = 1; + buf[1] = 0; + write_int(&buf[2], root_block, 4); + write_int(&buf[6], 1, 2); + + /* write the rest */ + off = 10; + for (i=1; idirlist_len; i++) { + struct iso_tree_dir *dirparent; + off_t block; + + dir = pathlist[i]; + name = getname(ISO_NODE(dir)); + len = namelen(ISO_NODE(dir)); + if (flags & ECMA119_JOLIET) { + dirparent = GET_DIR_INF(dir)->real_parent; + block = GET_DIR_INF(dir)->joliet_block; + } else { + dirparent = dir->parent; + block = dir->block; + } + + for (parent=0; parentstate_data.path_table +static void ecma119_write_path_table(struct ecma119_write_target *t, + unsigned char *buf, + int flags, + int m_type) +{ + int path_table_size; + + if (flags & ECMA119_JOLIET) { + path_table_size = t->path_table_size_joliet; + } else { + path_table_size = t->path_table_size; + } + + if (!SDATA.data) { + SDATA.data = calloc(1, ROUND_UP(path_table_size, 2048)); + ecma119_write_path_table_full(t, SDATA.data, flags, m_type); + } + + memcpy(buf, SDATA.data + SDATA.blocks*2048, 2048); + SDATA.blocks++; + + if (SDATA.blocks*2048 >= path_table_size) { + free(SDATA.data); + SDATA.data = NULL; + SDATA.blocks = 0; + if (!t->joliet && m_type) { + t->state = ECMA119_WRITE_DIR_RECORDS; + } else { + t->state++; + } + } +} +#undef SDATA + +#define SDATA t->state_data.dir_records +static void ecma119_write_dir_records(struct ecma119_write_target *t, + unsigned char *buf, + int flags) +{ + struct iso_tree_dir **dirlist; + + if (flags & ECMA119_JOLIET) { + dirlist = t->dirlist_joliet; + } else { + dirlist = t->dirlist; + } + + if (!SDATA.data) { + struct iso_tree_dir *dir = dirlist[SDATA.dir++]; + struct dir_write_info *inf = GET_DIR_INF(dir); + + SDATA.data = ecma119_write_dir(t, flags, dir); + SDATA.pos = 0; + + if (flags & ECMA119_JOLIET) { + SDATA.data_len = inf->joliet_len; + } else { + SDATA.data_len = inf->len + inf->susp_len; + } + } + + memcpy(buf, SDATA.data + SDATA.pos, 2048); + SDATA.pos += 2048; + + if (SDATA.pos >= SDATA.data_len) { + free(SDATA.data); + SDATA.data = 0; + SDATA.pos = 0; + SDATA.data_len = 0; + if (SDATA.dir == t->dirlist_len) { + SDATA.dir = 0; + if (!t->joliet || (flags & ECMA119_JOLIET)) { + t->state = t->filelist_len ? ECMA119_WRITE_FILES + : ECMA119_WRITE_DONE; + } else { + t->state = ECMA119_WRITE_DIR_RECORDS_JOLIET; + } + } + } +} +#undef SDATA + +#define SDATA t->state_data.files +static void ecma119_write_files(struct ecma119_write_target *t, + unsigned char *buf) +{ + int numread; + + if (!SDATA.fd) { + struct iso_tree_file *f = t->filelist[SDATA.file++]; + assert(t->curblock == f->block); + SDATA.fd = fopen(f->path, "r"); + SDATA.data_len = f->attrib.st_size; + if (!SDATA.fd) { + fprintf(stderr, "Error: couldn't open file %s: %s\n", + f->path, strerror(errno)); + } + } + + if (SDATA.fd) { + numread = fread(buf, 1, 2048, SDATA.fd); + } else { + numread = t->block_size; + } + if (numread == -1) { + fprintf(stderr, "Error reading file: %s\n", strerror(errno)); + return; + } + SDATA.pos += numread; + + if (!SDATA.pos || SDATA.pos >= SDATA.data_len) { + fclose(SDATA.fd); + SDATA.data_len = 0; + SDATA.fd = NULL; + SDATA.pos = 0; + if (SDATA.file == t->filelist_len) { + SDATA.file = 0; + t->state = ECMA119_WRITE_DONE; + } + } +} +#undef SDATA + +static unsigned char *ecma119_write_dir(struct ecma119_write_target *t, + int flags, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + int len = ROUND_UP(inf->susp_len + inf->len, 2048); + unsigned char *buf; + int i, pos; + struct iso_tree_node *ch; + + susp_len slen; + total_dirent_len dlen; + struct iso_tree_node **children; + int nchildren; + + assert ( ((flags & ECMA119_JOLIET) && t->curblock == inf->joliet_block) + || t->curblock == dir->block ); + + if (flags & ECMA119_JOLIET) { + children = ecma119_sort_joliet(dir); + nchildren = inf->real_nchildren + dir->nfiles; + len = ROUND_UP(inf->joliet_len, 2048); + slen = susp_len_joliet; + dlen = dirent_len_joliet; + } else { + children = ecma119_mangle_names(dir); + nchildren = dir->nchildren + dir->nfiles; + len = ROUND_UP(inf->susp_len + inf->len, 2048); + slen = susp_len_iso; + dlen = dirent_len_iso; + } + + buf = calloc(1, len); + pos = 0; + + /* write all the dir records */ + ecma119_write_dir_record(t, buf+pos, flags, ISO_NODE(dir), + RECORD_TYPE_SELF); + pos += 34 + slen(&inf->self_susp); + ecma119_write_dir_record(t, buf+pos, flags, ISO_NODE(dir), + RECORD_TYPE_PARENT); + pos += 34 + slen(&inf->parent_susp); + + for (i=0; iattrib.st_mode ) + && GET_FILE_INF(ch)->real_me) { + continue; + } + ecma119_write_dir_record(t, buf+pos, flags, ch, + RECORD_TYPE_NORMAL); + pos += dlen(ch); + } + + if (flags & ECMA119_JOLIET) { + free(children); + return buf; + } + + /* write all the SUSP continuation areas */ + susp_write_CE(t, &inf->self_susp, buf+pos); + pos += inf->self_susp.CE_len; + susp_write_CE(t, &inf->parent_susp, buf+pos); + pos += inf->parent_susp.CE_len; + for (i=0; isusp, buf+pos); + pos += ninf->susp.CE_len; + } + free(children); + return buf; +} + +static void ecma119_write_dir_record(struct ecma119_write_target *t, + unsigned char *buf, + int flags, + struct iso_tree_node *node, + enum RecordType type) +{ + if (flags & ECMA119_JOLIET) { + ecma119_write_dir_record_joliet(t, buf, node, type); + } else { + ecma119_write_dir_record_iso(t, buf, node, type); + } +} + +static void ecma119_write_dir_record_iso(struct ecma119_write_target *t, + uint8_t *buf, + struct iso_tree_node *node, + enum RecordType type) +{ + struct node_write_info *inf = GET_NODE_INF(node); + uint8_t len_dr, len_fi, flags; + uint8_t vol_seq_num = t->volnum; + + if (type == RECORD_TYPE_NORMAL) { + len_fi = node_namelen_iso(node); + len_dr = 33 + len_fi + 1 - len_fi%2 + inf->susp.non_CE_len; + flags = (S_ISDIR(node->attrib.st_mode)) ? 2 : 0; + iso_struct_pack(ecma119_dir_record_fmt, buf, + &len_dr, + &zero, + &node->block, + &node->attrib.st_size, + &t->now, + &flags, + &zero, + &zero, + &vol_seq_num, + &len_fi); + iso_struct_pack_long(&buf[33], len_fi, '<', 'B', 0, 0, 0, + node_getname_iso(node)); + susp_write(t, &inf->susp, &buf[len_dr - inf->susp.non_CE_len]); + } else if (type == RECORD_TYPE_PARENT && node->parent) { + ecma119_write_dir_record_noname(t, buf, node, type, + node->parent->block, + node->parent->attrib.st_size, 0); + susp_write(t, &GET_DIR_INF(node)->parent_susp, &buf[34]); + } else { + ecma119_write_dir_record_noname(t, buf, node, type, + node->block, + node->attrib.st_size, 0); + if (type != RECORD_TYPE_ROOT) { + susp_write(t, &GET_DIR_INF(node)->self_susp, &buf[34]); + } + } +} + +static void ecma119_write_dir_record_joliet(struct ecma119_write_target *t, + uint8_t *buf, + struct iso_tree_node *node, + enum RecordType type) +{ + uint8_t len_dr, len_fi, flags; + uint8_t vol_seq_num = t->volnum; + uint32_t block, size; + + if (type == RECORD_TYPE_NORMAL) { + if (S_ISDIR(node->attrib.st_mode)) { + block = GET_DIR_INF(node)->joliet_block; + size = GET_DIR_INF(node)->joliet_len; + flags = 2; + } else { + block = node->block; + size = node->attrib.st_size; + flags = 0; + } + len_fi = node_namelen_joliet(node); + len_dr = 34 + len_fi; + iso_struct_pack(ecma119_dir_record_fmt, buf, + &len_dr, + &zero, + &block, + &size, + &t->now, + &flags, + &zero, + &zero, + &vol_seq_num, + &len_fi); + iso_struct_pack_long(&buf[33], len_fi, '<', 'B', 0, 0, 0, + node->name.joliet); + } else if (type == RECORD_TYPE_PARENT && node->parent) { + struct iso_tree_dir *p = GET_DIR_INF(node)->real_parent; + struct dir_write_info *inf = GET_DIR_INF(p); + ecma119_write_dir_record_noname(t, buf, node, type, + inf->joliet_block, + inf->joliet_len, + 1); + } else { + struct dir_write_info *inf = GET_DIR_INF(node); + ecma119_write_dir_record_noname(t, buf, node, type, + inf->joliet_block, + inf->joliet_len, + 1); + } +} + +/* this writes a directory record for a file whose RecordType is not + * RECORD_TYPE_NORMAL. Since this implies that we don't need to write a file + * id, the only difference between Joliet and non-Joliet records is whetheror + * not we write the SUSP fields. */ +static void ecma119_write_dir_record_noname(struct ecma119_write_target *t, + uint8_t *buf, + struct iso_tree_node *node, + enum RecordType type, + uint32_t block, + uint32_t size, + int joliet) +{ + int file_id; + uint8_t len_dr; + uint8_t flags = 2; + uint8_t len_fi = 1; + uint8_t zero = 0; + struct dir_write_info *inf = GET_DIR_INF(node); + + assert( type != RECORD_TYPE_NORMAL ); + switch(type) { + case RECORD_TYPE_ROOT: + file_id = 0; + len_dr = 34; + break; + case RECORD_TYPE_SELF: + file_id = 0; + len_dr = 34 + (joliet ? 0 : inf->self_susp.non_CE_len); + break; + case RECORD_TYPE_PARENT: + file_id = 1; + len_dr = 34 + (joliet ? 0 : inf->parent_susp.non_CE_len); + break; + case RECORD_TYPE_NORMAL: /* shut up warning */ + assert(0); + } + + assert(iso_struct_calcsize(ecma119_dir_record_fmt) == 33); + iso_struct_pack(ecma119_dir_record_fmt, buf, + &len_dr, + &zero, + &block, + &size, + &t->now, + &flags, /* file flags */ + &zero, + &zero, + &len_fi, /* vol seq number */ + &len_fi); /* len_fi */ + buf[33] = file_id; +} + +static void ecma119_setup_path_tables_iso(struct ecma119_write_target *t) +{ + int i, j, cur; + struct iso_tree_node **children; + + t->pathlist = calloc(1, sizeof(void*) * t->dirlist_len); + t->pathlist[0] = TARGET_ROOT(t); + t->path_table_size = 10; /* root directory record */ + + cur = 1; + for (i=0; idirlist_len; i++) { + struct iso_tree_dir *dir = t->pathlist[i]; + children = ecma119_mangle_names(dir); + for (j=0; jnchildren + dir->nfiles; j++) { + if (S_ISDIR(children[j]->attrib.st_mode)) { + int len = 8 + node_namelen_iso(children[j]); + t->pathlist[cur++] = ISO_DIR(children[j]); + t->path_table_size += len + len % 2; + } + } + free(children); + } +} + +static void ecma119_setup_path_tables_joliet(struct ecma119_write_target *t) +{ + int i, j, cur; + struct iso_tree_node **children; + + t->pathlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len); + t->pathlist_joliet[0] = TARGET_ROOT(t); + t->path_table_size_joliet = 10; /* root directory record */ + + cur = 1; + for (i=0; idirlist_len; i++) { + struct iso_tree_dir *dir = t->pathlist_joliet[i]; + struct dir_write_info *inf = GET_DIR_INF(dir); + children = ecma119_sort_joliet(dir); + for (j=0; jreal_nchildren + dir->nfiles; j++) { + if (S_ISDIR(children[j]->attrib.st_mode)) { + int len = 8 + node_namelen_joliet(children[j]); + t->pathlist_joliet[cur++] = + ISO_DIR(children[j]); + t->path_table_size_joliet += len + len % 2; + } + } + } +} diff --git a/trunk/libisofs/ecma119.h b/trunk/libisofs/ecma119.h new file mode 100755 index 00000000..bda01ade --- /dev/null +++ b/trunk/libisofs/ecma119.h @@ -0,0 +1,220 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * \file ecma119.h + * + * Structures and definitions used for writing an emca119 (ISO9660) compatible + * volume. + */ + +#ifndef __ISO_ECMA119 +#define __ISO_ECMA119 + +#include +#include +#include "susp.h" + +/** + * Persistent data for writing directories according to the ecma119 standard. + */ +struct dir_write_info +{ + struct susp_info susp; /**< \see node_write_info */ + struct susp_info self_susp; /**< SUSP data for this directory's + * "." entry. + */ + struct susp_info parent_susp; /**< SUSP data for this directory's + * ".." entry. + */ + + int len; /**< The combined length of all children's + * Directory Record lengths. This includes + * the System Use areas. + */ + int susp_len; /**< The combined length of all children's + * SUSP Continuation Areas. + */ + + /* the parent/child information prior to relocation */ + struct iso_tree_dir *real_parent; + int real_nchildren; + struct iso_tree_dir **real_children; + int real_depth; + + /* joliet information */ + int joliet_block; /**< The block at which the Joliet version of + * this directory will be written. + */ + int joliet_len; /**< The combined length of all children's + * Joliet Directory Record lengths. + */ +}; + +/** + * Persistent data for writing files according to the ecma119 standard. + */ +struct file_write_info +{ + struct susp_info susp; /**< \see node_write_info */ + + struct iso_tree_dir *real_me; /**< If this is non-NULL, the file is + * a placeholder for a relocated + * directory and this field points to + * that relocated directory. + */ +}; + +/** + * The fields in common between file_write_info and dir_write_info. + */ +struct node_write_info +{ + struct susp_info susp; /**< The SUSP data for this file. */ +}; + +/** + * The possible states that the ecma119 writer can be in. + */ +enum ecma119_write_state +{ + ECMA119_WRITE_BEFORE, + + ECMA119_WRITE_SYSTEM_AREA, + ECMA119_WRITE_PRI_VOL_DESC, + ECMA119_WRITE_SUP_VOL_DESC_JOLIET, + ECMA119_WRITE_VOL_DESC_TERMINATOR, + ECMA119_WRITE_L_PATH_TABLE, + ECMA119_WRITE_M_PATH_TABLE, + ECMA119_WRITE_L_PATH_TABLE_JOLIET, + ECMA119_WRITE_M_PATH_TABLE_JOLIET, + ECMA119_WRITE_DIR_RECORDS, + ECMA119_WRITE_DIR_RECORDS_JOLIET, + ECMA119_WRITE_FILES, + + ECMA119_WRITE_DONE +}; + +/** + * Data describing the state of the ecma119 writer. Everything here should be + * considered private! + */ +struct ecma119_write_target +{ + struct iso_volset *volset; + int volnum; + + time_t now; /**< Time at which writing began. */ + int total_size; /**< Total size of the output. This only + * includes the current volume. */ + uint32_t vol_space_size; + + unsigned int rockridge:1; + unsigned int joliet:1; + unsigned int iso_level:2; + + int curblock; + uint16_t block_size; + uint32_t path_table_size; + uint32_t path_table_size_joliet; + uint32_t l_path_table_pos; + uint32_t m_path_table_pos; + uint32_t l_path_table_pos_joliet; + uint32_t m_path_table_pos_joliet; + + struct iso_tree_dir **dirlist; /* A pre-order list of directories + * (this is the order in which we write + * out directory records). + */ + struct iso_tree_dir **pathlist; /* A breadth-first list of directories. + * This is used for writing out the path + * tables. + */ + int dirlist_len; /* The length of the previous 2 lists. + */ + + struct iso_tree_file **filelist;/* A pre-order list of files with + * non-NULL paths and non-zero sizes. + */ + int filelist_len; /* Length of the previous list. */ + + int curfile; /* Used as a helper field for writing + out filelist and dirlist */ + + /* Joliet versions of the above lists. Since Joliet doesn't require + * directory relocation, the order of these list might be different from + * the lists above. */ + struct iso_tree_dir **dirlist_joliet; + struct iso_tree_dir **pathlist_joliet; + + enum ecma119_write_state state; /* The current state of the writer. */ + + /* persistent data for the various states. Each struct should not be + * touched except for the writer of the relevant stage. When the writer + * of the relevant stage is finished, it should set all fields to 0. + */ + union + { + struct + { + int blocks; + unsigned char *data; + } path_table; + struct + { + size_t pos; /* The number of bytes we have written + * so far in the current directory. + */ + size_t data_len;/* The number of bytes in the current + * directory. + */ + unsigned char *data; /* The data (combined Directory + * Records and susp_CE areas) of the + * current directory. + */ + int dir; /* The index in dirlist that we are + * currently writing. */ + } dir_records; + struct + { + size_t pos; /* The number of bytes we have written + * so far in the current file. + */ + size_t data_len;/* The number of bytes in the currently + * open file. + */ + FILE *fd; /* The currently open file. */ + int file; /* The index in filelist that we are + * currently writing. */ + } files; + } state_data; +}; + +/** + * Create a new ecma119_write_target from the given volume number of the + * given volume set. + * + * \pre \p volnum is less than \p volset-\>volset_size. + * \post For each node in the tree, writer_data has been allocated. + * \post The directory heirarchy has been reorganised to be ecma119-compatible. + */ +struct ecma119_write_target *ecma119_target_new(struct iso_volset *volset, + int volnum); + +/** Macros to help with casting between node_write_info and dir/file_write_info. + */ +#define DIR_INF(a) ( (struct dir_write_info*) (a) ) +#define FILE_INF(a) ( (struct file_write_info*) (a) ) +#define NODE_INF(a) ( (struct node_write_info*) (a) ) + +#define GET_DIR_INF(a) ( (struct dir_write_info*) (a)->writer_data ) +#define GET_FILE_INF(a) ( (struct file_write_info*) (a)->writer_data ) +#define GET_NODE_INF(a) ( (struct node_write_info*) (a)->writer_data ) + +#define TARGET_ROOT(t) ( (t)->volset->volume[(t)->volnum]->root ) + +#define NODE_NAMELEN(n,i) strlen(iso_tree_node_get_name(ISO_NODE(n), i)) +#define NODE_JOLLEN(n) ucslen(iso_tree_node_get_name(ISO_NODE(n), \ + ISO_NAME_JOLIET)) + + +#endif /* __ISO_ECMA119 */ diff --git a/trunk/libisofs/errors.c b/trunk/libisofs/errors.c new file mode 100755 index 00000000..860496fb --- /dev/null +++ b/trunk/libisofs/errors.c @@ -0,0 +1,14 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "errors.h" +#include + +void iso_warn(enum iso_warnings w) +{ + printf("WARNING: %u\n", w); +} + +void iso_error(enum iso_errors e) +{ + printf("ERROR: %u\n", e); +} diff --git a/trunk/libisofs/errors.h b/trunk/libisofs/errors.h new file mode 100755 index 00000000..d7a073c6 --- /dev/null +++ b/trunk/libisofs/errors.h @@ -0,0 +1,19 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __ERRORS +#define __ERRORS + +enum iso_warnings +{ + ISO_WARNING_FOO +}; + +enum iso_errors +{ + ISO_ERROR_FOO +}; + +void iso_warn(enum iso_warnings w); +void iso_error(enum iso_errors e); + +#endif /* __ERRORS */ diff --git a/trunk/libisofs/libisofs.h b/trunk/libisofs/libisofs.h new file mode 100755 index 00000000..064b03ea --- /dev/null +++ b/trunk/libisofs/libisofs.h @@ -0,0 +1,243 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * Create an ISO-9660 data volume with Rock Ridge and Joliet extensions. + * Usage is easy: + * - Create a new volume. + * - Add files and directories. + * - Write the volume to a file or create a burn source for use with Libburn. + */ + +#ifndef __LIBISOFS +#define __LIBISOFS + +#include "libburn/libburn.h" + +/** + * Data volume. + * @see volume.h for details. + */ +struct iso_volume; + +/** + * A set of data volumes. + * @see volume.h for details. + */ +struct iso_volset; + +/** + * Directory on a volume. + * @see tree.h for details. + */ +struct iso_tree_dir; + +/** + * File on a volume. + * @see tree.h for details. + */ +struct iso_tree_file; + +/** + * Either a file or a directory. + * \see tree.h + */ +struct iso_tree_node; + +/** + * Possible versions of a file or directory name or identifier. + */ +enum iso_name_version { + ISO_NAME_FULL, /**< In the current locale. */ + ISO_NAME_ISO, /**< Current ISO level identifier. */ + ISO_NAME_ISO_L1, /**< ISO level 1 identifier. */ + ISO_NAME_ISO_L2, /**< ISO level 2 identifier. */ + ISO_NAME_ROCKRIDGE, /**< Rock Ridge file or directory name. */ + ISO_NAME_JOLIET /**< Joliet identifier. */ +}; + +enum ecma119_extension_flag { + ECMA119_ROCKRIDGE = (1<<0), + ECMA119_JOLIET = (1<<1) +}; + +/** + * Create a new volume. + * The parameters can be set to NULL if you wish to set them later. + */ +struct iso_volume *iso_volume_new(const char *volume_id, + const char *publisher_id, + const char *data_preparer_id); + +/** + * Free a volume. + */ +void iso_volume_free(struct iso_volume *volume); + +/** + * Get the root directory for a volume. + */ +struct iso_tree_dir *iso_volume_get_root(const struct iso_volume *volume); + +/** + * Fill in the volume identifier for a volume. + */ +void iso_volume_set_volume_id(struct iso_volume *volume, + const char *volume_id); + +/** + * Fill in the publisher for a volume. + */ +void iso_volume_set_publisher_id(struct iso_volume *volume, + const char *publisher_id); + +/** + * Fill in the data preparer for a volume. + */ +void iso_volume_set_data_preparer_id(struct iso_volume *volume, + const char *data_preparer_id); + +/** + * Get the current ISO level for a volume. + */ +int iso_volume_get_iso_level(const struct iso_volume *volume); + +/** + * Set the current ISO level for a volume. + * ISO level must be 1 or 2. + */ +void iso_volume_set_iso_level(struct iso_volume *volume, int level); + +/** + * See if Rock Ridge (POSIX) is enabled for a volume. + */ +int iso_volume_get_rockridge(const struct iso_volume *volume); + +/** + * Enable or disable Rock Ridge (POSIX) for a volume. + */ +void iso_volume_set_rockridge(struct iso_volume *volume, int rockridge); + +/** + * See if Joliet (Unicode) is enabled for a volume. + */ +int iso_volume_get_joliet(const struct iso_volume *volume); + +/** + * Enable or disable Joliet (Unicode) for a volume. + */ +void iso_volume_set_joliet(struct iso_volume *volume, int joliet); + +/** + * Create a new Volume Set consisting of only one volume. + * @param volume The first and only volume for the volset to contain. + * @param volset_id The Volume Set ID. + * @return A new iso_volset. + */ +struct iso_volset *iso_volset_new(struct iso_volume *volume, + const char *volset_id); + +/** + * Add a file to a directory. + * + * \param path The path, on the local filesystem, of the file. + * + * \pre \p parent is non-NULL + * \pre \p path is non-NULL and is a valid path to a non-directory on the local + * filesystem. + * \return An iso_tree_file whose path is \p path and whose parent is \p parent. + */ +struct iso_tree_file *iso_tree_add_file(struct iso_tree_dir *parent, + const char *path); + +/** + * Add a directory from the local filesystem to the tree. + * Warning: this only adds the directory itself, no files or subdirectories. + * + * \param path The path, on the local filesystem, of the directory. + * + * \pre \p parent is non-NULL + * \pre \p path is non-NULL and is a valid path to a directory on the local + * filesystem. + * \return a pointer to the newly created directory. + */ +struct iso_tree_dir *iso_tree_add_dir(struct iso_tree_dir *parent, + const char *path); + +/** + * Recursively add an existing directory to the tree. + * Warning: when using this, you'll lose pointers to files or subdirectories. + * If you want to have pointers to all files and directories, + * use iso_tree_add_file and iso_tree_add_dir. + * + * \param path The path, on the local filesystem, of the directory to add. + * + * \pre \p parent is non-NULL + * \pre \p path is non-NULL and is a valid path to a directory on the local + * filesystem. + * \return a pointer to the newly created directory. + */ +struct iso_tree_dir *iso_tree_radd_dir(struct iso_tree_dir *parent, + const char *path); + +/** + * Creates a new, empty directory on the volume. + * + * \pre \p parent is non-NULL + * \pre \p name is unique among the children and files belonging to \p parent. + * Also, it doesn't contain '/' characters. + * + * \post \p parent contains a child directory whose name is \p name and whose + * POSIX attributes are the same as \p parent's. + * \return a pointer to the newly created directory. + */ +struct iso_tree_dir *iso_tree_add_new_dir(struct iso_tree_dir *parent, + const char *name); + +/** + * Get the name of a node. + */ +const char *iso_tree_node_get_name(const struct iso_tree_node *node, + enum iso_name_version ver); + +/** + * Set the name of a file. + * The name you input here will be the full name and will be used to derive the + * ISO, RockRidge and Joliet names. + */ +void iso_tree_file_set_name(struct iso_tree_file *file, const char *name); + +/** + * Set the name of a directory. + * The name you input here will be the full name and will be used to derive the + * ISO, RockRidge and Joliet names. + */ +void iso_tree_dir_set_name(struct iso_tree_dir *dir, const char *name); + +/** + * Recursively print a directory to stdout. + * \param spaces The initial number of spaces on the left. Set to 0 if you + * supply a root directory. + */ +void iso_tree_print(const struct iso_tree_dir *root, int spaces); + +/** Create a burn_source which can be used as a data source for a track + * + * The volume set used to create the libburn_source can _not_ be modified + * until the libburn_source is freed. + * + * \param volumeset The volume set from which you want to write + * \param volnum The volume in the set which you want to write (usually 0) + * \param level ISO level to write at. + * \param flags Which extensions to support. + * + * \pre \p volumeset is non-NULL + * \pre \p volnum is less than \p volset->volset_size. + * \return A burn_source to be used for the data source for a track + */ +struct burn_source* iso_source_new_ecma119 (struct iso_volset *volumeset, + int volnum, + int level, + int flags); + +#endif /* __LIBISOFS */ diff --git a/trunk/libisofs/rockridge.c b/trunk/libisofs/rockridge.c new file mode 100755 index 00000000..d6bab253 --- /dev/null +++ b/trunk/libisofs/rockridge.c @@ -0,0 +1,299 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include "rockridge.h" +#include "tree.h" +#include "util.h" +#include "volume.h" +#include "ecma119.h" +#include "susp.h" + +#include +#include +#include +#include +#include +#include + +/* create a PX field from the permissions on the current node. */ +unsigned char *rrip_make_PX(struct ecma119_write_target *t, + struct iso_tree_node *node) +{ + unsigned char *PX = malloc(44); + + PX[0] = 'P'; + PX[1] = 'X'; + PX[2] = 44; + PX[3] = 1; + iso_bb(&PX[4], node->attrib.st_mode, 4); + iso_bb(&PX[12], node->attrib.st_nlink, 4); + iso_bb(&PX[20], node->attrib.st_uid, 4); + iso_bb(&PX[28], node->attrib.st_gid, 4); + iso_bb(&PX[36], node->attrib.st_ino, 4); + return PX; +} + +/** See IEEE 1282 4.1.1 */ +void rrip_add_PX(struct ecma119_write_target *t, struct iso_tree_node *node) +{ + susp_append(t, node, rrip_make_PX(t, node)); +} + +void rrip_add_PX_dir(struct ecma119_write_target *t, struct iso_tree_dir *dir) +{ + susp_append(t, ISO_NODE(dir), rrip_make_PX(t, ISO_NODE(dir))); + susp_append_self(t, dir, rrip_make_PX(t, ISO_NODE(dir))); + susp_append_parent(t, dir, rrip_make_PX(t, ISO_NODE(dir))); +} + +void rrip_add_PN(struct ecma119_write_target *t, struct iso_tree_node *node) +{ + unsigned char *PN = malloc(20); + + PN[0] = 'P'; + PN[1] = 'N'; + PN[2] = 20; + PN[3] = 1; + iso_bb(&PN[4], node->attrib.st_dev >> 32, 4); + iso_bb(&PN[12], node->attrib.st_dev & 0xffffffff, 4); + susp_append(t, node, PN); +} + +static void rrip_SL_append_comp(int *n, unsigned char ***comps, + char *s, int size, char fl) +{ + unsigned char *comp = malloc(size + 2); + + (*n)++; + comp[0] = fl; + comp[1] = size; + *comps = realloc(*comps, (*n) * sizeof(void*)); + (*comps)[(*n) - 1] = comp; + + if (size) { + memcpy(&comp[2], s, size); + } +} + +static void rrip_SL_add_component(char *prev, char *cur, int *n_comp, + unsigned char ***comps) +{ + int size = cur - prev; + + if (size == 0) { + rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 3); + return; + } + + if (size == 1 && prev[0] == '.') { + rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 1); + return; + } + if (size == 2 && !strncmp(prev, "..", 2)) { + rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 2); + return; + } + + /* we can't make a component any bigger than 250 (is this really a + problem)? because then it won't fit inside the SL field */ + while (size > 248) { + size -= 248; + rrip_SL_append_comp(n_comp, comps, prev, 248, 1 << 0); + } + + rrip_SL_append_comp(n_comp, comps, prev, size, 0); +} + +void rrip_add_SL(struct ecma119_write_target *t, struct iso_tree_node *node) +{ + int ret, pathsize = 0; + char *path = NULL, *cur, *prev; + struct iso_tree_file *file = (struct iso_tree_file *)node; + int i, j; + + unsigned char **comp = NULL; + int n_comp = 0; + int total_comp_len = 0; + int written = 0, pos; + + unsigned char *SL; + + do { + pathsize += 128; + path = realloc(path, pathsize); + ret = readlink(file->path, path, pathsize); + } while (ret == pathsize); + if (ret == -1) { + fprintf(stderr, "Error: couldn't read symlink: %s\n", + strerror(errno)); + return; + } + path[ret] = '\0'; + + prev = path; + for (cur = strchr(path, '/'); cur && *cur; cur = strchr(cur, '/')) { + rrip_SL_add_component(prev, cur, &n_comp, &comp); + cur++; + prev = cur; + } + + /* if there was no trailing '/', we need to add the last component. */ + if (prev == path || prev != &path[ret - 1]) { + rrip_SL_add_component(prev, &path[ret], &n_comp, &comp); + } + + for (i = 0; i < n_comp; i++) { + total_comp_len += comp[i][1] + 2; + if (total_comp_len > 250) { + total_comp_len -= comp[i][1] + 2; + SL = malloc(total_comp_len + 5); + SL[0] = 'S'; + SL[1] = 'L'; + SL[2] = total_comp_len + 5; + SL[3] = 1; + SL[4] = 1; /* CONTINUE */ + pos = 5; + for (j = written; j < i; j++) { + memcpy(&SL[pos], comp[j], comp[j][2]); + pos += comp[j][2]; + } + susp_append(t, node, SL); + written = i - 1; + total_comp_len = comp[i][1]; + } + } + SL = malloc(total_comp_len + 5); + SL[0] = 'S'; + SL[1] = 'L'; + SL[2] = total_comp_len + 5; + SL[3] = 1; + SL[4] = 0; + pos = 5; + + for (j = written; j < n_comp; j++) { + memcpy(&SL[pos], comp[j], comp[j][1] + 2); + pos += comp[j][1] + 2; + } + susp_append(t, node, SL); + + free(path); + /* free the components */ + for (i = 0; i < n_comp; i++) { + free(comp[i]); + } + free(comp); +} + +static void rrip_add_NM_single(struct ecma119_write_target *t, + struct iso_tree_node *node, + char *name, int size, int flags) +{ + unsigned char *NM = malloc(size + 5); + + NM[0] = 'N'; + NM[1] = 'M'; + NM[2] = size + 5; + NM[3] = 1; + NM[4] = flags; + if (size) { + memcpy(&NM[5], name, size); + } + susp_append(t, node, NM); +} + +void rrip_add_NM(struct ecma119_write_target *t, struct iso_tree_node *node) +{ + struct iso_tree_file *file = (struct iso_tree_file *)node; + int len = strlen(file->name.rockridge); + char *pos = file->name.rockridge; + + if (len == 1 && pos[0] == '.') { + rrip_add_NM_single(t, node, pos, 0, 1 << 1); + return; + } + if (len == 2 && !strncmp(pos, "..", 2)) { + rrip_add_NM_single(t, node, pos, 0, 1 << 2); + return; + } + + while (len > 250) { + rrip_add_NM_single(t, node, pos, 250, 1); + len -= 250; + pos += 250; + } + rrip_add_NM_single(t, node, pos, len, 0); +} + +void rrip_add_CL(struct ecma119_write_target *t, struct iso_tree_node *node) +{ + unsigned char *CL = calloc(1, 12); + + CL[0] = 'C'; + CL[1] = 'L'; + CL[2] = 12; + CL[3] = 1; + susp_append(t, node, CL); +} + +void rrip_add_PL(struct ecma119_write_target *t, struct iso_tree_dir *node) +{ + unsigned char *PL = calloc(1, 12); + + PL[0] = 'P'; + PL[1] = 'L'; + PL[2] = 12; + PL[3] = 1; + susp_append_parent(t, node, PL); +} + +void rrip_add_RE(struct ecma119_write_target *t, struct iso_tree_node *node) +{ + unsigned char *RE = malloc(4); + + RE[0] = 'R'; + RE[1] = 'E'; + RE[2] = 4; + RE[3] = 1; + susp_append(t, node, RE); +} + +void rrip_add_TF(struct ecma119_write_target *t, struct iso_tree_node *node) +{ + unsigned char *TF = malloc(5 + 3 * 17); + + TF[0] = 'T'; + TF[1] = 'F'; + TF[2] = 5 + 3 * 17; + TF[3] = 1; + TF[4] = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 7); + iso_datetime_17(&TF[5], node->attrib.st_mtime); + iso_datetime_17(&TF[22], node->attrib.st_atime); + iso_datetime_17(&TF[39], node->attrib.st_ctime); + susp_append(t, node, TF); +} + +void rrip_finalize(struct ecma119_write_target *t, struct iso_tree_dir *dir) +{ + struct dir_write_info *inf; + struct file_write_info *finf; + int i; + + inf = dir->writer_data; + if (dir->parent != inf->real_parent) { + unsigned char *PL = susp_find(&inf->parent_susp, "PL"); + + iso_bb(&PL[4], inf->real_parent->block, 4); + } + + for (i = 0; i < dir->nfiles; i++) { + finf = dir->files[i]->writer_data; + if (finf->real_me) { + unsigned char *CL = susp_find(&finf->susp, "CL"); + + iso_bb(&CL[4], finf->real_me->block, 4); + } + } + + for (i = 0; i < dir->nchildren; i++) { + rrip_finalize(t, dir->children[i]); + } +} diff --git a/trunk/libisofs/rockridge.h b/trunk/libisofs/rockridge.h new file mode 100755 index 00000000..ba8e894a --- /dev/null +++ b/trunk/libisofs/rockridge.h @@ -0,0 +1,30 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** Functions and structures used for Rock Ridge support. */ + +#ifndef __ISO_ROCKRIDGE +#define __ISO_ROCKRIDGE + +struct ecma119_write_target; +struct iso_tree_node; +struct iso_tree_dir; + +void rrip_add_PX(struct ecma119_write_target *, struct iso_tree_node *); +void rrip_add_PN(struct ecma119_write_target *, struct iso_tree_node *); +void rrip_add_SL(struct ecma119_write_target *, struct iso_tree_node *); +void rrip_add_NM(struct ecma119_write_target *, struct iso_tree_node *); +void rrip_add_CL(struct ecma119_write_target *, struct iso_tree_node *); +void rrip_add_RE(struct ecma119_write_target *, struct iso_tree_node *); +void rrip_add_TF(struct ecma119_write_target *, struct iso_tree_node *); + +/* This is special because it doesn't modify the susp fields of the directory + * that gets passed to it; it modifies the susp fields of the ".." entry in + * that directory. */ +void rrip_add_PL(struct ecma119_write_target *, struct iso_tree_dir *); + +/* Add a PX field to the susp, self_susp and parent_susp entries */ +void rrip_add_PX_dir(struct ecma119_write_target *, struct iso_tree_dir *); + +void rrip_finalize(struct ecma119_write_target *, struct iso_tree_dir *); + +#endif /* __ISO_ROCKRIDGE */ diff --git a/trunk/libisofs/struct.c b/trunk/libisofs/struct.c new file mode 100755 index 00000000..1d8e3888 --- /dev/null +++ b/trunk/libisofs/struct.c @@ -0,0 +1,340 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include "struct.h" +#include "util.h" +#include +#include +#include +#include +#include + +struct struct_element { + uint8_t ch; + + int bytes; /* The number of bytes in the value to convert + * from/to. */ + uint8_t end; /* The endianness specifier. */ + int mul; /* The number of values to convert. */ + + union { /* Pointer to the value. */ + uint8_t *val8; + uint16_t *val16; + uint32_t *val32; + time_t *time; + } val; +}; + +/* check if a character is a valid endian-ness specifier */ +#define isend(a) ((a) == '=' || (a) == '<' || (a) == '>') + +static int iso_struct_element_make(struct struct_element *elem, + int mul, + char end, + char ch) +{ + if (!end) { +#ifdef WORDS_BIGENDIAN + elem->end = '>'; /* default endianness is native */ +#else + elem->end = '<'; +#endif + } else { + elem->end = end; + } + + elem->ch = ch; + elem->mul = mul; + elem->val.val8 = NULL; + switch(toupper(ch)) { + case 'X': + case 'B': + elem->bytes = 1; + break; + case 'H': + elem->bytes = 2; + break; + case 'L': + elem->bytes = 4; + break; + case 'S': + elem->bytes = 7; + elem->end = '<'; + break; + case 'T': + elem->bytes = 17; + elem->end = '<'; + break; + default: + elem->bytes = -1; + break; + } + return elem->bytes * elem->mul * ((elem->end == '=') ? 2 : 1); +} + +static int iso_struct_element_make_v(struct struct_element *elem, + va_list *ap) +{ + int mul = va_arg(*ap, int); + int end = va_arg(*ap, int); + int ch = va_arg(*ap, int); + return iso_struct_element_make(elem, mul, end, ch); +} + +static int iso_struct_element_parse(const char **ffmt, + struct struct_element *elem) +{ + off_t pos; + const char *fmt = *ffmt; + int mul; + char end = 0; + + mul = 1; + for (pos=0; isdigit(fmt[pos]) || isend(fmt[pos]); pos++) { + if (isdigit(fmt[pos])) { + mul = atoi( fmt + pos ); + while (isdigit(fmt[pos+1])) pos++; + } else { + end = fmt[pos]; + } + } + + (*ffmt) += pos + 1; + + return iso_struct_element_make(elem, mul, end, fmt[pos]); +} + +/* read a single integer from data[i] to elem[i], interpreting the endian-ness + * and offset appropriately. */ +static uint32_t iso_struct_element_read_int(struct struct_element *elem, + const uint8_t *data, + int i) +{ + uint32_t el; + + switch(elem->end) { + case '>': + el = iso_read_msb(data + i*elem->bytes, elem->bytes); + break; + case '<': + el = iso_read_lsb(data + i*elem->bytes, elem->bytes); + break; + case '=': + el = iso_read_bb(data + i*elem->bytes*2, elem->bytes); + } + + switch(elem->bytes) { + case 1: + elem->val.val8[i] = el; + break; + case 2: + elem->val.val16[i] = el; + break; + case 4: + elem->val.val32[i] = el; + break; + } + + return el; +} + +/* write a single integer from elem[i] to data[i]. */ +static uint32_t iso_struct_element_write1(struct struct_element *elem, + uint8_t *data, + int i) +{ + uint32_t el; + + switch(elem->bytes) { + case 1: + el = elem->val.val8[i]; + break; + case 2: + el = elem->val.val16[i]; + break; + case 4: + el = elem->val.val32[i]; + break; + } + + switch(elem->end) { + case '>': + iso_msb(data + i*elem->bytes, el, elem->bytes); + break; + case '<': + iso_lsb(data + i*elem->bytes, el, elem->bytes); + break; + case '=': + iso_bb(data + i*elem->bytes*2, el, elem->bytes); + } + + return el; +} + +static int iso_struct_element_read(struct struct_element *elem, + const uint8_t *data) +{ + int size = elem->bytes * ((elem->end == '=') ? 2 : 1); + int i; + + if (elem->ch == 'x') { + return size * elem->mul; + } + + for (i=0; imul; i++) { + switch(toupper(elem->ch)) { + case 'S': + /* + elem->val.time[i] = iso_datetime_read_7(&data[i*7]); + */ + break; + case 'T': + /* + elem->val.time[i] = iso_datetime_read_17(&data[i*17]); + */ + break; + default: + iso_struct_element_read_int(elem, data, i); + } + } + + return size * elem->mul; +} + +static int iso_struct_element_write(struct struct_element *elem, + uint8_t *data) +{ + int size = elem->bytes * ((elem->end == '=') ? 2 : 1); + int i; + uint32_t ret; + + if (elem->ch == 'x') { + return size*elem->mul; + } + + for (i=0; imul; i++) { + switch(toupper(elem->ch)) { + case 'S': + iso_datetime_7(&data[i*7], elem->val.time[i]); + ret = elem->val.time[i]; + break; + case 'T': + iso_datetime_17(&data[i*17], elem->val.time[i]); + ret = elem->val.time[i]; + break; + default: + ret = iso_struct_element_write1(elem, data, i); + break; + } + + if (islower(elem->ch) && ret == 0) { + memset(data + size*i, 0, size*(elem->mul-i)); + break; + } + } + + return size * elem->mul; +} + +int iso_struct_unpack(const char *fmt, const uint8_t *data, ...) +{ + int num_conv; + int ret; + va_list ap; + struct struct_element elem; + off_t off; + + va_start(ap, data); + num_conv = 0; + off = 0; + while(*fmt) { + ret = iso_struct_element_parse(&fmt, &elem); + if (ret < 0) { + va_end(ap); + return -1; + } + if (elem.ch != 'x') { + elem.val.val8 = va_arg(ap, void*); + } + off += iso_struct_element_read(&elem, data + off); + num_conv++; + } + + va_end(ap); + return num_conv; +} + +int iso_struct_pack(const char *fmt, uint8_t *data, ...) +{ + int num_conv; + int ret; + va_list ap; + struct struct_element elem; + off_t off; + + va_start(ap, data); + num_conv = 0; + off = 0; + while(*fmt) { + ret = iso_struct_element_parse(&fmt, &elem); + if (ret < 0) { + va_end(ap); + return -1; + } + if (elem.ch != 'x') { + elem.val.val8 = va_arg(ap, void*); + } + off += iso_struct_element_write(&elem, data + off); + num_conv++; + } + + va_end(ap); + return num_conv; +} + +int iso_struct_pack_long(uint8_t *data, ...) +{ + int num_conv; + int ret; + int i, j; + va_list ap; + struct struct_element *elem = NULL; + off_t off; + + va_start(ap, data); + num_conv = 0; + off = 0; + + elem = calloc(1, sizeof(struct struct_element)); + i=0; + while ((ret = iso_struct_element_make_v(&elem[i], &ap) > 0)) { + elem = realloc(elem, (++i + 1) * sizeof(struct struct_element)); + } + for (j=0; j big-endian + * = both-endian (ie. according to ecma119 7.2.3 or 7.3.3) + * + * Each conversion specifier may also be preceded by a length specifier. For + * example, "<5L" specifies an array of 5 little-endian 32-bit integers. Note + * that "=L" takes 8 bytes while "L" each take 4. + * + * You can use a lower-case conversion specifier instead of an upper-case one + * to signify that the (multi-element) conversion should stop when a zero is + * reached. This is useful for writing out NULL-terminated strings. Note that + * this has no effect when unpacking data from a struct. + */ + +#ifndef __ISO_STRUCT +#define __ISO_STRUCT + +#include + +/** + * Unpack a struct into its components. The list of components is a list of + * pointers to the variables to write. + * + * For example: + * uint8_t byte1, byte2; + * uint16_t uint; + * iso_struct_unpack("BB=H", data, &byte1, &byte2, &uint); + * + * \return The number of conversions performed, or -1 on error. + */ +int iso_struct_unpack(const char *fmt, const uint8_t *data, ...); + +/** + * Write out a struct from its components. The list of components is a list of + * pointers to the variables to write and the buffer to which to write + * is assumed to be large + * enough to take the data. + * + * \return The number of conversions performed, or -1 on error. + */ +int iso_struct_pack(const char *fmt, uint8_t *data, ...); + +/** + * Achieves the same effect as iso_struct_pack(), but the format is passed as + * a sequence of (int, char, char) triples. This list is terminated by + * (0, 0, 0) and the list of parameters follows. + * + * Example: iso_struct_pack_long(data, 4, '=', 'H', 0, 0, 0, &val) is the same + * as iso_struct_pack("4=H", 0, 0, 0, &val) + */ +int iso_struct_pack_long(uint8_t *data, ...); + +/** + * Calculate the size of a given format string. + * + * \return The sum of the length of all formats in the string, in bytes. Return + * -1 on error. + */ +int iso_struct_calcsize(const char *fmt); + +#endif diff --git a/trunk/libisofs/susp.c b/trunk/libisofs/susp.c new file mode 100755 index 00000000..e67d39f1 --- /dev/null +++ b/trunk/libisofs/susp.c @@ -0,0 +1,312 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include "susp.h" +#include "tree.h" +#include "util.h" +#include "ecma119.h" + +#include +#include +#include + +static void susp_insert_direct(struct ecma119_write_target *t, + struct susp_info *susp, unsigned char *data, + int pos) +{ + int i; + + assert(pos <= susp->n_susp_fields); + susp->n_susp_fields++; + susp->susp_fields = realloc(susp->susp_fields, + sizeof(void*) * susp->n_susp_fields); + + for (i = susp->n_susp_fields-1; i > pos; i--) { + susp->susp_fields[i] = susp->susp_fields[i - 1]; + } + susp->susp_fields[pos] = data; +} + +void susp_append(struct ecma119_write_target *t, + struct iso_tree_node *node, unsigned char *data) +{ + struct dir_write_info *inf = node->writer_data; + struct susp_info *susp = &inf->susp; + + susp_insert_direct(t, susp, data, susp->n_susp_fields); +} + +void susp_append_self(struct ecma119_write_target *t, + struct iso_tree_dir *dir, unsigned char *data) +{ + struct dir_write_info *inf = dir->writer_data; + struct susp_info *susp = &inf->self_susp; + + susp_insert_direct(t, susp, data, susp->n_susp_fields); +} + +void susp_append_parent(struct ecma119_write_target *t, + struct iso_tree_dir *dir, unsigned char *data) +{ + struct dir_write_info *inf = dir->writer_data; + struct susp_info *susp = &inf->parent_susp; + + susp_insert_direct(t, susp, data, susp->n_susp_fields); +} + +void susp_insert(struct ecma119_write_target *t, + struct iso_tree_node *node, unsigned char *data, int pos) +{ + struct dir_write_info *inf = node->writer_data; + struct susp_info *susp = &inf->susp; + + susp_insert_direct(t, susp, data, pos); +} + +void susp_insert_self(struct ecma119_write_target *t, + struct iso_tree_dir *dir, unsigned char *data, int pos) +{ + struct dir_write_info *inf = dir->writer_data; + struct susp_info *susp = &inf->self_susp; + + susp_insert_direct(t, susp, data, pos); +} + +void susp_insert_parent(struct ecma119_write_target *t, + struct iso_tree_dir *dir, unsigned char *data, int pos) +{ + struct dir_write_info *inf = dir->writer_data; + struct susp_info *susp = &inf->parent_susp; + + susp_insert_direct(t, susp, data, pos); +} + +unsigned char *susp_find(struct susp_info *susp, const char *name) +{ + int i; + + for (i = 0; i < susp->n_susp_fields; i++) { + if (!strncmp((char *)susp->susp_fields[i], name, 2)) { + return susp->susp_fields[i]; + } + } + return NULL; +} + +/* utility function for susp_add_CE because susp_add_CE needs to act 3 times + * on directories (for the "." and ".." entries. */ +#define CE_LEN 28 +static unsigned char *susp_add_single_CE(struct ecma119_write_target *t, + struct susp_info *susp, int len) +{ + int susp_length = 0, tmp_len; + int i; + unsigned char *CE; + + for (i = 0; i < susp->n_susp_fields; i++) { + susp_length += susp->susp_fields[i][2]; + } + if (susp_length <= len) { + /* no need for a CE field */ + susp->non_CE_len = susp_length; + susp->n_fields_fit = susp->n_susp_fields; + return NULL; + } + + tmp_len = susp_length; + for (i = susp->n_susp_fields - 1; i >= 0; i--) { + tmp_len -= susp->susp_fields[i][2]; + if (tmp_len + CE_LEN <= len) { + susp->non_CE_len = tmp_len + CE_LEN; + susp->CE_len = susp_length - tmp_len; + + /* i+1 because we have to count the CE field */ + susp->n_fields_fit = i + 1; + + CE = calloc(1, CE_LEN); + /* we don't fill in the BLOCK LOCATION or OFFSET + fields yet. */ + CE[0] = 'C'; + CE[1] = 'E'; + CE[2] = (char)CE_LEN; + CE[3] = (char)1; + iso_bb(&CE[20], susp_length - tmp_len, 4); + + return CE; + } + } + assert(0); + return NULL; +} + +/** See IEEE P1281 Draft Version 1.12/5.2. Because this function depends on the + * length of the other SUSP fields, it should always be calculated last. */ +void susp_add_CE(struct ecma119_write_target *t, struct iso_tree_node *node) +{ + struct dir_write_info *inf = node->writer_data; + unsigned char *CE; + + CE = susp_add_single_CE(t, &inf->susp, 255 - node->dirent_len); + if (CE) + susp_insert(t, node, CE, inf->susp.n_fields_fit - 1); + if (S_ISDIR(node->attrib.st_mode)) { + CE = susp_add_single_CE(t, &inf->self_susp, 255 - 34); + if (CE) + susp_insert_self(t, (struct iso_tree_dir *)node, CE, + inf->self_susp.n_fields_fit - 1); + CE = susp_add_single_CE(t, &inf->parent_susp, 255 - 34); + if (CE) + susp_insert_parent(t, (struct iso_tree_dir *)node, CE, + inf->parent_susp.n_fields_fit - 1); + } +} + +/** See IEEE P1281 Draft Version 1.12/5.3 */ +void susp_add_SP(struct ecma119_write_target *t, struct iso_tree_dir *dir) +{ + unsigned char *SP = malloc(7); + + SP[0] = 'S'; + SP[1] = 'P'; + SP[2] = (char)7; + SP[3] = (char)1; + SP[4] = 0xbe; + SP[5] = 0xef; + SP[6] = 0; + susp_append_self(t, dir, SP); +} + +#if 0 +/** See IEEE P1281 Draft Version 1.12/5.4 */ +static void susp_add_ST(struct ecma119_write_target *t, + struct iso_tree_node *node) +{ + unsigned char *ST = malloc(4); + + ST[0] = 'S'; + ST[1] = 'T'; + ST[2] = (char)4; + ST[3] = (char)1; + susp_append(t, node, ST); +} +#endif + +/** See IEEE P1281 Draft Version 1.12/5.5 */ +void susp_add_ER(struct ecma119_write_target *t, struct iso_tree_dir *dir) +{ + unsigned char *ER = malloc(182); + + ER[0] = 'E'; + ER[1] = 'R'; + ER[2] = 182; + ER[3] = 1; + ER[4] = 9; + ER[5] = 72; + ER[6] = 93; + ER[7] = 1; + memcpy(&ER[8], "IEEE_1282", 9); + memcpy(&ER[17], "THE IEEE 1282 PROTOCOL PROVIDES SUPPORT FOR POSIX " + "FILE SYSTEM SEMANTICS.", 72); + memcpy(&ER[89], "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, " + "PISCATAWAY, NJ, USA FOR THE 1282 SPECIFICATION.", 93); + susp_append_self(t, dir, ER); +} + +static void susp_fin_CE(struct ecma119_write_target *t, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = (struct dir_write_info *) + dir->writer_data; + struct node_write_info *cinf; + unsigned char *CE; + int i; + int CE_offset = inf->len; + + if (inf->self_susp.CE_len) { + CE = inf->self_susp.susp_fields[inf->self_susp.n_fields_fit - + 1]; + iso_bb(&CE[4], dir->block + CE_offset / 2048, 4); + iso_bb(&CE[12], CE_offset % 2048, 4); + CE_offset += inf->self_susp.CE_len; + } + if (inf->parent_susp.CE_len) { + CE = inf->parent_susp.susp_fields[inf->parent_susp. + n_fields_fit - 1]; + iso_bb(&CE[4], dir->block + CE_offset / 2048, 4); + iso_bb(&CE[12], CE_offset % 2048, 4); + CE_offset += inf->parent_susp.CE_len; + } + + for (i = 0; i < dir->nchildren; i++) { + cinf = dir->children[i]->writer_data; + if (!cinf->susp.CE_len) { + continue; + } + CE = cinf->susp.susp_fields[cinf->susp.n_fields_fit - 1]; + iso_bb(&CE[4], dir->block + CE_offset / 2048, 4); + iso_bb(&CE[12], CE_offset % 2048, 4); + CE_offset += cinf->susp.CE_len; + } + for (i = 0; i < dir->nfiles; i++) { + cinf = dir->files[i]->writer_data; + if (!cinf->susp.CE_len) { + continue; + } + CE = cinf->susp.susp_fields[cinf->susp.n_fields_fit - 1]; + iso_bb(&CE[4], dir->block + CE_offset / 2048, 4); + iso_bb(&CE[12], CE_offset % 2048, 4); + CE_offset += cinf->susp.CE_len; + } + assert(CE_offset == inf->len + inf->susp_len); +} + +void susp_finalize(struct ecma119_write_target *t, struct iso_tree_dir *dir) +{ + int i; + + if (dir->depth != 1) { + susp_fin_CE(t, dir); + } + + for (i = 0; i < dir->nchildren; i++) { + susp_finalize(t, dir->children[i]); + } +} + +void susp_write(struct ecma119_write_target *t, struct susp_info *susp, + unsigned char *buf) +{ + int i; + int pos = 0; + + for (i = 0; i < susp->n_fields_fit; i++) { + memcpy(&buf[pos], susp->susp_fields[i], + susp->susp_fields[i][2]); + pos += susp->susp_fields[i][2]; + } +} + +void susp_write_CE(struct ecma119_write_target *t, struct susp_info *susp, + unsigned char *buf) +{ + int i; + int pos = 0; + + for (i = susp->n_fields_fit; i < susp->n_susp_fields; i++) { + memcpy(&buf[pos], susp->susp_fields[i], + susp->susp_fields[i][2]); + pos += susp->susp_fields[i][2]; + } +} + +void susp_free_fields(struct susp_info *susp) +{ + int i; + + for (i=0; in_susp_fields; i++) { + free(susp->susp_fields[i]); + } + if (susp->susp_fields) { + free(susp->susp_fields); + } + memset(susp, 0, sizeof(struct susp_info)); +} diff --git a/trunk/libisofs/susp.h b/trunk/libisofs/susp.h new file mode 100755 index 00000000..78945819 --- /dev/null +++ b/trunk/libisofs/susp.h @@ -0,0 +1,75 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** Functions and structures used for SUSP (IEEE 1281). + */ + +#ifndef __ISO_SUSP +#define __ISO_SUSP + +/* SUSP is only present in standard ecma119 */ +struct ecma119_write_target; +struct iso_tree_node; +struct iso_tree_dir; + +/** This contains the information that needs to go in the SUSP area of a file. + */ +struct susp_info +{ + int n_susp_fields; /**< Number of SUSP fields */ + unsigned char **susp_fields; /**< Data for each SUSP field */ + + /* the next 3 relate to CE and are filled out by susp_add_CE. */ + int n_fields_fit; /**< How many of the above SUSP fields fit + * within this node's dirent. */ + int non_CE_len; /**< Length of the part of the SUSP area that + * fits in the dirent. */ + int CE_len; /**< Length of the part of the SUSP area that + * will go in a CE area. */ +}; + +void susp_add_CE(struct ecma119_write_target *, struct iso_tree_node *); + +/* these next 2 are special because they don't modify the susp fields of the + * directory that gets passed to them; they modify the susp fields of the + * "." entry in the directory. */ +void susp_add_SP(struct ecma119_write_target *, struct iso_tree_dir *); +void susp_add_ER(struct ecma119_write_target *, struct iso_tree_dir *); + +/** Once all the directories and files are laid out, recurse through the tree + * and finalize all SUSP CE entries. */ +void susp_finalize(struct ecma119_write_target *, struct iso_tree_dir *); + +void susp_append(struct ecma119_write_target *, + struct iso_tree_node *, + unsigned char *); +void susp_append_self(struct ecma119_write_target *, + struct iso_tree_dir *, + unsigned char *); +void susp_append_parent(struct ecma119_write_target *, + struct iso_tree_dir *, + unsigned char *); +void susp_insert(struct ecma119_write_target *, + struct iso_tree_node *, + unsigned char *, + int pos); +void susp_insert_self(struct ecma119_write_target *, + struct iso_tree_dir *, + unsigned char *, + int pos); +void susp_insert_parent(struct ecma119_write_target *, + struct iso_tree_dir *, + unsigned char *, + int pos); +unsigned char *susp_find(struct susp_info *, + const char *); + +void susp_write(struct ecma119_write_target *, + struct susp_info *, + unsigned char *); +void susp_write_CE(struct ecma119_write_target *, + struct susp_info *, + unsigned char *); + +void susp_free_fields(struct susp_info *); + +#endif /* __ISO_SUSP */ diff --git a/trunk/libisofs/test.c b/trunk/libisofs/test.c new file mode 100755 index 00000000..ff740999 --- /dev/null +++ b/trunk/libisofs/test.c @@ -0,0 +1,138 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set ts=8 sts=8 sw=8 noet : */ + +#define _GNU_SOURCE + +#include "libisofs/libisofs.h" +#include "libburn/libburn.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SECSIZE 2048 + +const char * const optstring = "JRL:h"; +extern char *optarg; +extern int optind; + +void usage() +{ + printf("test [OPTIONS] DIRECTORY OUTPUT\n"); +} + +void help() +{ + printf( +"Options:\n" +" -J Add Joliet support\n" +" -R Add Rock Ridge support\n" +" -L Set the ISO level (1 or 2)\n" +" -h Print this message\n" +); +} + +int main(int argc, char **argv) +{ + struct iso_volset *volset; + struct iso_volume *volume; + struct burn_source *src; + unsigned char buf[2048]; + FILE *fd; + int c; + int level=1, flags=0; + DIR *dir; + struct dirent *ent; + + while ((c = getopt(argc, argv, optstring)) != -1) { + switch(c) { + case 'h': + usage(); + help(); + exit(0); + break; + case 'J': + flags |= ECMA119_JOLIET; + break; + case 'R': + flags |= ECMA119_ROCKRIDGE; + break; + case 'L': + level = atoi(optarg); + break; + case '?': + usage(); + exit(1); + break; + } + } + + if (argc < 2) { + printf ("must pass directory to build iso from\n"); + usage(); + return 1; + } + if (argc < 3) { + printf ("must supply output file\n"); + usage(); + return 1; + } + fd = fopen(argv[optind+1], "w"); + if (!fd) { + perror("error opening output file"); + exit(1); + } + + volume = iso_volume_new( "VOLID", "PUBID", "PREPID" ); + volset = iso_volset_new( volume, "VOLSETID" ); + dir = opendir(argv[optind]); + if (!dir) { + perror("error opening input directory"); + exit(1); + } + + while ( (ent = readdir(dir)) ) { + struct stat st; + char *name; + + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) { + continue; + } + + name = malloc(strlen(argv[optind]) + strlen(ent->d_name) + 2); + strcpy(name, argv[optind]); + strcat(name, "/"); + strcat(name, ent->d_name); + if (lstat(name, &st) == -1) { + fprintf(stderr, "error opening file %s: %s\n", + name, strerror(errno)); + exit(1); + } + + if (S_ISDIR(st.st_mode)) { + iso_tree_radd_dir(iso_volume_get_root(volume), name); + } else { + iso_tree_add_file(iso_volume_get_root(volume), name); + } + free(name); + } + + iso_tree_print(iso_volume_get_root(volume), 0); + + src = iso_source_new_ecma119(volset, 0, level, flags); + + while (src->read(src, buf, 2048) == 2048) { + fwrite(buf, 1, 2048, fd); + } + fclose(fd); + + return 0; +} diff --git a/trunk/libisofs/tree.c b/trunk/libisofs/tree.c new file mode 100755 index 00000000..1d5c4862 --- /dev/null +++ b/trunk/libisofs/tree.c @@ -0,0 +1,409 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libisofs.h" +#include "tree.h" +#include "util.h" +#include "volume.h" + +struct iso_tree_dir *iso_tree_new_root(struct iso_volume *volume) +{ + struct iso_tree_dir *dir; + + assert(volume); + + dir = calloc(1, sizeof(struct iso_tree_dir)); + dir->attrib.st_mode = S_IFDIR; + dir->volume = volume; + + return dir; +} + +void iso_tree_free(struct iso_tree_dir *root) +{ + int i; + + assert(root); + + /* Free names. */ + free(root->name.full); + free(root->name.iso1); + free(root->name.iso2); + free(root->name.rockridge); + free(root->name.joliet); + + /* Free the children. */ + for (i = 0; i < root->nchildren; i++) + iso_tree_free(root->children[i]); + free(root->children); + + /* Free all files. */ + for (i = 0; i < root->nfiles; i++) { + struct iso_tree_file *file = root->files[i]; + + free(file->path); + free(file->name.full); + free(file->name.iso1); + free(file->name.iso2); + free(file->name.rockridge); + free(file->name.joliet); + free(file); + } + free(root->files); + + if (root->writer_data) { + fprintf(stderr, "Warning: freeing a directory with non-NULL " + "writer_data\n"); + } + + /* Free ourself. */ + free(root); +} + +struct iso_tree_file *iso_tree_add_new_file(struct iso_tree_dir *parent, + const char *name) +{ + struct iso_tree_file *file; + + assert(parent && name); + + file = calloc(1, sizeof(struct iso_tree_file)); + file->path = calloc(1, 1); + file->parent = parent; + file->volume = parent->volume; + file->attrib = parent->attrib; + file->attrib.st_mode &= ~S_IFMT; + file->attrib.st_mode |= S_IFREG; + file->attrib.st_size = 0; + iso_tree_file_set_name(file, name); + + /* Add the new file to the parent directory */ + parent->nfiles++; + parent->files = realloc(parent->files, sizeof(void *) * parent->nfiles); + parent->files[parent->nfiles - 1] = file; + + return file; +} + +struct iso_tree_file *iso_tree_add_file(struct iso_tree_dir *parent, + const char *path) +{ + struct iso_tree_file *file; + struct stat st; + int statret; + const char *name; + + assert( parent != NULL && path != NULL ); + statret = lstat(path, &st); + if (statret == -1) { + fprintf(stderr, "couldn't stat file %s: %s\n", + path, strerror(errno)); + return NULL; + } + + /* Set up path, parent and volume. */ + file = calloc(1, sizeof(struct iso_tree_file)); + file->path = strdup(path); + file->parent = parent; + file->volume = parent->volume; + file->attrib = st; + + /* find the last component in the path */ + name = strrchr(path, '/'); + if (name == NULL) { + name = path; + } else { + name++; + } + iso_tree_file_set_name(file, name); + + if (!S_ISREG(st.st_mode)) { + file->attrib.st_size = 0; + } + + /* Add the new file to the parent directory */ + parent->nfiles++; + parent->files = realloc(parent->files, sizeof(void *) * parent->nfiles); + parent->files[parent->nfiles - 1] = file; + + return file; +} + +struct iso_tree_dir *iso_tree_add_dir(struct iso_tree_dir *parent, + const char *path) +{ + struct iso_tree_dir *dir; + struct stat st; + int statret; + char *pathcpy; + char *name; + + assert( parent && path ); + statret = stat(path, &st); + if (statret == -1) { + fprintf(stderr, "couldn't stat directory %s: %s\n", + path, strerror(errno)); + return NULL; + } + + dir = calloc(1, sizeof(struct iso_tree_dir)); + dir->parent = parent; + dir->volume = parent->volume; + dir->attrib = st; + + /* find the last component in the path. We need to copy the path because + * we modify it if there is a trailing slash. */ + pathcpy = strdup(path); + name = strrchr(pathcpy, '/'); + if (name == &pathcpy[strlen(pathcpy) - 1]) { + /* get rid of the trailing slash */ + *name = '\0'; + name = strrchr(pathcpy, '/'); + } + if (name == NULL) { + name = pathcpy; + } else { + name++; + } + iso_tree_dir_set_name(dir, name); + free(pathcpy); + + + parent->nchildren++; + parent->children = realloc(parent->children, + parent->nchildren * sizeof(void*)); + parent->children[parent->nchildren - 1] = dir; + + return dir; +} + +struct iso_tree_dir *iso_tree_radd_dir(struct iso_tree_dir *parent, + const char *path) +{ + struct iso_tree_dir *nparent; + struct stat st; + int statret; + DIR *dir; + struct dirent *ent; + + assert( parent && path ); + statret = stat(path, &st); + + nparent = iso_tree_add_dir(parent, path); + + /* Open the directory for reading and iterate over the directory + entries. */ + dir = opendir(path); + + if (!dir) { + fprintf(stderr, "couldn't open directory %s: %s\n", + path, strerror(errno)); + return NULL; + } + + while ((ent = readdir(dir))) { + char *child; + + /* Skip current and parent directory entries. */ + if (strcmp(ent->d_name, ".") == 0 || + strcmp(ent->d_name, "..") == 0) + continue; + + /* Build the child's full pathname. */ + child = iso_pathname(path, ent->d_name); + + /* Skip to the next entry on errors. */ + if (stat(child, &st) != 0) + continue; + + if (S_ISDIR(st.st_mode)) { + iso_tree_radd_dir(nparent, child); + } else { + iso_tree_add_file(nparent, child); + } + + free(child); + } + + closedir(dir); + + return nparent; +} + +struct iso_tree_dir *iso_tree_add_new_dir(struct iso_tree_dir *parent, + const char *name) +{ + struct iso_tree_dir *dir; + + assert( parent && name ); + + dir = calloc(1, sizeof(struct iso_tree_dir)); + dir->parent = parent; + dir->volume = parent->volume; + + iso_tree_dir_set_name(dir, name); + + dir->attrib = parent->attrib; + dir->attrib.st_mtime = time(NULL); + dir->attrib.st_atime = time(NULL); + dir->attrib.st_ctime = time(NULL); + + + /* add the new directory to parent */ + parent->nchildren++; + parent->children = realloc(parent->children, + parent->nchildren * sizeof(void*)); + parent->children[parent->nchildren - 1] = dir; + + return dir; +} + +void iso_tree_file_set_name(struct iso_tree_file *file, const char *name) +{ + file->name.full = strdup(name); + file->name.iso1 = iso_1_fileid(name); + file->name.iso2 = iso_2_fileid(name); + file->name.rockridge = iso_p_filename(name); + file->name.joliet = iso_j_id(file->name.full); +} + +char *iso_tree_node_get_name_nconst(const struct iso_tree_node *node, + enum iso_name_version ver) +{ + if (ver == ISO_NAME_ISO) { + if (node->volume->iso_level == 1) { + return node->name.iso1; + } else { + return node->name.iso2; + } + } + + switch (ver) { + case ISO_NAME_FULL: + return node->name.full; + case ISO_NAME_ISO: + if (node->volume->iso_level == 1) { + return node->name.iso1; + } else { + return node->name.iso2; + } + case ISO_NAME_ISO_L1: + return node->name.iso1; + case ISO_NAME_ISO_L2: + return node->name.iso2; + case ISO_NAME_ROCKRIDGE: + return node->name.rockridge; + case ISO_NAME_JOLIET: + return (char*) node->name.joliet; + } + + assert(0); + return NULL; /* just to shut up warnings */ +} + +const char *iso_tree_node_get_name(const struct iso_tree_node *node, + enum iso_name_version ver) +{ + return iso_tree_node_get_name_nconst(node, ver); +} + +void iso_tree_dir_set_name(struct iso_tree_dir *dir, const char *name) +{ + dir->name.full = strdup(name); + /* Level 1 directory is a string of d-characters of maximum size 8. */ + dir->name.iso1 = iso_d_str(name, 8, 0); + /* Level 2 directory is a string of d-characters of maximum size 31. */ + dir->name.iso2 = iso_d_str(name, 31, 0); + dir->name.rockridge = iso_p_dirname(name); + dir->name.joliet = iso_j_id(dir->name.full); +} + +/* Compares file names for use with qsort. */ +int iso_node_cmp(const void *v1, const void *v2) +{ + struct iso_tree_node **f1 = (struct iso_tree_node **)v1; + struct iso_tree_node **f2 = (struct iso_tree_node **)v2; + + return strcmp(iso_tree_node_get_name(*f1, ISO_NAME_FULL), + iso_tree_node_get_name(*f2, ISO_NAME_FULL)); +} + +int iso_node_cmp_iso(const void *v1, const void *v2) +{ + struct iso_tree_node **f1 = (struct iso_tree_node **)v1; + struct iso_tree_node **f2 = (struct iso_tree_node **)v2; + + return strcmp(iso_tree_node_get_name(*f1, ISO_NAME_ISO), + iso_tree_node_get_name(*f2, ISO_NAME_ISO)); +} + +int iso_node_cmp_joliet(const void *v1, const void *v2) +{ + struct iso_tree_node **f1 = (struct iso_tree_node **)v1; + struct iso_tree_node **f2 = (struct iso_tree_node **)v2; + + return ucscmp((uint16_t*)iso_tree_node_get_name(*f1, ISO_NAME_JOLIET), + (uint16_t*)iso_tree_node_get_name(*f2, ISO_NAME_JOLIET)); +} + +void iso_tree_sort(struct iso_tree_dir *root) +{ + int i; + + qsort(root->files, root->nfiles, sizeof(struct iso_tree_file *), + iso_node_cmp); + + qsort(root->children, root->nchildren, sizeof(struct iso_tree_dir *), + iso_node_cmp); + + for (i = 0; i < root->nchildren; i++) + iso_tree_sort(root->children[i]); +} + +void iso_tree_print(const struct iso_tree_dir *root, int spaces) +{ + iso_tree_print_verbose(root, NULL, NULL, NULL, spaces); +} + +void iso_tree_print_verbose(const struct iso_tree_dir *root, + print_dir_callback dc, print_file_callback fc, + void *data, int spaces) +{ + int i, j; + + for (i = 0; i < spaces; i++) + printf(" "); + + /* Root directory doesn't have a name. */ + if (root->name.full != NULL) + printf("%s", iso_tree_node_get_name(ISO_NODE(root), + ISO_NAME_ISO)); + printf("/\n"); + if (dc) dc(root, data, spaces); + + spaces += 2; + + for (j = 0; j < root->nchildren; j++) { + iso_tree_print_verbose(root->children[j], dc, fc, data, + spaces); + } + + for (j = 0; j < root->nfiles; j++) { + for (i = 0; i < spaces; i++) + printf(" "); + printf("%s\n", + iso_tree_node_get_name(ISO_NODE(root->files[j]), + ISO_NAME_ISO)); + if (fc) fc(root->files[j], data, spaces); + } +} diff --git a/trunk/libisofs/tree.h b/trunk/libisofs/tree.h new file mode 100755 index 00000000..34d9240c --- /dev/null +++ b/trunk/libisofs/tree.h @@ -0,0 +1,259 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * \file tree.h + * + * Extra declarations for use with the iso_tree_dir and iso_tree_file + * structures. + */ + +#ifndef __ISO_TREE +#define __ISO_TREE + +#include +#include +#include +#include + +#include "libisofs.h" + +/** + * File or directory names or identifiers. + */ +struct iso_names +{ + char *full; /**< Full version: either part of the path or + user input. */ + char *iso1; /**< ISO level 1 identifier. */ + char *iso2; /**< ISO level 2 identifier. */ + char *rockridge; /**< Rock Ridge file or directory name. */ + uint16_t *joliet; /**< Joliet identifier. */ +}; + +/** + * Directory on a volume. + */ +struct iso_tree_dir +{ + struct iso_tree_dir *parent; /**< \see iso_tree_node */ + struct iso_volume *volume; /**< \see iso_tree_node */ + struct iso_names name; /**< \see iso_tree_node */ + struct stat attrib; /**< \see iso_tree_node */ + off_t block; /**< \see iso_tree_node */ + uint8_t dirent_len; /**< \see iso_tree_node */ + void *writer_data; /**< \see iso_tree_node */ + + int depth; /**< The depth of this directory in the + * Directory Heirarchy. This is 1 for + * the root directory. + */ + + int nchildren; /**< Number of child directories. */ + int nfiles; /**< Number of files in this directory. + */ + struct iso_tree_dir **children; /**< Child directories. */ + struct iso_tree_file **files; /**< Files in this directory. */ +}; + +/** + * File on a volume. + */ +struct iso_tree_file +{ + struct iso_tree_dir *parent; /**< \see iso_tree_node */ + struct iso_volume *volume; /**< \see iso_tree_node */ + struct iso_names name; /**< \see iso_tree_node */ + struct stat attrib; /**< \see iso_tree_node */ + off_t block; /**< \see iso_tree_node */ + uint8_t dirent_len; /**< \see iso_tree_node */ + void *writer_data; /**< \see iso_tree_node */ + + char *path; /**< Location of the file in the + * local filesystem. This can be a + * full or relative path. If this is + * NULL, then the file doesn't exist + * in the local filesystem and its + * size must be zero. + * + * FIXME: Allow references to files + * on other volumes belonging to the + * same volset as this file. + */ +}; + +/** + * Fields in common between iso_tree_file and iso_tree_dir. + */ +struct iso_tree_node +{ + struct iso_tree_dir *parent; /**< The parent of this node. Must be + * non-NULL unless we are the + * root directory on a volume. + */ + struct iso_volume *volume; /**< The volume to which this node + * belongs. + */ + struct iso_names name; /**< The name of this node in its parent + * directory. Must be non-NULL unless + * we are the root directory on a + * volume. + */ + struct stat attrib; /**< The POSIX attributes of this + * node as documented in "man 2 stat". + * + * Any node that is not a regular + * file or a directory must have + * \p attrib.st_size set to zero. Any + * node that is a directory will have + * \p attrib.st_size filled out by the + * writer. + */ + + /* information used for writing */ + off_t block; /**< The block at which this node's + * data will be written. + */ + uint8_t dirent_len; /**< The size of this node's + * Directory Record in its parent. + * This does not include System Use + * fields, if present. + */ + void *writer_data; /**< This is writer-specific data. It + * must be set to NULL when a node + * is created and it should be NULL + * again when the node is freed. + */ +}; + +/** A macro to simplify casting between nodes and files/directories. */ +#define ISO_NODE(a) ( (struct iso_tree_node*) (a) ) + +/** A macro to simplify casting between nodes and directories. */ +#define ISO_DIR(a) ( (struct iso_tree_dir*) (a) ) + +/** A macro to simplify casting between nodes and files. */ +#define ISO_FILE(a) ( (struct iso_tree_file*) (a) ) + +/** + * Create a new root directory for a volume. + * + * \param volume The volume for which to create a new root directory. + * + * \pre \p volume is non-NULL. + * \post \p volume has a non-NULL, empty root directory. + * \return \p volume's new non-NULL, empty root directory. + */ +struct iso_tree_dir *iso_tree_new_root(struct iso_volume *volume); + +/** + * Create a new, empty, file. + * + * \param parent The parent of the new file. + * \param name The name of the new file, encoded in the current locale. + * + * \pre \p parent is non-NULL. + * \pre \p name is non-NULL and it does not match any other file or directory + * name in \p parent. + * \post \p parent contains a file with the following properties: + * - the file's (full) name is \p name + * - the file's POSIX permissions are the same as \p parent's + * - the file is a regular file + * - the file is empty + * + * \return \p parent's newly created file. + */ +struct iso_tree_file *iso_tree_add_new_file(struct iso_tree_dir *parent, + const char *name); + +/** + * Recursively free a directory. + * + * \param root The root of the directory heirarchy to free. + * + * \pre \p root is non-NULL. + */ +void iso_tree_free(struct iso_tree_dir *root); + +/** + * Recursively sort all the files and child directories in a directory. + * + * \param root The root of the directory heirarchy to sort. + * + * \pre \p root is non-NULL. + * \post For each directory \p dir in the directory heirarchy descended fro + * root, the fields in \p dir.children and \p dir.files are alphabetically + * sorted according to \p name.full. + * + * \see iso_names + */ +void iso_tree_sort(struct iso_tree_dir *root); + +/** + * Compare the names of 2 nodes, \p *v1 and \p *v2. This is compatible with + * qsort. + */ +int iso_node_cmp(const void *v1, const void *v2); + +/** + * Compare the joliet names of 2 nodes, compatible with qsort. + */ +int iso_node_cmp_joliet(const void *v1, const void *v2); + +/** + * Compare the iso names of 2 nodes, compatible with qsort. + */ +int iso_node_cmp_iso(const void *v1, const void *v2); + +/** + * A function that prints verbose information about a directory. + * + * \param dir The directory about which to print information. + * \param data Unspecified function-dependent data. + * \param spaces The number of spaces to prepend to the output. + * + * \see iso_tree_print_verbose + */ +typedef void (*print_dir_callback) (const struct iso_tree_dir *dir, + void *data, + int spaces); +/** + * A function that prints verbose information about a file. + * + * \param dir The file about which to print information. + * \param data Unspecified function-dependent data. + * \param spaces The number of spaces to prepend to the output. + * + * \see iso_tree_print_verbose + */ +typedef void (*print_file_callback) (const struct iso_tree_file *file, + void *data, + int spaces); + +/** + * Recursively print a directory heirarchy. For each node in the directory + * heirarchy, call a callback function to print information more verbosely. + * + * \param root The root of the directory heirarchy to print. + * \param dir The callback function to call for each directory in the tree. + * \param file The callback function to call for each file in the tree. + * \param callback_data The data to pass to the callback functions. + * \param spaces The number of spaces to prepend to the output. + * + * \pre \p root is not NULL. + * \pre Neither of the callback functions modifies the directory heirarchy. + */ +void iso_tree_print_verbose(const struct iso_tree_dir *root, + print_dir_callback dir, + print_file_callback file, + void *callback_data, + int spaces); + +/** + * Get a non-const version of the node name. This is used for name-mangling + * iso names. It should only be used internally in libisofs; all other users + * should only access the const name. + */ +char *iso_tree_node_get_name_nconst(const struct iso_tree_node *node, + enum iso_name_version ver); +#endif /* __ISO_TREE */ diff --git a/trunk/libisofs/util.c b/trunk/libisofs/util.c new file mode 100755 index 00000000..0295ac86 --- /dev/null +++ b/trunk/libisofs/util.c @@ -0,0 +1,710 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set ts=8 sts=8 sw=8 noet : */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" + +int valid_d_char(char c) +{ + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_'); +} + +int valid_a_char(char c) +{ + return (c >= ' ' && c <= '"') || (c >= '%' && c <= '?') || (c >= 'A' && + c <= 'Z') + || (c == '_'); +} + +int valid_j_char(char msb, char lsb) +{ + return !(msb == '\0' && + (lsb < ' ' || lsb == '*' || lsb == '/' || lsb == ':' || + lsb == ';' || lsb == '?' || lsb == '\\')); +} + +int valid_p_char(char c) +{ + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && + c <= 'z') + || (c == '.') || (c == '_') || (c == '-'); +} + +char *iso_d_str(const char *src, int size, int pad) +{ + char *dest = strlen(src) > size || + pad ? malloc(size + 1) : malloc(strlen(src) + 1); + int i; + + /* Try converting to upper-case before validating. */ + for (i = 0; i < size && src[i]; i++) { + char c = toupper(src[i]); + + dest[i] = valid_d_char(c) ? c : '_'; + } + + /* Optionally pad with spaces and terminate with NULL. */ + if (pad) + while (i < size) + dest[i++] = ' '; + dest[i] = '\0'; + + return dest; +} + +char *iso_a_str(const char *src, int size) +{ + char *dest = malloc(size + 1); + int i; + + for (i = 0; i < size && src[i]; i++) { + char c = toupper(src[i]); + + dest[i] = valid_a_char(c) ? c : '_'; + } + while (i < size) + dest[i++] = ' '; + dest[i] = '\0'; + + return dest; +} + +uint16_t *iso_j_str(const char *src) +{ + int size = strlen(src) * 2; + uint16_t *dest = malloc(size + 2); + char *cpy, *in, *out; + size_t inleft, outleft; + iconv_t cd; + + /* If the conversion is unavailable, return NULL. Obviously, + nl_langinfo(..) requires the locale to be set correctly. */ + cd = iconv_open("UCS-2BE", nl_langinfo(CODESET)); + if (cd == (iconv_t) - 1) { + free(dest); + return NULL; + } + + /* In, out, inleft and outleft will be updated by iconv, that's why we + need separate variables. */ + cpy = strdup(src); /* iconv doesn't take const * chars, so we need our + * own copy. */ + in = cpy; + out = (char*)dest; + inleft = strlen(cpy); + outleft = size; + iconv(cd, &in, &inleft, &out, &outleft); + + /* Since we need to pad with NULLs, we can pad up to and including the + terminating NULLs. */ + outleft += 2; + while (outleft) { + *out++ = '\0'; + outleft--; + } + iconv_close(cd); + free(cpy); + + return dest; +} + +char *iso_1_fileid(const char *src) +{ + char *dest = malloc(15); /* 15 = 8 (name) + 1 (.) + 3 (ext) + 2 + (;1) + 1 (\0) */ + char *dot = strrchr(src, '.'); /* Position of the last dot in the + filename, will be used to calculate + lname and lext. */ + int lname, lext, pos, i; + + lext = dot ? strlen(dot + 1) : 0; + lname = strlen(src) - lext - (dot ? 1 : 0); + + /* If we can't build a filename, return NULL. */ + if (lname == 0 && lext == 0) { + free(dest); + return NULL; + } + + pos = 0; + /* Convert up to 8 characters of the filename. */ + for (i = 0; i < lname && i < 8; i++) { + char c = toupper(src[i]); + + dest[pos++] = valid_d_char(c) ? c : '_'; + } + /* This dot is mandatory, even if there is no extension. */ + dest[pos++] = '.'; + /* Convert up to 3 characters of the extension, if any. */ + for (i = 0; i < lext && i < 3; i++) { + char c = toupper(src[lname + 1 + i]); + + dest[pos++] = valid_d_char(c) ? c : '_'; + } + /* File versions are mandatory, even if they aren't used. */ + dest[pos++] = ';'; + dest[pos++] = '1'; + dest[pos] = '\0'; + dest = (char *)realloc(dest, pos + 1); + + return dest; +} + +char *iso_2_fileid(const char *src) +{ + char *dest = malloc(34); /* 34 = 30 (name + ext) + 1 (.) + 2 + (;1) + 1 (\0) */ + char *dot = strrchr(src, '.'); + int lname, lext, lnname, lnext, pos, i; + + /* Since the maximum length can be divided freely over the name and + extension, we need to calculate their new lengths (lnname and + lnext). If the original filename is too long, we start by trimming + the extension, but keep a minimum extension length of 3. */ + if (dot == NULL || dot == src || *(dot + 1) == '\0') { + lname = strlen(src); + lnname = (lname > 30) ? 30 : lname; + lext = lnext = 0; + } else { + lext = strlen(dot + 1); + lname = strlen(src) - lext - 1; + lnext = (strlen(src) > 31 && + lext > 3) ? (lname < 27 ? 30 - lname : 3) : lext; + lnname = (strlen(src) > 31) ? 30 - lnext : lname; + } + + if (lnname == 0 && lnext == 0) { + free(dest); + return NULL; + } + + pos = 0; + /* Convert up to lnname characters of the filename. */ + for (i = 0; i < lnname; i++) { + char c = toupper(src[i]); + + dest[pos++] = valid_d_char(c) ? c : '_'; + } + dest[pos++] = '.'; + /* Convert up to lnext characters of the extension, if any. */ + for (i = 0; i < lnext; i++) { + char c = toupper(src[lname + 1 + i]); + + dest[pos++] = valid_d_char(c) ? c : '_'; + } + dest[pos++] = ';'; + dest[pos++] = '1'; + dest[pos] = '\0'; + dest = (char *)realloc(dest, pos + 1); + + return dest; +} + +uint16_t *iso_j_id(char *src) +{ + char *dest = malloc(136); /* 136 = 128 (name + ext) + 2 (\0 .) + + 4 (\0 ; \0 1) + 2 (\0 \0) */ + char *dot = strrchr(src, '.'); + int lname, lext, lcname, lcext, lnname, lnext, pos, i; + size_t inleft, outleft; + char *cname, *cext, *in, *out; + iconv_t cd; + + if (dot == NULL || dot == src || *(dot + 1) == '\0') { + lname = strlen(src); + lext = 0; + } else { + lext = strlen(dot + 1); + lname = strlen(src) - lext - 1; + } + + if (lname == 0 && lext == 0) { + free(dest); + return NULL; + } + + cd = iconv_open("UCS-2BE", nl_langinfo(CODESET)); + if (cd == (iconv_t) - 1) { + free(dest); + return NULL; + } + + /* We need to convert the name and extension first, in order to + calculate the number of characters they have. */ + cname = malloc(lname * 2); + in = src; + out = cname; + inleft = lname; + outleft = lname * 2; + iconv(cd, &in, &inleft, &out, &outleft); + lcname = (lname * 2) - outleft; + iconv_close(cd); + + if (lext) { + cd = iconv_open("UCS-2BE", nl_langinfo(CODESET)); + if (cd == (iconv_t) - 1) { + free(dest); + free(cname); + return NULL; + } + cext = malloc(lext * 2); + in = dot + 1; + out = cext; + inleft = lext; + outleft = lext * 2; + iconv(cd, &in, &inleft, &out, &outleft); + lcext = (lext * 2) - outleft; + iconv_close(cd); + } else + lcext = 0; + + /* Again, divide the available characters over name and extension, but + keep a minimum of three characters for the new extension. */ + lnext = (lcname + lcext > 128 && + lcext > 6) ? (lcname < 122 ? 128 - lcname : 6) : lcext; + lnname = (lcname + lcext > 128) ? 128 - lnext : lcname; + + pos = 0; + /* Copy up to lnname bytes from the converted filename. */ + for (i = 0; i < lnname; i = i + 2) + if (valid_j_char(cname[i], cname[i + 1])) { + dest[pos++] = cname[i]; + dest[pos++] = cname[i + 1]; + } else { + dest[pos++] = '\0'; + dest[pos++] = '_'; + } + /* Dot is now a 16 bit separator. */ + dest[pos++] = '\0'; + dest[pos++] = '.'; + /* Copy up to lnext bytes from the converted extension, if any. */ + for (i = 0; i < lnext; i = i + 2) + if (valid_j_char(cext[i], cext[i + 1])) { + dest[pos++] = cext[i]; + dest[pos++] = cext[i + 1]; + } else { + dest[pos++] = '\0'; + dest[pos++] = '_'; + } + /* Again, 2 bytes per character. */ + dest[pos++] = '\0'; + dest[pos++] = ';'; + dest[pos++] = '\0'; + dest[pos++] = '1'; + dest[pos++] = '\0'; + dest[pos] = '\0'; + + dest = (char *)realloc(dest, pos + 1); + free(cname); + if (lext) + free(cext); + + /* Fill in the size in bytes (including the terminating NULLs) of the + destination string. */ + return (uint16_t *) dest; +} + +char *iso_p_filename(const char *src) +{ + char *dest = malloc(251); /* We can fit up to 250 characters in + a Rock Ridge name entry. */ + char *dot = strrchr(src, '.'); + int lname, lext, lnname, lnext, pos, i; + + if (dot == NULL || dot == src || *(dot + 1) == '\0') { + lname = strlen(src); + lnname = (lname > 250) ? 250 : lname; + lext = lnext = 0; + } else { + lext = strlen(dot + 1); + lname = strlen(src) - lext - 1; + lnext = (strlen(src) > 250 && + lext > 3) ? (lname < 246 ? 249 - lname : 3) : lext; + lnname = (strlen(src) > 250) ? 249 - lnext : lname; + } + + if (lnname == 0 && lnext == 0) { + free(dest); + return NULL; + } + + pos = 0; + for (i = 0; i < lnname; i++) + dest[pos++] = valid_p_char(src[i]) ? src[i] : '_'; + if (lnext) { + dest[pos++] = '.'; + for (i = 0; i < lnext; i++) + dest[pos++] = + valid_p_char(src[lname + 1 + i]) ? + src[lname + 1 + i] : '_'; + } + dest[pos] = '\0'; + dest = (char *)realloc(dest, pos + 1); + + return dest; +} + +char *iso_p_dirname(const char *src) +{ + char *dest = strlen(src) > 250 ? malloc(251) : malloc(strlen(src) + 1); + int i; + + if (strlen(src) == 0) { + free(dest); + return NULL; + } + + for (i = 0; i < 250 && src[i]; i++) + dest[i] = valid_p_char(src[i]) ? src[i] : '_'; + dest[i] = '\0'; + + return dest; +} + +char *iso_pathname(const char *dir, const char *file) +{ + char *path = malloc(strlen(dir) + strlen(file) + 2); + + strcpy(path, dir); + path[strlen(dir)] = '/'; + strcpy(path + strlen(dir) + 1, file); + + return path; +} + +void iso_a_strcpy(unsigned char *dest, int size, const char *src) +{ + int i = 0, d = 0; + + if (src) + for (; i < size && *src; ++i, ++src) { + char s = toupper(*src); + + if (valid_a_char(s)) + dest[d++] = s; + else + dest[d++] = '_'; + } + for (; i < size; ++i) { + /* pad with spaces */ + dest[d++] = ' '; + } +} + +void iso_d_strcpy(unsigned char *dest, int size, const char *src) +{ + int i = 0, d = 0; + + if (src) + for (; i < size && *src; ++i, ++src) { + char s = toupper(*src); + + if (valid_d_char(s)) + dest[d++] = s; + else + dest[d++] = '_'; + } + for (; i < size; ++i) { + /* pad with spaces */ + dest[d++] = ' '; + } +} + +char *iso_a_strndup(const char *src, int size) +{ + int i, d; + char *out; + + out = malloc(size + 1); + for (i = d = 0; i < size && *src; ++i, ++src) { + char s = toupper(*src); + + if (valid_a_char(s)) + out[d++] = s; + else + out[d++] = '_'; + } + out[d++] = '\0'; + out = realloc(out, d); /* shrink the buffer to what we used */ + return out; +} + +char *iso_d_strndup(const char *src, int size) +{ + int i, d; + char *out; + + out = malloc(size + 1); + for (i = d = 0; i < size && *src; ++i, ++src) { + char s = toupper(*src); + + if (valid_d_char(s)) + out[d++] = s; + else + out[d++] = '_'; + } + out[d++] = '\0'; + out = realloc(out, d); /* shrink the buffer to what we used */ + return out; +} + +char *iso_strconcat(char sep, const char *a, const char *b) +{ + char *out; + int la, lb; + + la = strlen(a); + lb = strlen(b); + out = malloc(la + lb + 1 + (sep ? 1 : 0)); + memcpy(out, a, la); + memcpy(out + la + (sep ? 1 : 0), b, lb); + if (sep) + out[la] = sep; + out[la + lb + (sep ? 1 : 0)] = '\0'; + return out; +} + +char *iso_strdup(const char *src) +{ + return src ? strdup(src) : NULL; +} + +void iso_lsb(uint8_t *buf, uint32_t num, int bytes) +{ + int i; + + assert(bytes <= 4); + + for (i = 0; i < bytes; ++i) + buf[i] = (num >> (8 * i)) & 0xff; +} + +void iso_msb(uint8_t *buf, uint32_t num, int bytes) +{ + int i; + + assert(bytes <= 4); + + for (i = 0; i < bytes; ++i) + buf[bytes - 1 - i] = (num >> (8 * i)) & 0xff; +} + +void iso_bb(uint8_t *buf, uint32_t num, int bytes) +{ + iso_lsb(buf, num, bytes); + iso_msb(buf+bytes, num, bytes); +} + + +void iso_datetime_7(unsigned char *buf, time_t t) +{ + static int tzsetup = 0; + static int tzoffset; + struct tm tm; + + if (!tzsetup) { + tzset(); + + tzoffset = -timezone / 60 / 15; + if (tzoffset < -48) + tzoffset += 101; + + tzsetup = 1; + } + + localtime_r(&t, &tm); + + buf[0] = tm.tm_year; + buf[1] = tm.tm_mon + 1; + buf[2] = tm.tm_mday; + buf[3] = tm.tm_hour; + buf[4] = tm.tm_min; + buf[5] = tm.tm_sec; + buf[6] = tzoffset; +} + +time_t iso_datetime_read_7(const uint8_t *buf) +{ + struct tm tm; + + tm.tm_year = buf[0]; + tm.tm_mon = buf[1] + 1; + tm.tm_mday = buf[2]; + tm.tm_hour = buf[3]; + tm.tm_min = buf[4]; + tm.tm_sec = buf[5]; + + return mktime(&tm) - buf[6] * 60 * 60; +} + +void iso_datetime_17(unsigned char *buf, time_t t) +{ + static int tzsetup = 0; + static int tzoffset; + struct tm tm; + char c[5]; + + if (t == (time_t) - 1) { + /* unspecified time */ + memset(buf, '0', 16); + buf[16] = 0; + } else { + if (!tzsetup) { + tzset(); + + tzoffset = -timezone / 60 / 15; + if (tzoffset < -48) + tzoffset += 101; + + tzsetup = 1; + } + + localtime_r(&t, &tm); + + /* year */ + sprintf(c, "%04d", tm.tm_year + 1900); + memcpy(&buf[0], c, 4); + /* month */ + sprintf(c, "%02d", tm.tm_mon + 1); + memcpy(&buf[4], c, 2); + /* day */ + sprintf(c, "%02d", tm.tm_mday); + memcpy(&buf[6], c, 2); + /* hour */ + sprintf(c, "%02d", tm.tm_hour); + memcpy(&buf[8], c, 2); + /* minute */ + sprintf(c, "%02d", tm.tm_min); + memcpy(&buf[10], c, 2); + /* second */ + sprintf(c, "%02d", MIN(59, tm.tm_sec)); + memcpy(&buf[12], c, 2); + /* hundreths */ + memcpy(&buf[14], "00", 2); + /* timezone */ + buf[16] = tzoffset; + } +} + +time_t iso_datetime_read_17(const uint8_t *buf) +{ + struct tm tm; + + sscanf((char*)&buf[0], "%4d", &tm.tm_year); + sscanf((char*)&buf[4], "%2d", &tm.tm_mon); + sscanf((char*)&buf[6], "%2d", &tm.tm_mday); + sscanf((char*)&buf[8], "%2d", &tm.tm_hour); + sscanf((char*)&buf[10], "%2d", &tm.tm_min); + sscanf((char*)&buf[12], "%2d", &tm.tm_sec); + tm.tm_year -= 1900; + tm.tm_mon -= 1; + + return mktime(&tm) - buf[16] * 60 * 60; +} + +void iso_split_filename(char *name, char **ext) +{ + char *r; + + r = strrchr(name, '.'); + if (r) { + *r = '\0'; + *ext = r + 1; + } else + *ext = ""; +} + +void iso_filecpy(unsigned char *buf, int size, const char *name, int version) +{ + char v[6]; + int nl, vl; + + assert(version >= 0); + assert(version < 0x8000); + + nl = strlen(name); + + memcpy(buf, name, nl); + + if (!version) + assert(size >= strlen(name)); + else { + sprintf(v, "%d", version); + vl = strlen(v); + assert(size >= nl + vl + 1); + + buf[nl] = ';'; + memcpy(&buf[nl + 1], v, vl); + + nl += vl + 1; + } + /* pad with spaces */ + if (nl < size) + memset(&buf[nl], ' ', size - nl); +} + +size_t ucslen(const uint16_t *str) +{ + int i; + + for (i=0; str[i]; i++) + ; + return i; +} + +int ucscmp(const uint16_t *s1, const uint16_t *s2) +{ + int i; + + for (i=0; 1; i++) { + if (s1[i] < s2[i]) { + return -1; + } else if (s1[i] > s2[i]) { + return 1; + } else if (s1[i] == 0 && s2[i] == 0) { + break; + } + } + + return 0; +} + +uint32_t iso_read_lsb(const uint8_t *buf, int bytes) +{ + int i; + uint32_t ret = 0; + + for (i=0; i + +/** + * Checks if the given character is a valid d-character. + */ +int valid_d_char(char c); + +/** + * Checks if the given character is a valid a-character. + */ +int valid_a_char(char c); + +/** + * Checks if the given character is part of the Joliet Allowed Character Set. + */ +int valid_j_char(char msb, char lsb); + +/** + * Checks if the given character is part of the POSIX Portable Filename Character Set. + */ +int valid_p_char(char c); + +/** + * Build a string of d-characters of maximum size 'size', optionally padded with spaces if necessary. + * Can be used to create directory identifiers if padding is turned off. + */ +char *iso_d_str(const char *src, int size, int pad); + +/** + * Build a string of a-characters of maximum size 'size', padded with spaces if necessary. + */ +char *iso_a_str(const char *src, int size); + +/** + * Build a string of j-characters of maximum size 'size', padded with NULLs if necessary. + * Requires the locale to be set correctly. + * @return NULL if the conversion from the current codeset to UCS-2BE is not available. + */ +uint16_t *iso_j_str(const char *src); + +/** + * Create a level 1 file identifier that consists of a name, extension and version number. + * The resulting string will have a file name of maximum length 8, followed by a separator (.), + * an optional extension of maximum length 3, followed by a separator (;) and a version number (digit 1). + * @return NULL if the original name and extension both are of length 0. + */ +char *iso_1_fileid(const char *src); + +/** + * Create a level 2 file identifier that consists of a name, extension and version number. + * The combined file name and extension length will not exceed 30, the name and extension will be separated (.), + * and the extension will be followed by a separator (;) and a version number (digit 1). + * @return NULL if the original name and extension both are of length 0. + */ +char *iso_2_fileid(const char *src); + +/** + * Create a Joliet file or directory identifier that consists of a name, extension and version number. + * The combined name and extension length will not exceed 128 bytes, the name and extension will be separated (.), + * and the extension will be followed by a separator (;) and a version number (digit 1). + * All characters consist of 2 bytes and the resulting string is NULL-terminated by a 2-byte NULL. + * Requires the locale to be set correctly. + * @param size will be set to the size (in bytes) of the identifier. + * @return NULL if the original name and extension both are of length 0 or the conversion from the current codeset to UCS-2BE is not available. + */ +uint16_t *iso_j_id(char *src); + +/** + * Create a POSIX portable file name that consists of a name and extension. + * The resulting file name will not exceed 250 characters. + * @return NULL if the original name and extension both are of length 0. + */ +char *iso_p_filename(const char *src); + +/** + * Create a POSIX portable directory name. + * The resulting directory name will not exceed 250 characters. + * @return NULL if the original name is of length 0. + */ +char *iso_p_dirname(const char *src); + +/** + * Build a pathname out of a directory and file name. + */ +char *iso_pathname(const char *dir, const char *file); + +#include + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +/** Copy a string of a-characters, padding the unused part of the + destination buffer */ +void iso_a_strcpy(unsigned char *dest, int size, const char *src); + +/** Copy a string of d-characters, padding the unused part of the + destination buffer */ +void iso_d_strcpy(unsigned char *dest, int size, const char *src); + +/** Returns a null terminated string containing the first 'size' a-characters + from the source */ +char *iso_a_strndup(const char *src, int size); + +/** Returns a null terminated string containing the first 'size' d-characters + from the source */ +char *iso_d_strndup(const char *src, int size); + +char *iso_strconcat(char sep, const char *a, const char *b); + +char *iso_strdup(const char *src); + +void iso_lsb(uint8_t *buf, uint32_t num, int bytes); +void iso_msb(uint8_t *buf, uint32_t num, int bytes); +void iso_bb(uint8_t *buf, uint32_t num, int bytes); + +uint32_t iso_read_lsb(const uint8_t *buf, int bytes); +uint32_t iso_read_msb(const uint8_t *buf, int bytes); +uint32_t iso_read_bb(const uint8_t *buf, int bytes); + +/** Records the date/time into a 7 byte buffer (9.1.5) */ +void iso_datetime_7(unsigned char *buf, time_t t); + +/** Records the date/time into a 17 byte buffer (8.4.26.1) */ +void iso_datetime_17(unsigned char *buf, time_t t); + +time_t iso_datetime_read_7(const uint8_t *buf); +time_t iso_datetime_read_17(const uint8_t *buf); + +/** Removes the extension from the name, and returns the extension. The + returned extension should _not_ be freed! */ +void iso_split_filename(char *name, char **ext); + +void iso_filecpy(unsigned char *buf, int size, const char *name, int version); + +int iso_filename_len(const char *name, int iso_level); + +/* replacement for strlen for joliet names (wcslen doesn't work on machines + * with 32-bit wchar_t */ +size_t ucslen(const uint16_t *); + +/* replacement for strcmp for joliet names */ +int ucscmp(const uint16_t*, const uint16_t*); + +#define DIV_UP(n,div) ( ((n)+(div)-1) / (div) ) +#define ROUND_UP(n,mul) ( DIV_UP(n,mul) * (mul) ) + +#endif /* __ISO_UTIL */ diff --git a/trunk/libisofs/volume.c b/trunk/libisofs/volume.c new file mode 100755 index 00000000..15a86b44 --- /dev/null +++ b/trunk/libisofs/volume.c @@ -0,0 +1,72 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set ts=8 sts=8 sw=8 noet : */ + +#include + +#include "libisofs.h" +#include "tree.h" +#include "util.h" +#include "volume.h" +#include + +struct iso_volset *iso_volset_new(struct iso_volume *vol, const char *id) +{ + struct iso_volset *volset = calloc(1, sizeof(struct iso_volset)); + + volset->volset_size = 1; + volset->volume = malloc(sizeof(void *)); + volset->volume[0] = vol; + volset->volset_id.cstr = strdup(id); + volset->volset_id.jstr = iso_j_str(id); + return volset; +} + +struct iso_volume *iso_volume_new(const char *volume_id, + const char *publisher_id, + const char *data_preparer_id) +{ + struct iso_volume *volume; + + volume = calloc(1, sizeof(struct iso_volume)); + volume->refcount = 1; + + /* Get a new root directory. */ + volume->root = iso_tree_new_root(volume); + + /* Set these fields, if given. */ + if (volume_id != NULL) { + volume->volume_id.cstr = strdup(volume_id); + volume->volume_id.jstr = iso_j_str(volume_id); + } + if (publisher_id != NULL) { + volume->publisher_id.cstr = strdup(publisher_id); + volume->publisher_id.jstr = iso_j_str(publisher_id); + } + if (data_preparer_id != NULL) { + volume->data_preparer_id.cstr = strdup(data_preparer_id); + volume->data_preparer_id.jstr = iso_j_str(data_preparer_id); + } + return volume; +} + +void iso_volume_free(struct iso_volume *volume) +{ + /* Only free if no references are in use. */ + if (--volume->refcount < 1) { + iso_tree_free(volume->root); + + free(volume->volume_id.cstr); + free(volume->volume_id.jstr); + free(volume->publisher_id.cstr); + free(volume->publisher_id.jstr); + free(volume->data_preparer_id.cstr); + free(volume->data_preparer_id.jstr); + + free(volume); + } +} + +struct iso_tree_dir *iso_volume_get_root(const struct iso_volume *volume) +{ + return volume->root; +} diff --git a/trunk/libisofs/volume.h b/trunk/libisofs/volume.h new file mode 100755 index 00000000..db53affa --- /dev/null +++ b/trunk/libisofs/volume.h @@ -0,0 +1,55 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet sts=8 ts=8 sw=8 : */ + +/** + * Extra declarations for use with the iso_volume structure. + */ + +#ifndef __ISO_VOLUME +#define __ISO_VOLUME + +#include "libisofs.h" + +struct iso_string +{ + char *cstr; + uint16_t *jstr; +}; + +/** + * Data volume. + */ +struct iso_volume +{ + int refcount; /**< Number of used references to th + volume. */ + + struct iso_tree_dir *root; /**< Root of the directory tree for the + volume. */ + + unsigned rockridge:1; + unsigned joliet:1; + unsigned iso_level:2; + + struct iso_string volume_id; /**< Volume identifier. */ + struct iso_string publisher_id; /**< Volume publisher. */ + struct iso_string data_preparer_id; /**< Volume data preparer. */ +}; + +/** + * A set of data volumes. + */ +struct iso_volset +{ + int refcount; + + struct iso_volume **volume; /**< The volumes belonging to this + volume set. */ + int volset_size; /**< The number of volumes in this + volume set. */ + + struct iso_string volset_id; /**< The id of this volume set, encoded + in the current locale. */ +}; + +#endif /* __ISO_VOLUME */ diff --git a/trunk/libisofs/writer.c b/trunk/libisofs/writer.c new file mode 100755 index 00000000..2ed2e19a --- /dev/null +++ b/trunk/libisofs/writer.c @@ -0,0 +1,1178 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "writer.h" +#include "util.h" +#include "volume.h" +#include +#include +#include +#include +#include + +#define APPLICATION_ID "LIBBURN SUITE (C) 2006 M.DANIC/L.BIDDELL/A.NARAYANAN" + +enum DirType { + DIR_TYPE_SELF, + DIR_TYPE_PARENT, + DIR_TYPE_NORMAL, + DIR_TYPE_ROOT /* the dir record in the volume descriptor */ +}; + +enum SUSPType { + SUSP_TYPE_SP = 1 << 0, + SUSP_TYPE_CE = 1 << 1, + SUSP_TYPE_NM = 1 << 2 +}; + +static int get_path_table_size(struct iso_tree_dir **ddir); +static void iso_next_state(struct iso_write_target *target); +static int iso_source_get_size(struct burn_source *src); +static void iso_source_free(struct burn_source *src); +static void iso_target_layout(struct iso_write_target *target); +static void iso_dir_layout(struct iso_write_target *target, + struct iso_tree_dir **dir); +static void iso_file_layout(struct iso_write_target *target, + struct iso_tree_dir **dir); +static int iso_write_system_area(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err); +static int iso_write_pri_volume(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err); +static int iso_write_volume_terminator(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err); +static int write_path_table_record(unsigned char *buffer, + struct iso_tree_dir **ddir, int *position, + int lsb); +static int write_path_table_records(unsigned char *buffer, + struct iso_tree_dir **ddir, + int level, int *position, int lsb); +static int iso_write_path_table(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err, int lsb); +static int iso_write_dir_record(struct iso_write_target *target, + unsigned char *buffer, + struct iso_tree_dir **dir, enum DirType type); +static int iso_write_file_record(struct iso_write_target *t, + unsigned char *buffer, + struct iso_tree_file **ffile, + enum DirType type); +static void iso_write_file_id(unsigned char *buf, int size, + struct iso_tree_file **f); +static struct iso_tree_dir **find_dir_at_block(struct iso_tree_dir **ddir, + int block); +static struct iso_tree_file **find_file_at_block(struct iso_tree_dir **ddir, + int block); +static void write_child_records(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err, + struct iso_tree_dir *dir); +static int iso_write_dir_records(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err); +static int get_directory_record_length(const char *isoname); +static int iso_write_er_area(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err); +static int copy_file_to_buffer(FILE * fd, unsigned char *buffer, int length); +static int iso_write_files(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err); + +int get_path_table_size(struct iso_tree_dir **ddir) +{ + const char *isoname; + int i, size = 0; + struct iso_tree_dir *dir = *ddir; + + assert(dir); + + isoname = iso_tree_dir_get_name(ddir, ISO_FILE_NAME_ISO); + + if (isoname) { + /* a path table record is 8 bytes + the length of the + directory identifier */ + size += (8 + strlen(isoname)); + } else { + /* if iso_tree_dir_get_name is NULL, this is the root dir and + will have a path record of 10 bytes */ + size += 10; + } + + /* pad the field if the directory identifier is an odd number of + bytes */ + if (size % 2) + ++size; + + for (i = 0; i < dir->nchildren; i++) + size += get_path_table_size(dir->children[i].me); + + return size; +} + +struct burn_source *iso_source_new(struct iso_volumeset *volumeset, int volume) +{ + struct burn_source *src; + struct iso_write_target *t; + + if (!volumeset) + return NULL; + if (volume >= volumeset->volumeset_size) + return NULL; + + t = malloc(sizeof(struct iso_write_target)); + + iso_tree_sort(volumeset->root); + + t->volset = volumeset; + t->volset->refcount++; + t->volume = volume; + t->now = time(NULL); + t->phys_sector_size = 2048; + iso_target_layout(t); + + t->state = ISO_WRITE_BEFORE; + iso_next_state(t); /* prepare for the first state */ + + src = malloc(sizeof(struct burn_source)); + src->refcount = 1; + src->free_data = iso_source_free; + src->read = iso_source_generate; + src->read_sub = NULL; + src->get_size = iso_source_get_size; + src->data = t; + + return src; +} + +void iso_source_free(struct burn_source *src) +{ + struct iso_write_target *target = src->data; + + assert(src->read == iso_source_generate && + src->read_sub == NULL && src->get_size == iso_source_get_size); + + iso_volumeset_free(target->volset); + free(target); + + return; +} + +static int get_susp_length(struct RRInfo *info, enum SUSPType fields) +{ + int len = 0; + + if (fields & SUSP_TYPE_SP) + len += 7; + if (fields & SUSP_TYPE_CE) + len += 28; + if (info->px[0] != 0) + len += info->px[2]; + if (info->tf) + len += info->tf[2]; + if (info->nm && (fields & SUSP_TYPE_NM)) + len += info->nm[2]; + + return len; +} + +int get_directory_record_length(const char *isoname) +{ + int size; + + /* size = the length of the filename + 33 bytes (9.1) */ + size = 33 + strlen(isoname); + /* if size is odd, pad it with a single byte (9.1.12) */ + if (size % 2) + ++size; + + return size; +} + +/* fill in the 'logical_block' and 'size' values for each directory */ +void iso_dir_layout(struct iso_write_target *target, + struct iso_tree_dir **ddir) +{ + int i, length, sectors, rr; + const char *name; + struct iso_tree_dir *dir = *ddir; + + rr = iso_volumeset_get_rr(target->volset); + /* Each directory record starts with a record pointing to itself + and another pointing to its parent; combined, they are 68 bytes. + If RR is in use, calculate the susp length as well. */ + length = 68; + if (rr) { + /* if this is the root dir recored, we need to include the + length of the CE and SP fields */ + if (ddir == target->volset->root) { + length += get_susp_length(&dir->rr, SUSP_TYPE_SP | SUSP_TYPE_CE); + length += get_susp_length(&dir->rr, 0); + } else { + length += get_susp_length(&dir->rr, 0); + length += get_susp_length(&dir->parent->rr, 0); + } + } + + /* calculate the length of this directory */ + name = iso_tree_dir_get_name(ddir, ISO_FILE_NAME_ISO); + if (name) + length += get_directory_record_length(name); + else + length += get_directory_record_length(""); + if (rr) + length += get_susp_length(&dir->rr, SUSP_TYPE_NM); + + for (i = 0; i < dir->nfiles; ++i) { + name = iso_tree_file_get_name(dir->files[i].me, + ISO_FILE_NAME_ISO); + length += get_directory_record_length(name); + if (rr) + length += get_susp_length(&dir->files[i].rr, SUSP_TYPE_NM); + } + + for (i = 0; i < dir->nchildren; ++i) { + name = iso_tree_dir_get_name(dir->children[i].me, + ISO_FILE_NAME_ISO); + length += get_directory_record_length(name); + if (rr) + length += get_susp_length(&dir->children[i].rr, SUSP_TYPE_NM); + } + + /* dir->size is the number of bytes needed to hold the directory + record for each file and subdirectory of 'dir', padded to use up + all of the bytes of a physical sector. */ + sectors = length / target->phys_sector_size; + if (length % target->phys_sector_size) + sectors++; + dir->size = sectors * target->phys_sector_size; + dir->logical_block = target->logical_block; + target->logical_block += sectors; + + /* now calculate the lengths of its children */ + for (i = 0; i < dir->nchildren; ++i) { + iso_dir_layout(target, dir->children[i].me); + } + + return; +} + +/* fill in the 'logical_block' and 'size' values for each file */ +void iso_file_layout(struct iso_write_target *target, + struct iso_tree_dir **ddir) +{ + int i, sectors; + struct iso_tree_dir *dir = *ddir; + + for (i = 0; i < dir->nfiles; ++i) { + dir->files[i].logical_block = target->logical_block; + sectors = dir->files[i].size / target->phys_sector_size; + if (dir->files[i].size % target->phys_sector_size) + sectors++; + /* ensure we move past this logical block if the file was empty */ + else if (!dir->files[i].size) + sectors++; + target->logical_block += sectors; + } + + for (i = 0; i < dir->nchildren; ++i) + iso_file_layout(target, dir->children[i].me); + + return; +} + +void iso_target_layout(struct iso_write_target *target) +{ + /* logical block numbering starts at 1, not 0 */ + target->logical_block = 1; + /* system area */ + target->logical_block += 16; + /* primary volume descriptor */ + target->logical_block++; + /* volume descriptor terminator */ + target->logical_block++; + + target->logical_block_size = 2048; + target->path_table_size = get_path_table_size(target->volset->root); + /* put a 1-block buffer before each path table */ + target->l_path_table_pos = 19; + target->logical_block += 2; + target->total_size += 2 * target->phys_sector_size; + /* put a 1-block buffer before each path table */ + target->m_path_table_pos = 21; + target->logical_block += 2; + + iso_dir_layout(target, target->volset->root); + /* iso_write_dir_records() puts a 1-block buffer after the directory + section, so increment this accordingly */ + target->logical_block++; + + /* the ce area only takes up one block */ + target->susp_er_pos = target->logical_block++; + + iso_file_layout(target, target->volset->root); + + target->volume_space_size = target->logical_block; + target->total_size = target->volume_space_size * target->phys_sector_size; + + return; +} + +int iso_source_get_size(struct burn_source *src) +{ + struct iso_write_target *target = src->data; + + assert(src->read == iso_source_generate && + src->read_sub == NULL && src->get_size == iso_source_get_size); + + return target->total_size; +} + +void iso_next_state(struct iso_write_target *target) +{ + switch (++target->state) { + case ISO_WRITE_BEFORE: + /* this should never occur */ + assert(0); + break; + case ISO_WRITE_SYSTEM_AREA: + target->state_data.system_area.sectors = 0; + break; + case ISO_WRITE_PRI_VOL_DESC: + target->total_size = target->logical_block - 1; + break; + case ISO_WRITE_VOL_DESC_TERMINATOR: + break; + case ISO_WRITE_L_PATH_TABLE: + target->state_data.path_tables.sectors = 0; + break; + case ISO_WRITE_M_PATH_TABLE: + target->state_data.path_tables.sectors = 0; + break; + case ISO_WRITE_DIR_RECORDS: + target->state_data.dir_records.sectors = 0; + break; + case ISO_WRITE_ER_AREA: + break; + case ISO_WRITE_FILES: + target->state_data.dir_records.sectors = 0; + break; + case ISO_WRITE_DONE: + break; + } + + return; +} + +int iso_source_generate(struct burn_source *src, unsigned char *buffer, + int size) +{ + int next = 0; + enum burn_source_status err = BURN_SOURCE_OK; + struct iso_write_target *target = src->data; + + assert(src->read == iso_source_generate && + src->read_sub == NULL && src->get_size == iso_source_get_size); + + /* make sure the app didn't fuck up badly. */ + if (size != target->phys_sector_size) + return BURN_SOURCE_FAILED; /* XXX better code */ + + /* make sure 'buffer' doesn't have anything in it */ + memset(buffer, 0, size); + + switch (target->state) { + case ISO_WRITE_BEFORE: + /* this should never occur */ + assert(0); + case ISO_WRITE_SYSTEM_AREA: + next = iso_write_system_area(target, buffer, &err); + break; + case ISO_WRITE_PRI_VOL_DESC: + /* set target->logical_block to the logical block containing + the root directory record */ + target->logical_block = 23; + next = iso_write_pri_volume(target, buffer, &err); + break; + case ISO_WRITE_VOL_DESC_TERMINATOR: + next = iso_write_volume_terminator(target, buffer, &err); + break; + case ISO_WRITE_L_PATH_TABLE: + next = iso_write_path_table(target, buffer, &err, 1); + break; + case ISO_WRITE_M_PATH_TABLE: + next = iso_write_path_table(target, buffer, &err, 0); + break; + case ISO_WRITE_DIR_RECORDS: + next = iso_write_dir_records(target, buffer, &err); + break; + case ISO_WRITE_ER_AREA: + next = iso_write_er_area(target, buffer, &err); + break; + case ISO_WRITE_FILES: + next = iso_write_files(target, buffer, &err); + break; + case ISO_WRITE_DONE: + err = BURN_SOURCE_EOF; + break; + } + + if (next) + iso_next_state(target); + + return err; +} + +/* writes 16 sectors of '0' */ +int iso_write_system_area(struct iso_write_target *t, + unsigned char *buffer, enum burn_source_status *err) +{ + struct iso_state_system_area *state = &t->state_data.system_area; + + memset(buffer, 0, t->phys_sector_size); + return (++state->sectors == 16); +} + +/* writes the primary volume descriptor */ +int iso_write_pri_volume(struct iso_write_target *t, + unsigned char *buffer, enum burn_source_status *err) +{ + /* volume descriptor type (8.4.1) */ + buffer[0] = 1; + /* standard identifier (8.4.2) */ + memcpy(&buffer[1], "CD001", 5); + /* volume descriptor version (8.4.3) */ + buffer[6] = 1; + /* unused field (8.4.4) */ + buffer[7] = 0; + /* system identifier (8.4.5) */ + /* XXX mkisofs puts "LINUX" here */ + memset(&buffer[8], ' ', 32); + /* volume identifier (8.4.6) */ + iso_d_strcpy(&buffer[40], 32, t->volset->volume_id[t->volume]); + /* unused field (8.4.7) */ + memset(&buffer[72], 0, 8); + /* volume space size (8.4.8) */ + iso_bb(&buffer[80], t->volume_space_size, 4); + /* unused field (8.4.9) */ + memset(&buffer[88], 0, 32); + /* volume set size (8.4.10) */ + iso_bb(&buffer[120], t->volset->volumeset_size, 2); + /* volume sequence number (8.4.11) */ + iso_bb(&buffer[124], t->volume + 1, 2); + /* logical block size (8.4.12) */ + iso_bb(&buffer[128], t->logical_block_size, 2); + /* path table size (8.4.13) */ + iso_bb(&buffer[132], t->path_table_size, 4); + /* location of occurance of type l path table (8.4.14) */ + iso_lsb(&buffer[140], t->l_path_table_pos, 4); + /* location of optional occurance of type l path table (8.4.15) */ + iso_lsb(&buffer[144], 0, 4); + /* location of occurance of type m path table (8.4.16) */ + iso_msb(&buffer[148], t->m_path_table_pos, 4); + /* location of optional occurance of type m path table (8.4.17) */ + iso_msb(&buffer[152], 0, 4); + /* directory record for root directory (8.4.18) */ + iso_write_dir_record(t, &buffer[156], t->volset->root, DIR_TYPE_ROOT); + /* volume set identifier (8.4.19) */ + iso_d_strcpy(&buffer[190], 128, t->volset->volumeset_id); + /* publisher identifier (8.4.20) */ + iso_a_strcpy(&buffer[318], 128, t->volset->publisher_id); + /* data preparer identifier (8.4.21) */ + iso_a_strcpy(&buffer[446], 128, t->volset->data_preparer_id); + /* application identifier (8.4.22) */ + iso_a_strcpy(&buffer[574], 128, APPLICATION_ID); + /* copyright file identifier (8.4.23) */ + iso_write_file_id(&buffer[702], 37, t->volset->copyright_file); + /* abstract file identifier (8.4.24) */ + iso_write_file_id(&buffer[739], 37, t->volset->abstract_file); + /* bibliographic file identifier (8.4.25) */ + iso_write_file_id(&buffer[776], 37, t->volset->bibliographic_file); + /* volume creation date and time (8.4.26) */ + /* XXX is this different for multisession? */ + iso_datetime_17(&buffer[813], t->now); + /* volume modification date and time (8.4.27) */ + iso_datetime_17(&buffer[830], t->now); + /* volume expiration date and time (8.4.28) */ + iso_datetime_17(&buffer[847], t->volset->expiration_time); + /* volume effective date and time (8.4.29) */ + iso_datetime_17(&buffer[864], + t->volset->effective_time == (time_t) - 1 ? + t->now : t->volset->effective_time); + /* file structure version (8.4.30) */ + buffer[881] = 1; + /* reserved for future standardization (8.4.31) */ + buffer[882] = 0; + /* application use (8.4.32) */ + /* XXX put something in here? does mkisofs? */ + memset(&buffer[883], 0, 512); + /* reserved for future standardization (8.4.33) */ + memset(&buffer[1395], 0, 653); + + return 1; +} + +void iso_write_file_id(unsigned char *buf, int size, struct iso_tree_file **f) +{ + if (!f) + memset(buf, ' ', size); + else + iso_filecpy(buf, size, + iso_tree_file_get_name(f, ISO_FILE_NAME_ISO), + (*f)->version); +} + +/* write the contents of the SP System Use Entry */ +/* SUSP-112 5.3 */ +static void fill_sp_sue(unsigned char *sp) +{ + sp[0] = 'S'; + sp[1] = 'P'; + sp[2] = 7; + sp[3] = 1; + sp[4] = 190; + sp[5] = 239; + sp[6] = 0; + + return; +} + +/* write the contents of the CE System Use Entry */ +/* SUSP-112 5.1 */ +static void fill_ce_sue(struct iso_write_target *t, + unsigned char *ce) +{ + ce[0] = 'C'; + ce[1] = 'E'; + ce[2] = 28; + ce[3] = 1; + iso_bb(&ce[4], 0, 4); + iso_bb(&ce[12], t->susp_er_pos, 4); + /* the length of the rock-ridge er area is 185, and that's all we + * store in the ce area right now */ + iso_bb(&ce[20], 185, 4); + + return; +} + +/* return a string containing the System Use Sharing Protocol information + * for 'ddir'. */ +static unsigned char* write_susp_info(struct iso_write_target *t, + struct RRInfo *rr, + enum SUSPType type, + int *len) +{ + unsigned char *buffer = NULL; + + *len = 0; + + /* check to see if susp is even needed (right now, only the rock-ridge + * extensions use it). */ + if (iso_volumeset_get_rr(t->volset)) { + int char_size, size = 0, offset = 0; + int len_sp, len_ce, len_px, len_tf, len_nm; + unsigned char *sp, *ce; + + char_size = sizeof (unsigned char *); + len_sp = len_ce = 0; + + if (type & SUSP_TYPE_SP) { + len_sp = 7; /* 7 bytes for the SP System Use Entry */ + sp = malloc(char_size * len_sp); + fill_sp_sue(sp); + size += len_sp; + } else { + sp = NULL; + } + + if (type & SUSP_TYPE_CE) { + len_ce = 28; /* 28 bytes for the CE System Use Entry */ + ce = malloc(char_size * len_ce); + fill_ce_sue(t, ce); + size += len_ce; + } else { + ce = NULL; + } + + if (rr->px[0] != 0) { + /* the 3rd byte of the PX field holds its length */ + len_px = rr->px[2]; + size += len_px; + } else { + len_px = 0; + } + + if (rr->tf) { + /* the 3rd byte of the TF field holds its length */ + len_tf = rr->tf[2]; + size += len_tf; + } else { + len_tf = 0; + } + + if ((type & SUSP_TYPE_NM) && rr->nm) { + /* the 3rd byte of the NM field holds its length */ + len_nm = rr->nm[2]; + size += len_nm; + } else { + len_nm = 0; + } + + /* fill 'buffer' with the susp info */ + buffer = malloc(char_size * (size + 1)); + if (sp) { + memcpy(buffer + offset, sp, len_sp); + offset += len_sp; + } + if (ce) { + memcpy(buffer + offset, ce, len_ce); + offset += len_ce; + } + if (len_px) { + memcpy(buffer + offset, rr->px, len_px); + offset += len_px; + } + if (len_tf) { + memcpy(buffer + offset, rr->tf, len_tf); + offset += len_tf; + } + if (len_nm) { + memcpy(buffer + offset, rr->nm, len_nm); + offset += len_nm; + } + buffer[offset] = '\0'; + *len = offset; + } + + return buffer; +} + +int iso_write_dir_record(struct iso_write_target *t, + unsigned char *buffer, + struct iso_tree_dir **ddir, enum DirType type) +{ + int len_fi, len_susp, sz; + const char *fi = NULL; + unsigned char *susp; /* the contents, if any, of the "system use sharing protocol" buffer */ + struct iso_tree_dir *dir = *ddir; + + if (type != DIR_TYPE_NORMAL) + len_fi = 1; + else { + assert(dir->parent); + fi = iso_tree_dir_get_name(ddir, ISO_FILE_NAME_ISO); + len_fi = strlen(fi); + } + + /* determine if this is the root directory record */ + if ((*t->volset->root == dir) && (type == DIR_TYPE_SELF)) + susp = write_susp_info(t, &dir->rr, SUSP_TYPE_SP | SUSP_TYPE_CE, &len_susp); + /* determine if this a SELF or PARENT directory record */ + else if (type != DIR_TYPE_NORMAL) + susp = write_susp_info(t, &dir->rr, 0, &len_susp); + /* none of the above == a normal directory record */ + else + susp = write_susp_info(t, &dir->rr, SUSP_TYPE_NM, &len_susp); + + sz = 33 + len_fi + len_susp + !(len_fi % 2); + + /* length of directory record (9.1.1) */ + buffer[0] = sz; + /* extended attribute record length (9.1.2) */ + buffer[1] = 0; /* XXX allow for extended attribute records */ + /* location of extent (9.1.3) */ + iso_bb(&buffer[2], dir->logical_block, 4); + /* data length (9.1.4) */ + iso_bb(&buffer[10], dir->size, 4); + /* recording date and time (9.1.5) */ + iso_datetime_7(&buffer[18], t->now); + /* file flags (9.1.6) */ + buffer[25] = ISO_FILE_FLAG_DIRECTORY; + /* file unit size (9.1.7) */ + buffer[26] = 0; /* XXX support this shit? */ + /* interleave gap size (9.1.8) */ + buffer[27] = 0; /* XXX support this shit? */ + /* volume sequence number (9.1.9) */ + iso_bb(&buffer[28], dir->volume + 1, 2); + /* length of file identifier (9.1.10) */ + buffer[32] = len_fi; + /* file identifier */ + switch (type) { + case DIR_TYPE_ROOT: + case DIR_TYPE_SELF: + buffer[33] = 0; + break; + case DIR_TYPE_PARENT: + buffer[33] = 1; + break; + case DIR_TYPE_NORMAL: + iso_d_strcpy(&buffer[33], len_fi, fi); + break; + } + if (!(len_fi % 2)) + buffer[33 + len_fi] = 0; + /* system use sharing protocol */ + if (susp) + { + int offset; + if (!(len_fi % 2)) + offset = 34; + else + offset = 33; + memcpy(&buffer[offset + len_fi], susp, len_susp); + } + + return sz; +} + +/* write the file record as per (9.1) */ +int iso_write_file_record(struct iso_write_target *t, + unsigned char *buffer, + struct iso_tree_file **ffile, enum DirType type) +{ + int len_fi, len_susp; + const char *fi = NULL; + unsigned char *susp; /* the contents, if any, of the "system use sharing protocol" buffer */ + int sz; + struct iso_tree_file *file = *ffile; + + if (type != DIR_TYPE_NORMAL) + len_fi = 1; + else { + fi = iso_tree_file_get_name(ffile, ISO_FILE_NAME_ISO); + len_fi = strlen(fi); + } + + susp = write_susp_info(t, &file->rr, SUSP_TYPE_NM, &len_susp); + + sz = 33 + len_fi + len_susp + !(len_fi % 2); + + /* length of directory record (9.1.1) */ + buffer[0] = sz; + /* extended attribute record length (9.1.2) */ + buffer[1] = 0; /* XXX allow for extended attribute records */ + /* location of extent (9.1.3) */ + iso_bb(&buffer[2], file->logical_block, 4); + /* data length (9.1.4) */ + iso_bb(&buffer[10], file->size, 4); + /* recording date and time (9.1.5) */ + iso_datetime_7(&buffer[18], t->now); + /* file flags (9.1.6) */ + buffer[25] = ISO_FILE_FLAG_NORMAL; + /* file unit size (9.1.7) */ + buffer[26] = 0; /* XXX support this shit? */ + /* interleave gap size (9.1.8) */ + buffer[27] = 0; /* XXX support this shit? */ + /* volume sequence number (9.1.9) */ + iso_bb(&buffer[28], file->volume + 1, 2); + /* length of file identifier (9.1.10) */ + buffer[32] = len_fi; + /* file identifier */ + switch (type) { + case DIR_TYPE_ROOT: + case DIR_TYPE_SELF: + buffer[33] = 0; + break; + case DIR_TYPE_PARENT: + buffer[33] = 1; + break; + case DIR_TYPE_NORMAL: + memcpy(&buffer[33], fi, len_fi); + break; + } + if (!(len_fi % 2)) + buffer[33 + len_fi] = 0; + /* system use sharing protocol */ + if (susp) + { + int offset; + if (!(len_fi % 2)) + offset = 34; + else + offset = 33; + memcpy(&buffer[offset + len_fi], susp, len_susp); + } + + return sz; +} + +/* writes the volume descriptor set terminator */ +int iso_write_volume_terminator(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err) +{ + /* volume descriptor type (8.3.1) */ + buffer[0] = 255; + /* standard identifier (8.3.2) */ + memcpy(&buffer[1], "CD001", 5); + /* volume descriptor version (8.3.3) */ + buffer[6] = 1; + /* reserved for future standardization (8.3.4) */ + memset(&buffer[7], 0, 2041); + + return 1; +} + +/* write the path record for 'ddir' into 'buffer' */ +int write_path_table_record(unsigned char *buffer, + struct iso_tree_dir **ddir, int *position, int lsb) +{ + int len, bytes_written; + short parent_position; + const char *name; + struct iso_tree_dir *dir = *ddir; + + /* set the position in the path table of this directory */ + dir->position = *position; + /* increment the position for the next path table record */ + *position += 1; + if (dir->parent) + parent_position = dir->parent->position; + else + parent_position = 1; + + name = iso_tree_dir_get_name(ddir, ISO_FILE_NAME_ISO); + if (name) + len = strlen(name); + else + len = 1; + + /* the directory identifier length (9.4.1) */ + buffer[0] = len; + /* the extended attribute record length (9.4.2) */ + buffer[1] = 0; + /* location of extent (9.4.3) */ + if (lsb) + iso_lsb(&buffer[2], dir->logical_block, 4); + else + iso_msb(&buffer[2], dir->logical_block, 4); + /* parent directory record (9.4.4) */ + if (lsb) + iso_lsb(&buffer[6], parent_position, 2); + else + iso_msb(&buffer[6], parent_position, 2); + /* the directory identifier (9.4.5) */ + if (name) + memcpy(&buffer[8], name, len); + else + memset(&buffer[8], 0, len); + /* padding field (9.4.6) */ + if (len % 2) { + bytes_written = 9 + len; + buffer[bytes_written] = 0; + } else + bytes_written = 8 + len; + + return bytes_written; +} + +/* recursive function used to write the path records for each level + * of the file system sequentially, i.e. root, then all children of + * root, then all grandchildren of root, then all great-grandchildren + * of root, etc. */ +int write_path_table_records(unsigned char *buffer, + struct iso_tree_dir **ddir, + int level, int *position, int lsb) +{ + int i, offset; + struct iso_tree_dir *dir = *ddir; + + /* ISO9660 only allows directories to be nested 8-deep */ + if (level >= 8) + return 0; + + if (!level) { + offset = write_path_table_record(buffer, ddir, position, lsb); + } else { + offset = 0; + for (i = 0; i < dir->nchildren; ++i) { + offset += write_path_table_records(buffer + offset, + dir->children[i].me, + level - 1, + position, lsb); + } + } + + return offset; +} + +/* writes set of path table records */ +int iso_write_path_table(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err, int lsb) +{ + int i, offset, position; + struct iso_state_path_tables *state = &t->state_data.path_tables; + + if (state->sectors) { + offset = 0; + position = 1; + + for (i = 0; i < 8; ++i) { + offset += write_path_table_records(buffer + offset, + t->volset->root, + i, &position, lsb); + } + } else { + /* empty buffer sector */ + memset(buffer, 0, t->phys_sector_size); + } + + return (++state->sectors == 2); +} + +struct iso_tree_dir **find_dir_at_block(struct iso_tree_dir **ddir, int block) +{ + int i; + struct iso_tree_dir *dir = *ddir; + struct iso_tree_dir **to_write = NULL; + + if (dir->logical_block == block) + to_write = ddir; + + for (i = 0; (i < dir->nchildren) && !to_write; ++i) { + to_write = find_dir_at_block(dir->children[i].me, block); + } + + return to_write; +} + +struct iso_tree_file **find_file_at_block(struct iso_tree_dir **ddir, int block) +{ + int i; + struct iso_tree_dir *dir = *ddir; + struct iso_tree_file **to_write = NULL; + + for (i = 0; (i < dir->nfiles) && !to_write; ++i) { + if (dir->files[i].logical_block == block) + to_write = dir->files[i].me; + } + + for (i = 0; (i < dir->nchildren) && !to_write; ++i) { + to_write = find_file_at_block(dir->children[i].me, block); + } + + return to_write; +} + +/* write the records for children of 'dir' */ +void write_child_records(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err, struct iso_tree_dir *dir) +{ + int file_counter, dir_counter, order, offset; + + file_counter = dir_counter = offset = 0; + + while ((file_counter < dir->nfiles) || (dir_counter < dir->nchildren)) { + /* if there are files and dirs, compare them to figure out + which comes 1st alphabetically */ + if ((file_counter < dir->nfiles) && + (dir_counter < dir->nchildren)) { + const char *dirname, *filename; + + dirname = iso_tree_dir_get_name + (dir->children[dir_counter].me, + ISO_FILE_NAME_ISO); + filename = iso_tree_file_get_name + (dir->files[file_counter].me, + ISO_FILE_NAME_ISO); + order = strcmp(dirname, filename); + } else { + if (file_counter < dir->nfiles) + /* only files are left */ + order = 1; + else + /* only dirs are left */ + order = -1; + } + + if (order < 0) { + offset += iso_write_dir_record(t, + buffer + offset, + dir-> + children[dir_counter]. + me, DIR_TYPE_NORMAL); + dir_counter++; + } else { + offset += iso_write_file_record(t, + buffer + offset, + dir-> + files[file_counter].me, + DIR_TYPE_NORMAL); + file_counter++; + } + + } + + return; +} + +/* write out the next directory record */ +int iso_write_dir_records(struct iso_write_target *t, + unsigned char *buffer, enum burn_source_status *err) +{ + int finished, offset; + struct iso_tree_dir **ddir, *dir; + struct iso_state_dir_records *state = &t->state_data.dir_records; + + finished = 0; /* set to 1 when all directory records have + been written */ + + if (state->sectors++) { + /* t->logical_block is the next block to write. find the dir + record which belongs there and write it out. if none + exists, we're done writing the directory records. */ + + ddir = find_dir_at_block(t->volset->root, t->logical_block); + + if (ddir) { + /* 1) write the record for this directory 2) write the + record for the parent directory 3) write the + records for all child files and directories */ + dir = *ddir; + offset = iso_write_dir_record(t, + buffer, + ddir, DIR_TYPE_SELF); + if (!dir->parent) { + /* this is the root directory */ + offset += iso_write_dir_record(t, + buffer + offset, + ddir, + DIR_TYPE_PARENT); + } else { + offset += iso_write_dir_record(t, + buffer + offset, + dir->parent->me, + DIR_TYPE_PARENT); + } + + write_child_records(t, buffer + offset, err, dir); + + /* dir->size should always be a multiple of + t->phys_sector_size */ + t->logical_block += (dir->size / t->phys_sector_size); + } else { + /* we just wrote out 2048 0's, so we still need to + increment this */ + t->logical_block++; + finished = 1; + } + } + + return finished; +} + +/* write the 'ER' area of the disc (RRIP-112: 4.3) */ +int iso_write_er_area(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err) +{ + if (iso_volumeset_get_rr(t->volset)) { + char *text; + int len; + + buffer[0] = 'E'; + buffer[1] = 'R'; + buffer[2] = 237; + buffer[3] = 1; + buffer[4] = 10; + buffer[5] = 84; + buffer[6] = 135; + buffer[7] = 1; + text = strdup("" + "RRIP_1991ATHE ROCK RIDGE INTERCHANGE PROTOCOL " + "PROVIDES SUPPORT FOR POSIX FILE SYSTEM " + "SEMANTICSPLEASE CONTACT DISC PUBLISHER FOR " + "SPECIFICATION SOURCE. SEE PUBLISHER IDENTIFIER IN " + "PRIMARY VOLUME DESCRIPTOR FOR CONTACT INFORMATION"); + len = strlen (text); + + memcpy (buffer + 8, text, len); + free (text); + + /* next logical block... */ + t->logical_block++; + } + + return 1; +} + +int copy_file_to_buffer(FILE * fd, unsigned char *buffer, int length) +{ + int read; + + read = fread(buffer, 1, length, fd); + + return read; +} + +int iso_write_files(struct iso_write_target *t, + unsigned char *buffer, enum burn_source_status *err) +{ + FILE *fd; + int finished, sectors; + struct iso_tree_file **ffile, *file; + struct iso_state_files *state = &t->state_data.files; + + finished = 0; + + if (state->sectors) { + /* continue writing the file at 't->logical_block' to + 'buffer', then incremenet 't->logical_block' to the next + file section. */ + + fd = t->current_fd; + file = *(t->current_file); + + /* copy one sector of 'ffile' into 'buffer' */ + state->sectors += + copy_file_to_buffer(fd, buffer, t->phys_sector_size); + + if (feof(fd)) { + fclose(fd); + state->sectors = 0; + sectors = file->size / t->phys_sector_size; + if (file->size % t->phys_sector_size) + sectors++; + t->logical_block += sectors; + } + } else { + /* start writing the file at 't->logical_block' to 'buffer'. + if none exists, we are done. */ + ffile = find_file_at_block(t->volset->root, t->logical_block); + if (ffile) { + t->current_file = ffile; + file = *ffile; + fd = fopen(file->path, "r"); + t->current_fd = fd; + + if (fd) { + state->sectors += copy_file_to_buffer(fd, + buffer, + t-> + phys_sector_size); + } + + if (feof(fd)) { + fclose(fd); + fd = NULL; + } + + if (!fd) { + state->sectors = 0; + sectors = file->size / t->phys_sector_size; + if (file->size % t->phys_sector_size) + sectors++; + /* ensure we move past this logical block if the file was empty */ + else if (!file->size) + sectors++; + t->logical_block += sectors; + } + } else + finished = 1; + } + + return finished; +} diff --git a/trunk/libisofs/writer.h b/trunk/libisofs/writer.h new file mode 100755 index 00000000..018fd01d --- /dev/null +++ b/trunk/libisofs/writer.h @@ -0,0 +1,103 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __WRITER +#define __WRITER + +#include "libisofs.h" + +#include +#include +#include + +enum iso_write_state +{ + ISO_WRITE_BEFORE, + + ISO_WRITE_SYSTEM_AREA, + ISO_WRITE_PRI_VOL_DESC, + ISO_WRITE_VOL_DESC_TERMINATOR, + ISO_WRITE_L_PATH_TABLE, + ISO_WRITE_M_PATH_TABLE, + ISO_WRITE_DIR_RECORDS, + ISO_WRITE_ER_AREA, + ISO_WRITE_FILES, + + ISO_WRITE_DONE +}; + +/** File Flags (9.1.6) */ +enum +{ + ISO_FILE_FLAG_NORMAL = 0, + ISO_FILE_FLAG_HIDDEN = 1 << 0, + ISO_FILE_FLAG_DIRECTORY = 1 << 1, + ISO_FILE_FLAG_ASSOCIATED = 1 << 2, + ISO_FILE_FLAG_RECORD = 1 << 3, + ISO_FILE_FLAG_PROTECTION = 1 << 4, + ISO_FILE_FLAG_MULTIEXTENT = 1 << 7 +}; + +struct iso_write_target +{ + struct iso_volumeset *volset; + int volume; + + /* the time at which the writing began */ + time_t now; + + /* size of a physical sector on the target disc */ + int phys_sector_size; + /* size of the total output */ + int total_size; + + /* when compiling the iso, this is the next available logical block. + when writing the iso, this is the next block to write. */ + int logical_block; + /* The number of Logical Blocks for the Volume Space */ + int volume_space_size; + /* The Logical Block size */ + int logical_block_size; + /* The Path Table size */ + int path_table_size; + /* Locations of Type L Path Table (Logical Block Number) */ + int l_path_table_pos; + /* Locations of Type M Path Table (Logical Block Number) */ + int m_path_table_pos; + /* Location of the SUSP ER area (Logical Block Number) */ + int susp_er_pos; + /* Current file being written in iso_write_files() */ + struct iso_tree_file **current_file; + FILE *current_fd; + + /* what we're doing when the generate function gets called next */ + enum iso_write_state state; + union + { + struct iso_state_system_area + { + /* how many sectors in the system area have been + written */ + int sectors; + } system_area; + struct iso_state_path_tables + { + /* how many sectors in the path table area have been + written */ + int sectors; + } path_tables; + struct iso_state_dir_records + { + /* how many sectors in the directory records area have + been written */ + int sectors; + } dir_records; + struct iso_state_files + { + /* how many sectors in the current file have been + written */ + int sectors; + } files; + } state_data; +}; + +#endif /* __WRITER */ diff --git a/trunk/test/Makefile b/trunk/test/Makefile new file mode 100644 index 00000000..062350dd --- /dev/null +++ b/trunk/test/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/trunk/test/blank.c b/trunk/test/blank.c new file mode 100644 index 00000000..5f294438 --- /dev/null +++ b/trunk/test/blank.c @@ -0,0 +1,100 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include + +#include +#include +#include +#include +#include + +static struct burn_drive_info *drives; +static unsigned int n_drives; + +static void blank_disc(struct burn_drive *drive) +{ + enum burn_disc_status s; + struct burn_progress p; + + if (!burn_drive_grab(drive, 1)) { + fprintf(stderr, "Unable to open the drive!\n"); + return; + } + + while (burn_drive_get_status(drive, NULL)) { + usleep(1000); + } + + while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY) + usleep(1000); + printf("%d\n", s); + if (s != BURN_DISC_FULL) { + burn_drive_release(drive, 0); + fprintf(stderr, "No disc found!\n"); + return; + } + + fprintf(stderr, "Blanking disc..."); + burn_disc_erase(drive, 1); + + while (burn_drive_get_status(drive, &p)) { + printf("%d\n", p.sector); + sleep(2); + } + fprintf(stderr, "Done\n"); + + burn_drive_release(drive, 0); +} + +void parse_args(int argc, char **argv, int *drive) +{ + int i; + int help = 0; + + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--drive")) { + ++i; + if (i >= argc) + printf("--drive requires an argument\n"); + else + *drive = atoi(argv[i]); + } else if (!strcmp(argv[i], "--verbose")) { + ++i; + if (i >= argc) + printf("--verbose requires an argument\n"); + else + burn_set_verbosity(atoi(argv[i])); + } else if (!strcmp(argv[i], "--help")) { + help = 1; + } + } + if (help) { + printf("Usage: %s [--drive ] [--verbose ]\n", + argv[0]); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char **argv) +{ + int drive = 0; + + parse_args(argc, argv, &drive); + + fprintf(stderr, "Initializing library..."); + if (burn_initialize()) + fprintf(stderr, "Success\n"); + else { + printf("Failed\n"); + return 1; + } + + fprintf(stderr, "Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + fprintf(stderr, "Done\n"); + + blank_disc(drives[drive].drive); + burn_drive_info_free(drives); + burn_finish(); + return 0; +} diff --git a/trunk/test/burn.c b/trunk/test/burn.c new file mode 100644 index 00000000..a46866d9 --- /dev/null +++ b/trunk/test/burn.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/trunk/test/burniso.c b/trunk/test/burniso.c new file mode 100644 index 00000000..b0c6c4cf --- /dev/null +++ b/trunk/test/burniso.c @@ -0,0 +1,147 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct burn_drive_info *drives; +static unsigned int n_drives; + +void burn_iso(struct burn_drive *drive, const char *path, off_t size) +{ + struct burn_source *src; + struct burn_disc *disc; + struct burn_session *session; + struct burn_write_opts *o; + enum burn_disc_status s; + struct burn_track *tr; + struct burn_progress p; + + disc = burn_disc_create(); + session = burn_session_create(); + burn_disc_add_session(disc, session, BURN_POS_END); + tr = burn_track_create(); + burn_track_define_data(tr, 0, 0, 0, BURN_MODE1); + if (path[0] == '-' && path[1] == 0) { + src = burn_fd_source_new(0, -1, size); + printf("Note: using standard input as source with %.f bytes\n", + (double) size); + } else + src = burn_file_source_new(path, NULL); + assert(src); + + if (burn_track_set_source(tr, src) != BURN_SOURCE_OK) { + printf("problem with the source\n"); + return; + } + burn_session_add_track(session, tr, BURN_POS_END); + burn_source_free(src); + + if (!burn_drive_grab(drive, 1)) { + printf("Unable to open the drive!\n"); + return; + } + while (burn_drive_get_status(drive, NULL)) + usleep(1000); + + while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY) + usleep(1000); + + if (s != BURN_DISC_BLANK) { + burn_drive_release(drive, 0); + printf("put a blank in the drive, corky\n"); + return; + } + o = burn_write_opts_new(drive); + burn_write_opts_set_perform_opc(o, 0); + burn_write_opts_set_write_type(o, BURN_WRITE_RAW, BURN_BLOCK_RAW96R); + burn_write_opts_set_simulate(o, 1); + + burn_structure_print_disc(disc); + burn_drive_set_speed(drive, 0, 0); + burn_disc_write(o, disc); + burn_write_opts_free(o); + + while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING) ; + while (burn_drive_get_status(drive, &p)) { + printf("S: %d/%d ", p.session, p.sessions); + printf("T: %d/%d ", p.track, p.tracks); + printf("L: %d: %d/%d\n", p.start_sector, p.sector, + p.sectors); + sleep(2); + } + printf("\n"); + burn_drive_release(drive, 0); + burn_track_free(tr); + burn_session_free(session); + burn_disc_free(disc); +} + +void parse_args(int argc, char **argv, int *drive, char **iso, off_t *size) +{ + int i; + int help = 0; + + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--drive")) { + ++i; + if (i >= argc) + printf("--drive requires an argument\n"); + else + *drive = atoi(argv[i]); + } else if (!strcmp(argv[i], "--stdin_size")) { + ++i; + if (i >= argc) + printf("--stdin_size requires an argument\n"); + else + *size = atoi(argv[i]); + } else if (!strcmp(argv[i], "--verbose")) { + ++i; + if (i >= argc) + printf("--verbose requires an argument\n"); + else + burn_set_verbosity(atoi(argv[i])); + } else if (!strcmp(argv[i], "--help")) { + help = 1; + } else + *iso = argv[i]; + } + if (help || !*iso) { + printf("Usage: %s [--drive ] [--stdin_size ] [--verbose ] isofile|-\n", argv[0]); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char **argv) +{ + int drive = 0; + char *iso = NULL; + off_t stdin_size= 650*1024*1024; + + parse_args(argc, argv, &drive, &iso, &stdin_size); + + printf("Initializing library..."); + if (burn_initialize()) + printf("Success\n"); + else { + printf("Failed\n"); + return 1; + } + + printf("Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + printf("Done\n"); + + burn_iso(drives[drive].drive, iso, stdin_size); + + burn_drive_info_free(drives); + burn_finish(); + return 0; +} diff --git a/trunk/test/devices.c b/trunk/test/devices.c new file mode 100644 index 00000000..6f3fdd59 --- /dev/null +++ b/trunk/test/devices.c @@ -0,0 +1,39 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include + +#include + +static struct burn_drive_info *drives; +static unsigned int n_drives; + +static void show_devices() +{ + unsigned int i; + + fprintf(stderr, "Devices: (%u found)\n", n_drives); + for (i = 0; i < n_drives; ++i) { + fprintf(stderr, "%s - %s %s\n", drives[i].location, + drives[i].vendor, drives[i].product); + } +} + +int main() +{ + fprintf(stderr, "Initializing library..."); + if (burn_initialize()) + fprintf(stderr, "Success\n"); + else { + printf("Failed\n"); + return 1; + } + + fprintf(stderr, "Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + fprintf(stderr, "Done\n"); + + show_devices(); + burn_drive_info_free(drives); + burn_finish(); + return 0; +} diff --git a/trunk/test/iso.c b/trunk/test/iso.c new file mode 100644 index 00000000..ff740999 --- /dev/null +++ b/trunk/test/iso.c @@ -0,0 +1,138 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set ts=8 sts=8 sw=8 noet : */ + +#define _GNU_SOURCE + +#include "libisofs/libisofs.h" +#include "libburn/libburn.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SECSIZE 2048 + +const char * const optstring = "JRL:h"; +extern char *optarg; +extern int optind; + +void usage() +{ + printf("test [OPTIONS] DIRECTORY OUTPUT\n"); +} + +void help() +{ + printf( +"Options:\n" +" -J Add Joliet support\n" +" -R Add Rock Ridge support\n" +" -L Set the ISO level (1 or 2)\n" +" -h Print this message\n" +); +} + +int main(int argc, char **argv) +{ + struct iso_volset *volset; + struct iso_volume *volume; + struct burn_source *src; + unsigned char buf[2048]; + FILE *fd; + int c; + int level=1, flags=0; + DIR *dir; + struct dirent *ent; + + while ((c = getopt(argc, argv, optstring)) != -1) { + switch(c) { + case 'h': + usage(); + help(); + exit(0); + break; + case 'J': + flags |= ECMA119_JOLIET; + break; + case 'R': + flags |= ECMA119_ROCKRIDGE; + break; + case 'L': + level = atoi(optarg); + break; + case '?': + usage(); + exit(1); + break; + } + } + + if (argc < 2) { + printf ("must pass directory to build iso from\n"); + usage(); + return 1; + } + if (argc < 3) { + printf ("must supply output file\n"); + usage(); + return 1; + } + fd = fopen(argv[optind+1], "w"); + if (!fd) { + perror("error opening output file"); + exit(1); + } + + volume = iso_volume_new( "VOLID", "PUBID", "PREPID" ); + volset = iso_volset_new( volume, "VOLSETID" ); + dir = opendir(argv[optind]); + if (!dir) { + perror("error opening input directory"); + exit(1); + } + + while ( (ent = readdir(dir)) ) { + struct stat st; + char *name; + + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) { + continue; + } + + name = malloc(strlen(argv[optind]) + strlen(ent->d_name) + 2); + strcpy(name, argv[optind]); + strcat(name, "/"); + strcat(name, ent->d_name); + if (lstat(name, &st) == -1) { + fprintf(stderr, "error opening file %s: %s\n", + name, strerror(errno)); + exit(1); + } + + if (S_ISDIR(st.st_mode)) { + iso_tree_radd_dir(iso_volume_get_root(volume), name); + } else { + iso_tree_add_file(iso_volume_get_root(volume), name); + } + free(name); + } + + iso_tree_print(iso_volume_get_root(volume), 0); + + src = iso_source_new_ecma119(volset, 0, level, flags); + + while (src->read(src, buf, 2048) == 2048) { + fwrite(buf, 1, 2048, fd); + } + fclose(fd); + + return 0; +} diff --git a/trunk/test/iso.py b/trunk/test/iso.py new file mode 100644 index 00000000..61da5fd7 --- /dev/null +++ b/trunk/test/iso.py @@ -0,0 +1,297 @@ + +import struct +import tree +import sys + +voldesc_fmt = "B" "5s" "B" "2041x" + +# all these fields are common between the pri and sec voldescs +privoldesc_fmt = "B" "5s" "B" "x" "32s" "32s" "8x" "8s" "32x" "4s" "4s" "4s" "8s" "4s4s" "4s4s" "34s" "128s" \ + "128s" "128s" "128s" "37s" "37s" "37s" "17s" "17s" "17s" "17s" "B" "x" "512s" "653x" + +# the fields unique to the sec_vol_desc +secvoldesc_fmt = "x" "5x" "x" "B" "32x" "32x" "8x" "8x" "32s" "4x" "4x" "4x" "8x" "4x4x" "4x4x" "34x" "128x" \ + "128x" "128x" "128x" "37x" "37x" "37x" "17x" "17x" "17x" "17x" "x" "x" "512x" "653x" + +dirrecord_fmt = "B" "B" "8s" "8s" "7s" "B" "B" "B" "4s" "B" # + file identifier, padding field and SU area + +pathrecord_fmt = "B" "B" "4s" "2s" # + directory identifier and padding field + +def read_bb(str, le, be): + val1, = struct.unpack(le, str) + val2, = struct.unpack(be, str) + if val1 != val2: + print "val1=%d, val2=%d" % (val1, val2) + raise AssertionError, "values are not equal in dual byte-order field" + return val1 + +def read_bb4(str): + return read_bb(str, "4xI") + +def read_bb2(str): + return read_bb(str, "2xH") + +def read_lsb4(str): + return struct.unpack("I", str)[0] + +def read_msb2(str): + return struct.unpack(">H", str)[0] + +class VolDesc(object): + def __init__(self, data): + print "fmt len=%d, data len=%d" % ( struct.calcsize(voldesc_fmt), len(data) ) + self.vol_desc_type, self.standard_id, self.vol_desc_version = struct.unpack(voldesc_fmt, data) + +class PriVolDesc(VolDesc): + def __init__(self, data): + self.vol_desc_type, \ + self.standard_id, \ + self.vol_desc_version, \ + self.system_id, \ + self.volume_id, \ + self.vol_space_size, \ + self.vol_set_size, \ + self.vol_seq_num, \ + self.block_size, \ + self.path_table_size, \ + self.l_table_pos, \ + self.l_table2_pos, \ + self.m_table_pos, \ + self.m_table2_pos, \ + self.root_record, \ + self.volset_id, \ + self.publisher_id, \ + self.preparer_id, \ + self.application_id, \ + self.copyright_file, \ + self.abstract_file, \ + self.bibliographic_file, \ + self.creation_timestamp, \ + self.modification_timestamp, \ + self.expiration_timestamp, \ + self.effective_timestamp, \ + self.file_struct_version, \ + self.application_use = struct.unpack(privoldesc_fmt, data) + + # take care of reading the integer types + self.vol_space_size = read_bb4(self.vol_space_size) + self.vol_set_size = read_bb2(self.vol_set_size) + self.vol_seq_num = read_bb2(self.vol_seq_num) + self.block_size = read_bb2(self.block_size) + self.path_table_size = read_bb4(self.path_table_size) + self.l_table_pos = read_lsb4(self.l_table_pos) + self.l_table2_pos = read_lsb4(self.l_table2_pos) + self.m_table_pos = read_msb4(self.m_table_pos) + self.m_table2_pos = read_msb4(self.m_table2_pos) + + # parse the root directory record + self.root_record = DirRecord(self.root_record) + + def readPathTables(self, file): + file.seek( self.block_size * self.l_table_pos ) + self.l_table = PathTable( file.read(self.path_table_size), 0 ) + file.seek( self.block_size * self.m_table_pos ) + self.m_table = PathTable( file.read(self.path_table_size), 1 ) + + if self.l_table2_pos: + file.seek( self.block_size * self.l_table2_pos ) + self.l_table2 = PathTable( file.read(self.path_table_size), 0 ) + else: + self.l_table2 = None + + if self.m_table2_pos: + file.seek( self.block_size * self.m_table2_pos ) + self.m_table2 = PathTable( file.read(self.path_table_size), 1 ) + else: + self.m_table2 = None + + def toTree(self, isofile): + ret = tree.Tree(isofile=isofile.name) + ret.root = self.root_record.toTreeNode(parent=None, isofile=isofile) + return ret + +class SecVolDesc(PriVolDesc): + def __init__(self, data): + super(SecVolDesc,self).__init__(data) + self.flags, self.escape_sequences = struct.unpack(secvoldesc_fmt, data) + +# return a single volume descriptor of the appropriate type +def readVolDesc(data): + desc = VolDesc(data) + if desc.standard_id != "CD001": + print "Unexpected standard_id " +desc.standard_id + return None + if desc.vol_desc_type == 1: + return PriVolDesc(data) + elif desc.vol_desc_type == 2: + return SecVolDesc(data) + elif desc.vol_desc_type == 3: + print "I don't know about partitions yet!" + return None + elif desc.vol_desc_type == 255: + return desc + else: + print "Unknown volume descriptor type %d" % (desc.vol_desc_type,) + return None + +def readVolDescSet(file): + ret = [ readVolDesc(file.read(2048)) ] + while ret[-1].vol_desc_type != 255: + ret.append( readVolDesc(file.read(2048)) ) + + for vol in ret: + if vol.vol_desc_type == 1 or vol.vol_desc_type == 2: + vol.readPathTables(file) + + return ret + +class DirRecord: + def __init__(self, data): + self.len_dr, \ + self.len_xa, \ + self.block, \ + self.len_data, \ + self.timestamp, \ + self.flags, \ + self.unit_size, \ + self.gap_size, \ + self.vol_seq_number, \ + self.len_fi = struct.unpack(dirrecord_fmt, data[:33]) + self.children = [] + + if self.len_dr > len(data): + raise AssertionError, "Error: not enough data to read in DirRecord()" + elif self.len_dr < 34: + raise AssertionError, "Error: directory record too short" + + fmt = str(self.len_fi) + "s" + if self.len_fi % 2 == 0: + fmt += "1x" + len_su = self.len_dr - (33 + self.len_fi + 1 - (self.len_fi % 2)) + fmt += str(len_su) + "s" + + if len(data) >= self.len_dr: + self.file_id, self.su = struct.unpack(fmt, data[33 : self.len_dr]) + else: + print "Error: couldn't read file_id: not enough data" + self.file_id = "BLANK" + self.su = "" + + # convert to integers + self.block = read_bb4(self.block) + self.len_data = read_bb4(self.len_data) + self.vol_seq_number = read_bb2(self.vol_seq_number) + + def toTreeNode(self, parent, isofile, path=""): + ret = tree.TreeNode(parent=parent, isofile=isofile.name) + if len(path) > 0: + path += "/" + path += self.file_id + ret.path = path + + if self.flags & 2: # we are a directory, recurse + isofile.seek( 2048 * self.block ) + data = isofile.read( self.len_data ) + pos = 0 + while pos < self.len_data: + try: + child = DirRecord( data[pos:] ) + pos += child.len_dr + if child.len_fi == 1 and (child.file_id == "\x00" or child.file_id == "\x01"): + continue + print "read child named " +child.file_id + self.children.append( child ) + ret.children.append( child.toTreeNode(ret, isofile, path) ) + except AssertionError: + print "Couldn't read child of directory %s, position is %d, len is %d" % \ + (path, pos, self.len_data) + raise + + return ret + +class PathTableRecord: + def __init__(self, data, readint2, readint4): + self.len_di, self.len_xa, self.block, self.parent_number = struct.unpack(pathrecord_fmt, data[:8]) + + if len(data) < self.len_di + 8: + raise AssertionError, "Error: not enough data to read path table record" + + fmt = str(self.len_di) + "s" + self.dir_id, = struct.unpack(fmt, data[8:8+self.len_di]) + + self.block = readint4(self.block) + self.parent_number = readint2(self.parent_number) + +class PathTable: + def __init__(self, data, m_type): + if m_type: + readint2 = read_msb2 + readint4 = read_msb4 + else: + readint2 = read_lsb2 + readint4 = read_lsb4 + pos = 0 + self.records = [] + while pos < len(data): + try: + self.records.append( PathTableRecord(data[pos:], readint2, readint4) ) + print "Read path record %d: dir_id %s, block %d, parent_number %d" %\ + (len(self.records), self.records[-1].dir_id, self.records[-1].block, self.records[-1].parent_number) + pos += self.records[-1].len_di + 8 + pos += pos % 2 + except AssertionError: + print "Last successfully read path table record had dir_id %s, block %d, parent_number %d" % \ + (self.records[-1].dir_id, self.records[-1].block, self.records[-1].parent_number) + print "Error was near offset %x" % (pos,) + raise + + def findRecord(self, dir_id, block, parent_number): + number=1 + for record in self.records: + if record.dir_id == dir_id and record.block == block and record.parent_number == parent_number: + return number, record + number += 1 + + return None, None + + # check this path table for consistency against the actual directory heirarchy + def crossCheckDirRecords(self, root, parent_number=1): + number, rec = self.findRecord(root.file_id, root.block, parent_number) + + if not rec: + print "Error: directory record parent_number %d, dir_id %s, block %d doesn't match a path table record" % \ + (parent_number, root.file_id, root.block) + parent = self.records[parent_number] + print "Parent has parent_number %d, dir_id %s, block %d" % (parent.parent_number, parent.dir_id, parent.block) + return 0 + + for child in root.children: + if child.flags & 2: + self.crossCheckDirRecords(child, number) + + +if len(sys.argv) != 2: + print "Please enter the name of the .iso file to open" + sys.exit(1) + +f = file(sys.argv[1]) +f.seek(2048 * 16) # system area +volumes = readVolDescSet(f) +vol = volumes[0] +t = vol.toTree(f) +vol.l_table.crossCheckDirRecords(vol.root_record) +vol.m_table.crossCheckDirRecords(vol.root_record) + +vol = volumes[1] +try: + t = vol.toTree(f) + vol.l_table.crossCheckDirRecords(vol.root_record) + vol.m_table.crossCheckDirRecords(vol.root_record) +except AttributeError: + pass diff --git a/trunk/test/master.c b/trunk/test/master.c new file mode 100644 index 00000000..fa6b8ffb --- /dev/null +++ b/trunk/test/master.c @@ -0,0 +1,126 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct burn_drive_info *drives; +static unsigned int n_drives; + +void burn_files(struct burn_drive *drive, struct burn_disc *disc) +{ + struct burn_write_opts *o; + enum burn_disc_status s; + + if (!burn_drive_grab(drive, 1)) { + printf("Unable to open the drive!\n"); + return; + } + while (burn_drive_get_status(drive, NULL)) + usleep(1000); + + while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY) + usleep(1000); + + if (s != BURN_DISC_BLANK) { + burn_drive_release(drive, 0); + printf("put a blank in the drive, corky\n"); + return; + } + o = burn_write_opts_new(drive); + burn_drive_set_speed(drive, 0, 2816); + burn_write_opts_set_perform_opc(o, 0); + burn_write_opts_set_write_type(o, BURN_WRITE_TAO, BURN_BLOCK_MODE1); + burn_write_opts_set_simulate(o, 1); +/* want failure on seggy while debugging :) */ + burn_write_opts_set_underrun_proof(o, 0); + burn_structure_print_disc(disc); + burn_disc_write(o, disc); + burn_write_opts_free(o); + + while (burn_drive_get_status(drive, NULL)) { + sleep(1); + } + printf("\n"); + burn_drive_release(drive, 0); + burn_disc_free(disc); +} + +void parse_args(int argc, char **argv, int *drive, struct burn_disc **disc) +{ + int i, tmode = BURN_AUDIO; + int help = 1; + struct burn_session *session; + struct burn_track *tr; + struct burn_source *src; + + *disc = burn_disc_create(); + session = burn_session_create(); + burn_disc_add_session(*disc, session, BURN_POS_END); + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--drive")) { + ++i; + if (i >= argc) + printf("--drive requires an argument\n"); + else + *drive = atoi(argv[i]); + } else if (!strcmp(argv[i], "--audio")) { + tmode = BURN_AUDIO; + } else if (!strcmp(argv[i], "--data")) { + tmode = BURN_MODE1; + } else if (!strcmp(argv[i], "--verbose")) { + ++i; + if (i >= argc) + printf("--verbose requires an argument\n"); + else + burn_set_verbosity(atoi(argv[i])); + } else if (!strcmp(argv[i], "--help")) { + help = 1; /* who cares */ + } else { + help = 0; + printf("%s is a track\n", argv[i]); + + tr = burn_track_create(); + src = burn_file_source_new(argv[i], NULL); + burn_track_set_source(tr, src); + burn_source_free(src); + burn_track_define_data(tr, 0, 0, 1, tmode); + burn_session_add_track(session, tr, BURN_POS_END); + } + } + if (help) { + printf("Usage: %s [--drive ] [--verbose ] files\n", + argv[0]); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char **argv) +{ + int drive = 0; + struct burn_disc *disc; + + parse_args(argc, argv, &drive, &disc); + printf("Initializing library..."); + if (burn_initialize()) + printf("Success\n"); + else { + printf("Failed\n"); + return 1; + } + + printf("Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + printf("Done\n"); + + burn_files(drives[drive].drive, disc); + burn_drive_info_free(drives); + burn_finish(); + return 0; +} diff --git a/trunk/test/poll.c b/trunk/test/poll.c new file mode 100644 index 00000000..cd22f158 --- /dev/null +++ b/trunk/test/poll.c @@ -0,0 +1,78 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "libburn/libburn.h" +#include "libburn/toc.h" +#include "libburn/mmc.h" + +#include +#include +#include +#include + +static struct burn_drive_info *drives; +static unsigned int n_drives; +int NEXT; + +static void catch_int () +{ + NEXT = 1; +} + +static void poll_drive(int d) +{ + enum burn_disc_status s; + + fprintf(stderr, "polling disc in %s - %s:\n", + drives[d].vendor, drives[d].product); + + if (!burn_drive_grab(drives[d].drive, 1)) { + fprintf(stderr, "Unable to open the drive!\n"); + return; + } + + while (burn_drive_get_status(drives[d].drive, NULL)) + usleep(1000); + + while ((s = burn_disc_get_status(drives[d].drive)) + == BURN_DISC_UNREADY) + usleep(1000); + + while (NEXT == 0) { + sleep(2); + mmc_get_event(drives[d].drive); + } + burn_drive_release(drives[d].drive, 0); +} + +int main() +{ + int i; + struct sigaction newact; + struct sigaction oldact; + fprintf(stderr, "Initializing library..."); + if (burn_initialize()) + fprintf(stderr, "Success\n"); + else { + printf("Failed\n"); + return 1; + } + + fprintf(stderr, "Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + fprintf(stderr, "Done\n"); + if (!drives) { + printf("No burner found\n"); + return 1; + } + + newact.sa_handler = catch_int; + sigaction(SIGINT, &newact, &oldact); + for (i = 0; i < n_drives; i++) { + NEXT=0; + poll_drive(i); + } + sigaction(SIGINT, &oldact, NULL); + burn_drive_info_free(drives); + burn_finish(); + return 0; +} diff --git a/trunk/test/rip.c b/trunk/test/rip.c new file mode 100644 index 00000000..0606cf49 --- /dev/null +++ b/trunk/test/rip.c @@ -0,0 +1,54 @@ +/* THIS IS NOT A PROPER EXAMPLE */ +#include +#include +#include +#include +#include +#include + +#include + +static struct burn_drive_info *drives; +static unsigned int n_drives; + +#warning this example is totally fried +int main() +{ +#if 0 + struct burn_drive *drive; + struct burn_read_opts o; + + burn_initialize(); + o.datafd = + open("/xp/burn/blah.data", O_CREAT | O_WRONLY, + S_IRUSR | S_IWUSR); + o.subfd = + open("/xp/burn/blah.sub", O_CREAT | O_WRONLY, + S_IRUSR | S_IWUSR); + o.raw = 1; + o.c2errors = 0; + o.subcodes_audio = 1; + o.subcodes_data = 1; + o.hardware_error_recovery = 1; + o.report_recovered_errors = 0; + o.transfer_damaged_blocks = 1; + o.hardware_error_retries = 1; + + printf("Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + printf("Done\n"); + drive = drives[0].drive; + burn_drive_set_speed(drive, 0, 0); + + if (!burn_drive_grab(drive, 1)) { + printf("Unable to open the drive!\n"); + return EXIT_FAILURE; + } + + while (burn_drive_get_status(drive, NULL)) + usleep(1000); + + burn_disc_read(drive, &o); +#endif + return EXIT_SUCCESS; +} diff --git a/trunk/test/structest.c b/trunk/test/structest.c new file mode 100644 index 00000000..defa274e --- /dev/null +++ b/trunk/test/structest.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + int i; + const char *path; + struct burn_track *track; + struct burn_disc *disc; + struct burn_session *session; + struct burn_source *src; + + disc = burn_disc_create(); + session = burn_session_create(); + burn_disc_add_session(disc, session, BURN_POS_END); + + /* Define a source for all of the tracks */ + path = strdup("/etc/hosts"); + src = burn_file_source_new(path, NULL); + + /* Add ten tracks to a session */ + for (i = 0; i < 10; i++) { + track = burn_track_create(); + burn_session_add_track(session, track, 0); + if (burn_track_set_source(track, src) != BURN_SOURCE_OK) { + printf("problem with the source\n"); + return 0; + } + } + + /* Add ten tracks to a session */ + for (i = 0; i < 10; i++) { + track = burn_track_create(); + burn_session_add_track(session, track, 0); + if (burn_track_set_source(track, src) != BURN_SOURCE_OK) { + printf("problem with the source\n"); + return 0; + } + } + + /* Delete a session */ + burn_session_remove_track(session, track); + + burn_structure_print_disc(disc); + return EXIT_SUCCESS; +} diff --git a/trunk/test/toc.c b/trunk/test/toc.c new file mode 100644 index 00000000..1ec7bcfd --- /dev/null +++ b/trunk/test/toc.c @@ -0,0 +1,103 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include + +static struct burn_drive_info *drives; +static unsigned int n_drives; + +static void show_tocs() +{ + struct burn_session **sessions; + struct burn_track **tracks; + struct burn_disc *disc; + int nses, ntracks, hidefirst; + unsigned int i, j, k; + struct burn_toc_entry e; + enum burn_disc_status s; + + for (i = 0; i < n_drives; ++i) { + fprintf(stderr, "TOC for disc in %s - %s:\n", + drives[i].vendor, drives[i].product); + + if (!burn_drive_grab(drives[i].drive, 1)) { + fprintf(stderr, "Unable to open the drive!\n"); + continue; + } + + while (burn_drive_get_status(drives[i].drive, NULL)) + usleep(1000); + + while ((s = burn_disc_get_status(drives[i].drive)) + == BURN_DISC_UNREADY) + usleep(1000); + if (s != BURN_DISC_FULL) { + burn_drive_release(drives[i].drive, 0); + fprintf(stderr, "No disc found!\n"); + continue; + } + + disc = burn_drive_get_disc(drives[i].drive); + + sessions = burn_disc_get_sessions(disc, &nses); + for (k = 0; k < nses; ++k) { + tracks = burn_session_get_tracks(sessions[k], + &ntracks); + hidefirst = burn_session_get_hidefirst(sessions[k]); + if (hidefirst) + fprintf(stderr, + "track: GAP (%2d) lba: %9d (%9d) %02d:%02d:%02d adr: X control: X mode: %d\n", + k + 1, 0, 0, 0, 2, 0, + burn_track_get_mode(tracks[0])); + + for (j = !!hidefirst; j < ntracks; ++j) { + burn_track_get_entry(tracks[j], &e); + fprintf(stderr, + "track: %3d (%2d) lba: %9d (%9d) %02d:%02d:%02d " + "adr: %d control: %d mode: %d\n", + e.point, e.session, + burn_msf_to_lba(e.pmin, e.psec, + e.pframe), + burn_msf_to_lba(e.pmin, e.psec, + e.pframe) * 4, + e.pmin, e.psec, e.pframe, e.adr, + e.control, + burn_track_get_mode(tracks[j])); + } + burn_session_get_leadout_entry(sessions[k], &e); + fprintf(stderr, + "track:lout (%2d) lba: %9d (%9d) %02d:%02d:%02d " + "adr: %d control: %d mode: %d\n", + k + 1, burn_msf_to_lba(e.pmin, e.psec, + e.pframe), + burn_msf_to_lba(e.pmin, e.psec, + e.pframe) * 4, e.pmin, + e.psec, e.pframe, e.adr, e.control, -1); + } + burn_disc_free(disc); + burn_drive_release(drives[i].drive, 0); + } +} + +int main() +{ + fprintf(stderr, "Initializing library..."); + if (burn_initialize()) + fprintf(stderr, "Success\n"); + else { + printf("Failed\n"); + return 1; + } + + fprintf(stderr, "Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + fprintf(stderr, "Done\n"); + + show_tocs(); + burn_drive_info_free(drives); + burn_finish(); + return 0; +} diff --git a/trunk/test/tree.py b/trunk/test/tree.py new file mode 100644 index 00000000..a7be3d80 --- /dev/null +++ b/trunk/test/tree.py @@ -0,0 +1,77 @@ +# a module to help with handling of filenames, directory trees, etc. + +import os +import os.path +import stat + +def pathsubtract(a, b): + index = a.find(b) + if index == -1: + return None + res = a[ (index + len(b)): ] + + if res.find("/") == 0: + res = res[1:] + return res + +# same as C strcmp() +def strcmp(a, b): + if a < b: + return -1 + if a > b: + return 1 + return 0 + +class TreeNode: + + # path is the location of the file/directory. It is either a full path or + # a path relative to $PWD + def __init__(self, parent, path=".", root=".", isofile=None): + if isofile: + self.root = os.path.abspath(isofile) + self.path = "" + else: + fullpath = os.path.abspath( path ) + fullroot = os.path.abspath( root ) + self.root = fullroot + self.path = pathsubtract( fullpath, fullroot ) + self.parent = parent + self.children = [] + + if self.path == None: + raise NameError, "Invalid paths %s and %s" % (fullpath, fullroot) + + # if this is a directory, add its children recursively + def addchildren(self): + if not stat.S_ISDIR( os.lstat(self.root + "/" + self.path).st_mode ): + return + + children = os.listdir( self.root + "/" + self.path ) + for child in children: + if self.path: + child = self.path + "/" + child + self.children.append( TreeNode(self, child, self.root) ) + for child in self.children: + child.addchildren() + + def printAll(self, spaces=0): + print " "*spaces + self.root + "/" + self.path + for child in self.children: + child.printAll(spaces + 2) + + def isValidISO1(self): + pass + +class Tree: + def __init__(self, root=None, isofile=None): + if isofile: + self.root = TreeNode(parent=None, isofile=isofile) + else: + self.root = TreeNode(parent=None, path=root, root=root) + self.root.addchildren() + + def isValidISO1(self): + return root.isValidISO1(); + +#t = Tree(root=".") +#t.root.printAll() diff --git a/trunk/test/tree.pyc b/trunk/test/tree.pyc new file mode 100644 index 0000000000000000000000000000000000000000..efc06d94f69107d01a8b45c266b315ee011aa41b GIT binary patch literal 3547 zcmb_f+ioL85Un2H;#_v4up0~QE<}q~vIN8e@c=@Cy#zrdWTY4o5ebbP&zMYhGUH57 zAV|&&-oN0P2R?((;5YaIIH!7SPn>-r#>(_mcUM(cSDmVweERps>PNr*^?V@3uY&he zJoXSnAo5b=$Ps2rcyi=PVB{6HDiU}~R3)g$5S&#`CnDb;)g-9tQhl~mr^;Tt0o~n! zhp}2bFYvb5ld(u-WNc(&J~k>2nRYx@j}i~>K*k{n@Y>Kytx2|B`#T~{yZC;jkZXwQk!0m7qlyweG7MV z(;yshi$B`=9FP4NLk?U51o{^OKLBV&ky;@RkAPJC)#Mcb4Tvf@kLq$zz*29vJ7Bcw zU20Yj{tjXcH$4UpjYOVA09}=EgI|3pa)c+z(=vww@~S3tnTkTYE@S+uWWFL}V7V?6 z;2kR%ovx<*s8RD*xsD_AH3fDH2dwj6PHgDO_OCb>nGrC%2(_-oAhv~JKl!O2N5PKb zX6K3B=_dhR?F};UiUe8|hn8_ot)g3bxz|4iD1kS+!G`fri7ZWXjl=LFj&++s;Y4>9 zL24P$Q9lb4Cp9>U;sCRG9yP(x?Vp8TWLcWI6r3Gc66V^>8a=9P{?vvW25GwKaI2i7 zUf++BDEIwGpzSsWF%5IyYqIGEj zKT(5-SZ9S}{1`_v0~N1fMoql@n&ofrhdsGcoIp)_U#lW0z_11Qw6Il_%BU&PngB3R z=YRzZCW=CHekg~R5A>iWG;XR!ShT?kZqEOZLt3N>-qpnDv{`K&9P)my`04rky}kW| zDSntavE}A0h9opzBs4w@oX!0+B(dsG0lIwhDkpN zrV)#YOiNfjY!6a<4>hZpS$x!eG(I3O zxzcrKlbnTqNpl$E$bQGNx!?QdgU_g%eih@26V3Jg1QpBowLkep@*-H zMWE{D)V;b{I&N;fQnxNz(=2(D=F^&bb!)S^zFt!sxjVq3{s>O(pF1F2|3v3x^TcN! zzV}L>dFYem?hZKeVJg4CV-GN(Fr^Q*|3MN}j7c@GT~kgjr^NX2n5 z9jdI66L?!=OpJBQJ)~Up>FsMRp{?jFv+(9qSInbB!Rr~Zkk|{LC;wv0nFs3DF`70O zbv`X+V|nPk_AOvnQ$%-p+a)bt(7n%ti@acz3dsN0 zy!fA?&qy)e&!%pvo4XB{x3m>sI7CNkcW1cKX7$>y7WWyd_qqllP8`w{M{IUCU`8H= gZK0$%eDRzmLqZ6A&NS5j4Vw5EX?o4IX5)?azrfqz2mk;8 literal 0 HcmV?d00001 diff --git a/trunk/version.h.in b/trunk/version.h.in new file mode 100644 index 00000000..13ada991 --- /dev/null +++ b/trunk/version.h.in @@ -0,0 +1,3 @@ +#define BURN_MAJOR_VERSION @BURN_MAJOR_VERSION@ +#define BURN_MINOR_VERSION @BURN_MINOR_VERSION@ +#define BURN_MICRO_VERSION @BURN_MICRO_VERSION@